# Interfacing 20x4 character LCD with TI TM4C1294 generates some random characters

I have a 20x4 character LCD connected to my TI TM4C1294 kit as follows:

1. Data bus to port A.
2. RS, RW and E pins to port L 0-2

I wrote this code in order to drive it:

#include <stdint.h>
#include <stdbool.h>
#include "inc/tm4c1294ncpdt.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"

void LCD_SendChar(short c)
{
GPIO_PORTA_AHB_DATA_R = c;
GPIO_PORTL_DATA_R |= 0x01;
GPIO_PORTL_DATA_R |= 0x04;
asm ("  NOP");
GPIO_PORTL_DATA_R &= ~(0x04);
LCD_IsBusy();
}

void LCD_SendCmd(short command)
{
GPIO_PORTA_AHB_DATA_R = command;
GPIO_PORTL_DATA_R |= 0x00;
GPIO_PORTL_DATA_R |= 0x04;
asm ("  NOP");
GPIO_PORTL_DATA_R &= ~(0x04);
LCD_IsBusy();
}

void LCD_SendString(char* value)
{
while(*value != 0)
{
LCD_SendChar(*value++);
}
}

void LCD_Init()
{
LCD_SendCmd(0x01);
LCD_SendCmd(0x38);
LCD_SendCmd(0x0E);
LCD_SendCmd(0x06);
LCD_SendCmd(0x02);
}

int main(void)
{
// Enable clock gating control for GPIO A and L
SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R10;
SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R0;

// Wait until GPIO blocks are ready.
while((SYSCTL_PRGPIO_R & SYSCTL_PRGPIO_R10) == 0);
while((SYSCTL_PRGPIO_R & SYSCTL_PRGPIO_R0 ) == 0){};

// Configure as Output, digital, no alternate or analog functions.
GPIO_PORTA_AHB_DIR_R = 0xFF;
GPIO_PORTA_AHB_DEN_R = 0xFF;
GPIO_PORTA_AHB_AFSEL_R = 0x0;
GPIO_PORTA_AHB_AMSEL_R = 0x0;
GPIO_PORTA_AHB_PCTL_R  = 0x0;

GPIO_PORTL_DIR_R = 0x7;
GPIO_PORTL_DEN_R = 0x7;
GPIO_PORTL_AFSEL_R = 0x0;
GPIO_PORTL_AMSEL_R = 0x0;
GPIO_PORTL_PCTL_R  = 0x0;

LCD_Init();

LCD_SendCmd(0x94);
LCD_SendString("Hello World!");
LCD_SendCmd(0xD4);
LCD_SendString("Hello World!");

while (1);

}


The board is running at a 120MHz speed.

The problem is with line addressing. I found out that the line addressing for a 20x4 LCD are 0x80, 0xC0, 0x94, 0xD4 for lines 1 to 3 respectively. When sending a "Hello World" on two lines as shown the two strings are displayed on the same line with a random character between. If I try one "Hello World" for one line while changing line address each time it works fine. What could be the reason for not addressing the lines correctly? and what's the deal with such random characters?

I suspect with:

1. Values provided for each delay function are not accurate.
2. I have read that I have to check if the LCD is busy or not but I did not understand the mechanism behind it.

Update: Besides Finbarr's solution, I managed to check the busy flag. Here is my function

void LCD_IsBusy()
{
GPIO_PORTA_AHB_DIR_R = 0x00;
GPIO_PORTL_DATA_R  = 0x00;
GPIO_PORTL_DATA_R |= 0x02;
while(GPIO_PORTA_AHB_DATA_R & 0x80)
{
GPIO_PORTL_DATA_R &= ~(0x04);
GPIO_PORTL_DATA_R |= 0x04;
}

GPIO_PORTA_AHB_DIR_R = 0xFF;
}


Which helped me to eliminate all of the delay calls. The above code is updated.

• Are you sure that your character display supports 3,3V logic? Some of them are 5V only.
– filo
Aug 31, 2017 at 9:57
• "2. I have read that I have to check if the LCD is busy or not but I did not understand the mechanism behind it". So you just ignored it. You certainly need to add that, it normally involves reading the status register, checking the "busy" flag and not sending anything more until it's clear. Aug 31, 2017 at 10:02
• @Finbarr The thing is some codes they pulse the E ping high then while loop check, then disable the E pin. Other codes in the while loop check they pulse high-to-low. Neither worked. Aug 31, 2017 at 10:24
• You would normally set E high, read the data then set it low again to complete the read cycle. Repeat until the busy flag shows clear. Aug 31, 2017 at 10:28
• @Finbarr What is the delay needed for the cycle from high to low? Aug 31, 2017 at 10:30

Your function LCD_SendCmd contains the following line:

GPIO_PORTL_DATA_R |= 0x00;


Which does nothing. What it should do is set bit 0 to zero to access the command register, so replace it with

GPIO_PORTL_DATA_R &= ~(0x01);


otherwise it will never get cleared once you've started writing data.

You also need to add code to check the busy status to make sure the display is ready for more data or commands; this normally involves reading the status register and testing the Busy flag. The display datasheet will tell you which bit this is.

Checking the busy flag means also that you don't have to put arbitrarily long delays into the code to make sure commands are completed other than for the initial setup operations as defined in the datasheet (because the busy flag doesn't start working until the display processor starts running) and for making sure that the setup, hold and minimum cycle times for the control lines are maintained. If in doubt, these are best checked with an oscilloscope rather than relying on delay functions.

• Another point for the OP is that if the Busy check is put in place most of the SysCtlDelay() calls can be eliminated except for the one that assures the width of the E pulse. The OP should look at the E pulse on a scope to ensure that it is the correct width. Who knows how much SysCtlDelay(18) really is. Aug 31, 2017 at 10:30
• That did the trick. Also I figured out how to check the busy flag for the LCD which helped me as @MichaelKaras said will eliminate most of the delay calls. Thank you very much! Aug 31, 2017 at 11:01