0
\$\begingroup\$

Background: I'm trying to get a gLCD working with an Atmega328p. I got it to work quite easily, but there was one catch: the library that I found, used portB for the datapins. This was unacceptable, because I had an external oscillator hooked up, which already uses PB6 and PB7. I modified the library to use portD instead. Now everything worked flawlessly, but... RX and TX are on PD0 and PD1. So if I use this port, I can no longer use U(S)ART. That leaves portC, but sadly, this port only has 7 pins, instead of the usual 8 (and I need all 8 of them to drive the LCD). So, that forces me to use pins of two different ports.

This was the original code (snippet):

#define GLCD_IO_DATA_OUTPUT(data)       PORTD = (data)
#define GLCD_IO_DATA_DIR_INPUT()        DDRD = 0x00;    
#define GLCD_IO_DATA_DIR_OUTPUT()       DDRD = 0xFF;

The last two lines are used to switch the Data Direction, I'm guessing for some kind of feedback. The first line places a full byte in the dataregister for the lcd to use. I want to free up PD0 and PD1, and use PC0 and PC1 instead (because I figured this would be the easiest).

I tried this:

#define GLCD_IO_DATA_OUTPUT(data)       PORTD |= (data & 0b11111100);\
                                          PORTC |= (data & 0b00000011)
#define GLCD_IO_DATA_DIR_INPUT()        DDRD &= ~(0b11111100); DDRC &= ~(0b00000011)   
#define GLCD_IO_DATA_DIR_OUTPUT()       DDRD |= 0b11111100; DDRC |= 0b00000011

Sadly, this didn't work.

Because I have nothing else hooked up (for now), I guessed there is no reason to not use the full port, for now:

#define GLCD_IO_DATA_OUTPUT(data)       PORTD = (data); PORTC = (data)
#define GLCD_IO_DATA_DIR_INPUT()        DDRD = 0x00;   \
                                                DDRC = 0x00  
#define GLCD_IO_DATA_DIR_OUTPUT()       DDRD = 0xFF;   \
                                                DDRC = 0xFF

It places the full databyte in both ports, but only the relevant pins (PC0-1, PD2-PD7) are connected.

I guessed this HAD to work, but it didn't. Maybe there's something obvious I'm missing, or some hardware limitation that prevents this method from working?

So... What would be the best way to implement a change like this, and what did I do wrong in the code above?

\$\endgroup\$
  • \$\begingroup\$ I'm extremely doubtful that you're going to solve any of this in the defines area. I'm very certain that you'll have to modify some of the library. \$\endgroup\$ – Harry Svensson Jun 4 at 15:11
  • \$\begingroup\$ @HarrySvensson: I was afraid of that myself. But when I looked at the code, it looked like the fully byte was simply placed in that dataregister through a macro. Honestly, I saw no reason why that macro would not be able to be split up? Why would it not be possible to send the 2LSB to the pins of one port, and the 6MSB to the pins of another? \$\endgroup\$ – Opifex Jun 4 at 15:20
  • \$\begingroup\$ And... you want to place some in one register and some in another register, which means you're going to have to clear parts of two registers with AND and OR in some other parts. Do you have that kind of control through the defines area? - TimWescott appears to have proven so, to my great surprise. \$\endgroup\$ – Harry Svensson Jun 4 at 15:30
  • \$\begingroup\$ Also (not the solution to the problem, but), when you macro something like (data & 0b11111100), data should have parentheses on its own or else something like MACRO(value1 | value2) will not behave as expected. \$\endgroup\$ – RaphaelP Jun 4 at 15:35
  • \$\begingroup\$ @HarrySvensson: No, but I added it after TimWescott's suggestion. Problem persists. \$\endgroup\$ – Opifex Jun 4 at 15:41
1
\$\begingroup\$

This

#define GLCD_IO_DATA_OUTPUT(data)       PORTD |= (data & 0b11111100);\
                                          PORTC |= (data & 0b00000011)

is wrong because eventually, bits 2-7 of PORTD and 0 & 1 of PORTC will be one. You need to mask off the relevant bits to zero, and then OR things in:

#define GLCD_IO_DATA_OUTPUT(data) \
    PORTD = ((PORTD & 0x00000011) | (data & 0b11111100));\
    PORTC = ((PORTC & 0x11111100) | (data & 0b00000011))

Note that you may want to make a PORT_D_MASK and a PORT_C_MASK.

\$\endgroup\$
  • \$\begingroup\$ Am I understanding you correctly that you're saying the remaining bits (the ones not used by the LCD) might become 1 at some point, and that my code will reset those to 0? (at least that's how I interpret your code, but I think you mixed up D and C in your sentence) It's good point you make, and I will correct it in my code. But... I don't think this can influence the working of the LCD, can it? Only if something else would be connected to those pins would it cause problems. Or am I missing something? \$\endgroup\$ – Opifex Jun 4 at 15:25
  • 1
    \$\begingroup\$ Work it through by hand. Set data = 0b00000001, then 0b00000010, etc., up to 0b10000000. When you're done, see if PORTD isn't 0b111111xx and PORTC isn't 0bxxxxxx11. Is that desired behavior? Or do you want PORD bits 2-7 and PORTC bits 0-1 to always equal the bits in data? \$\endgroup\$ – TimWescott Jun 4 at 15:33
  • \$\begingroup\$ Oh! Yes! I understand what you mean now! The code I wrote only 'sets' those bits, but it does not 'unset' the ones that needs to be unset. I added it to my code, but the problem still persists. There must be some other bug hidden in there somewhere. (+1 already though, because it was a very good catch!) \$\endgroup\$ – Opifex Jun 4 at 15:38
  • \$\begingroup\$ I agree with @TimWescott. You could use the macros on your main loop, delay between the values, put some LEDs on the pins, check what comes out. \$\endgroup\$ – RaphaelP Jun 4 at 15:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.