

#include "main.h"
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include "lcd_led_matrix__lcd.h"

#define disp_lines 2
#define line_chars 16

#ifndef LCD_ports
	#define LCD_DATA_PORT	PORTB
	#define LCD_CTRL_PORT	PORTD
	#define LCD_DATA_DDR	DDRB
	#define LCD_CTRL_DDR	DDRD
	#define LCD_RS_BIT		2
	#define LCD_RW_BIT		3
	#define LCD_EN_BIT		4
	#define LCD_BPR_BIT		5
#endif

#define DEL_CYC3_10US		(10e-6 * F_CPU / 3)
#define DEL_CYC4_40US		(40e-6 * F_CPU / 4)
#define DEL_CYC4_2MS		(1.6e-3 * F_CPU / 4)

unsigned char lcd_port_lock_reg = 0;


/* ****************************************************** */
unsigned char lcd_port_locked(void)
{
	return lcd_port_lock_reg;
} 


/* ****************************************************** */
void init_lcd(void)
{
	lcd_port_lock_reg = 1;
	LCD_DATA_DDR = 0xFF;
	LCD_CTRL_DDR |= (1 << LCD_EN_BIT) | (1 << LCD_RW_BIT) | (1 << LCD_RS_BIT) | (1 << LCD_BPR_BIT);
	
	LCD_DATA_PORT = 0x00;
	LCD_CTRL_PORT &= ~((1 << LCD_EN_BIT) | (1 << LCD_RW_BIT) | (1 << LCD_RS_BIT) | (1 << LCD_BPR_BIT));
	
	_delay_loop_2(DEL_CYC4_2MS);
	disp_function(0x06);		// set 8bit mode, 2 lines, 5x7dots
	move_cursor_home();
	clear_disp();
	set_entry_mode(2);			// increment cursor position, no display shift
	cursor_disp_ctrl(0x07);		// display ON, cursor ON, blinking cursor ON
	lcd_port_lock_reg = 0;
} 


/* ****************************************************** */
void beep(u08 half_per_10us, u08 cycles)
{
	u08 delay;
	
	while (cycles > 0) {
		LCD_CTRL_PORT |= (1 << LCD_BPR_BIT);
		for (delay = 0; delay < half_per_10us; delay++) _delay_loop_1(DEL_CYC3_10US);
		LCD_CTRL_PORT &= ~(1 << LCD_BPR_BIT);
		for (delay = 0; delay < half_per_10us; delay++) _delay_loop_1(DEL_CYC3_10US);
		cycles--;
	}
	
}


/* ****************************************************** */
void write_cmd(u08 cmd)
{
	lcd_port_lock_reg = 1;
	LCD_CTRL_PORT &= ~((1<<LCD_EN_BIT)|(1<<LCD_RW_BIT)|(1<<LCD_RS_BIT));
	asm volatile ("nop\n\t");
	LCD_DATA_PORT = cmd;
	asm volatile ("nop\n\t");
	LCD_CTRL_PORT |= (1<<LCD_EN_BIT);
	asm volatile ("nop\n\t");
	LCD_CTRL_PORT &= ~(1<<LCD_EN_BIT);
	_delay_loop_2(DEL_CYC4_40US);
	lcd_port_lock_reg = 0;
}


/* ****************************************************** */
void put_char(u08 data)
// Writes data to CGRAM or DDRAM.
{
	lcd_port_lock_reg = 1;
	LCD_CTRL_PORT = (LCD_CTRL_PORT & ~((1<<LCD_EN_BIT)|(1<<LCD_RW_BIT)|(1<<LCD_RS_BIT))) | (1<<LCD_RS_BIT);
	asm volatile ("nop\n\t");
	LCD_DATA_PORT = data;
	asm volatile ("nop\n\t");
	LCD_CTRL_PORT |= (1<<LCD_EN_BIT);
	asm volatile ("nop\n\t");
	LCD_CTRL_PORT &= ~(1<<LCD_EN_BIT);
	asm volatile ("nop\n\t");
	LCD_CTRL_PORT &= ~((1<<LCD_EN_BIT)|(1<<LCD_RW_BIT)|(1<<LCD_RS_BIT));
	_delay_loop_2(DEL_CYC4_40US);
	lcd_port_lock_reg = 0;
}


