LCD 20x04 with STM32F4

Thread Starter

luca290799

Joined Apr 4, 2022
53
Hi everyone!

I have a 20x04 LCD which I use with I2C (sda and scl pin) with a SMT43F407. This is my first time using an LCD and I'm having a bit of trouble using and implementing this function:

lcd_put_cur(int row, int col); //it places the cursor in the row and column I give it as input

I have this function in the lcd.c library that I use but it doesn't work properly (I enclose that library and the main.c). I have downloaded lcd.c library from the Internet and I think it was created for LCD 16x02 (not the 20x04) ... so something probably needs to be changed.
By the way, there is also another function in the library called 'lcd16x2_i2c_setCursor' but I haven't tried it and don't know if it works or not.

I ask you if you can code, change my lcd.c library, attach it and discuss with me what the function lcd_put_cur(int row, int col); should look like so that I can implement it and simulate the correct operation.


I think that probably the 20x04 LCD consists of two 16x02 LCDs connected in series, but the designers used one controller for rows 1 and 3 and the second controller for rows 2 and 4. Maybe there is a legitimate electrical engineering reason for doing it that way, or maybe someone was just crazy.
 

Attachments

MrSalts

Joined Apr 2, 2020
2,625
How did you connect the LCD to the display? The datasheet for your display says it requires a parallel device,(HD44780u). Do you have some sort of I2c driver between the display and the controller?

the HD44780u driver on the back of your display is designed for up to 20-wide display, and is the same one used for the 1602 displays. You may need some tweaks in code for character wrapping but everything should work.

post a schematic of your connections and part numbers/datasheets for any parts not already posted.
 

Thread Starter

luca290799

Joined Apr 4, 2022
53
How did you connect the LCD to the display? The datasheet for your display says it requires a parallel device,(HD44780u). Do you have some sort of I2c driver between the display and the controller?

the HD44780u driver on the back of your display is designed for up to 20-wide display, and is the same one used for the 1602 displays. You may need some tweaks in code for character wrapping but everything should work.

post a schematic of your connections and part numbers/datasheets for any parts not already posted.
I connected the 4 pins of the LCD display to the STM32F407 micro with cables:
- Vcc to 5V
- GND to GND
- SDA to the I2C1 SDA pin of the STM32F407
- SCL to I2C1 SCL pin of the STM32F407

prova.png


The function lcd_i2c_setRow works .. but the function lcd_i2c_setCol doesn't work properly if I test it like this:

Code:
uint8_t k = 0;

  for (k = 1; k <= 20; k++) {

  lcd_i2c_setCol(k);
  lcd_send_string("C");
  HAL_Delay(500);

    }
The character 'C' starts to appear in each column of the first row (every 500ms) but at column 20 (the last) it does not appear:

WhatsApp Image 2022-09-10 at 15.58.00.jpeg
 

MrSalts

Joined Apr 2, 2020
2,625
Your saying for I=1 and while i is less than or equal to 20. Which means from 1 to 19. Get rid of the less than. Change it to i <=21 or i==20
 

MrChips

Joined Oct 2, 2009
27,715
The cursor position addressing is not what one would expect.
Think of the positions as one long 80 character string, numbered from 0-79.
The controller was designed for 2 x 40 character display.
Line 1 begins at position 0.
Line 2 begins at position 40 on 16 x 4 and 20 x 4 displays.
Line 3 begins at position 20.
Line 4 begins at position 60.

Edit: The above information is incorrect. See post #7 for the correct information.
 

Thread Starter

luca290799

Joined Apr 4, 2022
53
Your saying for I=1 and while i is less than or equal to 20. Which means from 1 to 19. Get rid of the less than. Change it to i <=21 or i==20
It's correct as I wrote:
for (k=1; k<=20; k++) and I discovered that in the function "lcd_i2c_setCol" row 17 was missing (I added it) ... so I solved the column problem.
 

Thread Starter

luca290799

Joined Apr 4, 2022
53
The cursor position addressing is not what one would expect.
Think of the positions as one long 80 character string, numbered from 0-79.
The controller was designed for 2 x 40 character display.
Line 1 begins at position 0.
Line 2 begins at position 40 on 16 x 4 and 20 x 4 displays.
Line 3 begins at position 20.
Line 4 begins at position 60.
Following the information on this site, I implemented this setRow function and it works correctly:
1662822023568.png

1662822142541.png


At the moment, I do not know how to write the function void lcd_put_cur(int row, int col) ... I will try to think about it these days.
In the meantime, ideas/suggestions and code are welcome.
Thanks!
 

MrChips

Joined Oct 2, 2009
27,715
You can do (row, col) if you wish. For starters, just do (position) where position is a value from 0-79.

