HomeSTM32STM32 I2CInterface I2C LCD1602 (PCF8574) with STM32 | HAL Tutorial

Interface LCD 16×2 with STM32 using I2C

The STM32 I2C LCD1602 display is a simple yet powerful way to show text in embedded applications. In this guide, you’ll learn how to connect an I2C LCD with STM32 using the PCF8574 I/O expander and HAL library. By using only SDA and SCL lines, you can reduce GPIO usage and quickly display characters, numbers, and strings on a 16×2 LCD. This tutorial includes working code for lcd_init, lcd_send_cmd, and lcd_send_data, and works perfectly with STM32CubeIDE.

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:

STM32 LCD 16×2 Video Tutorial

Prefer video? Watch the same wiring, CubeMX setup, and code in action.

Watch the Video

What is LCD1602 I2C Display and How It Works with STM32

The LCD1602 is a widely used 16×2 alphanumeric display module capable of showing 2 lines of 16 characters each. The standard module requires at least 6 GPIO pins to interface with a microcontroller, but 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.

LCD16x2 displaying the string on both rows

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.

PCF8574 I2C Address Configuration Explained

The PCF8574 I/O Expander works with the I2C protocol, and every device on the I2C bus needs a unique address. The PCF8574 default address is fixed at the higher 4 bits (0100), while the lower 3 bits (A2, A1, A0) can be changed to avoid conflicts.

Why change the PCF8574 I2C address?

If you use only one PCF8574 chip, you don’t need to change its address. However, when you connect multiple devices—such as two or more I2C LCD1602 displays—you must assign different I2C addresses to each chip. Otherwise, address conflicts will occur, and the devices will not work properly on the same I2C bus.

PCF8574 Address Format

The address structure looks like this:
0100 A2 A1 A0 R/W

  • A2, A1, A0 → Selectable address pins
  • R/W → Defines Read (1) or Write (0) operation

Default PCF8574 Address

By default, all three pins (A0, A1, A2) are HIGH.

  • Default binary address: 0100 1110
  • Default hexadecimal address: 0x4E

How to change the PCF8574 address?

To set a new address:

  • Connect any of the pins A0, A1, A2 to GND.
  • Each change gives you a new valid I2C address.

Example:

  • If A0 = GND, the new address becomes 0100 1100 (0x4C).

This way, you can connect up to 8 PCF8574 devices (or 8 LCDs) on the same I2C bus.

Important: The last bit (R/W) is not part of the actual device address. It only tells whether the operation is read or write.

STM32 LCD1602 I2C Project Requirement

I’ve used products mentioned below in this project. I have also added affiliate links for your convenience — if you purchase through these links, it helps support my work at no extra cost to you.

STM32 LCD1602 I2C Schematic and Pinout (PCF8574 Module)

The image below shows the PCF8574 module which connects at the back of the LCD 16×2.

PCF8574 I2C 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:-

STM32 I2C LCD connection

The 16 pins from the PCF8574 module are connected to the LCD16x2. The connection with the STM32 is shown in the table below.

PCF8574 PinSTM32F103 PinDescription
VCC5VPower supply (5V)
GNDGNDGround
SDAPB7I²C Data line
SCLPB6I²C Clock line

STM32CubeMX Configuration

In this section, we’ll set up STM32CubeMX for I2C communication, pin mapping, clock settings, and code generation to prepare the project.

STM32 Clock Configuration

Below is the image showing the clock configuration for the project.

STM32 clock configuration for I2C  LCD 16x2

The STM32F103 is clocked by the external 8MHz crystal. We will use the PLL to run the system at maximum 72MHz clock.


LCD16x2 I2C Configuration

Below is the image showing the I2C configuraion.

STM32 I2C configuration for LCD 1602

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 I2C LCD1602 HAL Code Example

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

PCF8574 Pinout

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.

PCF8574 Address Definition

#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. It is already explained above in the Address section.


Sending LCD 16×2 Command

The function below actively sends a command byte to the LCD using the PCF8574 I2C expander in 4-bit mode.

void lcd_send_cmd (char cmd)
{
    char data_u, data_l;
    data_u = (cmd & 0xF0);           // extract upper 4 bits
    data_l = ((cmd << 4) & 0xF0);    // extract lower 4 bits

    uint8_t data_t[4];

    // send upper 4 bits with enable pulse
    data_t[0] = data_u | 0x0C;   // EN=1, RS=0  -> bxxxx1100
    data_t[1] = data_u | 0x08;   // EN=0, RS=0  -> bxxxx1000

    // send lower 4 bits with enable pulse
    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);
}

Step-by-Step Explanation

  • This function accepts a command byte as its input parameter.
  • Because the LCD runs in 4-bit mode, the function immediately splits the command into two parts:
    • The upper 4 bits (data_u)
    • The lower 4 bits (data_l)
  • Then, the function processes each part separately before transmitting it via I2C communication.

PCF8574 Pin Mapping

