Programming 12-pin 4-bit LCD

Hi there. I've been trying to get this LCD module up and running. It's a backlit, 12-pin model with 4-bit operation mode only.

Arduino Model: Arduino Uno WiFi Rev2 (Atmega 4809)
LCD Model: NHD-0108HZ-FSW-GBW
Controller: ST7066U
Link to Datasheet [PDF]
Link to Setup Images [Imgur.com]

I am only using the Arduino to prototype and test the LCD we are using for a larger embedded device later on. The LCD uses +3.3V power supply for logic and backlight LED, not +5V.

Using the Arduino LiquidCrystal.h library produced very weird and inconsistent results. Ultimately, I will be using this LCD in an embedded device, so felt it would be worthwhile to abandon the arduino library now since I will have to program it 'manually' later on the device. I figured this wouldn't be a straightforward endeavor, but at this point I'm getting nowhere in trying to diagnose why this isn't working on my own. This is my first attempt at programming an LCD in this manner, and interpreting the data sheet proved to be difficult, so I'm sure there are some gaps in my understanding that hopefully some of you may be able to point out.

My code is posted below. I tried to adapt it best to fit the example 4-bit initialization program provided in both linked datasheets. I used analogWrite() to make the DB pins 3.3V instead of 5, but not sure if this actually is viable. Regardless, running the program below produces a blank display.

const int volts = ((3.3/5.0)*255);
//const int volts = 200;
const int RS = 3;
const int EN = 2;
const int D4 = 5;
const int D5 = 6;
const int D6 = 9;
const int D7 = 10;
const int BACKLIGHT = 11;
int ct = 0;
char buffer[40];
char tempStr[10];
void setup()
{
  Serial.begin(115200);
  while(!Serial) {
    ; // Wait for serial to connect
  }
  Serial.print("Starting Program ~ \n");
  pinMode(BACKLIGHT, OUTPUT);
  pinMode(RS, OUTPUT);
  pinMode(EN, OUTPUT);
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  
  analogWrite(BACKLIGHT, volts);
  
  // Set all pins to low to reset/init device  
  putData(0x00);
  putData(0x00);
  delay(100);       // Wait > 40ms after power is supplied

  Serial.println("\nWake Up Sequence\n-------------------");
  Serial.flush();
  putData(0x30);    // Put wake up command on data port
  delay(30);
  Nybble();         // wake up #1
  delay(10);
  Nybble();         // wake up #2
  delay(10);      
  Nybble();         // wake up #3
  delay(10);

  Serial.println("\nSet 4-bit Operation\n----------------");
  Serial.flush();
  putData(0x20);    // Put 4bit operation mode
  Nybble();

  Serial.println("\nSet 4-bit 2 Rows\n----------------");
  Serial.flush();
  cmd(0x28);    // Set 4-bit 2 rows

  Serial.println("\nSet Cursor\n----------------");
  Serial.flush();
  cmd(0x10);    // Set cursor
  
  Serial.println("\nDisplay ON; Blinking Cursor\n----------------");
  Serial.flush();
  cmd(0x0F);    // Display ON; Blinking cursor
  
  Serial.println("\nSet Entry Mode\n----------------");
  Serial.flush();
  cmd(0x06);    // Entry mode set
  delay(2000);

  lcdWrite('h');

}

void loop() {
}



// Low Level LCD Commands
void putData (uint8_t c)
{
  uint8_t binArr[8];
  for (uint8_t i = 0; i < 8; i++) {
    binArr[7-i] = c & (1 << i);
  }

  for (uint8_t i = 0; i < 4; i++) {
    if (i == 0) {
      analogWrite(D7, ((binArr[i] == 0) ? 0 : volts));
    } else if (i == 1) {
      analogWrite(D6, ((binArr[i] == 0) ? 0 : volts));
    }
    else if (i == 2) {
      analogWrite(D5, ((binArr[i] == 0) ? 0 : volts));
    } else {
      analogWrite(D4, ((binArr[i] == 0) ? 0 : volts));
    }
  }
}

void Nybble() {
  digitalWrite(EN, HIGH);
  delay(2);                         // Enable pulse width >= 1200 ns
  digitalWrite(EN, LOW);       // Clock enable: falling edge -> Sends data in output port (D4 - D7)
}

void cmd (char c)
{
  digitalWrite(RS, LOW);  // RS=LOW: For sending instructions
  putData(c);             // Put first nybble on output port
  // RW = 0                // RW pin not used - always low because connected to ground
  Nybble();                // Send first nybble (4 bits) of data to LCD
  c = c << 4;              // Shift char by 4 bits so next nybble gets loaded into data pins (4 bit mode only)

  putData(c);             // Put last nybble on output port
  Nybble();            // Send last nybble (4 bits) of data to LCD
}

void lcdWrite (char c) {
  analogWrite(RS, volts);  // RS=HIGH: Send data
  putData(c);              // Put first nybble on output port
  // RW = 0                 // RW pin not used
  Nybble();                 // Send first nybble to LCD
  c = c << 4;               // Bit shift char by 4 bits so next nybble gets loaded into data pins
   
  putData(c);             // Put last nybble on output port
  Nybble();            // Send last nybble to LCD
}
1 Like

Ooops

Have you read the specification for the board: ARDUINO UNO WiFi REV2 β€” Arduino Official Store ?
Especially this bit:

The Arduino UNO WiFi Rev.2 has 14 digital input/output pinsβ€”5 can be used as PWM outputs

Do you understand that PWM is not a true analogue output, it is on and off for a variable time period? You have put 5V on the 3V3 inputs to your LCD. Use a potential divider to get 3V3 from 5V, maybe 5k for the top resistor and 10k for the bottom resistor. Be prepared to throw your LCD away and buy a new one as you might have damaged it.

2 Likes

I do understand that, but I also mistakenly thought that PWMing at a "3.3v" signal would suffice not thinking that it's still 5V peaks regardless of the signal I give it. I'll throw in some voltage dividers and post an update here with more information. I have a few LCDs to test with so if I have to throw this one away it's no big deal.

One thing I encourage people to do it try things, electronics and programming are hobbies where trying things out is easy and not generally expensive if you make a serious mistake, so I congratulate you on trying to use analogue write to get 3V3, but I'm afraid it was doomed to failure.

[Edit: not a failure if you learned from it!]

I would presume that the ST7066U controller is essentially equivalent to the HD44780 and as such, perfectly happy to operate on 5 V so should not be damaged at all except by excess "phantom powering".

Lacking adequate documentation on this particular module and specifically how the contrast voltage is arranged, it is impossible to determine just what the actual problem is. :roll_eyes:

I confirmed that the issue was as @PerryBebbington suggested - that I was frying the pins of the LCD with the Arduino's outputs.

I added 6 voltage dividers (R1=6.1kOhm, R2 = 10.0kOhm) for all communication pins, which stepped down signal outputs from +5V to +3.19V. After verifying the output signals, I connected the LCD module back to the breadboard and it worked instantly.

Thank you for helping me!

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