LCD troubleshooting (16 x 2 parallel)

Hello everyone,

I am having issues getting a blue pill to drive an "industry standard HD44780 equivalent LCD controller" that I got on AliExpress. I bought two of them, and have been able to drive them using the I2C backpack that came with them. The problem is that the serial has too great a latency (I am trying to update it while driving a stepper motor and it is causing the motor to miss steps). The blue pill has a genuine STM32F103.

I am fairly new to programming using C and Arduino in general, however not new at all to soldering, following schematics, etc.

When I connect the I2C version up to the oscilloscope, I see signals on every data pin, and on the register select and enable pins (and of course it prints "Hello World"). When I do the same thing for the parallel version, only data pins 1 and 6 and the register select and enable pins have signals, the rest of the pins are brought high for some reason, and nothing prints. I am not attempting to drive both displays the same time, there are no shorts, the contrast is set correctly and functioning, I promise it is wired correctly as the code indicates. I have spent hours verifying this, and reading forums as to how to correct it and have come up empty. Any assistance you can provide getting the "hello world" to print would be awesome.

#include <LiquidCrystal.h>

// rs = register select, rw = read/write, en = enable writing to shift registers
const int rs = 26, en = 21, d0 = 19, d1 = 25, d2 = 28, d3 = 27, d4 = 29, d5 = 38, d6 = 22, d7 = 18;
LiquidCrystal lcd(rs, en, d0, d1, d2, d3,  d4, d5, d6, d7);

void setup() 
{
  lcd.begin(16, 2);
}

void loop()
{
  lcd.clear();    
  lcd.setCursor(0,0);
  lcd.print("Hello World");
  delay(200);
}
#include <Wire.h>
#include <hd44780.h>                       // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip 
                    // 0x27 = default address of PCF8574 module
void setup() 
{
  lcd.begin(16, 2);
}

void loop()
{
  lcd.clear();    
  lcd.setCursor(0,0);
  lcd.print("Hello World");
  delay(200);
}

Most of us will drive a HD44780 LCD in 4-bit mode (D0, D1, D2 & D3 are not connected). That saves wiring and saves pins while achieving exactly the same results on the display.

The liquidcrystal library supports four bit mode.

const int rs = 26, en = 21, d4 = 29, d5 = 38, d6 = 22, d7 = 18;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

will do that in your code.

Thanks for the response, it was worth a try, however unfortunately I am still not getting signal outputs on all the remaining pins (d4-d7). I am starting to wonder if maybe there is something wrong with the Arduino itself? Again I have checked and rechecked everything with the multimeter and am confident that there are no shorts, and that I am specifying the correct pins. I have a spare STM32F3 that I may use to replace the one that's on the blue pill if I can't figure this out. It just doesn't make sense to me that some of the pins would be pulled high and never receive a signal, and the signal that is on the enable pin doesn't match what the I2C backpack outputs, not even close, I wouldn't expect it to be all that different. I can post oscilloscope screen captures or any other information if that really helps anyone figure this out.

I figured it out. I thought that the pin numbers and the pin names on the blue pill pinout diagrams were synonymous, turns out they're not I guess? Anyway if I replace the pin numbers with the pin designations as shown below, everything works as it should. Thanks again for your response Dougie, you got me thinking.

#include <LiquidCrystal.h>

// rs = register select, rw = read/write, en = enable writing to shift registers
const int rs = 26, en = 21, d0 = 19, d1 = 25, d2 = 28, d3 = 27, d4 = 29, d5 = 38, d6 = 22, d7 = 18;
// LiquidCrystal lcd(rs, en, d0, d1, d2, d3,  d4, d5, d6, d7);
LiquidCrystal lcd(PB13, PB10, PB1, PB12, PB15, PB14, PA8, PA15, PB11, PB0);

void setup() 
{
  lcd.begin(16, 2);
}

void loop()
{
  lcd.clear();    
  lcd.setCursor(0,0);
  lcd.print("Hello World");
  delay(200);
}
1 Like

But there really is no benefit to using the 8-bit mode. You just waste 4 I/O pins.

1 Like

Its not any faster?

