How to Interface LCD1602 via I2C with STM32

Today we will see how to interface LCD1602 with STM32 using an I2C device (PCF8574). PCF8574 is a port extender, which is used to connect multiple devices in the output while the input has only 2 pins, SCL and SDA. We will connect this PCF8574 with the LCD1602, thus controlling the entire LCD with only 2 pins.

As you can see above PCF8574 has 4 input pins GND, VCC, SDA, SCL and 16 output pins. We will connect our LCD to these 16 pins.

What is the ADDRESS for PCF8574

The higher 4bits of PCF8574 address are 0100 and these are fixed. But lower 4 bits can be modified according to our convenience.  The question you must be thinking is  why we need to modify lower 4 bits? 

Well you generally don’t but as you know that we can connect up to 128 devices on the same I2C line. Let’s say we want to connect two different LCDs on the same I2C line, then we can’t use two PCF8574 with same addresses and we need to modify the address for one of them.

So how do we modify the address?

  • The address of the PCF8574 is 0 1 0 0 A2 A1 A0 R/W. To change the address we are provided with A0, A1 and A2 pins.
  • By default these three pins are high so the address by default is 01001110 which is 0x4E.  
  • To change the address of this device, you have to connect any/all of these three pins to ground, which is provided just above them.
  • So let’s say you connected A0 to ground, new address will be 01001100 which is 0x4C.
  • In this manner, we can connect up to 8 LCDs to the same line.
  • I want to point to one more thing here, the last bit of the address is kept 0 intentionally, because this bit is responsible for read(1)/ write(0) operation.


HOW TO Connect LCD to PCF8574

As shown in the figure above, first pin of the device is Vss which is pin 1 of LCD. So all you have to do is connect first pins of the LCD to Vss above and rest will connect accordingly.   Starting with Vss as first pin, connection is as follows:-



CubeMX Configuration

Below is the image showing the clock diagram.

The STM32F103 is clocked by the external 8MHz crystal. The system is running at maximum 72MHz clock.

Below is the image showing the I2C configuraion.

I am using the I2C1 to connect the LCD. The I2C is configured in the standard mode, with the clock speed set to 100KHz.

The pins PB6 and PB7 are configured as the SCL and SDA pins. These pins are connected to the respective pins of the PCF8574.



Some Insight into the Code

#define SLAVE_ADDRESS_LCD 0x4E // change this according to ur setup

The default slave address defined in the i2c-lcd.c is 0x4E. This is default for the PCF8574.


Let’s take a detailed look at the pinout of the PCF8574.

As you can see above,

  • P0 is connected to the pin RS of the LCD. This RS pin is defines whether the transmitted byte is a command (0) or Data (1).
  • P1 is connected to the R/W pin of the LCD. This pin should be LOW when writing the data to the LCD and HIGH when reading the data from the LCD.
  • P2 is connected to the Enable pin of the LCD. This pin is used for the strobe (E=1 & E=0).
  • P3 is connected to the Backlight of the Display. Setting this pin to 1 will turn the backlight ON.
  • P4 – P7 are connected to the data pins D4 – D7. Since only 4 data pins are available in the PCF8574, we need to configure the LCD in the 4bit Mode.

Send Command

Below is the function to send the command byte to the LCD.

void lcd_send_cmd (char cmd)
{
  char data_u, data_l;
  data_u = (cmd&0xf0);
  data_l = ((cmd<<4)&0xf0);

This function takes the command byte as the parameter. Inside this function we will first separate the lower 4bits and upper 4bits of the command byte. This needs to be done because we are using the LCD in 4bit Mode, hence only 4bits should be sent at a time.

Remember that we have 8 pins (P0 – P7) in the PCF8574. You can treat each pin as a separate bit. Therefore we will have 8 bit data to control 8 pins of the PCF8574.

Pin P0 is the RS pin and in command mode it will be 0. P1 is the R/W pin and it will always be 0 while writing the data/command. To send the strobe, we need to send the same data twice, once when the Enable pin(P2) is set to 1, and again when the pin is reset to 0. P3 is connected to the backlight, so we will keep it to 1.

With the above configuration, our lower 4 bits of the modified command byte will be b1100 (E=1) and b1000 (E=0). The higher 4 bits are the 4 bits from the actual command.

We will send the upper 4 bits of the command byte first. This is as per the instructions in the LCD datasheet.

  uint8_t data_t[4];  
  data_t[0] = data_u|0x0C;  //en=1, rs=0 -> bxxxx1100
  data_t[1] = data_u|0x08;  //en=0, rs=0 -> bxxxx1000

Next we will send the lower 4 bits of the command byte, again with the strobe.

	data_t[2] = data_l|0x0C;  //en=1, rs=0 -> bxxxx1100
	data_t[3] = data_l|0x08;  //en=0, rs=0 -> bxxxx1000
	HAL_I2C_Master_Transmit (&hi2c1, SLAVE_ADDRESS_LCD,(uint8_t *) data_t, 4, 100);
}

