I recently got an Arduino Uno and a HItachi HD44780 16x2 LCD, after playing around with the LiquidCrystal library for a bit, I decided it was too easy and went about writing code from scratch so I really understood how it worked. After ironing out a few bugs I got it working fine in 8 and 4 bit modes, but came across a strange bug that I couldn't explain. Here's some simplified code:
void lcd_output (int bits) {
if (bits == 4) {
output_4bit();
}
else {
output_8bit();
}
}
void output_4bit() {
//code to set pin outputs
enable();
}
void output_8bit() {
//code to set pin outputs
enable();
}
void enable() {
digitalWrite(pin_enable, LOW);
delayMicroseconds(1);
digitalWrite(pin_enable, HIGH);
delayMicroseconds(1);
digitalWrite(pin_enable, LOW);
delayMicroseconds(1000);
}
If I call lcd_output(4), the LCD fails to initialise, but (and here's the weird bit) commenting out the line output_8bit() in the else statement fixes it! I've verified that at no point is output_8bit() called, and all the code works correctly up until the enable() is called.
I've actually found a number of fixes, such as defining pin_enable as volatile, which I think points directly at a compiling problem. It seems the compiler is trying to optimise the code, but fails to do it correctly when the function is called in more than one place. Unfortunately I don't have an oscilloscope to measure the actual output, and any attempt to measure timings in the code actually fixes the problem too!
Anyone have any more clues what's going on? Its not actually a problem for me, but it suggests there's a bug in delayMicroseconds(), digitalWrite() or even the compiler that might need fixing.
You do the test with an lcd display. The Arduino library LiquidCrystal is a great combined effort, it is not something you can write yourself. To initialize an lcd display without knowing in what state it is in, requires some magic tricks. An lcd display is also cricital with timing.
The compiler is able to optimize things (a lot). The keywords 'volatile' is sometimes needed, but only in rare cases.
I do not know how much the compiler optimizes, but I think it can switch between inline code and function calls whatever the compiler think is best. That might be going on here.
I think the digitalWrite() requires 3us. When you delay for 1us, the combined delay is 4us.
When you would measure the output pin with a oscilloscope, you will find that the Arduino library has good quality libraries. They have been around for some time, and have been improved and optimized. Nevertheless, sometimes a bug is found, but not this time.
delayMicroseconds(1);
too short, needs to be >= 3 or 4 uS.
"Caveats and Known Issues
This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times."
I've tried with a range of delays, it works with the volatile variable all the way up to ~30ms, so it actually doesn't seem too fussy with the timing of that input, but still the bug persists without volatile. I don't have any plans to actually use this code for anything, it's literally just a learning exercise.
Interestingly, if I hook up a multimeter to the pin, I see a DC voltage of 1.23v with the volatile variable and 10ms delays (total 40ms delays per loop, so that's a 25% duty = ~1.25v average would be expected), this changes to 1.53v if I remove the volatile keyword. So something's definitely changing with the timing/voltage, I might have to find an oscilloscope to see what's going on...
I still think that it is normal compiler behaviour.
Have a look at the libraries to see what they do. Sometimes assembly code is used, sometimes different code for different chips and the keyword 'volatile' is used a lot.
If possible, could you make two test sketches which shows the difference with the blinking led at pin 13 ?