This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

interfacing TM4C123GH6PM with LCD

Other Parts Discussed in Thread: TM4C123GH6PM

Hello I am having troubles getting my code to inteface with the LCD. My LCD has 16 Pins.

I am using code composer studio.

I am also working with one header tm4c123gh6pm.h

My configuration:

Pin Number Symbol
1 Vss -GND
2 Vdd - VBUS
3 VO - 10k pot
4 RS
5 R/W - GND to always write
6 E
7 DBO - GND
8 DB1 - GND
9 DB2 - GND
10 DB3 - GND
11 DB4 
12 DB5
13 DB6
14 DB7
15 VCC
16 GND

I am using PB0 to PB3 for D4 to D7

I am using pin PB4 for RS

i am using pin PB5 for EN

This is my code:

#include "G:\Embedded Folder CCS\tm4c123gh6pm.h"

#define RS 16 /* BIT0 mask */
#define EN 32 /* BIT1 mask 00100000*/
#define SCB_CPAC (*((volatile unsigned int*)0xE000ED88))
#define PORTB_DATA (*((volatile uint32_t *)0x400053FC))
#define SYSCTL_RCGCGPIO (*((volatile uint32_t *)0x400FE608))
#define DIR (*((volatile uint32_t *)0x40005400))
#define DEN (*((volatile uint32_t *)0x4000551C))
#include <stdint.h>

void delayMs(int n){
int i, j;
for (i = 0; i < n; i ++){
for( j = 0; j < 3180; j ++) {
} /* do nothing for 1 ms */
} /* delay n microseconds (16 MHz CPU clock) */
}

void LCD_nibble_write(unsigned char data, unsigned char control){
data &=0xF0; /* clear lower nibble for control */
control &= 0x0F; /* clear upper nibble for data */
PORTB_DATA = data|control; /* RS = 0, R/W =0 */
PORTB_DATA = data|control|EN; /* pulse E */
delayMs(1);
PORTB_DATA = data;
delayMs(1);
PORTB_DATA = 0;
}

void LCD_command(unsigned char command){
LCD_nibble_write(command & 0xF0, 0); /* upper nibble first */
LCD_nibble_write(command << 4, 0); /* then lower nibble */
if (command < 4){
delayMs(2); /*commands 1 and 2 need up to 1.64 ms */
}
else{
delayMs(1); /* all others 1 ms */
}
}

void LCD_data(unsigned char data){
LCD_nibble_write(data & 0xF0, RS); /* upper nibble first */
LCD_nibble_write(data << 4, RS); /* then lower nibble */
delayMs(1); /* delay 1 ms */
}

void LCD_init(void){
//SCB_CPAC |= 0x00F00000; /* this is required for the floating point coprocessor */
SYSCTL_RCGCGPIO |= 0x02; /* enable clock to GPIOB */
DIR = 0xFF; /* set all PORTB pins as output */
DEN = 0xFF; /* set all PORTB pins as digital pins */

delayMs(20); /* initialization sequence */
LCD_nibble_write(0x30, 0);

delayMs(5);
LCD_nibble_write(0x30, 0);

delayMs(1);
LCD_nibble_write(0x30, 0);

delayMs(1);
LCD_nibble_write(0x20, 0); /*use 4-bit data mode */

delayMs(1);
LCD_command(0x28); /* set 4-bit data, 2-line, 5x7 font */

LCD_command(0x06); /* move cursor right */

LCD_command(0x01); /* clear screen, move cursor to home */

LCD_command(0x0F); /* turn on display, cursor blinking */
}

int main(void){

LCD_init();

for(;;) {

LCD_command(1); /* clear display */
LCD_command(0x0F); /* LCD cursor location */

delayMs(500);
LCD_data('H');
LCD_data('e');
LCD_data('l');
LCD_data('l');
LCD_data('o');
delayMs(500);
}
}

The goal here is to write "hello" to the LCD.

