Configuring HD44780 LCD Display on STM32

Thread Starter

Xenon02

Joined Feb 24, 2021
495
Hello !

I've been working on with a librabry I've found on website.
It workes sometimes and sometimes no due to some delay issues. Although I have a question to ask here.

- Does HD44780 when powered up works in 4 bit more or 8 bit mode ? Couldn't find in datasheet or perhaps I am blind :) : https://www.sparkfun.com/datasheets/LCD/HD44780.pdf

- What if in initialization I send 0011 and after that 0000 ? I know I have to send 0011 but I sent into the same pin later 0000. In Init I've sent 0011 and 0000, the librabry I took it didn't mention anything about it.

- How it logically works (theoretically), that sending the display mode 0011 which switches to 8 bit mode still can read in 4 bit mode while initialization.

- Last question, how does it also work logically that reading 3 times 0011 it ignores the rest data **** but repeating the same command after the third time : 0011, we must add the rest so it reads it like 0011 0000. And then it works as 8 bit mode.

This is the library :

Init :


C:
void lcd_init (void)
{
    // 4 bit initialisation
    HAL_Delay(50);  // wait for >40ms
    lcd_send_cmd (0x30);
    HAL_Delay(5);  // wait for >4.1ms
    lcd_send_cmd (0x30);
    HAL_Delay(1);  // wait for >100us
    lcd_send_cmd (0x30);
    HAL_Delay(10);
    lcd_send_cmd (0x20);  // 4bit mode
    HAL_Delay(10);

  // dislay initialisation
    lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit mode), N = 1 (2 line display) F = 0 (5x8 characters)
    HAL_Delay(1);
    lcd_send_cmd (0x08); //Display on/off control --> D=0,C=0, B=0  ---> display off
    HAL_Delay(1);
    lcd_send_cmd (0x01);  // clear display
    HAL_Delay(1);
    HAL_Delay(1);
    lcd_send_cmd (0x06); //Entry mode set --> I/D = 1 (increment cursor) & S = 0 (no shift)
    HAL_Delay(1);
    lcd_send_cmd (0x0C); //Display on/off control --> D = 1, C and B = 0. (Cursor and blink, last two bits)
}
Send command :

C:
void lcd_send_cmd (char cmd)
{
    char datatosend;

    /* send upper nibble first */
    datatosend = ((cmd>>4)&0x0f);
    send_to_lcd(datatosend,0);  // RS must be 0 while sending command

    /* send Lower Nibble */
    datatosend = ((cmd)&0x0f);
    send_to_lcd(datatosend, 0);
}
Send to LCD :


C:
void send_to_lcd (char data, int rs)
{
    HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, rs);  // rs = 1 for data, rs=0 for command

    /* write the data to the respective pin */
    HAL_GPIO_WritePin(D7_GPIO_Port, D7_Pin, ((data>>3)&0x01));
    HAL_GPIO_WritePin(D6_GPIO_Port, D6_Pin, ((data>>2)&0x01));
    HAL_GPIO_WritePin(D5_GPIO_Port, D5_Pin, ((data>>1)&0x01));
    HAL_GPIO_WritePin(D4_GPIO_Port, D4_Pin, ((data>>0)&0x01));

    /* Toggle EN PIN to send the data
     * if the HCLK > 100 MHz, use the  20 us delay
     * if the LCD still doesn't work, increase the delay to 50, 80 or 100..
     */
    HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, 1);
    delay (20);
    HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, 0);
    delay (20);
}
 

JohnInTX

Joined Jun 26, 2012
4,787
Line 19 in init: increase the delay after clear screen. It takes about 3ms (80 characters * 37uS nominal processing time for one character) to clear the entire DD RAM. It is not in the posted version of the spec but clearing takes a long time compared to normal writing.

It powers up in the 8 bit mode - see RESET FUNCTION on page 23 of the datasheet but it’s a good idea to configure the display by instructions on every system reset.
 
Last edited:

Thread Starter

Xenon02

