• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
Resource icon

Simple Serial LCD 2013-04-08

Creating a serial LCD can be very useful for quick debugging; as well as projects requiring LCD support with minimal interface. The circuit presented here may not satisfy all needs, but it should serve as a good starting point for further work.

How does serial LCD interface work?
It is quite simple really. Using a microcontroller with a built-in HW UART, the information form the HW UART is received and passed on to the LCD using 4-bit control. The LCD is a standard 2x16 character based display compatible with a HD44780 controller. The microcontroller is a 16F688 with built-in UART. A baud-select function is included to allow for the selection of two different input baud rates. The following figure shows how the circuit used.




Code Description:
The code for the simple serial LCD was created using Oshonsoft Basic. This language is not as complete or provides as much functionality as others (e.g. CCS C), but it can be readily obtained and used.

The code can be summarized as follows:
  • On power-up read the baud-select input (select between 9600bps and 1200bps),
  • Continuously monitor the HW UART and parse any received characters,
  • a null character is ignored,
  • a carriage return starts the second line of the LCD,
  • an escape character clears the LCD,
  • everything else is passed on to the LCD.

Oshonsoft Code:
Code:
'* PIC16F688 Serial LCD Test Bed *'
'Title: Simple Serial LCD Test Program
'Description: This program uses PIC16F688 to drive a 2x16 LCD from an RS-232 input (9600bps)
'Author: languer (2010)
'Pin Allocation:
	'PIN#   Main_Fn
	'RA0 -> PGD - Programming data
	'RA1 -> PGC - Programming clock
	'RA2 -> not used
	'RA3 -> not used
	'RA4 -> LCD EBIT
	'RA5 -> LCD RSBIT
	'RC0 -> LCD DB4
	'RC1 -> LCD DB5
	'RC2 -> LCD DB6
	'RC3 -> LCD DB7
	'RC4 -> BAUD SELECT
	'RC5 -> RS232 RX
'Usage Information:
	'BAUD Select
		'low -> 1200
		'high -> 9600
	'RS232 Input
		'0x00 -> ignore
		'0x0D -> LCD line 2, clear line 2
		'0x1B -> LCD line 1, clear LCD

'General Device Configuration
Define CONF_WORD = 0x33c4
OSCCON = 0x70  'define internal 8MHz clock
Define CLOCK_FREQUENCY = 8

'HW UART Setup
Define ALLOW_MULTIPLE_HSEROPEN = 1

'LCD Configuration
Define LCD_BITS = 4  'allowed values are 4 and 8 - the number of data interface lines
Define LCD_DREG = PORTC
Define LCD_DBIT = 0  '0 or 4 for 4-bit interface, ignored for 8-bit interface
Define LCD_RSREG = PORTA
Define LCD_RSBIT = 5
Define LCD_EREG = PORTA
Define LCD_EBIT = 4
Define LCD_RWREG = 0  'set to 0 if not used, 0 is default
Define LCD_RWBIT = 0  'set to 0 if not used, 0 is default
Define LCD_COMMANDUS = 2000  'delay after LCDCMDOUT, default value is 5000
Define LCD_DATAUS = 50  'delay after LCDOUT, default value is 100
Define LCD_INITMS = 10  'delay used by LCDINIT, default value is 100


'Variable Declarations
Const trisa1 = %11001111
Const trisc1 = %11010000

Symbol lcd_db4 = PORTC.0
Symbol lcd_db5 = PORTC.1
Symbol lcd_db6 = PORTC.2
Symbol lcd_db7 = PORTC.3
Symbol lcd_rs = PORTA.4
Symbol lcd_e = PORTA.5

Symbol baud_select = PORTC.4
Symbol rs232_rx = PORTC.5

Dim _true As Bit
Dim _false As Bit
Dim char As Byte

_true = True
_false = False

'Main Program
main:
	Call init()
	WaitMs 1000
	While _true
		Call get_rs232_char()
		Call send_lcd_char()
	Wend
