#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>

#define RS 						6        
#define E 						5        
#define BIT_8 					3
#define BIT_4 					2
#define BITSET(var,bitno)		((var) |= 1 << (bitno))   
#define BITCLR(var,bitno)		((var) &= ~(1 << (bitno)))
#define BITTST1(var,bitno)		((var) & (1 << (bitno))) 


/*

а0-а3 дата,
а4- вход,
а5-строб, 
а6-инф\дата,
в2-сброс 

*/
void 							lcd_pre_init(void);
void 							lcd_com (unsigned char lcd);
void 							lcd_dat (unsigned char lcd);
void 							lcd_show(void);
void 							lcd_init (void);
//---------------------------------------------------------
static char 					m = 0; 			// сотни метров
EEMEM  unsigned long  			km = 430000; 	// расстояние в километрах полное в еепром
volatile unsigned long  		km_full;		// расстояние в километрах полное на дисплей
volatile unsigned int 			km_temp;		// расстояние в километрах за день
unsigned char 					dat;			// выводимый символ
volatile unsigned char 			cFlag;
volatile unsigned char 			not_zero;		// если 0, внешнего прерывания небыло
volatile unsigned char 			temp1;			// счетчик для "мигания"
volatile unsigned char 			m_false;		// если 1, количество метров не увеличиваем	
unsigned char 					c;
//---------------------------------------------------------
enum FLAGS{FLAG_READY=0,FLAG_END}FLAGS;



ISR(EXT_INT0_vect)
{
	/* обнуление текущего пробега по нажатию кнопки  */
	TCNT1 			= 25;
	m 				= 0;
	BITSET(cFlag,FLAG_READY);
	not_zero 		= 1;
}


ISR(TIM1_COMPA_vect)
{
	/*увеличение числа сотен метров. прерывание при достижении счетчиком значения max == 425*/
	TCNT1 			= 0; 				// при скорости 250 км/ч инкримент счетчика каждые 3,33мс
	temp1++;
	m_false 		= 1;
	if (temp1 == 17) {
		m++;
		temp1 		= 0;
		m_false 	= 0;
		}
	not_zero 		= 0;
	BITSET(cFlag,FLAG_READY);
}
//--------------------------------------------------------------------


int main (void)
{
	DDRA 			= 0b11101111;	 				
	DDRB 			= 0;					
	PORTB 			= 0b00001111;
	GIMSK			|= (1<<INT0);		
	MCUCR 			= 0;				// прерывание реагирует на 0 на INT0
	BITCLR(cFlag,FLAG_READY);
	TCCR1A 			= 0;
	TCCR1B			= 0X06;				// таймер считает импульсы от Т1 при переходе в нижний уровень
	OCR1A 			= 0x025;			// забиваем значение на сравнение
	TIMSK1 			|= (1<<OCIE1A);	// разрешаем прерывание OC1A
	_delay_ms(20);
	km_full 		= eeprom_read_dword(&km);		// Чтение EEPROM
	lcd_pre_init();
	lcd_show();
	sei();
	while(1){
		if(BITTST1(cFlag,FLAG_READY))
		{
			BITCLR(cFlag,FLAG_READY);
			if (m == 10) m = 0;
			if((m==0) & (not_zero == 0) & (m_false == 0)){
				km_temp++;
				km_full++; 
				eeprom_write_dword(&km, km_full);
				}
			if (not_zero == 1){
				m = 0; 
				km_temp = 0;
				not_zero = 0;
				}
			lcd_show();
		}

	}

}