Joined Feb 24, 2021
495
It powers up in the 8 bit mode - see RESET FUNCTION on page 23 of the datasheet but it’s a good idea to configure the display by instructions on every system reset.
- Then if it starts up in 8 bit mode why can I use only D4-D7 to set it to 4 bit mode and to 8 bit mode ? It also states that if the power supply conditions aren't met the Reset function might no initialize properly and to perform Initialization by Instruction, but it doesn't say in which mode it operates in 4 bit or in 8 bit.

1699791581474.png

- In my library to set 3 time 8 bit mode I sent 0x30. In library it says it sends first 0011 then 0000 to D4-D7 as if it works in 4 bit mode when start up. What happens to 0000 ? If it's ignored why ? What is the mechanism. Because it uses bit enable and write so 0000 should be sent properly and read by LCD during initialization. It also exceeds the busy state because there is HAL_Delay(20). So what happens to the data 0000 that wasn't expected in initial

1699790396090.png
- If I have only pins D4-D7 connected and D0-D3 are not connected and it works in 8 bit mode. How it know what is in D0-D3 if I write only in D4-D7 ?
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Then if it starts up in 8 bit mode why can I use only D4-D7 to set it to 4 bit mode and to 8 bit mode ?
Because the lower 4 bits of the FUNCTION SET instruction can be ignored during initialization by instruction.
There are three scenarios:
1) If the display is in the 8 bit mode, each of the 3 FUNCTION SET = 30h instructions just sets 8 bit mode again. The display uses 8 bit mode for the rest of the initialization and use.
2) If the display is in the 4 bit mode, the first two FUNCTION SET = 30h write an 8 bit value of 33h, the lower bits can be ignored so far. The display will be in the 8 bit mode and the next FUNCTION SET = 30h is redundant.
3) The display is in an unknown state because of power up problems OR the display WAS in 4 bit mode previously but between the two write cycles to D7-D4 and D3-D0 the processor was reset, leaving only half of the 4 bit write done. In that case the first FUNCTION SET = 30h completes the previous 4 bit write and the next two FUNCTION SET = 30h write the full 8 bits. The display is now guaranteed to be in the 8 bit mode.

Once the display is known to be in the 8 bit mode, initialization can continue. If the 4 bit mode is desired, one FUNCTION SET = 20h is written to change the interface from 8 bits to 4 bits. Note that this instruction is received as 8 bits with D3-D0 unknown (they are not hooked up).

After all of that, FUNCTION SET is written once more to set the number of lines and the font one 8 bit cycle or two 4 bits cycles depending on the interface you just set. The rest of initialization and operation goes from there.

It also states that if the power supply conditions aren't met the Reset function might no initialize properly and to perform Initialization by Instruction,...
That's why Initialization by Instruction should always be performed when initializing the system after RESET.

Hope that clears things up a little.

EDIT: One final note. The 44780 controller has been cloned by many manufacturers over the years. The timings can vary with some of these so it is best to build in some extra time for execution. Also note that the timings are based on a nominal 270KHz oscillator which can vary a bit.
 

Thread Starter

Xenon02

Joined Feb 24, 2021
495
Because the lower 4 bits of the FUNCTION SET instruction can be ignored during initialization by instruction.
There are three scenarios:
It clears up I think some part of the things. So in initialization mode Function set is differently interpreted (only 4 bits) after initialization the Function set is read fully (all 8 bits).

I would like to ask two thing about it if possible.

- it is basically curiosity, how does the microcontroller know when the initialization is finished ? Is there some kind of counter ? That counts how many times the 0011 was written ? I wanted to know out of curiosity how the microcontroler does it physically. I couldn't find any counter or mechanizm that tells the microcontroler how to determine function set up. As an initialization or as a normal function mode. It reads either only 4 bits (in initialization) of that function or 8 bits (in normal mode). So how it determines what is what.

- why setting 0011 3 times ? Does the logic functions require it to set 3 times the 8 bit format. It sounds really weird as if it wasn't fast enough to set the 8 bit format when he received 0011 so it tried 2 more times but with less time.

Those are rather specific questions of the hardware itself, sorry if it's to specific or unnecessary question. Curiosity leads me to this question on how they resolved this thing.


Because the lower 4 bits of the FUNCTION SET instruction can be ignored during initialization by instruction.
I've also read that the D0-D3 are ignored for the case if D0-D3 are not connected to the ground or so.
Here is the source of this knowladge : https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_initialization/lcd_initialization_index.html