End                                               
Proc init()
	AllDigital  'set all ports to digital
	TRISA = trisa1
	TRISC = trisc1
	WaitMs 1
	Lcdinit 1  'initialize LCD module; cursor is blinking
	Lcdcmdout LcdClear  'clear LCD display
	Lcdout "LCD Display"  'line 1
	Lcdcmdout LcdLine2Home  'set cursor at the beginning of line 2
	Lcdout "LR@2010"  'line 2
	WaitMs 1
	If baud_select = 1 Then
		Hseropen 9600
	Else
		Hseropen 1200
	Endif
End Proc                                          
Proc get_rs232_char()
		Hserin char
End Proc                                          
Proc send_lcd_char()
		Select Case char
		Case 0
			'do nothing
		Case 13
			Lcdcmdout LcdLine2Home
			Lcdcmdout LcdLine2Clear
		Case 27
			Lcdcmdout LcdHome
			Lcdcmdout LcdClear
		Case Else
			Lcdout char
		EndSelect
End Proc
CCS Code:
Code:
/*** PREPROCESSOR ***/
#include <16F688.h>
#device ADC=10
#fuses INTRC_IO,NOPROTECT,NOBROWNOUT,NOMCLR,NOCPD,NOWDT,PUT,NOIESO,NOFCMEN
#use delay(clock=8000000)

/*** INCLUDES ***/

/*** BUILD INFORMATION ***/
#ORG 0x01F0,0x01FF
const char build_rev[] = {"Rev 1.0"};

/*** DEFINITIONS ***/
#byte PORTA = 0x05
#byte PORTC = 0x07
#byte INTCON = 0x0B
#byte PIR1 = 0x0C
#byte CMCON = 0x19
#byte ADCON0 = 0x1F
#byte OPTION_REG = 0x81
#byte TRISA = 0x85
#byte TRISC = 0x87

#define LINE_16_1	0x00
#define LINE_16_2	0x40
#define LINE_16_3	0x10
#define LINE_16_4	0x50
// These defines are specifically for a 4x20 line display.
#define LINE_20_1	0x00
#define LINE_20_2	0x40
#define LINE_20_3	0x14
#define LINE_20_4	0x54
// Another define for use in LCD_PutCmd
#define CLEAR_DISP	0x01

struct lcd_pin_map
{
	int8 empty1: 4;	// RA0-RA3 unused
	int1 en :	1;	// RA5
	int1 rs :	1;	// RA4
	int1 na1:	1;	// RA6 - not implemented
	int1 na2:	1;	// RA7 - not implemented
	int8 empty2: 8;	// not implemented
	int8 data:	4;	// RC0-RC3
	int8 na3:	4;	// RC4-7 - not implemented
} lcd;

#byte LCD = PORTA	//PORT A & C

/*** VARIABLES / CONSTANTS ***/
#define _TRISA 0b11001111
#define _TRISC 0b11110000
#define scrollDelay 100
#define ioCommTx PIN_A0
#define ioHwUartRx PIN_C5
#define ioHwUartTx PIN_C4
#define ioBaudSelect PIN_C4
#define bufferSize 35	//includes 32bytes to cover 2x16 display, plus STX and ETX, plus 1
#define STX 0x81
#define ETX 0x18
int8 commMsg[bufferSize];
int8 msgCnt;
int1 baudSelect;

/*** FUNCTION PROTOTYPES ***/
void init();
void getMsg1200();
void getMsg9600();
void displayMsg();
void LCD_SetPosition( char cX );
void LCD_PutChar( char cX );
void LCD_ScrollChar( char cX );
void LCD_PutCmd( char cX );
void LCD_Init( void );
void LCD_pulseEN( void );

#use rs232 (baud=1200,xmit=ioCommTx,parity=N,bits=8,stream=DEBUG)
#use rs232 (baud=1200,xmit=ioHwUartTx,rcv=ioHwUartRx,parity=N,bits=8,stream=HWUART1200,ERRORS)
#use rs232 (baud=9600,xmit=ioHwUartTx,rcv=ioHwUartRx,parity=N,bits=8,stream=HWUART9600,ERRORS)