Send Data

Sending data is similar to that of sending the command. The only difference is that the RS bit (P0) will be 1. This is to indicate that the transmitted byte is a data byte.

void lcd_send_data (char data)
{
	char data_u, data_l;
	uint8_t data_t[4];
	data_u = (data&0xf0);
	data_l = ((data<<4)&0xf0);
	data_t[0] = data_u|0x0D;  //en=1, rs=1 -> bxxxx1101
	data_t[1] = data_u|0x09;  //en=0, rs=1 -> bxxxx1001
	data_t[2] = data_l|0x0D;  //en=1, rs=1 -> bxxxx1101
	data_t[3] = data_l|0x09;  //en=0, rs=1 -> bxxxx1001
	HAL_I2C_Master_Transmit (&hi2c1, SLAVE_ADDRESS_LCD,(uint8_t *) data_t, 4, 100);
}

Initialisation

Below is the function to initialise the LCD in the 4bit mode. The initialisation requires us to send a few set of command in a particular order. These commands and sequence are provided in the LCD1602 Datasheet. The code below is commented properly, so you can understand what is the function of each command.

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);

  // display 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(2);
  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)
}

Additional Functions

We can send a single data byte using the function lcd_send_data, but to send the entire string or a character array, we will write a separate function.

void lcd_send_string (char *str)
{
  while (*str) lcd_send_data (*str++);
}

The function lcd_send_string can be used to send an entire string to the display. The parameter of this function is the pointer to the string or array, that you want to send.

Below if the function to set the cursor at any point on the display.

void lcd_put_cur(int row, int col)
{
    switch (row)
    {
        case 0:
            col |= 0x80;
            break;
        case 1:
            col |= 0xC0;
            break;
    }
    lcd_send_cmd (col);
}

Before sending a string or a character to the display, we need to set the cursor position where we want to print it. The LCD1602 has 2 rows and 16 columns. The parameters of the function are:

  • @row is the Row number. It can be either 0 or 1.
  • @col is the Column number. It can be from 0 to 15.

The main function

Send Strings

We will first see how to print the strings on the display.

// Display Strings
  lcd_init ();
  lcd_put_cur(0, 0);
  lcd_send_string ("HELLO WORLD");
  lcd_put_cur(1, 0);
  lcd_send_string("from CTECH");

In the main function we will

  • Initialise the LCD by calling lcd_init() function.
  • Then put the cursor at the beginning of the 1st Row (0,0) and send the string “HELLO WORLD” to this location.
  • Then put the cursor at the beginning of the 2nd Row (1,0) and send the string “from CTECH” to this location.

Below is the output of the above code.

Send Number

We can not print the number directly on the display. The display is only capable of printing the Ascii characters by default. Therefore we need to convert the number to the Ascii form and then print it.

Below is the code to convert and print the number.

// Display Number
  lcd_init();
  int num = 1234;
  char numChar[5];
  sprintf(numChar, "%d", num);
  lcd_put_cur(0, 0);
  lcd_send_string (numChar);

In the main function we will

  • Initialise the LCD by calling lcd_init() function.
  • Let’s say we want to print the number 1234. It has 4 digits, so define a character buffer to store 1 extra byte, i.e 5 bytes.
  • Now we will use sprintf to convert the number to the character for and store it in the array we just defined.
    • The format specifier, %d, is used to convert integer values to character form.
  • Then put the cursor at the beginning of the 1st Row (0,0) and send the array.

Below is the output of the above code.

Send Floats

Just like numbers, we can not print the floats directly on the display. Therefore we need to convert the float value to the Ascii form and then print it.

Below is the code to convert and print the float.

// Display Float
  lcd_init();
  float flt = 12.345;
  char fltChar[7];
  sprintf(fltChar, "%.3f", flt);
  lcd_put_cur(0, 0);
  lcd_send_string (fltChar);