Some other website stated that when LCD is powered it works in 4 bit mode and not in 8 bit mode but maybe it wasn't true so I stayed with the above website.
 

MrChips

Joined Oct 2, 2009
30,467
This code works for me.
C:
void LCD_init(void)
{
  LCD_ir(0x28);         // dual line, 4 bits
  LCD_ir(0x06);         // increment mode
  LCD_ir(0x0C);         // cursor turned off
  LCD_ir(0x10);         // cursor right
  LCD_ir(0x01);         // clear display
}
 

Thread Starter

Xenon02

Joined Feb 24, 2021
495
This code works for me.
C:
void LCD_init(void)
{
  LCD_ir(0x28);         // dual line, 4 bits
  LCD_ir(0x06);         // increment mode
  LCD_ir(0x0C);         // cursor turned off
  LCD_ir(0x10);         // cursor right
  LCD_ir(0x01);         // clear display
}
Theoretically this coe should also work :

C:
void lcd_init (void)
{
    // 4 bit initialisation
    HAL_Delay(50);  // wait for >40ms
    lcd_send_cmd (0x30);
    HAL_Delay(5);  // wait for >4.1ms
    lcd_send_cmd (0x30);
    HAL_Delay(1);  // wait for >100us
    lcd_send_cmd (0x30);
    HAL_Delay(10);
    lcd_send_cmd (0x20);  // 4bit mode
    HAL_Delay(10);

  // dislay initialisation
    lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit mode), N = 1 (2 line display) F = 0 (5x8 characters)
    HAL_Delay(1);
    lcd_send_cmd (0x08); //Display on/off control --> D=0,C=0, B=0  ---> display off
    HAL_Delay(1);
    lcd_send_cmd (0x01);  // clear display
    HAL_Delay(1);
    HAL_Delay(1);
    lcd_send_cmd (0x06); //Entry mode set --> I/D = 1 (increment cursor) & S = 0 (no shift)
    HAL_Delay(1);
    lcd_send_cmd (0x0C); //Display on/off control --> D = 1, C and B = 0. (Cursor and blink, last two bits)
}
And it works but the logic behind it is unclear for this part :


C:
    // 4 bit initialisation

    HAL_Delay(50);  // wait for >40ms

    lcd_send_cmd (0x30);

    HAL_Delay(5);  // wait for >4.1ms

    lcd_send_cmd (0x30);

    HAL_Delay(1);  // wait for >100us

    lcd_send_cmd (0x30);

    HAL_Delay(10);

    lcd_send_cmd (0x20);  // 4bit mode

    HAL_Delay(10);
In which D4-D7 only connected and D0-D3 aren't connected to anything.
The lcd_send_cmd sends 2 times command. Which from 0x30 we have 0011 0000 so it sends first 0011 and then 0000 in documentation it says to send onl 0011 but in this librabry it also sends 0000 and it somehow works I don't know why. It exceeds this delay time so it reads the 0000 it doesn't ignore them. So first it reads 0011 and in second read instead of 0011 like in documentation it reads 0000.

I don't know if it's clear what I am trying to say so here is the picture:

1699804577064.png

So the second 0x30 should be now in the third row, and 0000 should be in the 4th row in this flowchart so it should set the data format but it is 0000. Third 0x30 is out of initialization but in code it says initialization. And then it is set to 0x20 as if it is still in initialization.
 

JohnInTX

Joined Jun 26, 2012
4,787
The lcd_send_cmd sends 2 times command. Which from 0x30 we have 0011 0000 so it sends first 0011 and then 0000 in documentation it says to send onl 0011 but in this librabry it also sends 0000 and it somehow works I don't know why. It exceeds this delay time so it reads the 0000 it doesn't ignore them. So first it reads 0011 and in second read instead of 0011 like in documentation it reads 0000.

I don't know if it's clear what I am trying to say so here is the picture:
I see what you mean. I think you are correct in your confusion. lcd_send_command in post #1 always sends TWO nibbles. The spec calls for only one nibble (3h) to be sent. Sending 3 then 0 violates the sequence shown. It may be working because the second nibble is sent during the wait time and is ignored. Not recommended. I just hard-code the write 30h x 3 times during init. You could also code a write nibble function and call it 3 times during init. Write data and write cmd would call the write nibble function twice to write 8 bit data. Either way would work.
Good catch!
 
