/**
 * Hitachi HD44780 interface code. Provides initialization and functions
 * to write data and commands to a display. The code is written for a
 * 20x4 display, but the line wrap constants can be changed for other displays.
 * The display should be connected as follows to an available port on the atmel:
 * PORTx [7:0] = E1 E0 RW RS D7 D6 D5 D4 (E1 is not normally used)
 * Define your LCD port in lcd.h. Make sure that port is set as output,
 * DDRx = 0xFF;
 * Call LCD_init() before sending commands or data.
 * us50_sleep() is defined as a 50uSec delay. Adjust the passed parameter
 * accordingly for your available delay routine.
 * Note: RW on the display can be tied to ground to save a pin.
 * The source compiles on avr-gcc
 *
 * 2005.05.01 - William S. Dubel
 */


#include "lcd.h"
#include <avr/io.h>
#include <util/delay.h>

int pos = 0;


void LCD_command(int db)
{
   LCD = (db >> 4) | E0;
   _delay_us(50);
   LCD = db >> 4;
   _delay_ms(1);
	
   LCD = (db & 0x0F) | E0;
   _delay_us(50);
   LCD = db & 0x0F;
   _delay_ms(5);
}

/**
 * Sends the command to clear the display, and resets the position
 * variable (used to track line wrap).
 */
void LCD_clear()
{
   //Clear Home
   LCD_command(0x01);
   pos = 0;
}

/**
 * Initialize a Hitachi 4x20 4 bit mode display
 * E1 E0 RW RS D7 D6 D5 D4
 */
void LCD_init()
{
   _delay_ms(15);   //15
   //initialize to 4 bit mode:
   LCD_command(0x33);
   _delay_ms(1);
   LCD_command(0x32);
   
   //2 lines 5x8 font
   LCD_command(0x28);
   //Display, no Curser, no Blink
   LCD_command(0x0C);
   // F curser and blink, E just curser

   LCD_clear();
}

/**
 * Allows you to print a string of data. While this method works fine, it does
 * not allow for special formatting such as printf. It requires null terminated
 * strings. For special formatting, call fdevopen(LCD_data, NULL, 0); after
 * initializing the display, and use printf instead to write data.
 */
void LCD_string(char db [])
{
	for (; *db!='\0';db++)
      LCD_data(*db);
}

/**
 * Moves the curser to the passed row and column, starting at (0,0)
 * written for a 4x20 display.
 */
void LCD_moveTo(int row, int col)
{
   pos = (row*20) + col;
   if (pos<20)
   LCD_command(0x80 | pos);
   if (pos>=20 && pos<40)
     LCD_command(0x80 | (pos%20 + 0x40));
   if (pos>=40 && pos<60)
     LCD_command(0x80 | (pos%40 + 0x14));
   if (pos>60)
     LCD_command(0x80 | (pos%60 + 0x54));
}


/**
 * This takes care of line wrap for a 20x4 display. The line wrap positions
 * could be defined in constants for other popular displays, such as 16x2.
 */
void LCD_data(char db)
{
   // proper line wrap:
   if (pos==20)
     LCD_command(0xC0);
   if (pos==40)
     LCD_command(0x94);
   if (pos==60)
     LCD_command(0xD4);
   //first nibble & strobe
   LCD = (db >> 4) | E0 | RS;
   _delay_us(50);
   LCD = (db >> 4) | RS;
   _delay_us(50);
   //second nibble & strobe
   LCD = (db & 0x0F) | E0 | RS;
   _delay_us(50);
   LCD = (db & 0x0F) | RS;

   _delay_us(50);
   if (++pos==80)
     pos = 0;
}

