Skip to content

Character LCD Display Interface ​

Complete Lab Manual

For the complete experiment including learning objectives, theoretical background, and detailed explanations, download the PDF manual: Download Experiment 7 PDF

Examples ​

Example: Basic LCD Driver Implementation ​

LCD Connection Schematic

The figure above shows the complete wiring diagram for connecting the 16×2 LCD module to the TM4C123 microcontroller using 4-bit mode. The connections are:

  • Power: VDD to VBus, VSS to Ground, V0 to contrast potentiometer
  • Control: RS to PB0, E to PB2, RW to Ground (write-only)
  • Data: D4-D7 to PB4-PB7 respectively
  • Backlight: A to VBus, K to Ground

The contrast potentiometer (typically 10kΩ) allows adjustment of the display visibility - rotating it changes the voltage on V0 pin between 0V and VDD.

The following code demonstrates a complete LCD driver in 4-bit mode with initialization, command/data transmission, and text display functions.

h
#ifndef LCD_H
#define LCD_H

#include "TM4C123.h"

// LCD pin definitions (connected to PORTB)
#define RS         (1 << 0)  // PB0
#define EN         (1 << 2)  // PB2
#define DATA_MASK  0xF0      // PB4-PB7

// Function prototypes
void LCD_Init(void);
void LCD_Command(unsigned char cmd);
void LCD_Data(unsigned char data);
void LCD_Clear(void);
void LCD_SetCursor(unsigned char row, unsigned char col);
void LCD_Print(char *str);
void delay_us(int us);
void delay_ms(int ms);

#endif
c
#include "lcd.h"

#define CYCLES_PER_US   (SystemCoreClock / 1000000u)



//====================[ SysTick Delay Functions ]====================
void SysTick_Init(void)
{
    SysTick->CTRL = 0;
    SysTick->LOAD = CYCLES_PER_US - 1;  // 1us delay at 50MHz
    SysTick->VAL = 0;
    SysTick->CTRL = 0x5;     // Enable with system clock
}

void delay_us(int us)
{
    SysTick->LOAD = (CYCLES_PER_US * us) - 1;
    SysTick->VAL = 0;
    SysTick->CTRL = 0x5; // Enable with system clock
    while ((SysTick->CTRL & 0x10000) == 0);
    SysTick->CTRL = 0;
}

void delay_ms(int ms)
{
    while (ms--)
        delay_us(1000);
}

//====================[ LCD Helper Functions ]====================
void LCD_EnablePulse(void)
{
    delay_us(1);
    GPIOB->DATA |= EN;
    delay_us(1);
    GPIOB->DATA &= ~EN;
    delay_us(1);
}

void LCD_SendNibble(unsigned char nibble)
{
    // Send nibble to PB4-PB7
    GPIOB->DATA = (GPIOB->DATA & ~DATA_MASK) | ((nibble << 4) & DATA_MASK);
    LCD_EnablePulse();
}

//====================[ LCD Initialization ]====================
void LCD_Init(void)
{
    // Enable clock to PORTB
    SYSCTL->RCGCGPIO |= (1 << 1);
    while ((SYSCTL->PRGPIO & (1 << 1)) == 0)
        ;

    // Configure PB0 (RS), PB1 (EN), PB4-PB7 (data) as output
    GPIOB->DIR |= RS | EN | DATA_MASK;
    GPIOB->DEN |= RS | EN | DATA_MASK;
    GPIOB->DATA &= ~(RS | EN | DATA_MASK);  // Clear all

    SysTick_Init();

    delay_ms(50);  // Wait for LCD to power up

    // Initialization sequence (8-bit interface mode to start)
    LCD_SendNibble(0x03);
    delay_ms(5);

    LCD_SendNibble(0x03);
    delay_us(150);

    LCD_SendNibble(0x03);
    delay_us(150);

    LCD_SendNibble(0x02);  // Set 4-bit mode
    delay_us(150);

    // Now in 4-bit mode: use full commands
    LCD_Command(0x28); // Function set: 4-bit, 2 lines, 5x8 dots
    LCD_Command(0x0C); // Display ON, Cursor OFF
    LCD_Command(0x06); // Entry mode: increment cursor
    LCD_Command(0x01); // Clear display
    delay_ms(2);
}

