0
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Are you sure that your character display supports 3,3V logic? Some of them are 5V only. \$\endgroup\$ – filo Aug 31 '17 at 9:57
  • 1
    \$\begingroup\$ "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. \$\endgroup\$ – Finbarr Aug 31 '17 at 10:02
  • \$\begingroup\$ @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. \$\endgroup\$ – 3bdalla Aug 31 '17 at 10:24
  • \$\begingroup\$ 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. \$\endgroup\$ – Finbarr Aug 31 '17 at 10:28
  • \$\begingroup\$ @Finbarr What is the delay needed for the cycle from high to low? \$\endgroup\$ – 3bdalla Aug 31 '17 at 10:30
4
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ – Michael Karas Aug 31 '17 at 10:30
  • \$\begingroup\$ 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! \$\endgroup\$ – 3bdalla Aug 31 '17 at 11:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.