I am trying to interface a PIC16F690 with a HD44780 LCD module, in 4-bit mode. My connections are as follows:
RA5 <-> RS
RA4 <-> EN
RB4 <-> D4
RB5 <-> D5
RB6 <-> D6
RB7 <-> D7
GND <-> R/W
In the code, I have implemented the initialization for 4-bit operation procedure as per page 46 of the HD44780 LCD's datasheet, and the Alfred state webpage: https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_initialization/lcd_initialization_index.html
I am facing a couple of issues here:
- When I try to write a message to the LCD module, it is displayed as a series of seemingly random characters. Commonly, a character consisting of several horizontal lines will appear.
- The clear function does not work at all. In the main program loop, it does pause, but new characters keep being written without the previous ones being cleared as expected. I have tried brute-forcing this by directly setting the data lines to get a particular character as per the datasheet, but the characters appearing are incorrect.
- There is a blinking cursor, despite the Alfred State initialization procedure specifying this would not occur.
What am I missing here in order to fix these issues?
Here is my full code, I am using the XC8 compiler:
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _XTAL_FREQ 4000000
#pragma config FOSC=INTRCIO, WDTE=OFF, PWRTE=OFF, MCLRE=ON, CP=OFF, \
CPD=OFF, BOREN=OFF, IESO=OFF, FCMEN=OFF
// LCD connections
#define LCD_RS PORTAbits.RA5
#define LCD_EN PORTAbits.RA4
#define LCD_D4 PORTBbits.RB4
#define LCD_D5 PORTBbits.RB5
#define LCD_D6 PORTBbits.RB6
#define LCD_D7 PORTBbits.RB7
// LCD commands
#define LCD_CLEAR 0x01
#define LCD_HOME 0x02
#define LCD_MODE_4BIT 0x00
#define LCD_DISPLAY_ON 0x0C
#define LCD_CURSOR_OFF 0x0C
/*
* Pulse the LCD's enable pin. Falling-edge triggered.
* Instructions executed upon falling edge of this signal.
*/
void pulse_enable()
{
LCD_EN = 0;
__delay_us(1);
LCD_EN = 1;
__delay_us(1);
LCD_EN = 0;
__delay_us(100);
}
/*
* Sends a nibble (half a byte) to the LCD.
*/
void lcd_send_nibble(unsigned char nibble)
{
LCD_D4 = (nibble >> 0) & 0x01;
LCD_D5 = (nibble >> 1) & 0x01;
LCD_D6 = (nibble >> 2) & 0x01;
LCD_D7 = (nibble >> 3) & 0x01;
pulse_enable();
}
/*
* Sends a command to the LCD.
* Using 4-bit mode, sends the upper 4 bits first followed by the lower 4 bits.
*/
void lcd_send_command(unsigned char command)
{
LCD_RS = 0; // command mode
lcd_send_nibble(command >> 4); // Send the upper 4 bits
lcd_send_nibble(command); // Send the lower 4 bits
}
/*
* Sends data to the LCD.
*/
void lcd_send_data(unsigned char data)
{
LCD_RS = 1; // data mode
lcd_send_nibble(data >> 4); // Send the upper 4 bits
lcd_send_nibble(data); // Send the lower 4 bits
}
/*
* Initialises the LCD in 4-bit mode.
* https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_initialization/lcd_initialization_index.html
*/
void initialise_lcd()
{
LCD_RS = 0; // command mode
// step 1
__delay_ms(105); // Wait for the LCD to power up
// step 2
lcd_send_nibble(0x03);
__delay_us(4500);
// step 3
lcd_send_nibble(0x03);
__delay_us(105);
// step 4
lcd_send_nibble(0x03);
__delay_us(105);
// step 5
lcd_send_nibble(0x02);
__delay_us(105);
// step 6
lcd_send_nibble(0x02);
lcd_send_nibble(0x08);
__delay_us(55);
// step 7
lcd_send_nibble(0x00);
lcd_send_nibble(0x08);
__delay_us(55);
// step 8
lcd_send_nibble(0x00);
lcd_send_nibble(0x01);
__delay_ms(4);
// step 9
lcd_send_nibble(0x00);
lcd_send_nibble(0x06);
__delay_us(55);
// step 10
// end of initialisation
// step 11
// D=1 (display on)
// C=0 (cursor off)
// B=0 (cursor blink off)
lcd_send_nibble(0x00);
lcd_send_nibble(0x0C);
__delay_us(60);
}
/*
* Clears the LCD screen.
*/
void lcd_clear()
{
lcd_send_command(LCD_CLEAR);
__delay_ms(2);
}
/*
* Writes a string to the LCD.
*/
void lcd_write_string(const char* string)
{
while (*string)
{
lcd_send_data(*string++);
}
}
/*
* Program entry point.
*/
void main(void)
{
TRISA = 0x00; // Port A output
TRISB = 0x00; // Port B output
ANSEL = 0x00; // analog off
initialise_lcd();
lcd_clear();
LCD_RS = 1; // data mode
while(1)
{
lcd_write_string("Hello world!");
__delay_ms(1000);
lcd_clear();
__delay_ms(1000);
}
}
```
PORTx
to output a value, always useLATx
\$\endgroup\$