/* ****************************************************** */
void clear_disp(void)
{
	write_cmd(0x01);
	_delay_loop_2(DEL_CYC4_2MS);
}


/* ****************************************************** */
void move_cursor_home(void)
// Moves cursor to home position
{
	write_cmd(0x02);
	_delay_loop_2(DEL_CYC4_2MS);
}


/* ****************************************************** */
void set_entry_mode(u08 mode)
// Sets cursor move direction and specifies display shift.
// Mode = 0 .. decrement cursor position, no display shift
// Mode = 1 .. decrement cursor position, display shift
// Mode = 2 .. increment cursor position, no display shift
// Mode = 3 .. increment cursor position, display shift
{
	mode &= 3;
	mode |= 4;
	write_cmd(mode);
}


/* ****************************************************** */
void cursor_disp_ctrl(u08 state)
// Sets Display On/Off (D), cursor On/Off (C) and blink of cursor position character (B).
// State = (0 0 0 0 0 D C B)bin
{
	state &= 7;
	state |= 8;
	write_cmd(state);
}


/* ****************************************************** */
void cursor_disp_shift(u08 state)
// Sets cursor-move or display-shift (S/C), shift direction (R/L). DDRAM contents remains unchanged.
// State = (0 0 0 0 0 0 S/C R/L)bin
{
	state &= 3;
	state |= 4;
	state <<= 2;
	write_cmd(state);
}


/* ****************************************************** */
void disp_function(u08 fnct)
// Sets interface data length (DL), number of display line (N) and character font(F).
// Fnct = (0 0 0 0 0 DL N F)bin
// DL 0/1 = 4-bit interface / 8-bit interface 
// N 0/1  = 1 line / 2 lines
// F 0/1  = 5x7 dots / 5x10 dots 
{
	fnct &= 7;
	fnct |= 8;
	fnct <<= 2;
	write_cmd(fnct);
}


/* ****************************************************** */
void set_cgram_addr(u08 addr)
// Sets CGRAM address. CGRAM data is sent and received after this setting.
{
	addr &= 0x3F;
	addr |= 0x40;
	write_cmd(addr);
}


/* ****************************************************** */
void set_ddram_addr(u08 addr)
// Sets CGRAM address. DDRAM data is sent and received after this setting.
{
	addr &= 0x7F;
	addr |= 0x80;
	write_cmd(addr);
}


/* ****************************************************** */
void goto_xy(u08 x, u08 y)
{
	if (x >= line_chars) x = (line_chars - 1);
	if (y > 1) y = 1;
	
	if (y != 0) y = 0x40;
	
	set_ddram_addr(x + y);
}


/* ****************************************************** */
unsigned char put_string(char *s)
{
	u08 i;
	
	i = 0;
	while ((*s != '\0') && (i < 255)) {
		put_char(*s);
		s++;
		i++;
	}
	return i;
}


/* ****************************************************** */
unsigned char put_string_xy(u08 x, u08 y, char *s)
{
	goto_xy(x, y);
	return put_string(s);
}


/* ****************************************************** */
unsigned char put_string_p(PGM_P str_ptr)
{
	u08 i;
	char ch;
	
	i = 0;
	while (((ch = pgm_read_byte(str_ptr)) != '\0') && (i < 255)) {
		put_char(ch);
		str_ptr++;
		i++;
	}
	return i;
}


/* ****************************************************** */
unsigned char put_string_p_xy(u08 x, u08 y, PGM_P str_ptr)
{
	goto_xy(x, y);
	return put_string_p(str_ptr);
}


/* ****************************************************** */
void put_space(u08 space)
{
	u08 i;
	
	if (space > line_chars) space = line_chars;
	for (i = 0; i < space; i++) put_char(' ');
}


/* ****************************************************** */
void put_space_xy(u08 x, u08 y, u08 space)
{
	goto_xy(x, y);
	put_space(space);
}