//--------------------------------------------------------------------
//программа перевода дисплея в 4-битный режим
void lcd_pre_init(void)
{
	/*
	троекратная команда установки 8-битного режима для раздупления дисплея. Вместо 0x30 подается
	0x03, т.к. у меня выводам D5-D8 дисплея соответствуют выводы D0-D3 МК
	*/
	PORTA 			= BIT_8;			// установка 8 битного режима
	asm ("nop");
	PORTA 			&= ~(1<<RS);		// сигнал "команда"
	asm ("nop");
	PORTA 			|= (1<<E);			// строб импульс в высокий ур.	
	_delay_us(50);					 		
	PORTA 			&= ~(1<<E);		// строб импульс в низкий ур. запись.
	_delay_ms(20);

	PORTA 			= BIT_8;
	asm ("nop");
	PORTA 			&= ~(1<<RS);	
	asm ("nop");
	PORTA 			|= (1<<E);			
	_delay_us(50);					 		
	PORTA 			&= ~(1<<E);
	_delay_ms(2);

	PORTA 			= BIT_8;
	asm ("nop");
	PORTA 			&= ~(1<<RS);	
	asm ("nop");
	PORTA 			|= (1<<E);			
	_delay_us(50);					 		
	PORTA 			&= ~(1<<E);
	_delay_ms(2);

	PORTA 			= BIT_4;			// установка 4 битного режима
	asm ("nop");
	PORTA 			&= ~(1<<RS);	
	asm ("nop");
	PORTA 			|= (1<<E);			
	_delay_us(50);					 		
	PORTA 			&= ~(1<<E);
	_delay_ms(2);						
	lcd_init(); 
}
//--------------------------------------------------------------------
// программа обработки команд для ЖКИ
void lcd_com (unsigned char lcd) 
{ 
	PORTA 			= lcd >> 4;			// выводим старшую тетраду данных
	asm ("nop");
	PORTA 			&= ~(1<<RS);			
	asm ("nop");
	PORTA 			|= (1<<E);				
	_delay_us(50);				
	PORTA 			&= ~(1<<E);		
	_delay_us(50);			


	PORTA = 		(lcd<<4)>>4;		// выводим младшую тетраду данных
	asm ("nop");
	PORTA 			&= ~(1<<RS);			
	asm ("nop");
	PORTA 			|= (1<<E);				
	_delay_us(50);				
	PORTA 			&= ~(1<<E);	
	_delay_us(50);				
}
//------------------------------------------------------------------------
// программа записи данных в ЖКИ
void lcd_dat (unsigned char lcd)
{ 
	PORTA = 		lcd >> 4;	
	asm ("nop");
	PORTA 			|= (1<<RS);			
	asm ("nop");
	PORTA 			|= (1<<E);				
	_delay_us(50);				
	PORTA 			&= ~(1<<E);		
	_delay_us(50);		

	PORTA 			= (lcd<<4)>>4;
	asm ("nop");
	PORTA 			|= (1<<RS);			
	asm ("nop");
	PORTA 			|= (1<<E);				
	_delay_us(50);				
	PORTA 			&= ~(1<<E);		
	_delay_us(50);	
}
//------------------------------------------------------------------------------
// программа обработки данных для вывода
void lcd_show(void) 
{
	/*полное пройденное расстояние*/
	lcd_com(0x80);					//Ставим курсор на начало 1-й строки ЖКИ
	dat 			= (km_full / 100000) % 10;  
	lcd_dat (dat + 0x30);			// 100k km
	dat 			= (km_full / 10000) % 10; 	
	lcd_dat (dat + 0x30);		
	dat 			= (km_full / 1000) % 10; 	
	lcd_dat (dat + 0x30);
	dat 			= (km_full / 100) % 10; 	
	lcd_dat (dat + 0x30);		
	dat 			= (km_full / 10) % 10; 	
	lcd_dat (dat + 0x30);
	dat 			= km_full % 10; 			
	lcd_dat (dat + 0x30);
	lcd_com(0xC0);					//Ставим курсор на начало 2-й строки ЖКИ
	
	/*расстояние, пройденное за день */
	dat 			= (km_temp / 1000) % 10; 	
	lcd_dat (dat + 0x30);
	dat 			= (km_temp / 100) % 10;	
	lcd_dat (dat + 0x30);		
	dat 			= (km_temp / 10) % 10; 	
	lcd_dat (dat + 0x30);
	dat 			= km_temp % 10; 			
	lcd_dat (dat + 0x30);
	lcd_dat (0X2C); 	
	dat 			= m;						
	lcd_dat(dat + 0x30);			// сотни метров
	lcd_dat (0x20);					// пустая ячейка
	lcd_dat (c^=0xFF); 				// последний символ "мигает"
	
}
//------------------------------------------------------
// программа инициализации
void lcd_init (void)
{

	lcd_com(0x28);		// 4-проводный интерфейс, 2 строки, 5х8 символ
	_delay_us(200);

	lcd_com(0x01);		// Очистить DDRAM и установить курсор на 0x00
	_delay_ms(5);

	lcd_com(0x06);		// инкремент адреса, сдвиг экрана отключен
	_delay_us(200);

	lcd_com(0x0C);		// Показать изображение, курсор не показывать
	_delay_us(200);
}
//-------------------------------------------------------