In the main function we will

  • Initialise the LCD by calling lcd_init() function.
  • Let’s say we want to print the number 12.345. It has 6 digits, so define a character buffer to store 1 extra byte, i.e 7 bytes.
  • Now we will use sprintf to convert the float to the character for and store it in the array we just defined.
    • The format specifier, %.3f, is used to convert float values to character form till 3 decimal places.
  • Then put the cursor at the beginning of the 1st Row (0,0) and send the array.

Below is the output of the above code.



RESULT

You can watch the video to see the complete working.

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

122 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Djadeja
8 months ago

Hi, i am trying out the code on another controller, the i2c waveforms are correct but still sometimes lcd does not print anything. In cases where it works, there is no difference in waveforms. What may be the issue. The POT is adjusted correctly.

Andrewtitoff
1 year ago

doesn’t write anything in the second line… (

Andrewtitoff
1 year ago

doesn’t write anything in the second line… (

Ramon
2 years ago

i add “i2c-lcd.h” and “i2c-lcd.c” into my project,1602 LCD can not print number(0,1,2) with ” lcd_send_data(1)”.howeverm LCD can print “string”.

Nam
2 years ago

void lcd_clear (void)
{
lcd_send_cmd(0x01);
   delay_ms(2);
}

Nam
2 years ago

//Add function
void lcd_gotoxy(unsigned char x, unsigned char y){ 
  unsigned char xy; 
  if(y==0){xy=0x80;}    
  if(y==1){xy=0xC0;}    
  if(y==2){xy=0x94;}    
  if(y==3){xy=0xD4;}
  xy=xy+x;
  lcd_send_cmd (xy);
}

Maryam
2 years ago

Hi, I want to suggest the following code for PC8574 with 9 pins (blue board)
//Rs–>P0, RW–>P1, E–>P2, D4–>P4, D5–>P5, D6–>P6, D7–>P7
void lcd_send_cmd(char cmd)
{
uint8_t cmd_t[4];

cmd_t[0]=(cmd&0xf0)|(0x04); //cmd_u ,E=1, RS=0
cmd_t[1]=(cmd&0xf0); //E=0, RS=0
cmd_t[2]=((cmd<<4)&0xf0)|(0x04); //cmd_l E=1, RS=0
cmd_t[3]=((cmd<<4)&0xf0); //E=0, RS=0
HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *) cmd_t, 4, 200);
}
void lcd_send_data(char data)
{
uint8_t data_t[4];
data_t[0]=(data&0xf0)|(0x05); //data_u , E=1, RS=1
data_t[1]=(data&0xf0)|(0x01); //E=0, RS=1
data_t[2]=((data<<4)&0xf0)|(0x05); //data_l, E=1, RS=1
data_t[3]=((data<<4)&0xf0)|(0x01); //E=0, RS=1
HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *) data_t, 4, 200);
}

thank you so much!!

Savino Giovanni
2 years ago

hi, i follow every steps but on my lcd i see only the ligth but no word. Can you help me plese?

JaysanX
Reply to  Savino Giovanni
2 years ago

try to adjust the potentiometer on your lcd i2c adapter-board. This solved that problem for me

Liam
2 years ago

it’s work very fine. Can you help me to print the value of the potentiometer linked on ADC please?

Iman
3 years ago

Hi I imported the project but it will not run it keeps saying “this launch configuration requires the selected build configuration to use the MCU ARM GCC toolchain”
how do I fix this?

Arvin Ghahremani
3 years ago

Hi everyone. I have a problem when my program does software reset, my LCD shows noisy characters after running LCD_init(). I have to reset it again to work properly. How can I fix it? It’s imprortant when I use IWDG

Sharan
3 years ago

When I try to display numbers above 9, it prints it corresponding ASCII characters.How will I print a value stored in a variable which is incremented or varying.

Thanks for the tutorial.It works fine

Sharan
Reply to  Sharan
3 years ago

I also wanted to print ADC values which have float and integer values.

Sharan
Reply to  admin
3 years ago

Do you have an example code for that function with the above-attached header files and source files ? Please send it or mention it if you have.

Windy
3 years ago

does it work in mode 8bit?

Marceli
Reply to  admin
3 years ago

Hi, as D0 – D3 are not connected – IMHO also 8-bit mode can not work. True?

sina
Reply to  admin
2 years ago

hi
I did not understand how to change the code for 8bit.
please give an example

