//#######################################################################################################################
//#
//#	ФУНКЦИИ ОБСЛУЖИВАНИЯ ПРЕРЫВАНИЯ
//#
//#######################################################################################################################


//ФУНКЦИЯ ОБРАБОТКИ ПРЕРЫВАНИЯ ПО СОВПАДЕНИЮ ТАЙМЕРА\СЧЕТЧИКА (частота вызова 800 Гц)
ISR (TIMER2_COMP_vect)
{
PORTB |= 0x04;

	static char LedBoardXCoun = 0;				//счетчик динамического вывода данных на бегущую строку
	LedBoardOut (LedBoardXCoun);				//динамическое обновление строки

	if (LedBoardXCoun == 1)
		if (Flags1 & b1ClockReadEnable)
			IICClockReadData ();				//чтение данных времени (после прорисовки второй строчки)

	if ((LedBoardXCoun >= 2) && (LedBoardXCoun <= 5))
		if (Flags1 & b1OneWireGet)				//обработка получения данных температуры 
			OneWireIntTermoGet ();				//сброс флага - Flags1 &= ~b1OneWireGet; - в обработчике

	if (LedBoardXCoun == 6){					//после прорисовки 
		BtnGet ();								//ф-ция обработки опроса кнопок
		VarProcessing ();						//ф-ция обработки глобальных переменных
	}

PORTB &= ~0x04;
PORTB |= 0x04;

	if (++LedBoardXCoun == 8){					//инкремент переменной развертки табло
		LedBoardXCoun = 0;
		Flags1 |= b1LedBoardPageCompleted;		//флаг завершения развертки строки (для фоновых процессов)
		ClockBufferProcess ();					//обработка дополнительного графического буфера часов
	}

PORTB &= ~0x04;
}


//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ФОРМИРОВАНИЯ ДОПОЛНИТЕЛЬНОГО ГРАФИЧЕСКОГО БУФЕРА ЧАСОВ 
//берутся данные двоично десятичного вида, считанные с МС часов DS1307, и преобразуются в графический вид в дополнительном 
//графическом буфере ClockGraphBuf. Цифры в дополн граф буфер прорисовываются с учетом плавного скроллинга цифр по вертикали
//после формирования графического буфера, происходит вывод в основной графический буфер до позиции ClockPosition (0 - выключено)

char* pClockBuf;												//глобальный указатель на графический буфер часов
void ClockBufferProcess (void) 
{	
	static char ShiftDelay;										//обработка скорости прокрутки
	static char ShiftMask;										//бит маска - счетчик ступени, сдвига цифр по вертикали (0x01 - 0x80)
	pClockBuf = ClockGraphBuf;									//инициализировать указатель на начало графического буфера часов

	char Flags = ClockChangeMask;								//маска изменившихся циферок
	if (! ShiftMask)											
		if (Flags){											
			ShiftMask = 1;										//инициализация счетчика сдвига цифр	
			ShiftDelay = 0;
	}

	if (! ShiftDelay--){
		ShiftDelay = 6;

		//печать символов в дополнительном графическом буфере часов
		char ShiftTemp;
		if (Flags & 0x20) ShiftTemp = ShiftMask;						//обработка кода символа десятков часов
		else ShiftTemp = 0;												
		ClockSimbToBuf ((ClockData[2]>>4)+0x30, ShiftTemp);	
		if (Flags & 0x10) ShiftTemp = ShiftMask;						//обработка кода символа единиц часов
		else ShiftTemp = 0;
		ClockSimbToBuf ((ClockData[2] & 0x0f)+0x30,ShiftTemp);			
		
		ClockSimbToBuf (':', 0);									//разделитель							
	
		if (Flags & 0x08) ShiftTemp = ShiftMask;						//обработка кода символа десятков минут
		else ShiftTemp = 0;
		ClockSimbToBuf ((ClockData[1]>>4)+0x30, ShiftTemp);				
		if (Flags & 0x04) ShiftTemp = ShiftMask;						//обработка кода символа единиц минут	
		else ShiftTemp = 0;
		ClockSimbToBuf ((ClockData[1] & 0x0f)+0x30, ShiftTemp);		

		ClockSimbToBuf (':', 0);									//разделитель						
	
		if (Flags & 0x02) ShiftTemp = ShiftMask;						//обработка кода символа десятков секунд
		else ShiftTemp = 0;
		ClockSimbToBuf ((ClockData[0]>>4)+0x30, ShiftTemp);				
		if (Flags & 0x01) ShiftTemp = ShiftMask;						//обработка кода символа единиц секунд
		else ShiftTemp = 0;
		ClockSimbToBuf ((ClockData[0] & 0x0f)+0x30, ShiftTemp);	
		
		ShiftMask <<= 1;
		if (! ShiftMask)
			ClockChangeMask = 0; 
	}


	//если позиция назначена - копируем из дополнительного графического буфера, в буфер экрана	
	if (ClockPosition){																				
		char* pLedBoardBuffer = LedBoardBuffer + ClockPosition + LedBoardSimbWidth - 1;	//последний байт проекции часов в буфер строки
		char* pClockGraphBuf = ClockGraphBuf + sizeof ClockGraphBuf -1;					//последний байт графического буфера часов

		for (uint8_t i =sizeof ClockGraphBuf; i; i--){
			if ((pLedBoardBuffer < LedBoardBuffer + ((LedBoardSize + 1) * LedBoardSimbWidth)) &&
			(pLedBoardBuffer >= LedBoardBuffer + LedBoardSimbWidth))
				*pLedBoardBuffer = *pClockGraphBuf;										//если байт попадает внутрь буфера экрана - копируем

			pLedBoardBuffer--;
			pClockGraphBuf--;
		}
	}
}


