////////////////////////////////////////////////////////////////////////////
// LCD with HD44780 drive chip
////////////////////////////////////////////////////////////////////////////
// Author(s): David Hobday
// Date 4 September 2003
//
// Designed and test for 16F84A
//
// Interface :
// RS = RA01
// WR = RA02
// E = RA00
//
// DB7 = RB07
// DB6 = RB06
// DB5 = RB05
// DB4 = RB04
//
// Using 4 Bit interface
//
/////////////////////////////////////////////////////////////////////////////
// Program to test LCD plugin - with 4 bit interface
// Designed and test for 16F84A
//
// **** Beware MPLABS assembler fails to report when program exceeds memory
// upto 4 words - this can then cause the program to fail!
// This program nearly totally fills the processor!
// 20MHz - frequency needs setting high for busy test,
// It write characters faster than they can be handled by the display and
// so get missed. If clock frequency set lower this may not happen
// Remeber also that the simualtor must also be told the clock rate so the plugins
// can convert clock ticks to actual time.
#pragma CLOCK_FREQ 20000000
// comment out when running on actual hardware - different delays used
#define RUN_UNDER_SIM
#include <system.h>
void LCD_Setup(void);
void LCD_FunctionMode(void);
void LCD_DataMode(void);
void LCD_RawWriteNibble(char);
void LCD_Write(char);
void LCD_RawWrite(char);
char LCD_Read();
void lprintf( const char *lcdptr ); //write string
void LCD_Clear();
void LCD_WaitForNotBusy();
char writeDelayType;
////////////////////////////////////////////////////////////////////////////
// Control signal bit definitions
////////////////////////////////////////////////////////////////////////////
#define LCD_E 0
#define LCD_RW 2
#define LCD_RS 1
////////////////////////////////////////////////////////////////////////////
// LCD Commands ( Refer to LCD Data Sheet )
////////////////////////////////////////////////////////////////////////////
#define clear_lcd 0x01 // Clear Display
#define return_home 0x02 // Cursor to Home position
#define entry_mode 0x06 // Normal entry mode
#define entry_mode_rev 0x04 // Normal entry mode -reverse direction
#define entry_mode_scroll 0x07 // - with shift
#define entry_mode_scroll_rev 0x05 // reverse direction
#define system_set_8_bit 0x38 //8 bit data mode 2 line ( 5x7 font )
#define system_set_4_bit 0x28 // 4 bit data mode 2 line ( 5x7 font )
#define system_set_reset 0x30 // Reset code
#define display_on 0x0C // Display ON - 2 line mode
#define display_off 0x08 // Display off
#define set_dd_line1 0x80 // Line 1 position 1
#define set_dd_line2 0xC0 // Line 2 position 1
#define set_dd_ram 0x80 // Line 1 position 1
#define write_data 0x00 // With RS = 1
#define cursor_on 0x0E // Switch Cursor ON
#define cursor_off 0x0C // Switch Cursor OFF
#define cursor_blink_on 0x0F // Cursor plus blink
#define cursor_shift_right 0x14 // Move cursor right
#define cursor_shift_left 0x10 // Move cursor left
#define display_shift_right 0x1C // Scroll display right
#define display_shift_left 0x18 // Scroll display left
#define WriteNoDelay 1
#define WriteDelayTime 0
#define WriteUseBusy 2
void TestDelay()
{
#ifdef RUN_UNDER_SIM
delay_ms( 2 );
#else
delay_s( 2 );
#endif
}
void TestDelayShort()
{
#ifdef RUN_UNDER_SIM
delay_ms( 1 );
#else
delay_ms( 100 );
#endif
}
void main()
{
#ifdef RUN_UNDER_SIM
#else
delay_s( 1 ); // power up delay
#endif
trisa = 0x00; // port a output
while( 1 )
{
LCD_Setup();
char i = 0;
//////////////////////////////////////////////////
// Test1 - wrap to line 2 test
//////////////////////////////////////////////////
lprintf( "4Bit" );
for( i = 0; i < 36; i++ )
lprintf( "x" );
TestDelay();
lprintf( "T1 L2?" );
for( i = 0; i < 34; i++ )
lprintf( "x" );
lprintf( "T1 L1?" );
TestDelay();
//////////////////////////////////////////////////
// Test2 - Display clearing
//////////////////////////////////////////////////
LCD_Clear();
lprintf( "T2-clr?" );
TestDelay();
//////////////////////////////////////////////////
// Test3 - Return Home
//////////////////////////////////////////////////
LCD_FunctionMode();
LCD_Write( return_home );
delay_ms(2);
LCD_Write( cursor_on );
LCD_DataMode();
lprintf( "T3 home?" );
TestDelay();
//////////////////////////////////////////////////
// Test4 - Reverse entry mode
//////////////////////////////////////////////////
LCD_FunctionMode();
LCD_Write( entry_mode_rev );
LCD_DataMode();
lprintf( "? veR 4T" ); // reverse entry test
// back to normal mode
LCD_FunctionMode();
LCD_Write( entry_mode );
LCD_DataMode();
TestDelay();
//////////////////////////////////////////////////
// Test5 - Setting dd ramAddr
//////////////////////////////////////////////////
LCD_Clear();
LCD_FunctionMode();
LCD_Write( set_dd_ram + 0x06 ); // start text top line, col 7
LCD_DataMode();
lprintf( "T5L1C7?" );
LCD_FunctionMode();
LCD_Write( set_dd_ram + 0x46 ); // start text bottom line 6, col 7
LCD_DataMode();
lprintf( "T5L2C7?" );
TestDelay();
//////////////////////////////////////////////////
// Test6 - Scrolling
//////////////////////////////////////////////////
test6:
int j;
LCD_FunctionMode();
LCD_Write( set_dd_ram + 0x46 ); // line 2 start, col 7
LCD_DataMode();
lprintf( "T6L2" );
LCD_FunctionMode();
LCD_Write( set_dd_ram + 0x06 ); // line 1 start, col 7
LCD_DataMode();
lprintf( "T6L1" );
LCD_FunctionMode();
LCD_Write( entry_mode_scroll );
LCD_DataMode();
TestDelay();
for ( i = 0; i < 30; i++ )
{
lprintf( "<" );
TestDelayShort();
}
lprintf( "End L2?" );
TestDelay();
//////////////////////////////////////////////////
// Test7 - Cursor
//////////////////////////////////////////////////
LCD_Clear();
LCD_FunctionMode();
LCD_Write( entry_mode ); // scroll off
LCD_Write( return_home );
delay_ms( 2 );
LCD_Write( cursor_on );
LCD_DataMode();
lprintf( "T7aCurOn?" );
TestDelay();
LCD_FunctionMode();
LCD_Write( cursor_off );
LCD_DataMode();
//////////////////////////////////////////////////
// Test8 - Scroll with no data being written
//////////////////////////////////////////////////
LCD_Clear();
LCD_FunctionMode();
LCD_Write( entry_mode ); // scroll off
LCD_Write( return_home );
delay_ms( 2 );
LCD_DataMode();
lprintf( "T8-scroll Left cursor static?" );
LCD_FunctionMode();
LCD_Write( set_dd_ram + 15 ); // position cursor at position 4
LCD_Write( cursor_on ); // show cursor
LCD_DataMode();
TestDelayShort();
LCD_FunctionMode();
for( i = 0; i < 20; i++ )
{
LCD_Write( display_shift_left );
TestDelayShort();
TestDelayShort();
}
for( i = 0; i < 20; i++ )
{
LCD_Write( display_shift_right );
TestDelayShort();
TestDelayShort();
}
LCD_DataMode();
//////////////////////////////////////////////////
// Test9 - Scroll with cursor moving to
//////////////////////////////////////////////////
test9:
LCD_Clear();
LCD_FunctionMode();
LCD_Write( entry_mode ); // scroll off
LCD_Write( return_home );
delay_ms( 2 );
LCD_DataMode();
lprintf( "T9-cursor" );
LCD_FunctionMode();
LCD_Write( set_dd_ram ); // position cursor at position 0
LCD_Write( cursor_blink_on ); // show cursor
LCD_DataMode();
TestDelayShort();
LCD_FunctionMode();
for( i = 0; i < 20; i++ )
{
LCD_Write( cursor_shift_right );
TestDelayShort();
}
for( i = 0; i < 20; i++ )
{
LCD_Write( cursor_shift_left );
TestDelayShort();
}
LCD_DataMode();
TestDelay();
//////////////////////////////////////////////////
// Test10 - busy bit usage
//////////////////////////////////////////////////
LCD_Clear();
lprintf( "T10 " );
writeDelayType = WriteNoDelay; // no delay
// some of these will be missing
LCD_Write( 'A' );
LCD_Write( 'A' );
LCD_Write( 'A' );
writeDelayType = WriteUseBusy;
// This character may not be displayed, depends on timing! as there may be
// a nibble left over from uncontrolled writes
LCD_Write( 'X' );
LCD_Write( 'B' );
LCD_Write( 'B' );
LCD_Write( 'B' );
lprintf( "\nNo AAA just BBB ?" );
TestDelay();
//////////////////////////////////////////////////
// Test11 - display read back
//////////////////////////////////////////////////
// Should cause AB to change to YZ by reading back
// and re-writing incremented values
LCD_FunctionMode();
LCD_Write( cursor_blink_on ); // show cursor
LCD_Clear();
lprintf( "T11 AB" );
char a,b;
for ( i = 0; i < 24; i++ )
{
LCD_FunctionMode();
LCD_Write( cursor_shift_left );
LCD_Write( cursor_shift_left );
LCD_DataMode();
LCD_WaitForNotBusy();
a = LCD_Read() + 1;
LCD_WaitForNotBusy();
b = LCD_Read() + 1;
LCD_FunctionMode();
LCD_Write( cursor_shift_left );
LCD_Write( cursor_shift_left );
LCD_DataMode();
LCD_Write( a );
LCD_Write( b );
}
TestDelay();
//////////////////////////////////////////////////
// Test12 - display on/off
//////////////////////////////////////////////////
// should never see display off message on its own
LCD_FunctionMode();
LCD_Clear();
lprintf( "T12 on/off?" );
LCD_FunctionMode();
for( i = 0; i < 5; i++ )
{
LCD_Write( display_off ); // display off
TestDelay();
LCD_Write( display_on ); // display on
TestDelay();
}
}
}
void LCD_Clear()
{
LCD_FunctionMode();
LCD_Write( clear_lcd ); // clear display
LCD_Write( return_home );
delay_ms( 2 );
LCD_DataMode();
}
void LCD_Setup(void)
{
writeDelayType = WriteNoDelay; // no delays in data writes
delay_ms(16); // Power up delay
LCD_FunctionMode();
// Reset sequence as described in data sheets
LCD_RawWriteNibble( system_set_reset >> 4 );
delay_ms(5); // min delay here of 4.1 ms
LCD_RawWriteNibble( system_set_reset >> 4 );
delay_us(100); // min delay here of 100us
LCD_RawWriteNibble( system_set_reset >> 4 );
// busy flag is valid from this point onwards
LCD_WaitForNotBusy();
LCD_RawWriteNibble( system_set_4_bit >> 4 );
writeDelayType = WriteUseBusy; // use busy
LCD_Write( system_set_4_bit );
LCD_Write( display_off );
LCD_Write( entry_mode );
LCD_Write( display_on );
LCD_Write( set_dd_ram );
LCD_DataMode();
}
void LCD_FunctionMode(void)
{
clear_bit( porta, LCD_RS );
}
void LCD_DataMode(void)
{
set_bit( porta, LCD_RS );
}
void LCD_Write(char d )
{
if ( writeDelayType == WriteUseBusy )
LCD_WaitForNotBusy();
LCD_RawWrite( d );
if ( writeDelayType == WriteDelayTime )
delay_us( 50 ); // enough time for normal command execution - clear and home need longer!!
}
void LCD_RawWrite( char d )
{
clear_bit( porta, LCD_RW ); // set writing mode
// set port b lower bits to output
trisb &= 0x0F;
// output upper nibble
LCD_RawWriteNibble( d >> 4 );
// output lower nibble
LCD_RawWriteNibble( d );
}
void LCD_RawWriteNibble(char d )
{
clear_bit( porta, LCD_RW ); // set writing mode
// port b upper nibble output
trisb &= 0x0F;
portb &= 0x0F;
portb |= d << 4;
// Clock data
asm NOP // setup time
asm NOP // setup time
asm NOP // setup time
set_bit( porta, LCD_E );
clear_bit( porta, LCD_E );
asm NOP ;// holdup time
}
char LCD_Read()
{
char d;
// portb upper nibble input
trisb |= 0xF0;
set_bit( porta, LCD_RW ); // set reading mode
// data valid while EN high -
// first high nibble
set_bit( porta, LCD_E );
asm NOP // setup time
asm NOP // setup time
asm NOP // setup time
d = portb & 0xF0 ;
clear_bit( porta, LCD_E );
// then low nibble
set_bit( porta, LCD_E );
asm NOP // setup time
asm NOP // setup time
asm NOP // setup time
d |= portb >> 4;
clear_bit( porta, LCD_E );
return d;
}
void LCD_WaitForNotBusy()
{
char old_RS;
// save RS current state
old_RS = porta & ( 1 << LCD_RS );
clear_bit( porta, LCD_RS );
while( LCD_Read() & 0x80 ); // wait while busy set
if ( old_RS ) // restore RS if necessary
set_bit( porta, LCD_RS );
}
void lprintf( const char *lcdptr )
{
char pi;
char c;
pi = 0;
while( 1 )
{
c = lcdptr[pi++];
if ( c + 1 == 1 ) // funny code here as c == 0 fails with compiler V5.1
break;
if ( c == '\n' )
{
LCD_FunctionMode();
LCD_Write( set_dd_ram + 0x40 );
LCD_DataMode();
}
else
LCD_Write( c );// Display on LCD
}
}