Thank you for the help.

  • Jenna Russwurm said:
    I am having troubles getting my code to inteface with the LCD

    What troubles?   Display too dark, too light, unstable, text inverted, text scrambled, random dots?   All fit your definition of "troubles" - do they not?

    How have you connected the Lcd's contrast pot?   The wiper should be able to swing from +5V to 0V - does it do that?

    Most always the display initialization is mis-timed - and 4 bit Lcd operation is (especially) unforgiving.

    Have you ohm'ed out - or better scoped out - your MCU signals at the actual display pins?   You need to confirm that your code is following your program's desires - do you not?

    This should serve as a beginning for you - detail (beyond troubles) most always IS required...

  • everything is working on the hardware part of the LCD side its just that fact that my program is not properly sending hello to it as I do not think i am writing correctly in my code. I have updated my code to use port A for EN = PA7 and RS = PA6. My data pins are still on port B.

    Here is my updated code:

    #include "G:\Embedded Folder CCS\tm4c123gh6pm.h"

    #define RS 0b0100000 /* BIT0 mask */
    #define EN 0b1000000 /* BIT1 mask 00100000*/
    #define SCB_CPAC (*((volatile unsigned int*)0xE000ED88))
    #define PORTB_DATA (*((volatile uint32_t *)0x400053FC))
    #define PORTA_DATA (*((volatile uint32_t *)0x400043FC))
    #define SYSCTL_RCGCGPIO (*((volatile uint32_t *)0x400FE608))
    #define DIRB (*((volatile uint32_t *)0x40005400))
    #define DENB (*((volatile uint32_t *)0x4000551C))
    #define DIRA (*((volatile uint32_t *)0x40004400))
    #define DENA (*((volatile uint32_t *)0x4000451C))
    #include <stdint.h>

    void delayMs(int n){
    int i, j;
    for (i = 0; i < n; i ++){
    for( j = 0; j < 3180; j ++) {
    } /* do nothing for 1 ms */
    } /* delay n microseconds (16 MHz CPU clock) */
    }

    void LCD_nibble_write(unsigned char data, unsigned char control){
    data &=0xF0; /* clear lower nibble for control */
    control &= 0x0F; /* clear upper nibble for data */
    PORTA_DATA &= ~RS;
    PORTB_DATA = data|control; /* RS = 0, R/W =0 */
    delayMs(1);
    PORTA_DATA |= EN;
    PORTB_DATA = data|control|EN; /* pulse E */
    delayMs(1);
    PORTB_DATA = data;
    delayMs(1);
    PORTB_DATA = 0;
    PORTA_DATA &= ~EN;
    delayMs(1);
    PORTA_DATA |= RS;
    }

    void LCD_command(unsigned char command){
    LCD_nibble_write(command & 0xF0, 0); /* upper nibble first */
    LCD_nibble_write(command << 4, 0); /* then lower nibble */
    if (command < 4){
    delayMs(2); /*commands 1 and 2 need up to 1.64 ms */
    }
    else{
    delayMs(1); /* all others 1 ms */
    }
    }

    void LCD_data(unsigned char data){
    LCD_nibble_write(data & 0xF0, RS); /* upper nibble first */
    LCD_nibble_write(data << 4, RS); /* then lower nibble */
    delayMs(1); /* delay 1 ms */
    }

    void LCD_init(void){
    SYSCTL_RCGCGPIO |= 0x02; /* enable clock to GPIOB */
    SYSCTL_RCGCGPIO |= 0x01; /* enable clock to GPIOA */
    DIRB = 0xFF; /* set all PORTB pins as output */
    DENB = 0xFF; /* set all PORTB pins as digital pins */
    DIRA = 0xFF; /* set all PORTA pins as output */
    DENA = 0xFF; /* set all PORTA pins as digital pins */

    delayMs(20); /* initialization sequence */
    LCD_nibble_write(0x30, 0);

    delayMs(5);
    LCD_nibble_write(0x30, 0);

    delayMs(1);
    LCD_nibble_write(0x30, 0);

    delayMs(1);
    LCD_nibble_write(0x20, 0); /*use 4-bit data mode */

    delayMs(1);
    LCD_command(0x28); /* set 4-bit data, 2-line, 5x7 font */

    LCD_command(0x06); /* move cursor right */

    LCD_command(0x01); /* clear screen, move cursor to home */

    LCD_command(0x0F); /* turn on display, cursor blinking */
    }

    int main(void){

    LCD_init();

    for(;;) {

    LCD_command(1); /* clear display */
    LCD_command(0x0F); /* LCD cursor location */

    delayMs(500);
    LCD_data('H');
    LCD_data('e');
    LCD_data('l');
    LCD_data('l');
    LCD_data('o');
    delayMs(500);
    }
    }
  • Jenna Russwurm said:
    everything is working on the hardware part of the LCD side

    May I ask, "How & why you believe this to be so?"   If this was the case - would not the display be performing to your expectations?

    I've provided direction toward your harvesting of (real) and meaningful data.   (via probing @ the Lcd - not the MCU)

    I've just skimmed your code - there are obvious deficiencies.   Quickly:

    LCD_command(1); /* clear display */
    LCD_command(0x0F); /* LCD cursor location */

    There must be a substantial delay after sending 0x01.   Does your code include that?   (unless it's embedded w/in "LCD_Command()" it does not!)   That same CLS (clear screen, 0x01) command also "homes the cursor" - thus your "Cursor Positioning" - immediately following - is unlikely to be required.

    You need to absolutely insure that your initialization codes are reaching the display - and including the specified delays - ALL of them!

  • There is an delay built into lcd_command allowing for their to be a 2 ms delay for any commands sent less than 4 and 1 ms for the rest. MY LCD lights up and I can adjust the brightness with my 10k pot. I just don't think that my LCD_nibble_write() function is working properly with setting my RS and EN values.
  • Thought some more about your code on the drive home - I (now) find that you did embed a delay w/in the "LCD_Command()" function when the command is 0x01 or 0x02.   And your follow w/ 0x0F also proves correct.   (although it is incorrectly commented as Lcd cursor location - it does NOT "locate" the cursor - instead it "formats" the cursor (could be on steady, blinking, block, line or off.)

    Your send of "Hello" to the screen will fail unless there are 40µS delays following each display write.   (I hadn't planned on reviewing each/every line of your (or others') code.)

    Your issue may result from:

    • bad connection between MCU board & display
    • incorrect initialization - either improper code or timing - often both
    • incorrect massage of "normal" 8 bit port data into 2 nibbles - which then are presented to the display.   Specifically, "I am using PB0 to PB3 for D4 to D7."   This proves curious - does it not?   Would it not have proven smarter, easier & more consistent - to have chosen PB4-PB7 for (Lcd: D4-D7?)   As a result - you must SHIFT each/every byte to get the most significant nibble (msn) into the (lsn) as the MCU may only transmit that (lower) nibble.   Again it is far simpler - and less krazy making - to employ "matching" MCU data & display data pins!

    I've designed/produced/sold many thousands of these displays - it is always unwise to "start w/4 bits."   Yes you save 4 signal lines - but time & effort will be sacrificed - and (most always) exceed the time saved by adding those 4 extra connections.   (when implemented with an ID cable & headers - that time/effort is zero!)

    Note too that when issuing a display Command - RS must remain at 0 throughout the 2 nibbles.   Lcd addressing & initialization are both considered as Commands.

    When issuing display Data - RS must remain at 1 throughout (again) 2 nibbles.   You cannot alter RS & E together (in a single instruction) as set-up and/or hold times are violated.

    Note that you zero your data as final instruction - that's unnecessary - simply presenting the correct data to your Port "B" will suffice.

    Really - never "hope or assume" that correct data passes from your MCU to the display.   Probing at the display IS required - and prevents "Krazy-Making" (both of you & hapless helpers...)

  • Ive changed my code into a simpler form and am still not getting and change from the LCD.

    I believe that my delays, EN and RS are set more so correct now though?

    #include "G:\Embedded Folder CCS\tm4c123gh6pm.h"

    #define RS 4 /* BIT4 mask */
    #define EN 8 /* BIT8 mask */
    #define SCB_CPAC (*((volatile unsigned int*)0xE000ED88))
    #define PORTB_DATA (*((volatile uint32_t *)0x400053FC))
    #define PORTA_DATA (*((volatile uint32_t *)0x400043FC))
    #define SYSCTL_RCGCGPIO (*((volatile uint32_t *)0x400FE608))
    #define DIRB (*((volatile uint32_t *)0x40005400))
    #define DENB (*((volatile uint32_t *)0x4000551C))
    #define DIRA (*((volatile uint32_t *)0x40004400))
    #define DENA (*((volatile uint32_t *)0x4000451C))
    #include <stdint.h>

    void delayMs(int n){
    int i, j;
    for (i = 0; i < n; i ++){
    for( j = 0; j < 3180; j ++) {
    } /* do nothing for 1 ms */
    } /* delay n microseconds (16 MHz CPU clock) */
    }

    void LCD_CMD(unsigned char command){
    unsigned char x;
    x = (command & 0xF0) >> 2;
    PORTB_DATA &= ~0x3F;
    PORTB_DATA |= x;

    delayMs(1);
    PORTA_DATA &= ~RS; //set RS to command (RS=0)
    delayMs(1);
    PORTA_DATA |= EN; //set enable high
    delayMs(5);
    PORTA_DATA &= ~EN; //set enable low
    delayMs(15);
    x = (command & 0x0F) << 2;

    PORTB_DATA &= ~0x3F;
    PORTB_DATA |= x;
    PORTA_DATA |= EN; //enable
    delayMs(5);
    PORTA_DATA &= ~EN; //enable set low
    delayMs(15);
    }

    void LCD_DATA(unsigned char data){
    unsigned char x;

    x = (data & 0xF0) >> 2;
    PORTB_DATA &= ~0x3C;

    PORTB_DATA |= x;
    delayMs(1);
    PORTA_DATA |= RS;
    delayMs(1);
    PORTA_DATA |= EN;
    delayMs(1);
    PORTA_DATA &= ~EN;
    delayMs(5);

    x = (data & 0xF0) << 2;
    PORTB_DATA &= ~0x3C;

    PORTB_DATA |= x;
    PORTA_DATA |= EN;
    delayMs(1);
    PORTA_DATA &= ~EN;
    delayMs(15);
    }

    void LCD_init(void){
    SYSCTL_RCGCGPIO |= 0x02; /* enable clock to GPIOB */
    SYSCTL_RCGCGPIO |= 0x01; /* enable clock to GPIOA */
    DIRB = 0xFF; /* set all PORTB pins as output */
    DENB = 0xFF; /* set all PORTB pins as digital pins */
    DIRA = 0xFF; /* set all PORTA pins as output */
    DENA = 0xFF; /* set all PORTA pins as digital pins */
    }


    int main(void){

    LCD_init();
    LCD_CMD(0x30);/*initialization sequence*/
    delayMs(1);
    LCD_CMD(0x30);
    delayMs(1);
    LCD_CMD(0x30);
    delayMs(1);
    LCD_CMD(0x28); /* set 4-bit data, 2-line, 5x7 font*/
    delayMs(1);

    LCD_CMD(0x06); /* move cursor right */
    delayMs(1);
    LCD_CMD(0x0E);
    delayMs(1);
    LCD_CMD(0x01);
    delayMs(1);
    LCD_CMD(0x80);
    delayMs(1);

    LCD_DATA('A');
    delayMs(1);
    LCD_DATA('B');
    delayMs(1);
    LCD_DATA('C');
    delayMs(1);



    for(;;);
    }
  • Jenna Russwurm said:
    I believe that my delays, EN and RS are set more so correct now though

    May we note that you've been asked repeatedly to test/verify that your "belief and/or hope" is real?   (by probing for - & measuring - that the correct signals & signal durations (arrive) at the LCD pins)   And no such confirmation arrives!

    Test equipment exists for good reason - in your case - even a simple, current limited Led could suffice.   (although time-consuming & only as a 'general" duration indicator)

    I strongly urge you (again) to move beyond (unjustified) "belief" and perform the indicated measurements.

    It is always wisest to employ separate ports for "Control" and "Data."   Unless you're very careful - and disciplined - it is likely that you'll corrupt the control signals when they share the same port.

    Minus your performing the suggested measurements I wish you well & exit...

  • I have gotten some output on the keypad and it seem to be working a bit  sending out a weird e character. Then it suddenly stopped working. I now have d0 to d7 on b0 to b7 and Rs on a2 and en on a3. So i now have some indication that there is contact

  • We are NOT communicating! And now - the added complexity of a newly introduced device (keypad) - arrives! How does that make sense?

    Your (required) detail in providing (necessary) info (may) require slight tweaking. ("seem to be working" - that's opinion - and minus description of, "how you came to that conclusion" is not especially comforting nor complelling!)

    It's good you accepted the advice to remove RS & E from the data port. And good that you've not "dumped" long program code, again.

    Can you borrow, buy, (even steal) some form of test equipment - and verify the presence & duration of signals - AT THE LCD? (really the lcd - not at the MCU)

    Assumption, belief and seem are NOT "good enough." (frankly they stink - better you hear this now than (later) "on the job.") Note that none of this is directed toward your person - but ALL is directed to "methods" which reveal not far from "witch-craft."
  • Sorry it's early I meant that I got a weird e like character on the LCD. I have not added a key pad. Would a logic analyzer suffice and use it for the pb pins then ?
  • No one wants your sorrow - you are clearly smart enough to conquer this - but your ability to "follow directions" (or explain why you reject such) does require some thought.

    A DVM would work - L.A. far better - scope probably easiest. Send easily recognizable chars to the display (such as 0xAA or 0x55 and confirm the "safe arrival" of each MCU bit "AT THE LCD's PIN!" (every single pin!) You can note the persistence of your "E" pulse - it should remain high for (past 450µS, now perhaps 300µS).

    There are few (actually NO) shortcuts. Only when the HW is tested/verified should we battle the code.

    The fact that you've gotten some char to appear is GREAT! The "weird e" may indicate that bit 7 has (mistakenly) been set high - and a Japanese character has revealed... Allez - hook up and VERIFY! (and verify one (better many) of my Answers!)
  • I hooked up a logic analyzer because that is what is available at my college. All the pins are reading values. My concern is that my timing in setting Rs, EN and sending data maybe be off. In using the timing diagram if I'm using a ms delay would that be too long ? Should I bring it down to using ns delays or can't I "get away with" only using ms delays?
  • I suggest that in this critical, initial design phase that one ALWAYS & ONLY use long (~5mS) delays! Who cares if it takes a (bit) longer for characters to march across your screen? The alternative - insufficient delay - GUARANTEES that your code will not be well received by the Lcd!

    You've not responded to my earlier note that RS & E "cannot" toggle together - separate instructions must toggle either.

    Also - when I wrote (long ago) the industry standard App Note for text displays we always employed separate functions for "Character Writes/Reads vs. Command Writes/Reads." You've bunched those together - adding complexity - thus your arrival (and parking) here!

    Your school appears unaware (or unmindful) of KISS. That's great - until you get a tech job - and boss demands results - TODAY! Simplify, shorten and exhaustively test.

    Are you able to get the screen to Clear when you send 0x01 w/RS LOW throughout?   Follow that w/0x0E (solid line cursor) and the display should be "empty" w/the pixel field (just) visible.   (you must adjust the contrast pot to achieve that)   If you cannot achieve that goal it is likely your connections - or initialization - or code is mistaken!

    Earlier I alerted you to the added complexity resulting from your choosing PB0-PB3 rather than PB4-PB7 for attachment to (like) Lcd pins D4-D7.   Have you made that change.   In that manner - when you send 0xAB to the display - you need only pass the unshifted byte (0xAB) first - then rotate 4 times left - and send the (remaining nibble) B.  Your E must properly pulse upon each nibble transmission.   There is no need to "and out" the unwanted nibble - only (and always) only 4 bits connect (and transfer) between MCU and display.  

    Is this understandable - might you follow?

    You should also fully describe your entire initialization sequence - first in plain English: (i.e. 3, 3, 3, 2, 2, 8, 0, E, 0, 1 etc) (this from memory - I've not used in years - the world (but for here) has moved to color graphic screens...)