//ВЫВОД СИМВОЛА В ДОПОЛНИТЕЛЬНЫЙ ГРАФИЧЕСКИЙ БУФЕР ЧАСОВ (с функцией прокрутки по вертикали)
//АРГУМЕНТ-1 код печатаемого символа часов
//АРГУМЕНТ-2 маска сдвига символа по вретикали (0x01 - 0x80), если 0 - сдвига нет.
//скорость скроллинга цифр часов (1..12)		(11 - значение(1..10) ) * 12 / 10

void ClockSimbToBuf (char byte, char shift)
{
	char* pSimbMass = SimbMass + ((byte-0x20) * FontSimbWidth);	//вычисляем начало нужного символа в фонте PROGMEM

	for (char i=0; i<FontSimbWidth; i++){						//цикл вывода символа в доп графический буфер
		char temp = pgm_read_byte (pSimbMass++);				
		if (shift){
			*pClockBuf >>= 1;									//вывод символа в доп буфер часов (с обработкой скроллинга)
			if (temp & shift)
				*pClockBuf |= 0x80;
		}
		else
			*pClockBuf = temp;									//вывод символа в доп буфер часов (без скроллинга)			
		pClockBuf++;	
	}

	for (char i=0; i<(LedBoardSimbWidth-FontSimbWidth); i++)	
		*pClockBuf++ = 0;										//добавить промежуток между символами
}


//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ПОСЛЕДОВАТЕЛЬНОГО ВЫВОДА ДАННЫХ В ЛИНИЮ ТАБЛО (в линейку МС HC595)
//АРГУМЕНТ - номер линии (0-7) в которую происходит вывод данных

void LedBoardOut (char NumLine) 
{
	char MaskXRow = (1<<NumLine);					//маска выводимой линии
	char* pLedBoardBuffer = LedBoardBuffer + LedBoardSimbWidth;
												//цикл для вывода строчки панели
	for (char i=0; i<(LedBoardSize * LedBoardSimbWidth); i++){
		char temp = *pLedBoardBuffer++;

		if (temp & MaskXRow)					//устанавливаем линию данных
			LedBoardDataPORT |= LedBoardData;
		else	
			LedBoardDataPORT &= ~LedBoardData;			
			
		_delay_us (1);
		LedBoardDataPORT |= LedBoardClc;		//импульс (продвигаем данные в регистры)
		_delay_us (1);
		LedBoardDataPORT &= ~LedBoardClc;
	}

	LedBoardRowPORT = 0xFF-LedBoardRowInvert;	//выключить линии табло	
	LedBoardDataPORT |= LedBoardStrobe;			//импульс строба накопленных данных в сдвиговых регистрах - на выход 
	_delay_us (1);
	LedBoardDataPORT &= ~LedBoardStrobe;
	_delay_us (1);
	LedBoardRowPORT = (~LedBoardRowInvert)^MaskXRow;//включаем обновленную линию табло
}