sina
Reply to  admin
2 years ago

8 bit initialization.
Can you send modified code for the “lcd_send_cmd” function?
i modify “lcd_init” function from data sheet but I could not modify the code of
lcd_send_cmd” and “lcd_send_data”

sina
Reply to  admin
2 years ago

no,this code for 8 bit mode

Juan Figueroa
3 years ago

its works, but i am having problems with the brightness and it is not the potentiometer, where should i change it?

Cool guy
Reply to  Juan Figueroa
3 years ago

I think you should use external power source

kishor sherolla
4 years ago

sir i want to toggle curser set position on lcd

EMRE
4 years ago

You are telling so many topics, you are wasting your time. We are watching you until the end, but you do not share any files. How are we going to experiment ourselves? Every download link contains ads. it still does not download. Why follow as long as you don’t provide any support here?

There are not even header and source files suitable for your program. Or you don’t even have a general program. If we get it wrong, at least somewhere, we can download your program and try it out.

But there is no file.

EMRE
Reply to  EMRE
4 years ago

Sorry man. I found it . Please change the still for download button:))

I am so sorry . My fault. I did not see it.

Antonio
4 years ago

where can download the files? I can`t see the link

yasar
4 years ago

ok

muros9
4 years ago

How can set cursor in another position?

ColinC
4 years ago

Another way to clear the screen is:
lcd_send_cmd(1);

Cheers

Cool guy
Reply to  ColinC
3 years ago

wow thank you so much it work better than lcd_clear() in the library

ColinC
4 years ago

Excellent. Worked first time. Thanks a lot.

lala
4 years ago

well done, thanks

Leonardo Ferreira
4 years ago

I had to use Direct manipulation on the I2C registers on my Nucleo STM32F767 to make this thing works -_-

Ron Craig
5 years ago

There are slightly different I2C parts with different I2C address 0x3F for -AT and 0x27 for -T
Refer to spec and modify you code for part you have.

Ron Craig
5 years ago

The PCF8574AT has I2C address as shown in this code starting with 0x3F.

The PCF8574T has different I2C address starting with 0x27.

Refer to part spec.
You may need to modify I2C address for the part you have.

imn
5 years ago

hi
how can send custom character with your library?
(is it even possible)

imn
Reply to  imn
5 years ago

hi again
i managed to create custom character using your library like below:
/* code
const char UserFont[8][8] =
{
{ 0x11,0x0A,0x04,0x1B,0x11,0x11,0x11,0x0E },
{ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 },
{ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18 },
{ 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C },
{ 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E },
{ 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F },
{ 0x0E,0x1F,0x11,0x11,0x13,0x17,0x1F,0x1F },
{ 0x1F,0x11,0x11,0x11,0x11,0x11,0x11,0x1F }
};

lcd_send_cmd(0x40); // Set CGRAM address counter to 0
char const *p1;
p1 = &UserFont[0][0];
for (int i = 0; i < sizeof(UserFont); i++, p1++){
lcd_send_data(*p1);
}
lcd_send_cmd(0x80);
*/
now sending character 0x00 to 0x07 displays 8 predefined custom character

Franco
5 years ago

If there is anyone that can help me i send him my code. The circuit that i mounted is the same of the youtube tutorial.

Franco
5 years ago

Hello, i have problem with this project. Can anyone help me? I found the i2c addres with i2c scanner did with keil, not arduino: it’s 0x3F. The code is the same that i have downloaded from this site; maybe change some setting into frequency or clock because i use an stm32f767zi. Please help me.

laki
5 years ago

Hi, is it possible to avoid using HAL_DELAY() in void lcd_init (void)? What else can I use in the init function instead of HAL_DEALY() ?

5 years ago

Error building…

../Core/Src/i2c-lcd.c:20: undefined reference to `hi2c1′

Laki
5 years ago

Hi Everyone, I am using with stm32f072 board and have an lcd with PCF8574 converter. The project is building successfully but for some reason I do not get anything on my lcd. What could be the reason? This is the first time I am trying to send something to my lcd.

Laki
Reply to  admin
5 years ago

I have done that. I am not sure the slave address though. How can I make sure 0x4E is an appropriate one?

Laki
Reply to  admin
5 years ago

That is interesting but I have different manual – by Philips Semiconductors. I do not have that address reference in there, but for addressing I have a slave address: S 0 1 0 0 A2 A1 A0 0 A

Laki
Reply to  admin
5 years ago