/*** FUNCTION - Main ***/
void main()
{
	init();									// initializes pic

	LCD_Init ();

	LCD_PutCmd ( CLEAR_DISP );
	LCD_SetPosition ( LINE_16_1 );
	printf ( LCD_PutChar, "SERIAL DISPLAY" );
	LCD_SetPosition ( LINE_16_2 );
	printf ( LCD_PutChar, "LR@2013 " );
	printf ( LCD_PutChar, build_rev );

	baudSelect = input(ioBaudSelect);

	delay_ms(2500);

	if (baudSelect == 0)
	{
		LCD_PutCmd ( CLEAR_DISP );
		LCD_SetPosition ( LINE_16_1 );
		printf ( LCD_PutChar, "1200 BAUD" );
	}
	else
	{
		LCD_PutCmd ( CLEAR_DISP );
		LCD_SetPosition ( LINE_16_1 );
		printf ( LCD_PutChar, "9600 BAUD" );
	}

	while( true )
	{
		if (baudSelect == 0)
		{
			getMsg1200();
		}
		else
		{
			getMsg9600();
		}
		displayMsg();
	}
}

/*** FUNCTION - MCU Intialization ***/
void init()
{
	set_tris_A( _TRISA );
	set_tris_C( _TRISC );
	setup_adc( ADC_OFF );
	setup_adc_ports( NO_ANALOGS );
	setup_comparator( NC_NC_NC_NC );
}

/*** FUNCTION - Get UART Message (1200baud) ***/
void getMsg1200()
{
	int8 commChar = 0;
	int8 msgState = 0;
	int1 msgComplete = False;
	while ( ~msgComplete )
	{
		if ( kbhit(HWUART1200) )
		{
			commChar = fgetc(HWUART1200);			// receive character
			switch (msgState)
			{
				case 0:
				{
					if (commChar == STX)
					{
						msgState++;
						msgCnt = 0;
					}
					else
					{
						msgState = 0;
						msgCnt = 0;
					}
					break;
				}
				case 1:
				{
					if (commChar == ETX)
					{
						msgState = 0;
						msgComplete = True;
					}
					else
					{
						if (msgCnt >= bufferSize)
						{
							msgState = 0;
							msgCnt = 0;
						}
						else
						{
							commMsg[msgCnt] = commChar;
							msgCnt++;
						}
					}
					break;
				}
				default:
				{
					msgState = 0;
					msgCnt = 0;
					msgComplete = False;
					break;
				}
			}
		}
	}
}

/*** FUNCTION - Get UART Message (9600baud) ***/
void getMsg9600()
{
	int8 commChar = 0;
	int8 msgState = 0;
	int1 msgComplete = False;
	while ( ~msgComplete )
	{
		if ( kbhit(HWUART9600) )
		{
			commChar = fgetc(HWUART9600);			// receive character
			switch (msgState)
			{
				case 0:
				{
					if (commChar == STX)
					{
						msgState++;
						msgCnt = 0;
					}
					else
					{
						msgState = 0;
						msgCnt = 0;
					}
					break;
				}
				case 1:
				{
					if (commChar == ETX)
					{
						msgState = 0;
						msgComplete = True;
					}
					else
					{
						if (msgCnt >= bufferSize)
						{
							msgState = 0;
							msgCnt = 0;
						}
						else
						{
							commMsg[msgCnt] = commChar;
							msgCnt++;
						}
					}
					break;
				}
				default:
				{
					msgState = 0;
					msgCnt = 0;
					msgComplete = False;
					break;
				}
			}
		}
	}
}

/*** FUNCTION - Display Message On LCD ***/
void displayMsg()
{
	int8 cnt;

	for (cnt=0;cnt<msgCnt;cnt++)
	{
		LCD_PutChar(commMsg[cnt]);
	}
}

