hitachi lcd.c


/****************************************************************************
**									   **
**		    	Hitachi Character-Type LCD Display		   **
**									   **
****************************************************************************/
//
//		NOTE: 	This is set up for 4 MHz oscillator
//

#define	LCD_C								// "I am LCD.C"

#ifndef CLRWDT
#include <pic.h>
#endif
#include "defs.h"
#include "lcd.h"
#include "delay.h"							// Contains delay routines & macros

const byte const LCD_ROW_ADDRESS[] =					// Row/Column information for lcd_gotoxy()
	{
#if LCD_MODE_1x8
	0x00
#endif
#if LCD_MODE_1x16_A
	0x00
#endif
#if LCD_MODE_1x16_B
	0x00,
	0x40
#endif
#if LCD_MODE_1x40
	0x00,
#endif
#if LCD_MODE_2x8
	0x00,
	0x40
#endif
#if LCD_MODE_2x12
	0x00,
	0x40
#endif
#if LCD_MODE_2x16
	0x00,
	0x40
#endif
#if LCD_MODE_2x20
	0x00,
	0x40
#endif
#if LCD_MODE_2x24
	0x00,
	0x40
#endif
#if LCD_MODE_2x40
	0x00,
	0x40
#endif
#if LCD_MODE_4x16
	0x00,
	0x40,
	0x10,
	0x50
#endif
#if LCD_MODE_4x20
	0x00,
	0x40,
	0x14,
	0x54
#endif
#if LCD_MODE_4x24
	0x00,
	0x40,
	0x80,
	0xc0
#endif
	};

const byte const LCD_INIT_STRING [] =					// LCD Init String on powerup
	{
	0b00000001,							//	Clear display
	0b00000010,							//	Home cursor
	0b00000100							//	Entry Mode
#if LCD_CURSOR_INCREMENT
	| 0b00000010							//		Increment cursor
#endif
#if LCD_CURSOR_SHIFT
	| 0b00000001							//		Shift on cursor
#endif
		,							//		end
	0b00001000							//	Display Control
#if LCD_DISPLAY_ON
	| 0b00000100							//		Display on
#endif
#if LCD_CURSOR_ON
	| 0b00000010							//		Cursor on
#endif
#if LCD_CURSOR_BLINK
	| 0b00000001							//		Blink on
#endif
		,							//		end
	0b00100000							//	Function Set
#if LCD_8_BIT_MODE
	| 0b00010000							//		8-bit data bus
#endif
#if LCD_MULTI_LINE
	| 0b00001000							//		2-line refreshing
#endif
#if LCD_DISPLAY_5x10
	| 0b00000100							//		5x10 matrix
#endif
	};

#if LCD_4_BIT_MODE
void lcd_putnybble (byte c)						// Write nybble to port in current RS mode
	{
	c = c << LCD_D4_BIT;						// Shift over to correct bit column
	c &= LCD_TRIS_DATAMASK;						// Remove any extraneous bits
	LCD_DATA_PORT = (LCD_DATA_PORT & ~LCD_TRIS_DATAMASK) | c;	// Write data bits to port
	delay_uS (1);
	LCD_E = 1;							// Start to write it
	delay_uS (2);
	LCD_E = 0;							// Finish write cycle
	}
#endif

byte lcd_getbyte (void)							// Read byte at cursor (RS=1) or ready status (RS=0)
	{
	byte	retval;
#if LCD_4_BIT_MODE
	byte	highbits;

	LCD_TRIS_PORT |= LCD_TRIS_DATAMASK;				// Set port to read mode for data pins
	LCD_RW = 1;							// Tell LCD we want to read
	delay_uS (1);
	LCD_E = 1;
	highbits = (((LCD_DATA_PORT & LCD_TRIS_DATAMASK) >> LCD_D4_BIT) << 4);// Grab high bits and shift to right place
	LCD_E = 0;
	delay_uS (1);
	LCD_E = 1;
	delay_uS (1);
	retval = ((LCD_DATA_PORT & LCD_TRIS_DATAMASK) >> LCD_D4_BIT);	// Grab low bits
	LCD_E = 0;
	retval |= highbits;
	LCD_TRIS_PORT &= ~LCD_TRIS_DATAMASK;				// Set port back to output mode
#else
	LCD_TRIS_PORT = 0xFF;						// Set port to all input
	LCD_RW = 1;							// Tell LCD we want to read
	delay_uS (1);
	LCD_E = 1;							// Do the read
	delay_uS (1);
	retval = LCD_DATA_PORT;
	LCD_E = 0;
	LCD_TRIS_PORT = 0x00;						// Set port back to outputs
#endif
	return (retval);						// Give answer to caller
	}

void lcd_putbyte (byte c)						// Write byte to port in current RS mode
	{
	byte	RS_Status;

	RS_Status = LCD_RS;						// Get old pin state
	LCD_RS = 0;							// Force into command mode to read state
	while (lcd_getbyte () & 0x80);					// Wait for read state
	if (RS_Status)
		LCD_RS = 1;						// Restore RS to old state
	delay_uS (1);
	LCD_RW = 0;							// Set to write mode
	delay_uS (1);
#if LCD_4_BIT_MODE
	lcd_putnybble (c >> 4);						// Send the character out
	lcd_putnybble (c);
#else
	LCD_DATA_PORT = c;						// Send the character out
#endif
	LCD_E = 1;
	delay_uS (1);
	LCD_E = 0;
	}