C:
void LCD_posn(char n)
// direct positioning where n = 0-79
{
  char ch;
  ch = n;
  if (ch  >= 40) ch += 24;
  LCD_ir(ch | 0x80);        
}
where LCD_ir(ch) is whatever function is in your library to send a byte to the instruction register such as lcd_send_cmd( ).
 

Thread Starter

luca290799

Joined Apr 4, 2022
53
You can do (row, col) if you wish. For starters, just do (position) where position is a value from 0-79.

C:
void LCD_posn(char n)
// direct positioning where n = 0-79
{
  char ch;
  ch = n;
  if (ch  >= 40) ch += 24;
  LCD_ir(ch | 0x80);       
}
where LCD_ir(ch) is whatever function is in your library to send a byte to the instruction register such as lcd_send_cmd( ).
I have a couple of question for you:

1. Why inside the if and inside LCD_ir did you put the variable ch and not directly n ?
2. Why in several functions (such as lcd_send_command etc.) you have to write | 0x80 ?
3. I'm sorry but I have difficulty understanding your function :(
 

MrChips

Joined Oct 2, 2009
27,715
I have a couple of question for you:

1. Why inside the if and inside LCD_ir did you put the variable ch and not directly n ?
2. Why in several functions (such as lcd_send_command etc.) you have to write | 0x80 ?
3. I'm sorry but I have difficulty understanding your function :(
1. You are correct. I don't know why I used another variable.

2. If you look at the HD44780 data sheet you will see that when DB7 is set to 1 you are writing to the DDRAM when sending to the instruction register.
n = (n | 0x80) performs a bit-wise OR operation, i.e. it sets DB7 to 1.

HD44780 DDRAM address.jpg
 

MrChips

Joined Oct 2, 2009
27,715
Your LCD setrow is a good start. Now all you have to do is to add the column.

C:
void LCD_posn(uint8_t row, uint8_t col)
// row = 1 - 4
// col = 1 - 20
{
  switch (row)
  {
     case 1: lcd_send_cmd( ( 0x80 | 0x00 ) + col -1);
     break;
     case 2: lcd_send_cmd( ( 0x80 | 0x40 ) + col -1);
     break;
     case 3: lcd_send_cmd( ( 0x80 | 0x14 ) + col -1);
     break;
     case 4: lcd_send_cmd( ( 0x80 | 0x54 ) + col -1);
     break;
  }
}
 

Thread Starter

luca290799

Joined Apr 4, 2022
53
Your LCD setrow is a good start. Now all you have to do is to add the column.

C:
void LCD_posn(uint8_t row, uint8_t col)
// row = 1 - 4
// col = 1 - 20
{
  switch (row)
  {
     case 1: lcd_send_cmd( ( 0x80 | 0x00 ) + col -1);
     break;
     case 2: lcd_send_cmd( ( 0x80 | 0x40 ) + col -1);
     break;
     case 3: lcd_send_cmd( ( 0x80 | 0x14 ) + col -1);
     break;
     case 4: lcd_send_cmd( ( 0x80 | 0x54 ) + col -1);
     break;
  }
}
I tested this function (I think it's the same as the one you attached to me):
Code:
void LCDSetCursor(uint8_t r, uint8_t c)  //row from 0 to 3  and  col from 0 to 19
{
  char firstArr[4]={0x00, 0x40, 0x14, 0x54};  //array of addresses for 4x20 lcd
  lcd_send_cmd(0x80 + firstArr[r] + c);
}
But I prefer to use the "switch" as you did.

1) In your function there is "col -1" .. is this because the function accepts from 0 to 19 columns while the user enters a number from 1-20 ?

2) Another question ... in the LCD library I found online the function to 'clean' the screen is this:
Code:
void lcd_clear (void)
{
    lcd_send_cmd (0x00);
    for (int i=0; i<100; i++)
    {
        lcd_send_data (' ');
    }
}
It works and I have used it several times .. but I don't think it is the correct approach. Looking at the datasheet I read in table 6 on page 24 that for "Clear display" you have to set the DB0 bit to 1 (the others to 0) .. so I think such a function would be correct:
Code:
void lcd_clear_MIO (void)  //I tested it and it works!
{
    lcd_send_cmd (0x01);
}
Am I wrong?
 

MrChips

Joined Oct 2, 2009
27,715
1) It would be preferable to number your rows and columns using 0-3 and 0-19.
For this you need to change the switch/case statements for rows 0-3.
Also remove -1 for columns 0-19.

2) Writing spaces to every location is a brute force method of clearing the sceen.
Instead, simply send 0x01 as you have done. That is what it is for.
 
Top