Stuck 20x4 LCD - Page 1
Forum section: electronics / general

author: Remark
author_rank: Contributor
timestamp: January 22, 2023, 07:26:00 pm
content: Hello everyone.I am making a user settings panel with an 20x4 LCD screen.I had a problem that after a few minutes of operation the LCD screen goes blank or some hieroglyphs start to appear:I'm using STM32F103 (bluePill) and 20x4 LCD with PCF8574 I/O expander for I2C bus. I tried to change the I2C bus clock speed from standard to fast mode (from 100000 to 400000 Hz). LCD freezing has become less common (it works fine longer), but the problem has not gone away.The LCD is not placed very close to the microcontroller, but connected via a <50cm flex cable...This is my LCD init sequence:Code:void lcd_init(void) {// 4 bit initialisationHAL_Delay(50); // wait for >40mslcd_send_cmd(0x30);HAL_Delay(5); // wait for >4.1mslcd_send_cmd(0x30);HAL_Delay(1); // wait for >100uslcd_send_cmd(0x30);HAL_Delay(10);lcd_send_cmd(0x20); // 4bit modeHAL_Delay(10);// display initialisationlcd_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 offHAL_Delay(1);lcd_send_cmd(0x01); // clear displayHAL_Delay(1);HAL_Delay(1);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)}LCD clear:Code:void lcd_clear(void) {lcd_send_cmd(0x00);for (int i = 0; i < 100; i++) {lcd_send_data(' ');}//HAL_Delay(2);}Send command:Code:void lcd_send_cmd(char cmd) {char data_u, data_l;uint8_t data_t;data_u = (cmd & 0xf0);data_l = ((cmd << 4) & 0xf0);data_t = data_u | 0x0C; //en=1, rs=0data_t = data_u | 0x08; //en=0, rs=0data_t = data_l | 0x0C; //en=1, rs=0data_t = data_l | 0x08; //en=0, rs=0//HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t*) data_t, 4,//100);HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t*) data_t, 4,100);//HAL_Delay(2);}And send string function:Code:void lcd_send_string(char *str) {while (*str)lcd_send_data(*str++);//HAL_Delay(1);}maybe I should use some sort of interrupts to solve the problem? Looks like I2C frame misses some bits. Maybe someone have faced a similar problem?Thank you.Remark.

author: Andy Watson
author_rank: Super Contributor
timestamp: January 22, 2023, 07:45:55 pm
content: I am not familiar with the use of I2C on these displays, however, I do not see any provision to read the "busy" status of the display, unless it is built-in to the library calls. These displays are relatively slow and it is easy to send data too fast.

author: ozcar
author_rank: Frequent Contributor
timestamp: January 22, 2023, 08:46:14 pm
content: When I did this, I also did not read the busy status, and just relied on delays as necessary.I did however notice that the various LCD controller specs that I consulted said that it was necessary for the RS and R/W lines to be valid for some time before raising E, so I transmitted an extra byte if required:Code:// pins on PCF8574Aconst uint8_t lcd_rs = 0x01; // LOW: instruction/command. HIGH: data.const uint8_t lcd_rw = 0x02; // LOW: write to LCD. HIGH: read from LCD.const uint8_t lcd_enable = 0x04; // HIGH = enable (latch at fall back to LOW)const uint8_t lcd_backlight = 0x08;uint8_t lcd_idata = {0xff}; // up to 5 i2C bytes to send 1 byte to LCDuint8_t lcd_light; // 0 or lcd_backlight bit onHAL_StatusTypeDef I2C_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);// One i2c byte has to be sent for each change of state of the lcd lines.// E has to be raised and lowered to clock out the data, however, LCD controller specs// say that the RS and R/W lines have to be valid anywhere between 40 to 150nS BEFORE// raising E, hence an additional i2c byte may be required initially, keeping E low.// (currently, R/W is always low)void lcd_wi4(uint8_t c) // 4 bits to lcd instruction/command register{uint8_t l,i=0;l = c<<4 | lcd_light; // shift lower 4 bits to correct positions// Last byte sent kept in first byte of lcd_idata. Check if RS (or R/W) have changed:if ( (l&(lcd_rs|lcd_rw)) != (lcd_idata&(lcd_rs|lcd_rw)) )lcd_idata[i++] = l; // ENABLE=0, RS=0, R/W=0lcd_idata[i++] = l|lcd_enable; // ENABLE=1, RS=0, R/W=0lcd_idata[i++] = l; // ENABLE=0, RS=0, R/W=0I2C_Transmit (&hi2c1, i2c_lcd, lcd_idata, i, 100);lcd_idata = l; // keep for next time}void lcd_wi8(uint8_t c) // 8 bits to lcd instruction/command register{uint8_t u,l,i=0;u = (c&0xf0) | lcd_light; // leaves upper 4 bits in correct positionsl = c<<4 | lcd_light; // shift lower 4 bits to correct positions// Last byte sent kept in first byte of lcd_idata. Check if RS (or R/W) have changed:if ( (u&(lcd_rs|lcd_rw)) != (lcd_idata&(lcd_rs|lcd_rw)) )lcd_idata[i++] = u; // ENABLE=0, RS=0, R/W=0lcd_idata[i++] = u|lcd_enable; // ENABLE=1, RS=0, R/W=0lcd_idata[i++] = u; // ENABLE=0, RS=0, R/W=0lcd_idata[i++] = l|lcd_enable; // ENABLE=1, RS=0, R/W=0lcd_idata[i++] = l; // ENABLE=0, RS=0, R/W=0I2C_Transmit (&hi2c1, i2c_lcd, lcd_idata, i, 100);lcd_idata = l; // keep for next time}void lcd_wd8(uint8_t c) // 8 bits to lcd DD ram{uint8_t u,l,i=0;u = (c&0xf0) | lcd_rs | lcd_light; // leaves upper 4 bits in correct positionsl = c<<4 | lcd_rs | lcd_light; // shift lower 4 bits to correct positions// Last byte sent kept in first byte of lcd_idata. Check if RS (or R/W) have changed:if ( (u&(lcd_rs|lcd_rw)) != (lcd_idata&(lcd_rs|lcd_rw)) )lcd_idata[i++] = u; // ENABLE=0, RS=1, R/W=0lcd_idata[i++] = u|lcd_enable; // ENABLE=1, RS=1, R/W=0lcd_idata[i++] = u; // ENABLE=0, RS=1, R/W=0lcd_idata[i++] = l|lcd_enable; // ENABLE=1, RS=1, R/W=0lcd_idata[i++] = l; // ENABLE=0, RS=1, R/W=0I2C_Transmit (&hi2c1, i2c_lcd, lcd_idata, i, 100);lcd_idata = l; // keep for next time}I never tried it without that extra check, so maybe that is not necessary.