//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ с режимом автоускорения счета при удержании (вызывается с частотой 100 Гц)
//короткое нажатие устанавливает биты ShortXXXX глобальной переменной BtnFlags
//длинное нажатие устанавливает биты LongXXXX глобальной переменной BtnFlags

void BtnGet (void)
{	 //задержки обработки событий кнопки
	#define BtnLockTime 30						/*время обработки дребезга в милисекундах (10-100)*/
	#define BtnLongPressTime 2000				/*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/

	//переменные управления одиночными событиями (короткое\длинное нажатие кнопки)
	static unsigned char BtnLockBit;			//ащелка (защита от дребезга)
	static unsigned char BtnLockCoun;			//счетчик защелки (защита от дребезга)
	static unsigned char BtnLongCoun;			//счетчик длинного нажатия
	static unsigned char BtnTemp;				//бит последней	нажатой кнопки

	//переменные управления длительными событиями (авто инкремент\декремент при удержании кнопки)
	static unsigned char BtnLongSpeedTmp;		//значение скорости для *BtnLongSpeed
	static unsigned char BtnLongTimer;			//таймер управления приростом BtnLongSpeedTmp

	//обработка маски кнопок
	char BtnStack = 0;
	char temp = ~BtnPIN;
	if (temp & Button1)
		BtnStack = 0x01;
	if (temp & Button2)
		BtnStack = 0x02;
	if (temp & Button3)
		BtnStack = 0x04;
	if (temp & Button4)
		BtnStack = 0x08;

	//основной алгоритм обработки событий кнопки
	if (BtnStack){								//клавиша нажата
		BtnTemp = BtnStack;
		BtnStack = 0;

		if (BtnLockCoun < (BtnLockTime/10)){
			BtnLockCoun++;
			return;
		}
		BtnLockBit=1;							//нажатие зафиксировано		
		if (BtnLongCoun >= (BtnLongPressTime/10)){								
			LedBoardFlashCoun = 0;				//сбросить на начало цикл FLASH дисплея
			BtnLongTimer ++;					//обработка переменных автоинкремента при удержании кнопки	
			if (! (BtnLongTimer & 0b00001111))	//каждые 0,16 сек обновлять содержимое BtnLongSpeed из BtnLongSpeedTemp
				BtnLongSpeed = BtnLongSpeedTmp;
			if (BtnLongTimer == 128)			//каждые 1,28 сек удваивать содержимое BtnLongSpeedTemp
			{
				BtnLongTimer = 0;
				if (BtnLongSpeedTmp < 64)
					BtnLongSpeedTmp <<= 1;	
			}
			return;
		}
		BtnLongCoun ++;
		if (BtnLongCoun >= (BtnLongPressTime/10)){
			BtnFlags |= (BtnTemp << 4);			//установка бита длинного нажатия (старшие 4 бита флагов ButtonByte)
					
			BtnLongSpeed = 1;					//инициализация переменных автоинкремента при удержании кнопки	
			BtnLongSpeedTmp = 1;						
			BtnLongTimer = 0;
		}
	}		
	else{										//клавиша отжата			
		if (BtnLockCoun != 0){
			BtnLockCoun --;
			return;
		}
		if (! (BtnLockBit))						//отжатие зафиксировано
			return;
		BtnLockBit =0;
		if (BtnLongCoun < (BtnLongPressTime/10)){
			BtnFlags |= (BtnTemp);				//установка бита короткого нажатия (младшие 4 бита флагов ButtonByte)
			LedBoardFlashCoun = 0;				//сбросить на начало цикл FLASH дисплея
		}
		BtnLongCoun = 0;
		BtnLongSpeed = 0;						//окончание работы функции автоинкремента при удержании кнопки
	}
}


//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ОБСЛУЖИВАНИЯ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ (частота вызова должна быть 100Гц)