At this point I am basically finished with the project so if I end up needing extra pins I will remove d0-d3. Probably will not use all 8 wires in the future I was just unaware of the latency difference and figured I would rather not find out later that I needed the other 4 pins so I just bit the bullet and wired them all.

Trivially.

Sending the command/ data to the HD44780 is easy - and fast.

Then you have to wait for it to implement the corresponding function. You can look at (read) the "busy flag" but that requires an addition pin and a lot more work, so even Bill Perry's "HD44780" library simply waits the nominal time.

All yours, Bill! :grin:

Thanks for the info I greatly appreciate it!

The real overhead in trying to use BUSY status checking, is flipping all the i/o pins from output to input to read the BUSY bit, then flipping them back. This is particularly true on the AVR since the Arduino.cc core library code is not written as well as it could be. On that platform it takes WAY longer to flip the i/o pins (even in 4 data pin mode) back and forth than the amount of time to run all the instructions other than clear and home.

While other cores are better, there is still quite a bit of overhead to flip i/o pins around.
The hd44780 library does wait for instructions to complete, but only if necessary. This allows the Arduino processor to run in parallel with the LCD processing the LCD instruction. In many cases the library will not have to wait for instructions to complete.

8 bit mode with the LiquidCrystal library might run a tiny bit faster than 4 bit mode, but if really you want more speed, switch to the hd44780 library.

The switch is easy, just use the hd44780_pinIO i/o class.
Include the header, and change the lcd object declaration.
That should be all you need to change and the code will then update the LCD faster and free up more CPU cycles.
Here is an example of the update time for writing a byte to the LCD using a 16Mhz AVR - not the same processor but you can get an idea of the potential difference:

LiquidCrystal     LCDispeed    284us
hd44780_pinIO     LCDispeed     91us

--- bill

Bill,

Sorry I took so long to respond. I wanted to research the topics you discussed in order to understand them better. If I'm understanding you correctly, at least in layman's terms, you're saying that the LiquidCrystal library checks the LCD to see if it is busy before sending data, and in doing that, it must flip the I/O pins from write to read, which takes a significant amount of time. However your program only does this if necessary, making it significantly faster even in 4 bit mode versus the LiquidCrystal in a 8 bit mode? Anyway thanks for taking the time to explain this all to me, I don't know why but I never considered that your program also works with LCDs in parallel mode (I did attempt to use it in serial and while it was an improvement the serial method still wasn't fast enough). The switch from I2C serial to parallel seems to have fixed the issue I was having with my stepper motor, however I will still update the code to run your library since it is clearly superior, and good practice since this probably won't be my last Arduino project with an LCD.

Thanks again

-Garrett

No. Neither the LiquidCrystal library nor the hd44780 library use the BUSY status.
LiquidCrystal does a blind delay for each LCD instruction it sends to the LCD for the full time of the instruction.
The hd44780 library is smarter about how it handles timing delays.
This allows the Arduino processor to run in parallel with the LCD processing the LCD instruction. The hd44780 library will only do a delay if necessary and for only as long as needed, and even then, it will only happen if the Arduino processor attempts to do back to back instructions faster than the LCD can process them.
In many cases, there is little to no delay needed.

The comment about using BUSY status was a short explanation as to why using the BUSY status will end up being slower than using a delay, when using the slow Arduino i/o API functions of pinMode(), digitalRead(), and digitalWrite(), particularly on AVR platforms.
I have seen one LCD library from adafruit that cheated by not properly setting all 8 pins to input mode when polling the BUSY status. While this can potentially dramatically improve performance, it can also potentialy damage the processor since there is bus collision happening on either 3 or 7 pins, (depending on 4 bit or 8 bit mode) when the BUSY bit is being read.
Also, on an AVR, the Arduino digital pin i/o API functions are so slow that the Adafruit library will still likely be slower than using a delay.

Note: much of the overhead and slowness in the AVR i/o API functions can be eliminated, and in fact the Arduino.cc dev team turned down using a better implementation of the code many years ago that was 50 times faster.
Today that faster code is in the Teensy core so if you use a Teensy AVR based product your digital pin i/o is 50x faster than when using a board like an UNO or Mega which uses the Arduino.cc bundled AVR core library.

--- bill

That makes a lot of sense. I never would have learned that on my own. Thanks again for your help, and your library.

-Garrett

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.