
/****************************** differences *****************************/

#ifdef __C51__

#include <reg51.h>                      // 8051 io definitiones

#define LCD_RS          2               // P1.2
#define LCD_E           3               // P1.3
#define LCD_PORT        P1

#define SetLcdOutputs()                 // not needed on 8051

#define RELOAD60us      (XTAL / 12.0 / 2 * 60e-6) // Keil C51: 2 cycle

#else

#include <io.h>                         // AVR io definitions

#define LCD_PORT        PORTB
#define LCD_DDR         DDRB
#define LCD_RS          PB2
#define LCD_E           PB3

#define SetLcdOutputs() LCD_DDR |= 1<<LCD_E^1<<LCD_RS^0xF0

#define RELOAD60us      (XTAL / 3 * 60e-6)      // WINAVR: 3 cycle

#endif


/****************************** common code *****************************/

typedef unsigned char  u8;
typedef   signed char  s8;
typedef unsigned short u16;
typedef   signed short s16;
typedef unsigned long  u32;
typedef   signed long  s32;


#define XTAL    11.0592e6

u8 position;


void wait_60us( u8 n )                  // wait n * 60µs (max 15ms)
{
  u8 i;
  do{
    i = RELOAD60us;
    while( --i );
  }while( --n );
}


u8 lcd_nibble( u8 d )
{
  LCD_PORT &= 0x0F;
  LCD_PORT |= d & 0xF0;
  LCD_PORT |= 1<<LCD_E;
  d <<= 4;
  LCD_PORT &= ~(1<<LCD_E);
  return d;
}


void lcd_byte( u8 d )
{
  lcd_nibble( lcd_nibble( d ));
  wait_60us( 1 );                       // wait > 46us
}


void lcd_command( u8 d )
{
  LCD_PORT &= ~(1<<LCD_RS);             // RS = 0
  lcd_byte( d );
  if( d & 0x80 )
    position = d;
  switch( d ){
    case 1:
    case 2:
    case 3: wait_60us( 27 );            // wait > 1.6ms
  }
}


void lcd_data( u8 d )
{
  LCD_PORT |= 1<<LCD_RS;                // RS = 1
  lcd_byte( d );
  position++;
  if( position & 0x10 ){
    position ^= 0x50;                   // switch line 1<->2
    lcd_command( position );
  }
}


void lcd_init( void )
{
  SetLcdOutputs();                      // set direction bits
  LCD_PORT &= ~(1<<LCD_E);              // E = 0
  LCD_PORT &= ~(1<<LCD_RS);             // RS = 0
  wait_60us( 250 );                     // wait >15ms
  lcd_nibble( 0x30 );
  wait_60us( 69 );                      // wait >4.1ms
  lcd_nibble( 0x30 );
  wait_60us( 2 );                       // wait >100µs
  lcd_nibble( 0x30 );                   // 8 bit mode
  wait_60us( 2 );                       // wait >100µs
  lcd_nibble( 0x20 );                   // 4 bit mode
  wait_60us( 2 );
  lcd_command( 0x28 );                  // 2 lines 5*7
  lcd_command( 0x08 );                  // display off
  lcd_command( 0x01 );                  // display clear
  lcd_command( 0x06 );
  lcd_command( 0x0C );                  // no cursor, no blink
  position = 0x80;
}