void VarProcessing (void)
{	 
	if (++ LedBoardFlashCoun == 100)		//инкремент счетчика FLASH (за секунду счет 0-99)
		LedBoardFlashCoun = 0;

	if (++ ClockMiliSecond == 120)			//инкремент счетчика милисекунд 
		ClockMiliSecond = 0;				//(фактически он обнуляется там - ClockReadData())

	static char OneWireCicle = 0;			//счетчик циклического запуска измерения DS1820 (1 раз\сек)
	if (++ OneWireCicle == 100){
		OneWireCicle = 0;		
		Flags1 |= b1OneWireGet;			//при значении 0 - запустить преобразование температуры

		//здесь обслуживание переменных с частотой 1 Гц
		if (BtnTimeOutCoun)					//обработка счетчика timeout-а
			BtnTimeOutCoun--;
	}
}


//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ЧТЕНИЯ ДАННЫХ ТЕМПЕРАТУРЫ С 2-x ДАТЧИКОВ DS18B20
//данная функция вызываемая из прерывания обеспечивает адаптивное чтение данных с устройств 1wire
//при каждом вызове обрабатывается 1 байт для того чтобы время работы не превышало периода между вызовами прерывания

void OneWireIntTermoGet (void)
{
	static unsigned char step = 0;
	char mask;

	if (step < 16)								//от 0 до 15 обслуживание датчика №1
		mask = OneWireLine1;
	else{
		if (step < 32)							//от 16 до 31 обслуживание датчика №2
			mask = OneWireLine2;
		else
			mask = OneWireLine1 | OneWireLine2;	//от 32 команды обоим датчикам
	}

	unsigned char byte;
	byte = pgm_read_byte (&DSReadingMap [step++]);//при каждом вызове, обрабатываем 1 байт
	if (byte < 8)								//чтение данных из DS18B20 и размещение их в массиве OneWireReadBuf[]
		OneWireReadBuf [byte] = OneWireData(0xff, mask);
	if (byte == 0x0b)			
		OneWireReadBuf [8] = OneWireCheckSumm;	//сохранение CRC первого канала
	if (byte == 0x0c)			
		OneWireData(0xff, mask);				//просто чтение для подсчета CRC
	if (byte == 0x0d)							
		OneWireReset (mask);					//сброс 1Wire устройств
	if (byte == 0x0e){
		char OneWireCRC	= OneWireReadBuf[8];
		char* pBufData = OneWireReadBuf;

		for (unsigned char i=0; i<2; i++){
			char DevType = *pBufData++;			//тип 1wire устройства
			int termo = 0;						//рассчет загруженных данных 
			termo = *pBufData++;				//младший байт температуры
			termo |= (*pBufData++) << 8;		//старший байт температуры
			char Config = *pBufData++;			//байт конфигурации устр-ва
			if (OneWireCRC 						//проверка контрольной суммы
			|| !((DevType == 0x28) 				//0x28 - семейство DS18B20
			|| (DevType == 0x10))) 				//0x10 - семейство DS18S20
				OneWireErr |= (1<<i);			//контрольная сумма не верна, или незнакомое устройство, установить флаг ошибки соответствующего устройства
			else{
				OneWireErr &= ~(1<<i);			//контрольная сумма верна, сбросить флаг ошибки соответствующего устройства
				if (DevType == 0x10)
					Config = 0;	
				Config >>= 5;					//подготовка счетчика преобразования из байта конфигурации
				Config ^= 0xff;
				Config &= 0x03;

				while (Config--)
					termo <<= 1;				//сдвигаем данные к разрядности 12 бит
				termo *= 10;					//приводим данные температуры в формат с одной цифрой после запятуи
				termo /= 16;
				OneWireTermoData [i] = termo;	//записываем в массив

			}
			OneWireCRC = OneWireCheckSumm;
		}
	}
	if (byte == 0x0f){
		Flags1 &= ~b1OneWireGet;				//сброс флага вызова
		step =0;								//Обнуление счетчика 
	}
	if (byte >= 0x10){							//0x33 чтение номера устройства. 0xCC обращение к одному устройству на линии. 
		OneWireData (byte, mask);				//0xBE чтение содержимого памяти устройства. 0x44 преобразование
		if (byte == 0xBE)
			OneWireCheckSumm = 0;				//перед чтением памяти обнулить счетчик CRC
	}
}


//#######################################################################################################################
//#
//#	END
//#
//#######################################################################################################################

 