author: james_s
author_rank: Super Contributor
timestamp: January 22, 2023, 09:17:04 pm
content: Are you sure all of the connections are good? The picture is a bit dark but the solder joints look pretty messy. What is your supply voltage? Some of these displays will run on 3.3V but others need 5V for correct operation.

author: ozcar
author_rank: Frequent Contributor
timestamp: January 23, 2023, 01:24:27 am
content: Quote from: james_s on January 22, 2023, 09:17:04 pmGood point about the voltage. I had the LCD and I2C converter running on 5V, with SDA and SCL going through a level converter.Also, the mention of "stuck" and "freezing" does remind me now of trouble that I had right in the beginning, which was the reason for me having the I2C_Transmit() routine rather than directly calling HAL_I2C_Master_Transmit(). But that did not cause rubbish to appear on the display, so this must be something different.

author: CDN_Torsten
author_rank: Regular Contributor
timestamp: January 23, 2023, 02:13:31 am
content: I have seen this quite a few times over the years with these displays...usually when run in parallel mode.In pretty well all cases it was related to signal integrity.I your case it may be either in the serial-to-parallel converter or (most likely) it's related to the long serial cable. Either it's picking up noise or it's excess capacitance attenuating the clk/data signal.Things to try (for the cable):To help with noise pick-up, use a cable with alternating GND conductors (I'm assuming you are using ribbon cable). This will help in many cases if the problem is noise or signal ringing.unless:If it's excess capacitance, you should be able to see this with a scope...but be careful not to load the line further with the scope probe....

author: tooki
author_rank: Super Contributor
timestamp: January 29, 2023, 03:57:18 pm
content: Quote from: Remark on January 22, 2023, 07:26:00 pm”Image not found”. Please attach it to your post here, using the forum’s own hosting.Have you looked at the I2C on an oscilloscope?Have you tried reducing the value of the pull-up resistors (or adding another pull-up)?

author: Remark
author_rank: Contributor
timestamp: February 05, 2023, 09:41:54 pm
content: Thank you all very much for the replies.For some reason, increasing the frequency (leaving it at max) and passing an extra byte based on ozcar's answer solved the problem.Also, I had foolishly left the LCD initialization function being called repeatedly.Now the system works without interruptions. I will try to test further and see if there are any problems.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: February 06, 2023, 10:58:58 pm
content: Timing can be problematic. The datasheet of the HD44780 assumes a certain clock speed for the timing, and there is no guarantee your display has the same clock speed. There are probably also multiple clones of this chip. It's quite likely the orignal is not made anymore, which means the datasheet is also likely to not fit entirely correct.But I'm happy you got it working. If it was still going badly, I'd recommend to hook up an EUR 10 Logic analyzer and look at the signals with Sigrok.Half a meter of I2C cables can also start to become a problem. Have you checked rise times on an oscilloscope? Stiffer pullup resistors may help. And of course power supply quality, both decoupling and buffer capacitors and the other usual suspects.