In the pervious guide (here), we took a look how to interface Liquid Crystal Display (LCD) in 4-bit mode which requires 4-line of data, RS line and Enable line, in total 6 lines of the mcu has to be reserved for the LCD. In this guide, we shall use PCF8574 which is 8-bit expander over I2C bus.
In this guide, we shall cover the following:
- Connection diagram
- Code
- Demo
1. Connection Diagram:
In this guide, we need the following part:
- LCD (16×2 or 20×4)
- PCF8574 for LCD module
- STM32F411RE Nucelo-64
2. Code:
First things first, we need to initialize the I2C of the stm32, you can find more information about I2C and how initialize it from here
Now, we need a function that allow us to write 4-bytes to the i2c bus with no memory address, just data
The function shall be as following:
void lcd_write_i2c(char saddr,uint8_t *buffer, uint8_t length) { while (I2C1->SR2 & I2C_SR2_BUSY); //wait until bus not busy I2C1->CR1 |= I2C_CR1_START; //generate start while (!(I2C1->SR1 & I2C_SR1_SB)){;} //wait until start is generated volatile int Temp; I2C1->DR = saddr<< 1; // Send slave address while (!(I2C1->SR1 & I2C_SR1_ADDR)){;} //wait until address flag is set Temp = I2C1->SR2; //Clear SR2 //sending the data for (uint8_t i=0;i<length;i++) { I2C1->DR=buffer[i]; //filling buffer with command or data while (!(I2C1->SR1 & I2C_SR1_BTF)); } I2C1->CR1 |= I2C_CR1_STOP; //wait until transfer finished }
Now, for the LCD. In order to send data or command, we need to mask the two lines of the LCD which they are En line and RS line.
When we want to display a character, we need to send data, hence we need to set RS low and high for command. This can be achieved as following:
For sending command
void lcd_send_cmd (char cmd) { char data_u, data_l; uint8_t data_t[4]; data_u = (cmd&0xf0); data_l = ((cmd<<4)&0xf0); data_t[0] = data_u|0x0C; //en=1, rs=0 data_t[1] = data_u|0x08; //en=0, rs=0 data_t[2] = data_l|0x0C; //en=1, rs=0 data_t[3] = data_l|0x08; //en=0, rs=0 lcd_write_i2c(SLAVE_ADDRESS_LCD,(uint8_t *)data_t,4); }
For sending data
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 data_t[1] = data_u|0x09; //en=0, rs=1 data_t[2] = data_l|0x0D; //en=1, rs=1 data_t[3] = data_l|0x09; //en=0, rs=1 lcd_write_i2c(SLAVE_ADDRESS_LCD,(uint8_t *)data_t,4); }
For the initializing the LCD, we need the send the following sequence
void lcd_init (void) { // 4 bit initialisation delay(50); // wait for >40ms lcd_send_cmd (0x3); delay(5); // wait for >4.1ms lcd_send_cmd (0x3); delay(1); // wait for >100us lcd_send_cmd (0x3); delay(10); lcd_send_cmd (0x2); // 4bit mode delay(10); // dislay initialisation lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit mode), N = 1 (2 line display) F = 0 (5x8 characters) delay(1); lcd_send_cmd (0x08); //Display on/off control --> D=0,C=0, B=0 ---> display off delay(1); lcd_send_cmd (0x01); // clear display delay(1); delay(1); lcd_send_cmd (0x06); //Entry mode set --> I/D = 1 (increment cursor) & S = 0 (no shift) delay(1); lcd_send_cmd (0x0C); //Display on/off control --> D = 1, C and B = 0. (Cursor and blink, last two bits) }
for set a cursor at certain location:
void setCursor(int a, int b) { int i=0; switch(b){ case 0:lcd_send_cmd(0x80);break; case 1:lcd_send_cmd(0xC0);break; case 2:lcd_send_cmd(0x94);break; case 3:lcd_send_cmd(0xd4);break;} for(i=0;i<a;i++) lcd_send_cmd(0x14); }
For writing a string:
void lcd_send_string (char *str) { while (*str) lcd_send_data (*str++); }
To clear the LCD, we can send the following command:
void lcd_clear (void) { #define LCD_CLEARDISPLAY 0x01 lcd_send_cmd(LCD_CLEARDISPLAY); delay(100); }
In our main.c file,
#include "i2c.h" #include "uart.h" #include "stdlib.h" #include "LiquidCrystal_PCF8574.h" uint16_t var; char str[40]; int main() { USART2_Init(); i2c_init(); lcd_init(); lcd_clear(); while(1) { setCursor(0,0); lcd_send_string("LCD 2004 I2C w STM32"); setCursor(0,1); lcd_send_string("EmbeddedExpert.io"); setCursor(0,2); lcd_send_string("Bare Metal"); setCursor(0,3); sprintf(str,"var=%d",++var); lcd_send_string(str); for (int i=0;i<100000;i++); } }
You can download the source code from here
3. Demo:
Happy coding 😀
Add Comment