# Strange interaction between LED array and LCD controller

#### odougs

Sep 12, 2014
7
Hello everyone,

I am attempting to drive an LCM-1602C LCD with an AVR ATTiny48. I am using my own code to interface with the LCD's HD44780 controller. The attached picture shows a working setup, in which the LCD will initialize and display

R1: "hello, world!"
R2: "HELLO, WORLD!"

However, if I move the LED array or the 10k resistor attached to it's cathodes, some of the commands fail. For instance, if I move the LED array one space to the right, I get

R1: "ello,worl!"
R2: "EO,WOR!"

If I remove the 10K resistor attached to the LED array's cathodes, I get something like (it sometimes differs by a few characters)

R1: "hello,world!ELLO"
R2: (enabled, but blank)

If I remove the LED array altogether, I get

R1: "eowo!EOWO!"
R2: (uninitialized)

I am struggling to figure out why the LED array is affecting the LCD controller at all. From my limited circuits knowledge (little more than Ohm's Law and Kirchoff's Laws), I expect the only significant result of including the LED array to be an additional ~10mA draw on the breadboard power supply per active LED. Removing the 10k on the LED array's cathodes should be equivalent to removing the LED array itself, as the result in both cases is an open circuit that cannot draw any current. However, the LCD output described above makes it clear that removing the 10k is not equivalent to removing the LED array.

Here are a few facts that may be of interest:
• The LCD works properly when driven by an Arduino, without the LED array present.
• The breadboard power supply is the following: 9v, 650mA wallwart -> 100uF cap -> 5v regulator -> 10uF cap.
• I'm using an AVR ATtiny48 @ 1Mhz, with default fuse settings. I also tested this with an ATmega48 using the same settings, and got the same results. All of the ports being used have a 10k pulldown resistor.
• Changing the value of the resistor connected to the LED array's cathode prevents some characters from showing up. At 220Ω, 'D' is only present ~50% of the time. At 1MΩ, 'H' and 'D' never show up.
In case you were wondering, the purpose of the LED array was to confirm that the proper bit-strings are being written to the LCD controller's data and control pins, by compiling my code with a macro set to pause execution for a few seconds during each write operation. The behavior described above is identical with the macro disabled. The bits being sent to the LCD controller are exactly what I expect them to be.

I have a multimeter, but no oscilloscope. I will glady post my code or perform additional tests upon request. Thank you very much for reading this, and please let me know if you have any ideas about what might be causing this unexpected behavior, or suggestions as to what I should try next.

#### Attachments

• circuit.jpg
125.5 KB · Views: 115

#### Arouse1973

Dec 18, 2013
5,178
Can you supply a circuit diagram please?

#### odougs

Sep 12, 2014
7
I will make a circuit diagram and post it as soon as possible.

Dec 18, 2013
5,178
Ok thanks

#### odougs

Sep 12, 2014
7
Thanks for your patience. I simplified the wiring between the LED array's cathodes and R3, but I'm 99% sure this is logically equivalent. My apologies for any confusion or poor practices evident in the diagram, this is my first time drawing such a complicated diagram. Criticism is always appreciated.

Thanks again for taking the time to help with my circuit!

EDIT: Removed a short from the power supply in the diagram!

#### Attachments

• lcd.png
136.6 KB · Views: 140
Last edited:

#### (*steve*)

##### ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,506
I would remove all the LEDs and the pulldown resistors (neither of which should be required to drive the LCD.

Then interface the LEDs to different pins.

Is there any reason you want these LEDs? And why have you added the pulldown resistors?

At the moment the question you're asking is asking to "My car drives straight until I attach a bungee cord from the accelerator to the steering wheel. Now I wonder why whenever I accelerate the car drifts to the right".

There is an additional load that may be affecting voltage levels and even introducing some crosstalk between the pins due to the capacitance of the LEDs.

Now, I would agree that the load should be small. However, given that you've written the code yourself, you may have (for example) driven the interface too fast. While this may work in some cases, it may be marginal and what you're seeing is the effects of the conditions varying between marginally working and marginally not working.

I recommend you get it going without any LEDs at all first (and perhaps consider a 4 wire rather than 8 wire interface to free up more pins for separately driving LEDs).

#### Arouse1973

Dec 18, 2013
5,178
I agree with Steve, remove the LEDs from the LCD data lines. The data line should only go to the micro.

#### odougs

Sep 12, 2014
7
I think you guys misunderstood my post. The problem is that it works with the LEDs, and stops working when I remove the LEDs. As I described in the original post, I get different failure modes for each of the following: moving the LEDs, changing the value of their resistor (R3), removing R3, and completely removing the LEDs. The picture attached to my first post and the diagram attached to post #5 show working configurations!

The LEDs were only in place for debugging firmware. I would like to remove them now, but first I need to figure out why things break when I remove them, and how to fix that issue.

#### Arouse1973

Dec 18, 2013
5,178
Well thats very strange I dont understand why adding extra components would make it work unless there was a timing issue in your code which is why it works with the LEDs.

#### odougs

Sep 12, 2014
7
Indeed, it is very strange. And a timing issue seems unlikely: the AVR is operating at 1Mhz, and I delay a few extra μs after setting the data pins during a write, so I'm well above the minimum times specified in the data sheet (p.15).

The only time parameter I'm at all unsure of is the max Enable rise/fall time of 25ns (p.15). Unfortunately I don't have a scope to check this out. But I think a greater overall load or capacitance should increase the rise/fall time on the LCD's pins... right?

Here are the relevant bits of code, if you're interested. Note that I've run this many, many times with PIN_DELAY_IF_DEBUG undefined, as well as defined to delay for various times between [500ms, 2000ms], all with identical behavior to what I describe in the first post.

Code:
/*  Define PIN_DELAY_IF_DEBUG *elsewhere* as _delay_ms(x) if we want to use LEDs to
observe bits sent to LCD controller. Leave this blank so there is no delay otherwise.
See libavr's utils/delay.h for info on _delay_ms( ).
*/
#ifndef PIN_DELAY_IF_DEBUG
# define PIN_DELAY_IF_DEBUG
#endif

#define LCD_RS_ON  (LCD_CTRL_PORT |= (1 << LCD_RS_PIN))     ;PIN_DELAY_IF_DEBUG
#define LCD_RS_OFF (LCD_CTRL_PORT &= ~(1 << LCD_RS_PIN))   ;PIN_DELAY_IF_DEBUG
#define LCD_RW_ON  (LCD_CTRL_PORT |= (1 << LCD_RW_PIN))     ;PIN_DELAY_IF_DEBUG
#define LCD_RW_OFF (LCD_CTRL_PORT &= ~(1 << LCD_RW_PIN))   ;PIN_DELAY_IF_DEBUG
#define LCD_EN_ON  (LCD_CTRL_PORT |= (1 << LCD_EN_PIN))     ;PIN_DELAY_IF_DEBUG
#define LCD_EN_OFF (LCD_CTRL_PORT &= ~(1 << LCD_EN_PIN))   ;PIN_DELAY_IF_DEBUG

//time to hold data pins to ensure proper xmit
#define LCD_DELAY_MICRO _delay_us(2.0);

....

/* lcd_xmit - write commands and data to LCD controller
ctrl - The LSB of 'ctrl' is the  Register Select (RS) bit:
0 for the instruction register, 1 for the data register.
data - 8-bit interface for commands and data; normal bit order
(DB0 gets (data & 0x01), DB7 gets (data & 0x80), etc)
*/
inline void lcd_xmit (uint8_t ctrl, uint8_t data)
{
if (ctrl & 1)
LCD_RS_ON;

LCD_EN_ON;
LCD_DATA_PORT = data;
LCD_DELAY_MICRO;

//if defined, allows us to use LEDs to see data
PIN_DELAY_IF_DEBUG;

LCD_EN_OFF;
LCD_RS_OFF;
LCD_DATA_PORT = 0;
}

#### (*steve*)

##### ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,506
Without the LEDs, but with the "PIN_DELAY_IF_DEBUG" defined as some short delay, does the interface work?

And as a separate issue...

We know (and obviously expect) that the C statement X = X | Y (for example) will end up with X containing the intended value, but it is not always true that it will be assigned exactly once. It is possible that the compiler may decide to use it for holding intermediate results. Without looking at the actual instructions emitted, it can be hard to tell.

As an example, the code might decide to implement

LCD_CTRL_PORT |= (1 << LCD_RS_PIN)​

as

LOOP:
Compare ACCB with 0
Jump if LE to OUT
Shift Left LCD_CTRL_PORT by 1
OUT:
OR AccA with LCD_CTRL_PORT
Store AccA to LCD_CTRL_PORT
Now that is a contrived example using something that is unlikely to be anything like the assembly language :-D. However, if the architecture is such that memory (and port) access is achievable in the same number of clock cycles as register access (which is almost guaranteed) then it may be no less optimal to hold intermediate results in the port. In this case, what you get is a 1 zipping up the port, then the final value being set.

I can't guarantee that this is the issue, but it is a well known issue that affects things where access to the results is asynchronous with the execution of lines of code. In your case the output is essentially samples continuously, not at the end of each line of code. In other cases (multi-threading, for example) other parts of the program may access the memory while it contains an intermediate result. Interrupt routines will be subject to this effect in microcontrollers. Assuming you're not going to use semaphores, the easiest solution is to ensure that the final destination is not available as a temporary location.

One cure is to code it as:

temp = LCD_CTRL_PORT;
temp |= (1 << LCD_RS_PIN);
LCD_CTRL_PORT = temp;​

This will ensure exactly a single write to the data register.

Beware that if the last statement here is not atomic (i.e. the microcontroller cannot do the entire write in a single instruction) you can still hve the problem. In your case, you're talking about a single port, and writes to a port will be atomic.

If this is what is going on, the additional capacitance of the LEDs may be sufficient to attenuate the small pulses generated as the output changes from one value, to an intermediate (and unexpected) value, and then very quickly back to the value you want.

#### odougs

Sep 12, 2014
7
Steve,

No luck with using small values for PIN_DELAY_IF_DEBUG. I tried using delays of 2^N μs for n in [0,9] (arbitrary, but simple). I'm not sure if that's actually a good sample range. What sort of issue are you thinking about, and how sensitive might it be to timing? I will do more tests if needed.

By the way, I am really glad you mentioned the problem of intermediate assignment! So far in my short experience with microcontrollers I've been naively ignoring the fact that compilers can emit code that is correct but very different from what we expect; obviously I need to start taking that more seriously.

I don't think that turned out to be an issue in this case:

Code:
0000005c <lcd_xmit>:
5c:    80 fd           sbrc    r24, 0
5e:    5d 9a           sbi    0x0b, 5
60:    5f 9a           sbi    0x0b, 7
62:    65 b9           out    0x05, r22
64:    00 c0           rjmp    .+0
66:    5f 98           cbi    0x0b, 7
68:    5d 98           cbi    0x0b, 5
6a:    15 b8           out    0x05, r1
6c:    08 95           ret

...but thanks nonetheless; I learned something important!

Last edited:

#### (*steve*)

##### ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,506
This is a perfect case for having a 16 channel digital analyser!

I have an 8 channel one sitting right here, but I'm a bit far away for you to come and borrow it (8 channels would be enough if you monitor all the control signals and just some of the data signals.

There's often no substitute for seeing what it;s doing.

Normally I'd suggest some temporary LEDs, and slowing things right down, but in your case that's changing the problem in your case. I'd kinda fear that analyzer might do the same, but it will place a much lower load on the pins.

#### odougs

Sep 12, 2014
7
Well, I'll just have to be patient and work on something else for a while. I can't afford a scope or logic analyzer, but I'll have access to a scope soon enough - I'm in my first semester studying ECE at university, and will be learning to use the scope in my lab two weeks from now. Don't think we have logic analyzers in our 'Basic Electronics Lab', but maybe if I ask nicely the TA will help me out with that... I'll post here if we manage to figure it out.

Thank you both for working with me on this!

#### (*steve*)

##### ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,506
I was thinking about something like this.

Replies
10
Views
98
Replies
4
Views
431
Replies
27
Views
2K
Replies
5
Views
384
Replies
4
Views
855