Last edited:

Thread Starter

Xenon02

Joined Feb 24, 2021
495
I see what you mean. I think you are correct in your confusion. lcd_send_command in post #1 always sends TWO nibbles. The spec calls for only one nibble (3h) to be sent. It may be working because the second nibble is sent during the wait times and is ignored. Not recommended. I just hard-code the write 30h x 3 times during init. You could also code a write nibble function and call it 3 times during init. Write data and write cmd would call the write nibble function twice to write 8 bit data. Either way would work.
Good catch!
Yes ! That's exactly what I was trying to say at the beggining !

I want to add something to this. The second nibble is sent exactly after the waiting time which confused me the most. First nibble is sent and waits 40ms (HAL_Delay(20) for Enable pin 20 ms and disable Enable pin next 20 ms), second nibble is sent and also waits 40 ms, so after the LCD receives first nibble the time is fulfilled because it says (wait more than 4.1 ms and it waited 40 ms).

Hmmm I am still confused why it worked. So weird.
 

Thread Starter

Xenon02

Joined Feb 24, 2021
495
It may be working because the second nibble is sent during the wait time and is ignored.
What do you think about post #11 ?
That these second nibble should be readed because it is sent after this wait time. But couldn't find out why it worked though.

I thought maybe there is a counter ? That counts up if the 0011 is written ? Couldn't find this info in datasheet so I wondered why "0000" worked in that case.
Maybe you have any idea ?
 

Thread Starter

Xenon02

Joined Feb 24, 2021
495
No, the second nibble of 30h is sent immediately after the first in lcd_send_cmd(). The delay comes after lcd_send_cmd() returns.
Please, take a look again at this code :

C:
void send_to_lcd (char data, int rs)
{
    HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, rs);  // rs = 1 for data, rs=0 for command

    /* write the data to the respective pin */
    HAL_GPIO_WritePin(D7_GPIO_Port, D7_Pin, ((data>>3)&0x01));
    HAL_GPIO_WritePin(D6_GPIO_Port, D6_Pin, ((data>>2)&0x01));
    HAL_GPIO_WritePin(D5_GPIO_Port, D5_Pin, ((data>>1)&0x01));
    HAL_GPIO_WritePin(D4_GPIO_Port, D4_Pin, ((data>>0)&0x01));

    /* Toggle EN PIN to send the data
     * if the HCLK > 100 MHz, use the  20 us delay
     * if the LCD still doesn't work, increase the delay to 50, 80 or 100..
     */
    HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, 1);
    delay (20);
    HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, 0);
    delay (20);
}

0011 is sent to D4-D7 and EN pin is turned on after this there is delay 20ms, after 20ms the EN pin is turned off it indicates that the message is sent after that it waits 20ms (after the message is sent the documantation says to wait more than 4.1 ms it stays even longer because of 20 ms in the delay after EN is turned off), and then next nibble 0000 is also passed to the function, because the first message was sent (0011) and waited more than 4.1 ms (here it was 20ms), the next nibble can be read. After the similar procedure for 0000 nibble the send_to_lcd finished it's job and waits 5 ms (the delay that was meant for 0011) but it waited before more than 4.1 ms in send_to_lcd.

Can you see it ?
That's why I wondered then does it read 0000 or it ignores it ? If so why it ignores it when datasheets says it will do unpredictable thing, but for 0000 it does nothing.
 

BobaMosfet

Joined Jul 1, 2009
2,105
I've written drivers for this display, and here is a datasheet that will give you the answers you need (plus you can program your own character sets). You tell it whether or not you want 4-bit or 8-bit interface when you first connect. There are 2 modes to talk to it in, and you have to tell it which mode- data or command. Plus, you need to leave 1-5ms after each issuance of a command so the display can respond. The datasheet for the chipset has all the information you need. It's very easy once you understand the datasheet.

In ~60 lines of (not clever, just simple & clean) code your whole library could utilize all features of the display.
 

Attachments

Top