//====================[ LCD Command/Data API ]====================
void LCD_Command(unsigned char command)
{
    GPIOB->DATA &= ~RS; // RS = 0 for command
    delay_us(1);
    LCD_SendNibble(command >> 4);     // Upper nibble
    LCD_SendNibble(command & 0x0F);   // Lower nibble
    delay_ms(2);
}

void LCD_Data(unsigned char data)
{
    GPIOB->DATA |= RS; // RS = 1 for data
    delay_us(1);
    LCD_SendNibble(data >> 4);
    LCD_SendNibble(data & 0x0F);
    delay_ms(1);
}

void LCD_Clear(void)
{
    LCD_Command(0x01);
    delay_ms(2);
}

void LCD_SetCursor(unsigned char row, unsigned char col)
{
    unsigned char address = (row == 0) ? 0x80 + col : 0xC0 + col;
    LCD_Command(address);
    delay_ms(1);
}

void LCD_Print(char *str)
{
    while (*str)
    {
        LCD_Data(*str++);
    }
}
c

#include "TM4C123.h"
#include "lcd.h"




int main(void)
{

    LCD_Init();
    LCD_Clear();        // Ensure display is clear
    LCD_SetCursor(0,0); // Set cursor to beginning
    LCD_Print("ENCS4110 Lab");
    
    while(1)
    {	
    }
}

Code Explanation ​

Initialization Sequence

The LCD_Init() function implements the complete 4-bit initialization:

  1. Enables GPIO PORTB clock and configures pins as outputs
  2. Waits 50 ms for LCD power-on stabilization
  3. Sends 0x03 (upper nibble) three times with delays (8-bit mode reset)
  4. Sends 0x02 (upper nibble) to switch to 4-bit mode
  5. Sends configuration commands: 0x28 (4-bit, 2 lines), 0x0C (display on), 0x06 (entry mode), 0x01 (clear)

Nibble Transmission

The LCD_SendNibble() function:

  • Masks out current data bits (PB4-PB7)
  • Places the 4-bit nibble on PB4-PB7 (shifted left by 4)
  • Generates enable pulse: delay → E high → delay → E low → delay

Command vs. Data

  • LCD_Command(): Sets RS=0, sends upper nibble, sends lower nibble
  • LCD_Data(): Sets RS=1, sends upper nibble, sends lower nibble

Cursor Positioning

The LCD_SetCursor(row, col) function calculates the DDRAM address:

c
address = (row == 0) ? 0x80 + col : 0xC0 + col;

Then sends the address as a command.

String Printing

The LCD_Print(str) function iterates through the string and sends each character using LCD_Data().

Tasks ​

Task 1: Display Your Name and ID ​

Update the main program to display your name on the first line and your student ID on the second line of the LCD.

Requirements:

  • Clear the display
  • Set cursor to line 1, column 0
  • Print your name (up to 16 characters)
  • Set cursor to line 2, column 0
  • Print your student ID

Hint:

c
LCD_Clear();
LCD_SetCursor(0, 0);  // Line 1
LCD_Print("Your Name");
LCD_SetCursor(1, 0);  // Line 2
LCD_Print("ID: 1234567");

Task 2: Button-Controlled Name Scrolling ​

Write a program that displays your name on the LCD and allows the user to scroll the text left or right using the two on-board push buttons (SW1 and SW2).

Requirements:

  • Display your name on line 1
  • Configure SW1 (PF4) and SW2 (PF0) with GPIO interrupts (falling edge, internal pull-up)
  • When SW1 is pressed: Shift display left (command 0x18)
  • When SW2 is pressed: Shift display right (command 0x1C)
  • The display should not scroll automatically; only respond to button presses

Task 3: Bidirectional Continuous Scrolling ​

Write a program that displays your name on line 1 and your student ID on line 2, with continuous scrolling in opposite directions after a button press.

Requirements:

  • Display your name on line 1 and ID on line 2
  • Initially, the display is static (no scrolling)
  • When SW1 is pressed, start continuous scrolling:
    • Line 1 scrolls right
    • Line 2 scrolls left
  • Pressing SW1 again stops the scrolling
  • Use a timer interrupt to handle the scrolling at a fixed interval (e.g., every 500 ms)