/****************************************************************
* LCD Ausgabe für Hitachi HD44780 kompatible LCD Controller
* LCD-Initialisierung: 4-Bit Interface
* Mikrocontroller: ATmega88 @ 8MHz
* LCD-Display: Displaytech 162B (LCD-Controller KS0070B)
*
* Konfiguration: PORT D7 = LCD D7
* PORT D6 = LCD D6
* PORT D5 = LCD D5
* PORT D4 = LCD D4
* PORT D3 = LCD E
* PORT D2 = LCD RS
* GND = LCD Vss + LCD R/W + LCD K
* +5V = LCD Vdd
* Poti = LCD V0
* +5V (über 1kOhm) = LCD A
****************************************************************/
#include <avr/io.h>
#include <util/delay.h>

#include  // Definitionen für die ITOA-Funktion einbinden

/***** LCD Pinbelegung *****/
#define LCDPORT		PORTD
#define LCDDDR		DDRD
#define LCD_PIN_RS	2
#define LCD_PIN_E	3
#define LCD_PIN_D4	4
#define LCD_PIN_D5	5
#define LCD_PIN_D6	6
#define LCD_PIN_D7	7

/***** LCD STEUERBEFEHLE *****/
#define LCD_CLEAR		0x01 // Display löschen
#define LCD_HOME		0x02 // Cursor an den Anfang zurück
#define LCD_ON			0x0C // 0x0C = LCD ON, Cursor OFF
#define LCD_ON_C		0x0E // 0x0E = LCD ON, Cursor ON
#define LCD_OFF			0x08 // Display OFF
#define LCD_SET_4Bit	0x28 // Set Function = 4-Bit, 2 Zeilen, 5x7 Matrix
#define LCD_SET_2		0x06 // Entry Mode = Cursor increase, Display is not shifted
#define LCD_SETDDRAM	0x80 // Set Display RAM Address

/***** HILFSDEFINITIONEN *****/
#define BEFEHL 0
#define DATEN 1
#define sbi(port, bit) (port |= (1<<bit))
#define cbi(port, bit) (port &= ~(1<<bit))

/****** Sendet ein Byte an den LCD Controller ******/
void lcd_send(uint8_t type, uint8_t lcd_command)
{
	if (type == BEFEHL)
		cbi(LCDPORT, LCD_PIN_RS); // RS=0: Befehl folgt ...
	else
		sbi(LCDPORT, LCD_PIN_RS); // RS=1: Daten folgen ...
	/* (1) HIGH NIBBLE wird gesendet */
	if (bit_is_set(lcd_command, 7))
		sbi(LCDPORT, LCD_PIN_D7);
	else 
		cbi(LCDPORT, LCD_PIN_D7);
	if (bit_is_set(lcd_command, 6))
		sbi(LCDPORT, LCD_PIN_D6);
	else 
		cbi(LCDPORT, LCD_PIN_D6);
	if (bit_is_set(lcd_command, 5))
		sbi(LCDPORT, LCD_PIN_D5);
	else 
		cbi(LCDPORT, LCD_PIN_D5);
	if (bit_is_set(lcd_command, 4))
		sbi(LCDPORT, LCD_PIN_D4);
	else 
		cbi(LCDPORT, LCD_PIN_D4);
	/* Flanke zur Übernahme der Daten für das High-Nibble senden ... */
	sbi(LCDPORT, LCD_PIN_E);
	_delay_us(1);
	cbi(LCDPORT, LCD_PIN_E);
	_delay_us(1);
	/* (2) LOW NIBBLE wird gesendet */
	if (bit_is_set(lcd_command, 3))
		sbi(LCDPORT, LCD_PIN_D7);
	else 
		cbi(LCDPORT, LCD_PIN_D7);
	if (bit_is_set(lcd_command, 2))
		sbi(LCDPORT, LCD_PIN_D6);
	else 
		cbi(LCDPORT, LCD_PIN_D6);
	if (bit_is_set(lcd_command, 1))
		sbi(LCDPORT, LCD_PIN_D5);
	else 
		cbi(LCDPORT, LCD_PIN_D5);
	if (bit_is_set(lcd_command, 0))
		sbi(LCDPORT, LCD_PIN_D4);
	else 
		cbi(LCDPORT, LCD_PIN_D4);
	/* Flanke zur Übernahme der Daten für das Low-Nibble senden ... */
	sbi(LCDPORT, LCD_PIN_E);
	_delay_us(1);
	cbi(LCDPORT, LCD_PIN_E);
	/* (3) Auf den LCD Controller warten ...*/
	_delay_ms(5);
}

/****** Stellt eine Zeichenkette am LCD dar ******/
void lcd_write(char *t) // *t = pointer auf übergebenes Zeichen
{
	uint8_t i;
	for (i=0; i<255; i++)
	{
		if (t[i]==0) // String-Ende (bei Wert = 0)? Dann raus hier ...
			return;
		else
			lcd_send(DATEN, t[i]);
	}
}

/****** Enable Impuls zur Übernahme der LCD-Daten ******/
void toggle_enable(void)
{
	sbi(LCDPORT, LCD_PIN_E);
	_delay_us(1);
	cbi(LCDPORT, LCD_PIN_E);
}

/****** Initialisierung des LCD Controllers *****/
void lcd_init()
{
	_delay_ms(60);				// LCD-Power-On delay (min. 40ms)
	/* LCD Soft-Reset 1 – Kommando 0011xxxx */
	sbi(LCDPORT, LCD_PIN_D5);	// D5 = 1
	sbi(LCDPORT, LCD_PIN_D4);	// D4 = 1
	toggle_enable();			// Kommando übernehmen
	_delay_ms(40);				// Auf die Ausführung des LCD warten
	/* LCD Soft-Reset 2 – Kommando 0011xxxx */
	toggle_enable();			// Kommando übernehmen (D5=1, D4=1)
	_delay_ms(1);				// Auf die Ausführung des LCD warten
	/* LCD Soft-Reset 3 – Kommando 0011xxxx */
	toggle_enable();			// Kommando übernehmen (D5=1, D4=1)
	_delay_ms(5);				// Auf die Ausführung des LCD warten
	// Ab hier ist sicher der 8-Bit Mode eingestellt!
	// Jetzt folgt die Umstellung auf den 4-Bit Modus!
	/* 4-Bit Modus einschalten */
	sbi(LCDPORT, LCD_PIN_D5);	// LCD D5 = 1
	cbi(LCDPORT, LCD_PIN_D4);	// LCD D4 = 0
	toggle_enable();			// Kommando übernehmen
	_delay_ms(5);
	// Ab hier im 4-Bit Modus (High-Nibble / Low-Nibble senden)
	// LCD auf 2 Zeilen, 4-Bit Modus, 5x7 Matrix einstellen
	lcd_send(BEFEHL, LCD_SET_4Bit);		// Register SET FUNCTION = 0010 1000
	lcd_send(BEFEHL, LCD_OFF);			// Set LCD OFF
	lcd_send(BEFEHL, LCD_CLEAR);		// Clear LCD
	lcd_send(BEFEHL, LCD_SET_2);		// Set ENTRY MODE = 0000 0110
	lcd_send(BEFEHL, LCD_ON);			// Set LCD ON
}

/****** Hilfsfunktion: Warte i Sekunden ******/
void sekunde(uint8_t i)
{
	uint8_t k;
	for (;i>0;i--)			// Solange i>0 dekrementiere i um 1
	{
		for (k=0;k<10;k++)	// 10 Durchläufe mit je 50ms
		_delay_ms(50);
	}
}

/***** HAUPTPROGRAMM *****/
int main(void)
{
	LCDDDR = 0xFF;		// Port auf Ausgang schalten
	LCDPORT = 0x00;
	
	lcd_init();			// LCD initialisieren
	lcd_write("Starting LCD...");
	
	char Buffer[20];	// Datentyp char für Zeichen
	uint8_t dezimalwert = 22;
	
	while (1)
	{
		// Aufruf der itoa-Funktion zur ASCII-Darstellung einer Dezimalzahl (Basis = 10)
		itoa(dezimalwert,Buffer,10);
		sekunde(5);
		lcd_send(BEFEHL, LCD_CLEAR);			// LCD clear
		lcd_write("HALLO LCD!");				// Der Cursor befindet sich an 1.Position Zeile 1
		sekunde(5);
		lcd_send(BEFEHL, LCD_CLEAR);			// LCD clear
		lcd_send(BEFEHL, LCD_SETDDRAM +0x40);	// Cursor auf die 1.Position Zeile 2
		lcd_write("Wert = ");
		lcd_write(Buffer);						// Schreibe den Inhalt des itoa-Buffers
		sekunde(5);
		lcd_send(BEFEHL, LCD_CLEAR);			// LCD clear
		lcd_send(BEFEHL, LCD_SETDDRAM +0x42);	// Cursor auf die 3.Position Zeile 2
		lcd_write("Wert = ");
		lcd_write(Buffer);						// Schreibe den Inhalt des itoa-Buffers
	}
}