/** \file ******************************************************************
* \brief Dotmatrix LCD Library
* 
* \authors 	Dominic Rathje (dominic.rathje@uni-ulm.de)<br>
* 			Christian Degenhart (christian.degenhart@uni-ulm.de)
* \version 1.1
* \note
* - Compiler          : WinAVR 20100110
* - Supported devices : ATMega8
*
*****************************************************************************/

#include <avr/io.h>
#include <stdio.h>

///size of the input buffer used when scrolling is enabled
#define INPUT_BUFFER_SIZE 60

//set the port you want to use for your LCD
#define LCD_D_PORT		PORTC		///< Port for Data Lines DB4-DB7 = Px0-Px3
#define LCD_E_PORT 		PORTD		///< Port for Control Line E
#define LCD_RS_PORT 	PORTD		///< Port for Control Line RS
#define LCD_RW_PORT 	PORTD		///< Port for Control Line RW
#define LCD_DATAMSK		0x0f		///< Mask of the Data Pins

//set the Pins for Rs,R/W,E 
#define LCD_RS			4			///< Register Set Pin Bitposition
#define LCD_E			2			///< LCD Enable Pin Bitposition
#define LCD_RW			3			///< Read / Write Pin Bitposition
	

///defines the format of the connected Display
///	0 =	16*2   1 = 16*4   2 = 40*2   3 = 4*20 
#define LCD_TYPE	3

/// if the controller is a KS0073 the addresses of the LDC-Rows are slightly different
#define ks0073						


//Constants:
//Initial Configurations
#define ENTRY_MODE			0b00000100
#define DISPLAY_STATUS		0b00001100
#define FUNCTION_SET		0b00101000
#define FUNCTION_SET_RE		0b00100100
#define EXT_FUNCTION_SET	0b00001001

//Command Definitions
#define LCD_CMD_CLEAR		0x01	///< LCD Command: Clear Screen
#define LCD_CMD_HOME		0x02	///< LCD Command: move cursor to upper left corner
#define LCD_CMD_MOVE_RIGHT	0x14	///< LCD Command: move cursor richt by one position
#define LCD_CMD_MOVE_LEFT	0x10	///< LCD Command: move cursor left by one position
#define LCD_CMD_SHIFT_RIGHT	0x1C	///< LCD Command: shift right
#define LCD_CMD_SHIFT_LEFT	0x18	///< LCD Command: shift left
#define LCD_CMD_DISPLAY_OFF	0x08	///< LCD Command: Display off
#define LCD_CMD_DISPLAY_ON	0x0C	///< LCD Command: Display on (CUESOR and BLINK is OFF)
#define LCD_CMD_CURSOR_ON	0x0E	///< LCD Command: Cursor on
#define LCD_CMD_BLINK_ON	0x0D	///< LCD Command: Blink on



#ifndef ASM_FILE

/** \brief send one data byte
 * 
 * \param data Byte to send
 * \return void
 */
void lcd_data(uint8_t data);

/** \brief send a command byte
 *
 * \param cmd Command to send
 */
void lcd_command(uint8_t cmd);

/** \brief set a specific display address
 * 
 * Moves the cursor to a specific address, so the next character sent will be displayed at this position.
 * To get the address of a certain position use the offset of the line (LCD_LINE[1..4]) and add the desired column.
 * 
 * \param adr Address
 */
void lcd_setadr(uint8_t adr);

/** \brief write a string
 * 
 * writes a sequence of characters until the first zero-byte.
 * 
 * \param c Pointer to a terminated string
 */
void lcd_puts(char const *c);

/** \brief write a string from flash
 * 
 * same as lcd_puts expect that the string is located in flash memory.
 * \sa lcd_puts
 * \param c Pointer to a terminated string
 */
void lcd_puts_P(char const *);

/** \brief initialise the display
 * 
 * executes the initialisation sequence defined by the HD44780 datasheet
 */
void lcd_init(void);

/** \brief read the position of the cursor
 * 
 * \return current address of the cursor position
 */
uint8_t lcd_readadr(void);

/** \brief Write one Character on the Display
 * 
 * Characters received are transformer by a lookup-table because the charset of the Display is not equal to ASCII.
 * As input to this function any ASCII character that can be typed ba a German or American Keyboard is valid and displayed correctly on the screen.
 * 
 * As the Display has some amazing special characters, like fancy arrows, there are some sequences that are parsed and replaced by the corresponding symbol.
 * See https://wiki.fs-et.de/student-lab/UartDisplay for details on the parser.
 * 
 * \param data ASCII Character to print.
 * \param stream (is necessary to be compatible with printf)
 * \return 0 on success.
 */
int lcd_putchar(char data, FILE *sream);

/** \brief send the buffer contents to the display
 * 
 * prints the contents of the buffer on the screen.
 * This function is called automatically by lcd_putchar when a \r is received.
 */
void print_buffer(void);

/** \brief scroll individual lines
 * 
 * This function should be called by the main loop regularly if scroll mode is activated.
 * Each time this function is executed the content of a line longer than the physical display lentgh is rotated by one position.
 */
void lcd_scroll(void);

//Macros

/// move the Cursor to to the upper left corner of the screen.
#define lcd_home() 		lcd_command(LCD_CMD_HOME);
/// sends the command clear the display
#define lcd_clear()		lcd_command(LCD_CMD_CLEAR);

#endif

#define LCD_D_DDR	(LCD_D_PORT-1)
#define LCD_D_PIN	(LCD_D_PORT-2)
#define LCD_E_DDR	(LCD_E_PORT-1)
#define LCD_RS_DDR	(LCD_RS_PORT-1)
#define LCD_RW_DDR	(LCD_RW_PORT-1)

#if LCD_TYPE == 0
#define LCD_ROWS 2
#define LCD_COLS 16
#endif

#if LCD_TYPE == 1
#define LCD_ROWS 4
#define LCD_COLS 16
#endif

#if LCD_TYPE == 2
#define LCD_ROWS 2
#define LCD_COLS 40
#endif

#if LCD_TYPE == 3
#define LCD_ROWS 4
#define LCD_COLS 20
#endif

  #ifndef ks0073
	#if LCD_ROWS==4
	#define LCD_LINE1	0x00
	#define LCD_LINE2	0x40
	#define LCD_LINE3	0x14
	#define LCD_LINE4	0x54
	#endif
  #else
	#if LCD_ROWS==2
	#define LCD_LINE1	0x00
	#define LCD_LINE2	0x40
	#elif LCD_ROWS==4
	#define LCD_LINE1	0x00
	#define LCD_LINE2	0x20
	#define LCD_LINE3	0x40
	#define LCD_LINE4	0x60
	#endif
  #endif