Ok, I do not know how it came but it is working now 🙂 Thank you.

Laki
Reply to  Laki
5 years ago

Do I need to change / modify libs for sending ADC temperature results to my lcd ?

Venkat
Reply to  Laki
5 years ago

Hi, I am struggling to get it working. For some reason, am not successful. Can you share your code (vrtata@gmail.com) and wiring of the setup? It will be a great help.

Anh Tu Nguyen
5 years ago

For binary 01001100 , I think it should be 76 (decimal) and it means 0x4C instead of 0x4B

Pavel
5 years ago

Hello!
Redid the project in STM32CUBEIDE does not work. Blue pill board.

Pavel
Reply to  admin
5 years ago

How can I send a project?

Pavel
Reply to  admin
5 years ago

I don’t understand why the code does not work. I have the address I2C = 3F.PB8-SCL, PB9-SDA.
I collected the project with this data, there are no errors. The code does not work. Other code in this configuration works. What is wrong?

Pavel
Reply to  admin
5 years ago

I am using PCF8574AT. The I2C address scanner on the terminal shows address = 3F.

Pavel
Reply to  Pavel
5 years ago

Everything began to work. In the i2c-lcd.c file I replaced #define SLAVE_ADDRESS_LCD 0x4E //., #Define SLAVE_ADDRESS_LCD 0x3F << 1 //. Thank you very much for your work.

Pavel
Reply to  Pavel
5 years ago

Are you planning to make an LCD 1602 project without an I2C interface in CUBEIDE?

Pavel
Reply to  admin
5 years ago

This is very good. I will look forward to it.

Sattar
5 years ago

Hello, Thank you for this code.I use your code, when I initialize LCD my LCD’s back-light is goes off,why this happens?could you help me?

John
5 years ago

I’m using your code but all the i2c bus is sending are ‘Setup Write to [&] + NAK’ packets over and over and over again. I’m using salae’s logic program and an analyzer to get the data. The board is a blue pill with some chinese knockoff STM32f103c8 but it’s working as normal so far except for the code. Any ideas??

John
Reply to  admin
5 years ago

It’s an I2C backpack module for the 16×2 lcd screen. The chip is a PFC8574AT and using the arduino and some handy I2C scanner code I found that the address is 0x27 which I also placed in the code instead of your provided address.

Ric
5 years ago

It works now, my chip is PCF8574AT, the address is 7E, thanks,
I changed on backlight side, replace the transistor with 470 ohm resistor, so it’s on always…cheers…

Ric
5 years ago

it’s not working yet with STM32F107VCT6, will it work with 3.3V supply and bus ? thanks

Anonymous123
5 years ago

i’m having problems using it, i did an exact copy of the code, activated the I2C on cubemx and the display isn’t doing anything at all besides turning on and i already adjusted brightness, i tried to measure RW pin with multimeter and i got a 5V signal everytime i did it, so i think it’s the RW but i don’t know how i change it

kami
Reply to  admin
5 years ago

can i send my code and check it please?
best regards sir

Gass
6 years ago

it doesn’t work in my 16×4 lcd, can you help me to use 16×4 lcd?

Vojta
6 years ago

Hello, can I use your i2c-lcd library for STM32F072B-discovery? Is it ok when I replace in i2c-lcd.h #include “stm32f4xx_hal.h” for #include “stm32f0xx_hal.h”. Thank you.

Vojta
Reply to  admin
6 years ago

I don’t know where i do mistake. The address of display I have default 0x4E.

/* Includes ——————————————————————*/
#include “main.h”
#include “i2c-lcd.h”
/* Private variables ———————————————————*/
I2C_HandleTypeDef hi2c1;

/* Private function prototypes ———————————————–*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

/* Private user code ———————————————————*/

int main(void)
{

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* Configure the system clock */
SystemClock_Config();

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* Infinite loop */
while (1)
{

lcd_init ();
lcd_send_string (“HELLO WORLD”);
HAL_Delay(100);

}
}

/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}

/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{

hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00101D2D;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_DISABLE) != HAL_OK)
{
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */

/* USER CODE END I2C1_Init 2 */

}

/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();

}

ra7e
Reply to  Vojta
5 years ago

Did you find mistake?

Jose Ronaldo
6 years ago

I use STM32F103C8 + PCF8574A + Keil-5, does not work.
The program scans and finds the address 0x3F.
Works well with STM32F103C8 + PCF8574A + Arduino IDE using address 0x3F.
Works well STM32F103C8 + SSD1306 + Keil-5.
Does anyone have an idea what the problem is?
Regards

