/*
 * ul_lcda.c
 *
 *  Created on: Oct 4, 2011
 *      Author: qwer1
 */

#include <string.h>
#include "pt.h"
#include "ul_ptx.h"
#include "ul_timing.h"
#include "ul_lcda.h"

U8        lcd_buf[LCD_ROWS * LCD_COLS];
U8        lcd_col;
U8        lcd_row;
U8        lcd_cur_col;
U8        lcd_cur_row;
U8        lcd_init;
U16       lcd_upd_period = 100;

U8        lcd_led_state;
U8        lcd_vo;
U8        lcd_vo_cnt; //LCD contrast voltage counter

U8        lcd_2col_shift;


void setup_lcd( U8 led, U8 vo ) {
	memset(&lcd_buf, ' ', sizeof(lcd_buf));
	lcd_led_state = led;
	lcd_vo  = vo;

	ptx_add( pt_lcd, NULL, 1); //LCD refresh thread
}

#if (LCD_WIRES == 3)
	//write single byte to LCD, RS - RS line select
	void lcd_wr_U8( U8 data, U8 rs ) {
		lcd_s(1); //prepare SCK
		lcd_l(0); //prepare LCK
		for (U8 bc=8; bc; bc--) {   //shift byte to register
			lcd_d( (data & 0x80) ); //set data line to bit 7
			lcd_s(0);               //SCK=0
			data <<= 1;             //shift bit
//			delay_us(5);
			lcd_s(1);               //SCK=1
			delay_us(5);
		}
//		delay_us(10);
		lcd_d(rs);     //set RS line
//		delay_us(5);
		lcd_l(1);      //strobe data to register (LCK=1)
//		delay_us(5);
		lcd_l(0);      //strobe data to LCD      (E=0)
//		delay_us(5);
		lcd_led( lcd_led_state ); //set LCD light on/off
//		delay_us(15);
		lcd_vo((lcd_vo_cnt > lcd_vo)); //set current VO level
		delay_us(40); //command delay
	}

#elif (LCD_WIRES == 6)
	//write lower nibble
	void lcd_wr_U4( U8 data, U8 rs ) {
		lcd_rs(rs);
		lcd_d4(data & 1);
		lcd_d5(data & 2);
		lcd_d6(data & 4);
		lcd_d7(data & 8);
		delay_us(25);
		lcd_e(0);
		delay_us(40);
		lcd_e(1);
		delay_us(150);
//		lcd_vo((lcd_vo_cnt > lcd_vo)); //set LCD contrast level
//		lcd_led( lcd_led_state );      //set LCD light on/off
	}
	//write higher, then lower nibble
	void lcd_wr_U8( U8 data, U8 rs ) {
		lcd_wr_U4( data >> 4, rs );
		delay_us(40);
		lcd_wr_U4( data, rs );
		delay_us(40);
	}
#endif


PT_THREAD( pt_lcd( struct pt *pt, struct ptx *ptx ) ) {
	//update LCD VO voltage
	if (t_1ms) {
		lcd_vo_cnt++;
		if (lcd_vo_cnt >= LCD_VO_MAX) lcd_vo_cnt = 0;
//		lcd_vo((lcd_vo_cnt > lcd_vo)); //set current VO level
	}

	PT_BEGIN(pt);
	while (1) {

		if (lcd_init == 0) {                 //LCD not initialized ? Initialize in 8-bit mode
#if (LCD_WIRES == 3)
			PTX_DELAY_MS( LCD_DELAY_STARTUP ); //power-on delay
			lcd_wr_U8( LCD_CMD_FUNC8, 0 );     //function set
			PTX_DELAY_MS( LCD_DELAY_SETUP );
			lcd_wr_U8( LCD_CMD_FUNC8, 0 );     //function set
			PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_FUNC8, 0 );     //function set
			PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_FUNC8 | ((LCD_ROWS & 1) << 3), 0 ); //function set
			PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_OFF, 0 );       //display off
			PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_CLR, 0 );       //display clear
			PTX_DELAY_MS( 3 );          //delay
			lcd_wr_U8( LCD_CMD_ENTRY_INC, 0 ); //entry mode set
			PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_ON, 0 );        //switch display on
			PT_YIELD( pt );
#elif (LCD_WIRES == 6)
			lcd_d4(0); lcd_d5(0); lcd_d6(0); lcd_d7(0);
			lcd_rs(0); lcd_e(0);

			PTX_DELAY_MS( LCD_DELAY_STARTUP ); //power-on delay
			lcd_wr_U4( 0x03, 0 ); PTX_DELAY_MS( LCD_DELAY_SETUP );
			lcd_wr_U4( 0x03, 0 ); delay_us(LCD_DELAY_CMD); PT_YIELD(pt);
			lcd_wr_U4( 0x03, 0 ); delay_us(LCD_DELAY_CMD); PT_YIELD(pt);
			lcd_wr_U4( 0x02, 0 ); delay_us(LCD_DELAY_CMD); PT_YIELD(pt);

			lcd_wr_U8( LCD_CMD_FUNC4 | (((LCD_ROWS & 1) + 1) << 3), 0 );
			delay_us(LCD_DELAY_CMD); PT_YIELD(pt);
			lcd_wr_U8( LCD_CMD_DISPLAY, 0 );   //display off
			delay_us(LCD_DELAY_CMD); PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_DISPLAY | LCD_CMD_DISPLAY_ON, 0 ); //switch display on
			delay_us(LCD_DELAY_CMD); PT_YIELD( pt );
			lcd_wr_U8( LCD_CMD_CLR, 0 );       //display clear
			PTX_DELAY_MS( LCD_DELAY_CLR );
			lcd_wr_U8( LCD_CMD_ENTRY_INC, 0 ); //entry mode set
			delay_us(LCD_DELAY_CMD); PT_YIELD( pt );
#endif
			lcd_init = 1;                      //LCD init completed
		} else {
			//display update loop
			for (lcd_row=0; lcd_row<LCD_ROWS; lcd_row++) {
				for (lcd_col=0; lcd_col<LCD_COLS; lcd_col++) {

					if (lcd_col == 0) { //set DDRAM address
						if (lcd_row == 0) lcd_wr_U8( LCD_CMD_DDRAM, 0 );
						else if (lcd_row == 1) lcd_wr_U8( LCD_CMD_DDRAM + 0x40, 0 );
						else if (lcd_row == 2) lcd_wr_U8( LCD_CMD_DDRAM + LCD_COLS + lcd_2col_shift, 0 );
						else if (lcd_row == 3) lcd_wr_U8( LCD_CMD_DDRAM + 0x40 + LCD_COLS + lcd_2col_shift, 0 );
						PT_YIELD(pt);
					}

					lcd_wr_U8( lcd_buf[lcd_row * LCD_COLS + lcd_col], 1 );
					PT_YIELD(pt);
				}
			}

			PTX_DELAY_MS( lcd_upd_period);
		}
	}
	PT_END(pt);
}
