How to Interface 16x2 LCD with Microcontroller in Embedded C
To interface a
16x2 LCD with a microcontroller in Embedded C, connect the LCD data pins and control pins (RS, RW, EN) to microcontroller ports, then send commands and data using specific functions. Initialize the LCD with commands for 4-bit or 8-bit mode, then write characters to display text.Syntax
The basic steps to interface a 16x2 LCD in Embedded C include initializing the LCD, sending commands, and sending data. You use functions like lcd_command() to send instructions and lcd_data() to send characters.
Typical pins used are:
- RS: Register Select (command/data)
- RW: Read/Write (usually grounded for write)
- EN: Enable pin to latch data
- D4-D7: Data pins for 4-bit mode
Example function calls:
lcd_command(0x38); // Function set: 8-bit, 2 lines, 5x7 dots
lcd_command(0x0C); // Display ON, Cursor OFF
lcd_command(0x01); // Clear display
lcd_data('A'); // Display character 'A'
c
void lcd_command(unsigned char cmd); void lcd_data(unsigned char data); void lcd_init(void);
Example
This example shows how to initialize a 16x2 LCD in 4-bit mode and display "HELLO" on the first line.
c
#include <xc.h> #define _XTAL_FREQ 4000000 // Define LCD control pins #define RS LATBbits.LATB0 #define EN LATBbits.LATB1 // Define LCD data pins (D4-D7) #define D4 LATDbits.LATD4 #define D5 LATDbits.LATD5 #define D6 LATDbits.LATD6 #define D7 LATDbits.LATD7 void pulse_enable() { EN = 1; __delay_ms(1); EN = 0; __delay_ms(1); } void lcd_command(unsigned char cmd) { RS = 0; // Command mode // Send higher nibble D4 = (cmd >> 4) & 1; D5 = (cmd >> 5) & 1; D6 = (cmd >> 6) & 1; D7 = (cmd >> 7) & 1; pulse_enable(); // Send lower nibble D4 = cmd & 1; D5 = (cmd >> 1) & 1; D6 = (cmd >> 2) & 1; D7 = (cmd >> 3) & 1; pulse_enable(); __delay_ms(2); } void lcd_data(unsigned char data) { RS = 1; // Data mode // Send higher nibble D4 = (data >> 4) & 1; D5 = (data >> 5) & 1; D6 = (data >> 6) & 1; D7 = (data >> 7) & 1; pulse_enable(); // Send lower nibble D4 = data & 1; D5 = (data >> 1) & 1; D6 = (data >> 2) & 1; D7 = (data >> 3) & 1; pulse_enable(); __delay_ms(2); } void lcd_init() { TRISBbits.TRISB0 = 0; // RS output TRISBbits.TRISB1 = 0; // EN output TRISD = 0x00; // Data pins output __delay_ms(20); // Wait for LCD to power up lcd_command(0x02); // Initialize LCD in 4-bit mode lcd_command(0x28); // 4-bit, 2 line, 5x7 font lcd_command(0x0C); // Display ON, Cursor OFF lcd_command(0x06); // Entry mode lcd_command(0x01); // Clear display __delay_ms(2); } void main() { lcd_init(); char *msg = "HELLO"; while(*msg) { lcd_data(*msg++); } while(1); }
Output
HELLO displayed on the first line of the 16x2 LCD
Common Pitfalls
- Not initializing the LCD properly before sending data causes no display.
- Incorrect wiring of RS, EN, or data pins leads to garbled output.
- Forgetting to set RW pin to write mode (usually grounded) can block communication.
- Sending commands/data without proper delays can cause LCD to miss instructions.
- Mixing 4-bit and 8-bit mode commands without matching wiring causes errors.
c
/* Wrong: No delay after command */ lcd_command(0x01); // Clear display lcd_data('A'); // May not show 'A' because LCD is busy /* Right: Add delay to let LCD process */ lcd_command(0x01); __delay_ms(2); lcd_data('A');
Quick Reference
| Command | Hex Code | Description |
|---|---|---|
| Clear Display | 0x01 | Clears the LCD screen |
| Return Home | 0x02 | Cursor returns to first position |
| Entry Mode Set | 0x06 | Cursor moves right, no display shift |
| Display ON/OFF | 0x0C | Display ON, cursor OFF |
| Function Set | 0x28 | 4-bit mode, 2 lines, 5x7 font |
| Set DDRAM Address | 0x80 | Set cursor position |
Key Takeaways
Always initialize the LCD with correct commands before sending data.
Use proper delays after commands to ensure LCD processes instructions.
Connect RS, EN, and data pins correctly and set RW to write mode (usually grounded).
Choose 4-bit or 8-bit mode and match wiring and commands accordingly.
Send commands with RS=0 and data with RS=1 to control the LCD properly.