As explained in the image above, the PCF8574 I/O expander controls the LCD through its 8 pins (P0–P7). Here is the mapping:

  • P0 → RS (Register Select): set RS = 0 for command mode.
  • P1 → R/W (Read/Write): always 0 because we only write.
  • P2 → EN (Enable pin): toggled high and low to strobe data.
  • P3 → Backlight control: set to 1 to keep the LCD backlight ON.
  • P4–P7 → Data lines (D4–D7): carry the actual 4-bit data.

By controlling these pins, we actively manage how each command reaches the LCD.


How the Function Sends Data

To transmit data reliably, the function uses a strobe method:

  1. Send the upper 4 bits first
    • Set EN = 1 → latch the data
    • Set EN = 0 → complete the transfer
    • This process follows the LCD datasheet requirement.
  2. Send the lower 4 bits next
    • Again, toggle EN from 1 to 0 to ensure proper latching.
  3. Transmit all 4 bytes via I2C
    • The function finally calls HAL_I2C_Master_Transmit to send the 4 prepared bytes to the LCD using the STM32 I2C peripheral.

Why This Function Works

Because the LCD only accepts 4 bits at a time in 4-bit mode, this function ensures smooth data transfer. It actively splits the command, applies the correct PCF8574 pin configuration, and strobes the enable pin at the right moments. As a result, the LCD receives each command correctly and performs the desired operation.


Sending LCD16x2 Data

The data is sent in the similar manner as 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);
}

Initialize I2C LCD16x2

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

The above code is with reference to the pattern given for the initialization in the datasheet of the device.

Basically the function initializes the LCD in 4-bit mode.

  • It starts with delays and 0x30 commands to ensure the LCD resets properly, then switches to 4-bit mode with 0x20.
  • Next, it configures the display with 0x28 (4-bit, 2-line, 5×8 font).
  • It turns the display off (0x08), clears it (0x01), sets entry mode (0x06, auto-increment cursor, no shift), and finally turns the display on without cursor/blink (0x0C).

Send String to I2C LCD16x2

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.

The loop while (*str) keeps running as long as the current character in the string is not the null terminator. In other words, it processes each character of the string one by one until the end.


Set Cursor Position on LCD16x2

The function lcd_put_cur() moves the cursor to a specific row and column of the LCD.

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

For a 16×2 LCD:

  • Row 0 starts at DDRAM address 0x00 → Command = (0x80 | col)
  • Row 1 starts at DDRAM address 0x40 → Command = (0x80 | 0x40 | col) → (0xC0 | col)

For Example: lcd_put_cur(1,3) → cursor goes to row 1, column 3 (0xC3).

STM32 LCD16x2 HAL main() function

Now we will print some data on the LCD16x2 display using our functions inside the main function of the STM32 project.

Print 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.
  • Now put the cursor at the beginning of the 1st Row (0,0) and send the string “HELLO WORLD” to this location.
  • Next 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.

LCD1602 prints string

Print Number

We cannot print a number directly on the LCD16x2 display. By default, the display can only show ASCII characters. Therefore, before sending a number to the LCD, we must first convert the number into its ASCII representation. After conversion, the LCD can print it just like any other character or string.

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, which has 4 digits. Therefore, 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.
  • Next, put the cursor at the beginning of the 1st Row (0,0) and send the array.

Below is the output of the above code.

LCD1602 prints Number

Print 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.
  • Next put the cursor at the beginning of the 1st Row (0,0) and send the array.

Below is the output of the above code.

LCD1602 prints float

This STM32 I2C LCD1602 project shows how to print strings, numbers, and float values using only two pins. With the PCF8574 I2C expander and HAL, you get a scalable, low-GPIO solution ideal for IoT and embedded systems. Moreover, the STM32 LCD I2C method is efficient and easy to extend. However, if you prefer the classic approach, check the parallel LCD16x2 STM32 interface tutorial.

PROJECT DOWNLOAD

Info

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

FAQs — STM32 LCD 16×2 Using I2C

Subscribe
Notify of

122 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Djadeja
1 year 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
2 years ago

doesn’t write anything in the second line… (

Andrewtitoff
2 years 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
3 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
4 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
4 years ago

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

Sharan
Reply to  ControllersTech
4 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
4 years ago

does it work in mode 8bit?

Marceli
Reply to  ControllersTech
4 years ago

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

sina
Reply to  ControllersTech
3 years ago

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

sina
Reply to  ControllersTech
3 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  ControllersTech
3 years ago

no,this code for 8 bit mode

Juan Figueroa
4 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
4 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
5 years ago

How can set cursor in another position?

ColinC
5 years ago

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

Cheers

Cool guy
Reply to  ColinC
4 years ago

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

ColinC
5 years ago

Excellent. Worked first time. Thanks a lot.

lala
5 years ago

well done, thanks

Leonardo Ferreira
5 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  ControllersTech
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  ControllersTech
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  ControllersTech
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 ([email protected]) 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  ControllersTech
5 years ago

How can I send a project?

Pavel
Reply to  ControllersTech
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  ControllersTech
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  ControllersTech
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  ControllersTech
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
6 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
6 years ago

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

Anonymous123
6 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  ControllersTech
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  ControllersTech
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
6 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  ControllersTech
6 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  ControllersTech
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  ControllersTech
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  ControllersTech
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  ControllersTech
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
7 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
7 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
8 years ago

Nice.