PIC18F4550 LCD Interface

Nearly all small monochrome LCD modules utilize the Hitachi HD44780 controller. So when interfacing to an LCD module it’s the HD44780 that is actually being interacted with. The HD44780 has an 11 bit bus that can be re-configured as a 7 bit bus. One of the bits is a Read/Write pin. By grounding this pin the LCD module is placed in write-only mode. This reduces the bus size to either 10 or 6 bits. Because the LCD module is an output device it’s not essential to ever read it so this is a practical approach. Applications that do read the device are typically checking to see if it’s busy. However, by keeping the timing to the device at a relative slow pace, busy conditions can be avoided. The configuration below utilizes a 6 bit bus.

PIC 18 LCD Schematic


// PIC18F4550 Subroutines for the LCD

// Global Definitions
#define lcd_E LATBbits.LATB0
#define lcd_RS LATBbits.LATB3

void LCD_config(void)
{
// LCD Port Config
TRISBbits.TRISB0 = 0; // RB0 pin 33 set for output
TRISBbits.TRISB3 = 0; // RB3 pin 36 set for output
TRISDbits.TRISD7 = 0; // RD7 pin 30 set for output
TRISDbits.TRISD6 = 0; // RD6 pin 29 set for output
TRISDbits.TRISD5 = 0; // RD5 pin 28 set for output
TRISDbits.TRISD4 = 0; // RD5 pin 27 set for output
}

void LCD_cmd(unsigned char cmd)
{
lcd_E = 0; lcd_RS = 0; // Register Select is 0 for the command register
// In the HW, only the high nibble is connected to the LCD
// The high nibble is first
LATD = cmd; wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0;
// Shift the low nibble up and repeat
LATD = (cmd << 4); wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; wait_ms(5); // The low nibble of the D-port is being used by the key pad as an input // There is no interference because this routine only uses the D-latch } void LCD_data(unsigned char data) { lcd_E = 0; lcd_RS = 1; // Register Select is 1 for the data register LATD = data; wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; LATD = (data << 4); wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; } void LCD_init(void) { char cmd; lcd_E=0; lcd_RS=0; LCD_config(); cmd = 0x32; // Set Function - 8 bit interface, repeat (LCD wake up) LATD = cmd; wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; // put the command on the D port latch LATD = cmd; wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; LATD = cmd; wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; LATD = (cmd << 4); wait_ms(1); lcd_E=1; wait_ms(1); lcd_E=0; wait_ms(5); // Configuration LCD_cmd(0x28); // Set Function - 4 bit interface wih 2 lines 5x7 // Display Control - on, cursor, blink LCD_cmd(0x0C); // 0000 1 1 0 0 LCD_cmd(0x01); // Display Control - clear display LCD_cmd(0x06); // Entry Mode - auto increment cursor, no shift display LCD_cmd(0x80); // Line 1 pos 0; 0xC0 = Line 2 pos 0, add 1->F for other pos
}

void LCD_str_var(char *ptr_str)
{
char data = *ptr_str;
while(data) // the end of a string is a null character
{
LCD_data(data);
*ptr_str++; data = *ptr_str;

}
}

// void LCD_str_const(static const rom char *ptr_str) // C18
void LCD_str_const( const char *ptr_str) // xc8
{
char data = *ptr_str;
while(data)
{
LCD_data(data);
*ptr_str++; data = *ptr_str;
}
}

void LCD_int(int n)
{
int i = 15, r, m = n;
char c, digits[] = "display length "; // assign to get a null termination

while(m!=0)
{
r = m%10; m = m/10;
c = r+48;
digits[i] = c; // working backwards to fill the digit string
i--;
}
LCD_str_var(digits + i + 1);
}

void LCD_byte(unsigned char c)
{
char u_nib, l_nib;
u_nib = ((c >> 4) & 0x0F);
if(u_nib < 10) u_nib = u_nib + 48; else u_nib = u_nib + 55; l_nib = (c & 0x0F); if(l_nib < 10) l_nib = l_nib + 48; else l_nib = l_nib + 55; LCD_data(u_nib); LCD_data(l_nib); }