void lcd_command (byte c)						// Send command to LCD port
	{
	LCD_RS = 0;
	lcd_putbyte (c);
	}

#if LCD_ALLOW_USER_CHARS
void lcd_define_char (byte c, const byte *bitmap)			// Define user-defined chars
	{
	byte	i;

	lcd_command ((0b01000000) | (c << 3));					// Select char to define

	LCD_RS = 1;
	for (i = 0; i < 8; i++)
		lcd_putbyte (*bitmap++);				//	Put in each byte of memory
	}
#endif

byte lcd_lineof (byte CursorAddress)					// Calculate cursor row from it's address
	{
	CursorAddress &= 0x50;						//	Strips out uniquely the address bits
	switch (CursorAddress)
		{
		case 0x00:						// Note - this handles all cases except for some
			CursorAddress = 1;				//	of those unsupported displays listed in
		case 0x40:						//	lcd.h file.
#if LCD_MODE_1x16_B
			CursorAddress = 1;				//	Only 1 row this type of display
#else
			CursorAddress = 2;
#endif
		case 0x10:
			CursorAddress = 3;
		case 0x50:
			CursorAddress = 4;
		default:
			CursorAddress = 1;
		}
	return (CursorAddress);
	}

byte lcd_cursorpos (void)						// Return address of cursor position
	{
	LCD_RS = 0;
	return (lcd_getbyte ());					//	Get cursor position
	}

void lcd_putc (byte c)							// Write character to LCD
	{
#if !LCD_ALLOW_USER_CHARS
	byte	CursAddr;
#endif
#if LCD_ALLOW_USER_CHARS						// Allow user-defined characters - no terminal mode
		LCD_RS = 1;
		lcd_putbyte (c);
#else
	switch (c)
		{
		case '\b':						//	Backspace?
			lcd_command (LCD_COMMAND_BACKSPACE);		//		back cursor up
#if LCD_DESTRUCTIVE_BS
			LCD_RS = 1;					//		set display mode
			lcd_putbyte (' ');				//		erase previous character
			lcd_command (LCD_COMMAND_BACKSPACE);		//		move cursor back again
#endif
			break;
		case '\n':						//	Newline?
			RS = 0;
			CursAddr = lcd_getbyte ();			//		Get cursor position
			CursAddr = lcd_lineof (CursAddr);
#if LCD_ENABLE_SCROLL
			if (CursAddr >= LCD_MAXROWS)			//		Bottom line?
				lcd_scroll ();				//			Yes, force scroll
			else						//			No, just go to start of next line
				lcd_gotoxy (CursAddr+1,1);
#else
			lcd_gotoxy (CursAddr+1, 1);			//		Position cursor to start of line
#endif
			break;
		case '\f':						//	Form Feed (clear screen)?
			lcd_command (LCD_COMMAND_CLEAR);		//		Erase screen
			lcd_gotoxy (1,1);				//		Position cursor to top of screen
			break;
		default:						//	Printable?
			LCD_RS = 1;					//		Set to display mode
			lcd_putbyte (c);				//		Send character out
		}
#endif
	}

#if LCD_ENABLE_GETC
byte lcd_getc (void)							// Read character at cursor
	{
	byte	retval;

	LCD_RS = 1;
	retval = lcd_getbyte ();
	LCD_RS = 0;
	return (retval);
	}
#endif

#if LCD_ENABLE_GOTOXY
void lcd_gotoxy (byte row, byte col)					// Position cursor
	{
#if LCD_MODE_1x16_B
	if (col > 7)							// 1x16 is treated the same as 2x8 for addressing
		{
		row++;
		col -= 8;
		}
	if (col > 8)
		col = 8;
	if (row > 2)
		row = 2;
#else
	if (row > LCD_MAXROWS)						// Range limit
		row = LCD_MAXROWS;
	if (col > LCD_MAXCOLS)
		col = LCD_MAXCOLS;
#endif

	row = LCD_ROW_ADDRESS[row-1];					// Get address of first byte on desired row
	row += col - 1;

	lcd_command (0x80 | row);					// Write new cursor address
	}

void lcd_getxy (byte *row, byte *col)					// Return row and column of cursor position
	{
	byte	rr,
		cc;

	cc = lcd_cursorpos ();
	rr = lcd_lineof (cc);						//	Get row of the cursor
	cc = (cc & 0x7f) - LCD_ROW_ADDRESS[rr-1];			//	Find the column
	*row = rr;							//	Convert to lcd_gotoxy() units
	*col = cc;
	}

#endif

#if LCD_ENABLE_PRINTF
void lcd_printf (const char* message)					// Write message to LCD (C string type)
	{
	while (*message)						//	Look for end of string
		lcd_putc (*message++);					//	Show and bump
	}
