It is best if we package up the commands to make the LCD work as a set of functions which we can then re-use.
Note how I have put comments at the top for anyone who wants to change my program to another hardware configuration. I've also arranged things so that the only connection-specific code is in two functions, which I've identified in the comments. This is just good programming practice. If you know something at the time you do it you should write it down. It is amazing how quickly you will forget how you did something!
/* EX 5.1 LCD utilities */
/* Rob Miles April 2000 */
/* connects 2 line LCD panel */
/* using Hitachi 44780 chip */
/* assigments as follows: */
/* PORTB:0 -----> LCD bit 4 */
/* PORTB:1 -----> LCD bit 5 */
/* PORTB:2 -----> LCD bit 6 */
/* PORTB:3 -----> LCD bit 7 */
/* PORTB:4 -----> LCD RS */
/* PORTB:5 -----> LCD E */
/* lcd display driver code */
/* Rob Miles 2000 */
/* Include the hardware include*/
/* for register access */
#include <hardware.h>
/* function prototypes */
char lcd_start ( void ) ;
char lcd_clear ( void ) ;
char lcd_print_ch ( char ch ) ;
char lcd_print (
const char * message ) ;
char lcd_cursor ( char x, char y ) ;
/* You will have to change these */
/* bits if the hardware changes */
/* You will also need to change: */
/* setup_lcd and lcd_raw_send */
/* masks for control bits */
#define RSMASK 0x10
#define EBIT 0x05
/* defined values for delays */
/* tested up to 20Mhz PICmicro */
/* standard write delay */
#define PUTCH_DELAY 3000
/* clear and cursor home take */
/* longer: special delay for them */
#define CLEAR_DELAY 20000
/* delay to let the LCD settle */
#define POWER_UP_DELAY 10000
/* delay to let the ports settle */
#define BIT_DELAY 2
void lcd_delay ( int size )
{
int i ;
for ( i = 0 ; i < size ; i++ ) ;
}
/* sends a byte out to the LCD */
/* the byte is given by in, the */
/* mask is used to allow the */
/* state of RS to be set as well */
void lcd_raw_send (
unsigned char in,
unsigned char mask )
{
unsigned char pb ;
/* use a PICmicro assembler */
/* to swap the nibbles in the */
/* input */
/* puts high nibble at the */
/* bottom of the byte */
asm
{
swapf _in,F
}
/* OR in the mask */
pb = (in & 0x0f ) | mask ;
/* OR in the other bits */
/* PORTB */
pb = pb | ( PORTB & 0xc0 ) ;
/* send the data */
/* send the data */
/* don't disturb the other */
/* bits in PORTB */
PORTB = pb ;
/* let the bits settle */
lcd_delay ( BIT_DELAY ) ;
/* now clock the bit out */
/* by raising and lowering E */
PORTB = pb | 1 << EBIT ;
/* let the bits settle */
lcd_delay ( BIT_DELAY ) ;
PORTB = pb ;
/* put the low nibble back */
/* into in */
asm
{
swapf _in,F
}
/* OR in the mask */
pb = (in & 0x0f ) | mask ;
/* OR in the other bits */
/* PORTB */
pb = pb | ( PORTB & 0xc0) ;
/* send the data */
/* don't disturb the other */
/* bits in PORTB */
PORTB = pb ;
/* let the bits settle */
lcd_delay ( BIT_DELAY ) ;
/* now clock the bit out */
/* by raising and lowering E */
PORTB = pb | 1 << EBIT ;
/* let the bits settle */
lcd_delay ( BIT_DELAY ) ;
PORTB = pb ;
/* do the delay here */
lcd_delay (PUTCH_DELAY) ;
}
/* puts a character at the */
/* cursor position */
char lcd_print_ch ( char in )
{
/* use raw send with RS set */
lcd_raw_send ( in, RSMASK ) ;
return 1 ;
}
/* sends a command to the LCD */
void lcd_command (
unsigned char in )
{
lcd_raw_send ( in, 0 ) ;
}
/* clear the display */
/* and home the cursor */
char lcd_clear ( void )
{
lcd_command ( 0x01 ) ;
/* do extra delay here */
lcd_delay (CLEAR_DELAY) ;
lcd_command ( 0x02 ) ;
/* do extra delay here */
lcd_delay (CLEAR_DELAY) ;
return 1 ;
}
/* position the cursor */
/* 0,0 is the top left corner */
char lcd_cursor ( unsigned char x,
unsigned char y )
{
if ( y==0 )
{
/* position for line 0 */
y=0x80 ;
}
else
{
/* position for line 1 */
y=0xc0 ;
}
lcd_command ( y+x ) ;
return 1 ;
}
const unsigned char lcd_init [5] =
{
/* LCD initialize */
0x33,
/* Set for 4 bit operation */
0x32,
/* Set for 2 line LCD */
0x2c,
/* Select move after write */
0x06,
/* disp. on cursor off blink off*/
0x0c
} ;
char lcd_start (void)
{
unsigned char i, t ;
/* set bits of PORTB for output */
/* change for different hardware */
/* clear bottom five bits for LCD*/
/* don't change any other bits */
TRISB = (TRISB & 0xc0) ;
/* give the LCD time to settle */
/* from power up */
lcd_delay ( POWER_UP_DELAY ) ;
for ( i=0 ; i < 5 ; i++ )
{
lcd_command ( lcd_init [i] ) ;
}
lcd_clear () ;
return 1 ;
}
/* lcd_print accepts a pointer */
/* parameter which points at the*/
/* string to be printed */
char lcd_print (
const unsigned char * mess )
{
unsigned char i = 0 ;
/* treat the pointer as an */
/* array and send bytes until*/
/* we find an element with 0 */
/* in it */
while ( mess [i] != 0 )
{
lcd_print_ch ( mess[i] ) ;
i++ ;
}
return 1 ;
}
const unsigned char hello [6] =
{
'h','e','l','l','o',0x00
} ;
void main ( void )
{
unsigned char x, y ;
unsigned char i = 0 ;
lcd_start () ;
lcd_cursor ( 5, 0 ) ;
lcd_print ( hello ) ;
lcd_cursor ( 0, 0 ) ;
lcd_print_ch ( 'x' ) ;
lcd_cursor ( 15, 0 ) ;
lcd_print_ch ( 'x' ) ;
lcd_cursor ( 0, 1 ) ;
lcd_print_ch ( 'x' ) ;
lcd_cursor ( 15, 1 ) ;
lcd_print_ch ( 'x' ) ;
while (1) ;
}