How to Interface I2C LCD1602 with STM32 using PCF8574 (HAL Library)
The I2C LCD1602 display is a popular choice for embedded projects due to its simplicity and reduced pin usage. In this tutorial, we’ll learn how to interface a 16×2 LCD with an STM32 microcontroller using the PCF8574 I/O expander over the I2C bus. By using only two data lines—SCL and SDA—you can easily display characters, strings, and messages on the LCD. This guide includes STM32 HAL code, initialization steps, and function implementations like lcd_send_cmd
, lcd_send_data
, and lcd_init
. Whether you’re using STM32CubeIDE or CubeMX, this tutorial is fully compatible and beginner-friendly.

In this tutorial, you’ll learn how to connect and control a 16×2 I2C LCD (LCD1602) with an STM32 microcontroller using the PCF8574 I/O expander. We’ll walk through the wiring setup, explain how the I2C communication works with the LCD, and implement essential HAL functions to initialize the display, send commands, and print characters or strings. The tutorial also provides a clean and reusable code structure compatible with STM32CubeIDE, making it easy to integrate into your own projects.
You can also check out some other Displays interfaced with STM32 Microcontrollers:
- Interface LCD 20×4 with STM32 using the I2C.
- Interface LCD1602 display with built‑in I²C driver AIP31068.
- Interface SSD1306 OLED Display with STM32 (via I²C).
- Interface SH1106 1.3″ OLED with STM32 via I²C.
- Interface LCD 16×2 with STM32 without I²C.
- Interface 2.8″ ILI9341 Display with STm32 using SPI.
VIDEO TUTORIAL
You can check the video to see the complete explanation and working of this project.
Check out the Video Below
Introducing LCD16x2 with PCF8574
The LCD1602 is a widely used 16×2 alphanumeric display module capable of showing 2 lines of 16 characters each. While the standard module requires at least 6 GPIO pins to interface with a microcontroller, adding the PCF8574 I/O expander simplifies this by enabling communication over the I²C bus using just two lines—SDA and SCL. This greatly reduces wiring complexity and conserves GPIO resources on the STM32 or any microcontroller. The PCF8574 handles parallel-to-serial data conversion, making the setup ideal for compact or resource-constrained embedded systems.
Some of its important features are:
- I²C Communication: Reduces pin count from 6+ to just 2 (SDA and SCL), freeing up valuable MCU GPIOs.
- Built-in Backlight and Contrast Control: Allows brightness adjustment and contrast tuning using onboard trimpots or software.
- Compact and Readable Display: Displays 16 characters × 2 lines, suitable for status messages, sensor values, and basic UI.
- Wide Compatibility: Works seamlessly with STM32, Arduino, ESP32, and other microcontrollers through I²C protocols.
Why should you use the LCD16x2 Display ?
The LCD16x2 display is a simple yet highly effective solution for showing text-based information in embedded systems. It’s ideal for projects that need basic human-readable output without the complexity or resource demands of graphical displays. Whether you’re displaying sensor data, system status, or debug messages, the LCD16x2 offers a reliable and cost-efficient option. Its wide availability, low power consumption, and straightforward interfacing make it a go-to choice for hobbyists and professionals alike.
Reasons to Use the LCD16x2 Display
- Easy to Interface – Works with 4-bit/8-bit parallel or I²C using PCF8574.
- Low Power Consumption – Suitable for battery-powered and low-power applications.
- Cost-Effective – Very affordable for displaying basic textual output.
- Readability – Sharp contrast and backlight make it easy to read even in low light.
- Widely Supported – Compatible with STM32, Arduino, ESP32, and many platforms.
- No Complex Libraries Needed – Simple functions like send command/data are enough to operate.
The I2C Slave 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.
Wiring Diagram
Below is the image showing PCF8574 module.
The 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 of them will connect accordingly. Starting with Vss as first pin, connection is as follows:-
The 16 pins from the PCF8574 module are connected to the LCD16x2. The connection with the STM32 is shown in the table below.
PCF8574 Pin | STM32F103 Pin | Description |
---|---|---|
VCC | 5V | Power supply (5V) |
GND | GND | Ground |
SDA | PB7 | I²C Data line |
SCL | PB6 | I²C Clock line |
CubeMX Configuration
Clock Configuration
Below is the image showing the clock configuration for the project.
The STM32F103 is clocked by the external 8MHz crystal. We will use the PLL to run the system at maximum 72MHz clock.
I2C Configuration
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 as shown in the connection diagram.
STM32 HAL Code to Interface LCD1602 via I2C (PCF8574)
#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 Address for the PCF8574, if the pins A0 A1 and A2 are not modified.
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.
This tutorial demonstrates how to interface a 16×2 LCD (LCD1602) with an STM32 microcontroller using the PCF8574 I²C I/O expander. By leveraging the I²C protocol, the number of required GPIO pins is reduced to just two—SCL and SDA—making the setup more efficient and scalable. The post covers wiring connections, CubeMX configuration, and STM32 HAL-based code implementation for initializing the LCD, sending commands, and displaying text. With address configuration details and reusable functions, this guide enables you to easily integrate multiple LCDs into your STM32 projects using minimal resources. If you do not want to use the I2C for LCD16x2, you can check out the parallel version here: Interface LCD 16×2 with STM32 without I²C.
PROJECT DOWNLOAD
Info
You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.
STM32 I2C LCD Project FAQs
When using the PCF8574 I/O expander, only two pins—SCL and SDA—are needed from the STM32 to communicate with the LCD1602 via I²C, instead of the usual six or more for direct parallel interfacing.
Yes, you can connect up to 8 LCD1602 modules by configuring the A0, A1, and A2 address pins of the PCF8574 differently for each display, allowing each to have a unique I²C address.
Most PCF8574-based LCD modules are designed to run at 5V, which also powers the LCD backlight and logic. Ensure that your STM32 I²C lines are 5V-tolerant or use level shifters if needed.
Pull-up resistors (typically 4.7kΩ) on the SDA and SCL lines are usually required for proper I²C communication, but many PCF8574 breakout boards come with these resistors pre-installed.
Yes, the tutorial uses STM32CubeMX for peripheral setup and relies entirely on the HAL library for I²C communication and LCD control, making it easy to integrate into CubeMX-based projects.
🙏 Support Us by Disabling Adblock
We rely on ad revenue to keep Controllerstech free and regularly updated. If you enjoy the content and find it helpful, please consider whitelisting our website in your ad blocker.
We promise to keep ads minimal and non-intrusive.
Thank you for your support! 💙
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.
doesn’t write anything in the second line… (
doesn’t write anything in the second line… (
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”.
displays can only print ascii characters.
You need to convert numbers into the relevant ascii character.
sprintf (buffer, “%d”, num);
lcd_send_string (buffer);
void lcd_clear (void)
{
lcd_send_cmd(0x01);
delay_ms(2);
}
//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);
}
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!!
hi, i follow every steps but on my lcd i see only the ligth but no word. Can you help me plese?
try to adjust the potentiometer on your lcd i2c adapter-board. This solved that problem for me
it’s work very fine. Can you help me to print the value of the potentiometer linked on ADC please?
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?
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
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
I also wanted to print ADC values which have float and integer values.
you should use sprintf to convert the numbers to characters.
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.
let’s say you want to convert 1234 to characters.
unsigned int num = 1234;
char buffer[4];
sprintf (buffer, “%u”, num);
does it work in mode 8bit?
You need to modify the init code as per the 8 bit initialization
also the lcd_send_cmd and data functions. There we split the data into upper and lower half, so just don’t split it and it will work
Hi, as D0 – D3 are not connected – IMHO also 8-bit mode can not work. True?
yes not with this setup. But the same code with little modification can work..
hi
I did not understand how to change the code for 8bit.
please give an example
what you mean 8 bit ?
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”
you mean this ? https://controllerstech.com/interface-lcd-16×2-with-stm32-without-i2c/
no,this code for 8 bit mode
why 8 bit mode ? The PCF only connects to 4 data pins of the LCD. The 8 bit code won’t work
its works, but i am having problems with the brightness and it is not the potentiometer, where should i change it?
I think you should use external power source
sir i want to toggle curser set position on lcd
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.
Sorry man. I found it . Please change the still for download button:))
I am so sorry . My fault. I did not see it.
where can download the files? I can`t see the link
now check. I am making some changes in the website… so it might have got deleted… FIXED NOW
https://controllerstech.com/wp-content/uploads/2019/12/TUT_LCD_I2C_F103_CUBEIDE.zip
ok
How can set cursor in another position?
Another way to clear the screen is:
lcd_send_cmd(1);
Cheers
wow thank you so much it work better than lcd_clear() in the library
Excellent. Worked first time. Thanks a lot.
well done, thanks
I had to use Direct manipulation on the I2C registers on my Nucleo STM32F767 to make this thing works -_-
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.
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.
hi
how can send custom character with your library?
(is it even possible)
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
Ok I will test it and include it in the code.. Do you want the reference ?
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.
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.
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() ?
yeah It’s possible.. You can use that microsecond delay instead.. Or just use some empty loop
Error building…
../Core/Src/i2c-lcd.c:20: undefined reference to `hi2c1′
did you select I2C while setting up cubemx ?
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.
Try adjusting the potentiometer for the contrast
I have done that. I am not sure the slave address though. How can I make sure 0x4E is an appropriate one?
if you are using pcf8574 (not any other version of it) than the address is 0x4E. Read the datasheet for the address related querry
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
that is how the addressing is. 0100 (4) and A2 A1 A0 pins are high by default. which makes the default address as 0x4E.
Ok, I do not know how it came but it is working now 🙂 Thank you.
Do I need to change / modify libs for sending ADC temperature results to my lcd ?
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.
For binary 01001100 , I think it should be 76 (decimal) and it means 0x4C instead of 0x4B
Yeah you are right… My bad, I’ll fix it.
Hello!
Redid the project in STM32CUBEIDE does not work. Blue pill board.
It should work. I’ll check and upload the CUBEIDE working code..
How can I send a project?
Download the project now. It works alright with CUBEIDE
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?
If you are using PCF8574, the address is 0x4E.
I am using PCF8574AT. The I2C address scanner on the terminal shows address = 3F.
Stm32 uses the i2c protocol in a manner that the address would be (address+read/write). This way in order to write (0), the address would be 0x7E for you.
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.
Are you planning to make an LCD 1602 project without an I2C interface in CUBEIDE?
Sure, could do that. Nobody ever requested it so i never bothered 😁. I will do that soon.
This is very good. I will look forward to it.
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?
The backlight was the issue but it’s fixed now. I don’t know what is happening in your case. Check the send command function in i2clcd.c file and confirm if it is same as below
0x0c , 0x08, 0x0c, 0x08
And data function should be 0x0d, 0x09, 0x0d, 0x09.
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??
What are u using to connect lcd to the I2C ?
If it is pcf8574, are u using the address 0x4E ?
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.
The address for the PCF8574 is not 0x27. Don’t use the arduino one. Look at the datasheet and use the proper address. I think it’s 0x4E
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…
it’s not working yet with STM32F107VCT6, will it work with 3.3V supply and bus ? thanks
What error are u facing ? Are you using pcf8574 ? Is the slave address correct ? Do the debugging and find out the error type. Only than i can help you.
And keep the vcc at 5v for the lcd
Sck and sda are connected to the microcontroller pins so they will be at 3.3v always.
Also try using pull up at sck and sda and see if it helps.
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
Can you share your entire code with cubemx file to help@controllerstech.com
can i send my code and check it please?
best regards sir
Try downloading the code again. It’s updated just now. If it doesn’t work than contact me at admin@controllerstech.com
it doesn’t work in my 16×4 lcd, can you help me to use 16×4 lcd?
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.
yeah sure you can use it
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();
}
Did you find mistake?
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
use the address 0x4E
i try it use 0x4E and others address but can’t work too,
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
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
1. If you are using pcf8574, the default address is 0x4E, so don’t test anything else.
2. I have updated the code here now it works with CUBEIDE by default. If u want to use keil, you have to create project and than unclude those 2 files.
Everything is working properly. I have rechecked it.
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?
Make sure the contrast is set properly. Most of the times, it is working but you can’t see because of contrast.
You can try using slave address 0x7E
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.
Check your system setup. Looks like some problem with the clock setup. Before attempting this, try blinking the LED.
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.
Working for me. Was a faulty bluepill
What did you do to work
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.
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
I never used delay after init not here not even in pic and arm7.. Things works sometimes
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
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
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?
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 ?
To get the commands just google lcd 16×2 commands.
To use it, there is a function available i.e. lcd_send_cmd (cmd);
for eg to clear screen, type lcd_send_cmd (0x01);
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++);
}
}
Nice work
Could you send me the library that you have used ( i2c-lcd )
You can download the code.. Libraries are inside it in SRC and INC folders
Would you know how to insert special characters in CGRAM in this I2C communication to the LCD?
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!
Nice.