/*** FUNCTION - Initialize LCD ***/
void LCD_Init ( void )
{
	// Initializing into 4-bit mode is a very exact sequence
	// as defined in the datasheets for these displays.
	delay_ms( 200 );		/* wait enough time after Vdd rise */
	lcd.rs = 0;
	lcd.data = 0x00;
	lcd.data = 0x03;		/* init with specific nibbles to start 4-bit mode */
	LCD_pulseEN();
	LCD_pulseEN();
	LCD_pulseEN();
	lcd.data = 0x02;		/* set 4-bit interface */
	LCD_pulseEN();			/* send dual nibbles hereafter, MSN first */
	LCD_PutCmd( 0x2C );		/* function set (2 lines, 5x7 characters) */
	LCD_PutCmd( 0x0C );		/* display ON, cursor OFF, blink OFF */
//	LCD_PutCmd( 0x0E );		/* display ON, cursor ON, blink OFF */
//	LCD_PutCmd( 0x0F );		/* display ON, cursor ON, blink ON */
//	LCD_PutCmd( 0x01 );		/* clear display */
	LCD_PutCmd( 0x01 );		/* clear display */
	LCD_PutCmd( 0x06 );		/* entry mode set, increment */
}

/*** FUNCTION - Set LCD X,Y Position ***/
void LCD_SetPosition( cX )
{
	/*
	This subroutine works specifically for 4-bit Port A.
	Value received cX will place cursor at a particular line and offset.
					16-char display		20-char display
					===============		===============
	Line 1 is hex:		00 - 0F				00 - 13
	Line 2 is hex:		40 - 4F				40 - 53
	Line 3 is hex:		10 - 1F				14 - 27
	Line 4 is hex:		50 - 5F				54 - 67
	*/
	lcd.data = swap( cX ) | 0x08;
	LCD_pulseEN();
	lcd.data = swap( cX );
	LCD_pulseEN();
}

/*** FUNCTION - Write Character To LCD ***/
void LCD_PutChar( cX )
{
	/* this subroutine works specifically for 4-bit Port A */
	switch ( cX )
	{
		case '\f':
		{
			LCD_PutCmd( CLEAR_DISP );
			break;
		}
		case '\n':
		{
			LCD_SetPosition ( LINE_16_2);
			break;
		}
		case '\r':
		{
			LCD_SetPosition ( LINE_16_2);
			break;
		}
		default:
		{
			lcd.rs = 1;
			lcd.data = swap( cX );		// send high nibble
			LCD_pulseEN();
			lcd.data = swap( cX );		// send low nibble
			LCD_pulseEN();
			lcd.rs = 0;
			break;
		}
	}
}

/*** FUNCTION - Scroll Character On LCD ***/
void LCD_ScrollChar( cX )
{
	/* this subroutine works specifically for 4-bit Port A */
	lcd.rs = 1;
	lcd.data = swap( cX );		/* send high nibble */
	LCD_pulseEN();
	lcd.data = swap( cX );		/* send low nibble */
	LCD_pulseEN();
	lcd.rs = 0;
	delay_ms(scrollDelay);
}

/*** FUNCTION - Write Command To LCD ***/
void LCD_PutCmd( cX )
{
	/* this subroutine works specifically for 4-bit Port A */
	lcd.data = swap( cX );		/* send high nibble */
	LCD_pulseEN();
	lcd.data = swap( cX );		/* send low nibble */
	LCD_pulseEN();
}

/*** FUNCTION - Toggle Enable Pin On Lcd To Load Command Or Character Into LCD ***/
void LCD_pulseEN( void )
{
	lcd.en = 1;
	delay_us( 10 );
	lcd.en = 0;
	delay_ms( 5 );
}
Author
languer
First release
Last update
Rating
4.00 star(s) 1 ratings

More resources from languer

Latest threads

EE World Online Articles

Loading

 
Top