#endif

#if LCD_ENABLE_SCROLL
void lcd_scroll (void)							// Scroll up one line
	{
	byte	CursorPos,						// Hold position of cursor
		Character,						// Hold character being moved
		SrcAddr,						// Source Address
		DestAddr,						// Destination Address
		EndAddr;						// Ending copy address (last address of Source)

	LCD_RS = 0;
	CursorPos = lcd_getbyte () | 0x80;				// Get cursor position

	lcd_gotoxy (2,1);
	LCD_RS = 0;
	SrcAddr = lcd_getbyte () | 0x80;				// Find address of copy start
	lcd_gotoxy (1,1);
	LCD_RS = 0;
	DestAddr = lcd_getbyte () | 0x80;				// Find address of copy destination
	lcd_gotoxy (LCD_MAXROWS, LCD_MAXCOLS);
	LCD_RS = 0;
	EndAddr = lcd_getbyte () | 0x80;				// Find address of last byte to copy over

	do
		{
		LCD_RS = 0;						//	Position to source of copy char
		lcd_putbyte (SrcAddr);
		LCD_RS = 1;
		Character = lcd_getbyte ();				//	Read the character there
		LCD_RS = 0;
		lcd_putbyte (DestAddr);					//	Move to the destination
		LCD_RS = 1;
		lcd_putbyte (Character);				//	Write it the char there
		SrcAddr++;
		DestAddr++;
		}
	while (SrcAddr <= EndAddr);					// Loop through all memory

	for (Character = 1; Character <= LCD_MAXCOLS; Character++)
		{
		lcd_gotoxy (LCD_MAXROWS, Character);			//	Position on last line
		lcd_putc (' ');						//	Blank out the char
		}
	lcd_gotoxy (lcd_lineof (CursorPos) + 1,1);			// Home cursor next line
	}
#endif

#if LCD_ENABLE_UNSCROLL
void lcd_unscroll (void)						// Roll scroll backwards one line
	{
	byte	CursorPos,						// Hold position of cursor
		Character,						// Hold character being moved
		SrcAddr,						// Source Address
		DestAddr;						// Destination Address

	LCD_RS = 0;
	CursorPos = lcd_getbyte () | 0x80;				// Get cursor position

	lcd_gotoxy (LCD_MAXROWS-1,LCD_MAXCOLS);
	LCD_RS = 0;
	SrcAddr = lcd_getbyte () | 0x80;				// Find address of copy start

	lcd_gotoxy (LCD_MAXROWS,LCD_MAXCOLS);
	LCD_RS = 0;
	DestAddr = lcd_getbyte () | 0x80;				// Find address of copy destination

	do
		{
		LCD_RS = 0;						//	Position to source of copy char
		lcd_putbyte (SrcAddr);
		LCD_RS = 1;
		Character = lcd_getbyte ();				//	Read the character there
		LCD_RS = 0;
		lcd_putbyte (DestAddr);					//	Move to the destination
		LCD_RS = 1;
		lcd_putbyte (Character);				//	Write it the char there
		SrcAddr--;
		DestAddr--;
		}
	while (SrcAddr != 0x80);					// Loop through all memory

	for (Character = 0; Character < LCD_MAXCOLS; Character++)
		{
		lcd_gotoxy (1, Character);				//	Position on top row
		lcd_putc (' ');						//	Blank out the char
		}
	lcd_gotoxy (lcd_lineof (CursorPos),1);				// Home cursor same line as before
	}
#endif

#if LCD_ENABLE_CLEAR
void lcd_clear (void)							// Clear LCD screen
	{
	lcd_command (LCD_COMMAND_CLEAR);
	}
#endif

void lcd_init (void)							// Reset display from software
	{
	byte	i;

	LCD_E = 0;							// Set up control pin I/O
	LCD_TRIS_E = 0;
	LCD_RW = 0;							// Write mode
	LCD_TRIS_RW = 0;
	LCD_RS = 0;							// Command mode
	LCD_TRIS_RS = 0;

	LCD_TRIS_PORT &= ~LCD_TRIS_DATAMASK;				// Set data bus to output

	LCD_E = 0;							// Start talking to LCD
	delay_mS (15);							// Wait a little while

#if LCD_4_BIT_MODE							// Set LCD into 4-bit mode
	lcd_putnybble (0b0011);						// Select 8-bit mode
	delay_mS (5);							// Spec calls for 4.1 mS
	lcd_putnybble (0b0011);						// Do it again
	delay_uS (100);
	lcd_putnybble (0b0011);
	lcd_putnybble (0b0010);						// Off and running...
#else
	lcd_putbyte (0b00110000);					// Select 8-bit mode
	delay_mS (5);							// Spec calls for 4.1 mS
	lcd_putbyte (0b00110000);					// Do it again
	delay_uS (100);
	lcd_putbyte (0b00110000);
	lcd_putbyte (0b00110000);					// Off and running...
#endif
	for (i = 0; i < sizeof(LCD_INIT_STRING); i++)			// Send other LCD initialization stuff
		lcd_command (LCD_INIT_STRING[i]);
	}

Questions: