/*
 * LCD.c
 *
 *  Created on: Nov 4, 2011
 *      Author: RomanM
 */
#include "hal_LCD.h"
#include "LCD_internal.h"
#include "LCD.h"

/*
 * Initialization of LCD
 */
void LCD_Init(void) {
	hal_LCD_BL_Init();			// Backlight initialization
	hal_LCD_Init();				// Port initialization

	hal_LCD_Wait15ms();			// Wait after power-on

#ifdef LCD_4bit
	LCD_DataOut(0x3);
	hal_LCD_Wait4_1ms();
	LCD_DataOut(0x3);
	hal_LCD_Wait100us();
	LCD_DataOut(0x3);

	LCD_DataOut(0x2);			// Set 4-bit mode
#else
	LCD_DataOut(0b00110000);
	hal_LCD_Wait4_1ms();
	LCD_DataOut(0b00110000);
	hal_LCD_Wait100us();
	LCD_DataOut(0b00110000);

#endif

	LCD_Instruction(0x28);		// 2-Line, Font 5x8
	LCD_Instruction(0x06);		// Move cursor to right, no shift
	LCD_Clear();
	LCD_On(1);
}

/*
 * Write data and make Enable pulse
 */
void LCD_DataOut(uint8_t data) {
	hal_LCD_RW(0);
	hal_LCD_Wait1us()
	hal_LCD_E(1);
	hal_LCD_Wait1us()
#ifdef	LCD_4bit
	hal_LCD_DataOut_4bit(data);
#else
	hal_LCD_DataOut_8bit(data);
#endif

	hal_LCD_Wait1us()
	hal_LCD_E(0);
	hal_LCD_Wait1us();
}

/*
 * Read data from port with Enable pulse
 */
uint8_t LCD_DataIn(void) {
	uint8_t retValue;

	hal_LCD_RW(1);
	hal_LCD_Wait1us();
	hal_LCD_E(1);
	hal_LCD_Wait1us();

#ifdef	LCD_4bit
	retValue = hal_LCD_DataIn_4bit();
#else
	retValue = hal_LCD_DataIn_8bit();
#endif

	hal_LCD_Wait1us();
	hal_LCD_E(0);
	hal_LCD_Wait1us();

	return retValue;
}

/*
 * Write byte to LCD
 */
void LCD_WriteByte(uint8_t byte) {
#ifdef LCD_4bit
	LCD_DataOut(byte >> 4);		// High nibble
	LCD_DataOut(byte);			// Low nibble
#else
	LCD_DataOut(byte);			// All bits
#endif
}

/*
 * Read byte from LCD
 */
uint8_t LCD_ReadByte(void) {
	uint8_t retValue;
	hal_LCD_Port_In();
#ifdef LCD_4bit
	retValue = LCD_DataIn() << 4;	// High nibble
	retValue |= LCD_DataIn();		// Low nibble
#else
	retValue = LCD_DataIn();		// All bits
#endif
	hal_LCD_Port_Out();
	return retValue;
}

/*
 * Send instruction to LCD
 */
void LCD_Instruction(uint8_t instruction) {
	while (LCD_IsBusy());
	hal_LCD_RS(0);
	LCD_WriteByte(instruction);
}

/*
 * Send Data to LCD
 */
void LCD_Data(uint8_t data) {
	while (LCD_IsBusy());
	hal_LCD_RS(1);
	LCD_WriteByte(data);
}

/*******************************************************************************
 * Functions for writing text to LCD
 ******************************************************************************/

/*
 * Write char
 */
void LCD_PutC(char c) {
	LCD_Data(c);
}

/*
 * Write string
 */
void LCD_PutS(char *str) {
	while (*str) {
		LCD_PutC(*(str++));
	}
}

/*******************************************************************************
 * Commands
 ******************************************************************************/

/*
 * Read LCD state
 */
uint8_t LCD_ReadState(void) {
	hal_LCD_RS(0);
	return LCD_ReadByte();
}

/*
 * Clear display
 */
void LCD_Clear(void) {
	LCD_Instruction(0x01);
}

/*
 * Turn on display
 */
void LCD_On(uint8_t on) {
	if (on) {
		LCD_Instruction(0x0c);
		LCD_BackLight(LCD_BL_MAX);
	}
	else {
		LCD_BackLight(0);
		LCD_Instruction(0x08);
	}
}

/*
 * Set cursor to position
 */
void LCD_GotoXY(uint8_t x, uint8_t y) {
	unsigned char address;

	address = (y * 40) + x;
	address = 0x80 + (address & 0x7F);
	LCD_Instruction(address);
}

/*
 * Show cursor
 */
void LCD_Cursor(LCD_CursorState_t state) {
	LCD_Instruction(0x0c | (state & 0x03));
}

/*
 * Define user character in CGRAM
 */
void LCD_DefineCharacter(uint8_t charNo, const uint8_t *layoutPtr) {
	uint8_t n;
	uint8_t cursorAddr = LCD_ReadState() & 0x7f;

	LCD_Instruction(0x40 | (charNo << 3));
	for (n=0; n<8; n++) LCD_Data(*(layoutPtr++));

	LCD_Instruction(cursorAddr | 0x80);
}

/*******************************************************************************
 * Other functions
 ******************************************************************************/

/*
 * Set intensity of backlight
 */
void LCD_BackLight(uint16_t value) {
	hal_LCD_BL_Set(value);
}
