Interfacing with a PIC-n-LCD Serial LCD

copyright, Peter H. Anderson, Baltimore, MD, Dec, '98


Introduction

Routine LCD_F84.C provides a number of utilities for displaying information on a serial LCD. This might be used as a part of a design, or in the debugging phase to display critical values. This is particularly useful for those who do not have an emulator. This collection of routines may be used with a PIC16F84, PIC16C558 or similar.

Functions include;

void lcd_delay_ms(int t);		// provide a delay of t ms
void lcd_delay_10us(unsigned char t);	// delay of t * 10 usecs
void lcd_init(void);			// initialize LCD (0x0c)
void out_ROM_str(const char *s);	// output a string from ROM
void out_RAM_str(char *s);		// from RAM
void lcd_hex_byte(unsigned char val);	// display a value in hex
void lcd_dec_byte(unsigned char val);	// display a value in decimal
char num_to_char(unsigned char val);	// convert to ASCII
void lcd_char(char c);			// output a character to LCD
void lcd_new_line(void);   		// output 0x0d, 0x0a

In the following, program TST_LCD.C is used to test all of these functions.
// TST_LCD.C
// 
// Routine for demonstrating various utilities for displaying on 
// PIC-n-LCD.
//
// Build project with
//
//	TST_LCD.C
//	LCD_F84.C
//
// copyright, Peter H. Anderson, Baltimore, MD, Jan, '99

#include 
#include 

// LCD_F84.C routines
extern void lcd_delay_ms(int t);
extern void lcd_delay_us(unsigned char t);
extern void lcd_init(void);
extern void out_ROM_str(const char *s);
extern void out_RAM_str(char *s);
extern void lcd_hex_byte(unsigned char val);
extern void lcd_dec_byte(unsigned char val);
extern char num_to_char(unsigned char val);
extern void lcd_char(char c);
extern void lcd_new_line(void);

main(void)
{
   char s[10];
   unsigned int value;
   while(1)
   {
   lcd_init();		// send init char (0x0c) to lcd
   lcd_delay_ms(1000);
   strcpy(s, "Hello");	// display "Hello"
   out_RAM_str(s);
   lcd_new_line();	// new line
   
   value = 200;		// display 200 in hex 
   lcd_hex_byte(value);

   lcd_char(' ');
   lcd_dec_byte(value);	// display 200 in decimal
   lcd_delay_ms(1000);
 }  
#asm
DONE:
   goto DONE
#endasm      
}   

Timing

In the following collection of utilities such time critical tasks as the 9600 baud serial output and the delay routine are implemented in assembly.

Assembly routines reference global C variables with a leading underscore.

For example, unsigned byte LCD_DLY is declared as global. It is referenced in the assembly program as;

	DECFSZ _LCD_DLY, F
LCD_F84.C
// LCD_F84.C
//
// Utilities for interfacing with PIC-n-LCD.  Uses RA.0 as output
// to PIC-n-LCD.
//
// Note that serial is inverted.
//
// Intended for such PICs as 16F84 and 16C558.
//
// copyright P. H. Anderson, Baltimore, MD, Dec, '98
//

#include  // modify for your application

//	define Serial output pin

static bit	TxData @ (unsigned)&PORTA*8+0;		/* bit0 in port A */

void lcd_delay_ms(int t);
void lcd_delay_10us(unsigned char t);
void lcd_init(void);
void out_ROM_str(const char *s);
void out_RAM_str(char *s);
void lcd_hex_byte(unsigned char val);
void lcd_dec_byte(unsigned char val);
char num_to_char(unsigned char val);
void lcd_char(char c);
void lcd_new_line(void);

/globals
unsigned char LCD_DLY, LCD_BITNO, LCD_CH;
 
void lcd_delay_ms(int t) 	// delays t millisecs
{
   do
   {
     lcd_delay_10us(100);
   } while(--t);	
}

void lcd_delay_10us(unsigned char t)	// delays t * usecs
{	
   LCD_DLY=t;
#asm
LCD_DELAY_10US_1:
   clrwdt			;10 usec loop
   nop
   nop
   nop
   nop
   nop
   nop
   decfsz _LCD_DLY, f
   goto LCD_DELAY_10US_1
#endasm      
}      	

void lcd_init(void)	// sets TxData in idle state and resets PIC-n-LCD
{
   TxData = 0;	// idle condition
   TRISA0=0;	// make TxData an output
   lcd_char(0x0c);
   lcd_delay_ms(250);
}
	
void out_ROM_str(const char *s)	// outputs null terminated string
{
   while(*s)	// if what is pointed to by s is not zero
   {
      lcd_char(*s);
      ++s;
   }
}

void out_RAM_str(char *s)
{
   while(*s)
   {
      lcd_char(*s);
      ++s;
   }
}

void lcd_hex_byte(unsigned char val) // displays val in hex format
{
   char c;
   c = num_to_char((val>>4) & 0x0f);
   lcd_char(c);
   c = num_to_char(val&0x0f);
   lcd_char(c);
}

void lcd_dec_byte(unsigned char val) // displays byte in decimal
{
   unsigned char d;
   char c;
   d=val/100;
   c=num_to_char(d);
   lcd_char(c);
   
   val=val%100;
   d=val/10;
   c=num_to_char(d);
   lcd_char(c);   	
   
   d=val % 10;
   c=num_to_char(d);
   lcd_char(c);
}   
   
char num_to_char(unsigned char val)	// converts val to hex character
{
   char c;
   if (val < 10)
   {
     c=val+'0';
   }
   else
   {
     val=val-10;	   
     c=val + 'A';
   }
   return(c);
}

void lcd_new_line(void)	// outputs 0x0d, 0x0a
{
   lcd_char(0x0d);
   lcd_delay_ms(10);	// give the PIC-n-LCD time to perform the 
   lcd_char(0x0a);	// new line function
   lcd_delay_ms(10);
}   

void lcd_char(char c)	// serial output to PIC-n-LCD, 9600 baud
{
  
   		// start bit + 8 data bits
   LCD_CH = c;
#asm
   movlw 9 		;start bit + 8 data bits
   movwf _LCD_BITNO	;
   bcf _STATUS, 5	; switch to bank 0, RP0 is 5
   bcf _STATUS, 0	; 0 is carry - start bit

LCD_CHAR_1:		; overhead is 8~
   btfss _STATUS, 0	; 0 is carry
   bsf _PORTA, 0	;   output a zero
   btfsc _STATUS, 0
   bcf _PORTA, 0
   movlw 32		; 32 * 3~ + 8~ = 104 usecs
   movwf _LCD_DLY

LCD_CHAR_2:
   decfsz _LCD_DLY, f	; three ~
   goto LCD_CHAR_2
   rrf _LCD_CH, f	; 1 is F
   decfsz _LCD_BITNO, f	; 1 is F
   goto LCD_CHAR_1
   
   bcf _PORTA, 0

   movlw 96		; time for stop bits
   movwf _LCD_DLY;

LCD_CHAR_3:
   decfsz _LCD_DLY, f	; three ~
   goto LCD_CHAR_3
#endasm   
}