hapl
Reply to  admin
5 years ago

i try it use 0x4E and others address but can’t work too,

kami
Reply to  hapl
5 years ago

exactly me too. did you have any solution for your problem? all of my problem is similar you. and i dont know why. if you have any suggestion please tell me.
best regards

kami
Reply to  admin
5 years ago

exactly me too. did you have any solution for your problem? i have problem like him. and i dont know why. if you have any suggestion please tell me. i tested lots of addresses but i did not get any answer.
best regards

Venkat
Reply to  admin
5 years ago

I downloaded your code and tried executing but its not working for me. My setup is STM32F103C8 + PCF8574T + STM32CubeIDE. Any idea what could be the problem?

Venkat
Reply to  admin
5 years ago

It was not working for me. What I realized is the execution is going into infinite loop at HAL_Delay(50). Any inputs on why this could be? Preemption Priority for System tick timer is set to 0.

Venkat
Reply to  admin
5 years ago

It was the clock issue. Now I do not have issues with HAL_Delay. The program runs but am unable to get any output on either the display or LogicAnalyzer. I have a feeling I am doing something stupid in my wiring. Your exact code does not produce any results. Need help with verifying my wiring. Is there a diagram I can refer to. Now am obsessed with making this work.

Venkat
Reply to  Venkat
5 years ago

Working for me. Was a faulty bluepill

RONALDO MARTINS
Reply to  Venkat
4 years ago

What did you do to work

MUSTAFA
Reply to  RONALDO MARTINS
4 years ago

I do not know why. But for some reason the code doesn’t work from I2C1. I changed the bluepill chip. There is no problem with the hardware. It works fine from I2C2.

Marcel
6 years ago

Thanks for your code it works even with a 20×4 display with some changes.
I have a question: Why does your code work without the required waitings between the init commands?
The datasheet says that you have to wait more tha 4.1 ms between the first instructions.
Thanks

mehar
6 years ago

hi how to write code for scrolling display for 20*4 now i am able to write but it is improper please help me in the issuse

JoaquinS
6 years ago

I can’t thank you enough for this lib!
Such a great and working lib and so simple!!!

Feel free to contact me for colaboration, I’d love to!

JS

Thomas Christensen
7 years ago

Great tutorials/examples (not only this one). Thanks for sharing.
Can you give an estimate of the refresh rate when writing all 2×16 characters. Is it 1 sec. to write all characters or 0.1 sec.?
Do you consider such a display setup an acceptable debugging option?

Stasiu
7 years ago

I’ve got one question.
How to use it it other commands, for example command to clear screen ?
Can I find anywhere list of hex code for functions ?

PawelDNB
7 years ago

I wrote to you in YT but its proper place . When you initialize LCD you must send 4-bit mode at first with delay
void lcd_init (void)
{
uint8_t i=0;
HAL_Delay(100);
for(i=0;i<3;i++)//sending 3 times: select 4-bit mode
{
lcd_send_cmd(0x03);
HAL_Delay(45);
}
lcd_send_cmd (0x02);
HAL_Delay(100);
lcd_send_cmd (0x28);
HAL_Delay(1);
lcd_send_cmd (0x0c);
HAL_Delay(1);
lcd_send_cmd (0x80);
HAL_Delay(1);
}

and some additional function to set proper location on screen :
void lcd_print_x_y(uint8_t line , uint8_t row, char *str )
{
if (line == 0 ){
uint8_t line_x_y = 0x80 + row ;
lcd_send_cmd(line_x_y);
while (*str) lcd_send_data (*str++);
}else if (line == 1 ){
uint8_t line_x_y = 0x80 |( 0x40 + row) ;
lcd_send_cmd(line_x_y);
while (*str) lcd_send_data (*str++);
}
}

Assia
Reply to  PawelDNB
6 years ago

Nice work
Could you send me the library that you have used ( i2c-lcd )

Diego
Reply to  PawelDNB
6 years ago

Would you know how to insert special characters in CGRAM in this I2C communication to the LCD?

Marco
Reply to  PawelDNB
3 years ago

Thank you very much PawelDNB (even after 4 years)!

Using the old initalization I faced the problem that from time to time
the display was showing crap after power up.
With your initalization routine the error disappeared!

Ramo
7 years ago

Nice.