;*******************************************************************************
;				WH1602__M8_IR_NEC.asm
;	 FM-РАДИО НА МОДУЛЕ RDA5807M С LCD-индикатором WH1602
;Программа для ATMEGA8 работает на частоте внутр. генератора 8 Мгц.
; 		Основные функции программы:
;-*- Дистанционное управление (7 основных команд) по протоколу NEC.
;-*- Функция обучения для любого пульта ДУ по протоколу NEC.
;-*- Переход в режим обучения происходит если перед включением
;    питания удерживать кнопку V-.
;-*- Переход к следующему коду кнопки при обучении по нажатию на V+.
;-*- Выход из режима обучения по окончании обучения или по нажатию на CH-.
;-*- Сигнал от приёмника ДУ подаётся на вход сигнала захвата ICP.
;-*- Непрерывное изменение громкости при удержании одной из кнопок V+ или V-.
;-*- Полное выключение громкости при уровне равном 00.
;-*- Перебор станций, сохранённых в памяти программ вверх и вниз по списку, а
;    также автопоиск станций вверх и вниз по диапазону кнопками CH+ или CH-.
;-*- Переход из режима ручного выбора радиостанции в режим автопоиска и обратно
;     производится длительным нажатием на одну из кнопок CH+ или CH-.
;-*- Опознавание названия станции по её частоте при автопоиске.
;-*- Вывод на индикатор частоты и названия станции.
;-*- Поддержка вывода кириллических знаков.
;-*- Запись громкости и последней частоты настройки в память EEPROM при
;    каждой настройке на станцию.
;
;-*- Запись уровня громкости при каждой настройке на станцию.
;-*- Индикация уровня громкости на дисплее цифрами от 00 до 16.
;-*- Индикация уровня сигнала на дисплее четырьмя столбиками.
;-*- Светодиодная индикация приёма команды от ДУ.
;-*- Индикация "Моно-Стерео" светодиодом.
;-*- Подсветка дисплея при включении и при настройке в течение 10 сек.
;
;Распределение портов микропроцессора:
; PB0 - Входной сигнал от приёмника ДУ.
; PB1 - Св. диодный индикатор приёма команды от ДУ.
;
; PD0 - D7 - Линия данных индикатора WH1602A
; PD1 - D6 - Линия данных индикатора WH1602A
; PD2 - D5 - Линия данных индикатора WH1602A
; PD3 - D4 - Линия данных индикатора WH1602A
; PD4 - E  - Выбор индикатора WH1602A
; PD5 - RS - Сброс индикатора WH1602A
; PD6 - Выходной сигнал управления подсветкой дисплея.
; PD7 - Св. диодный индикатор стерео сигнала,
;
; PС0 - Кнопка V+ увеличения громкости
; PС1 - Кнопка V- уменьшения громкости
; PС2 - Кнопка CH+ выбора следующей станции
; PС3 - Кнопка CH- выбора предыдущей станции
; PC4 - (SDA) - Двунаправленная шина данных I2C
; PC5 - (SCL) - Двунаправленная шина тактирования I2C
;
;*******************************************************************************
	;Подключить файл описаний имён регистов ввода/вывода
    .NOLIST                 ;Отключить листинг
	.include "m8def.inc"	;Подключить файл имён контр-ра ATMega8
    .LIST                   ;Включить листинг
    .device ATmega8         ;Определить устройство
    #define F_CPU (8000000) ;Частота процессора 8 МГц
	
  	;Задание символических имён регистров
  	.def flg_tch = R11		;Регистр счёта команд ДУ
	.def temp  = r16 		;Регистры
	.def temp1 = r17 		; временного
	.def temp2 = r18 		; хранения
	.def temp3 = r19 		;  данных
	;	.def ir_flag = r20 		;Регистр флагов приёма команды от ДУ
	.def time  = r21		;Счётчик циклов таймера
	.def F_3H  = r22		;Ст. байт настройки частоты
	.def F_3L  = r23		;Мл. байт настройки частоты
	.def freq  = r24		;Текущая частота настройки (номер канала)
	
	.equ Tm_l  = 255		;Время действия подсветки - 10 сек.
	.equ Tm_2  = 50			;Время ожидания второй цифры от ДУ
	
;===============================================================================
	.DSEG					;Сегмент  памяти SRAM (ОЗУ)
	.ORG 0x060				;Начальный адрес размещения данных
	decnum:		.BYTE 5		;Aдрес десятичного числа в ОЗУ
	seek_fm:	.byte 1		;Результат поиска станции
	read_fm:	.byte 1 	;Частота (номер) найденного канала
	level:		.byte 1		;Уровень сигнала
	volume:		.byte 2		;Громкость
	prev_num:	.byte 1		;Первая цифра номера канала от пульта ДУ
	prev_ch:	.byte 1		;Предыдущий номер канала
	IR_code:    .byte 2		;Адрес хранения принятого кода пульта ДУ
	Key_plt:	.byte 7		;Адрес хранения кодов кнопок пульта ДУ
	
;=============================================================================
	.ESEG						;Сегмент  памяти EEPROM
	 Fst_EE:	.BYTE 1 		;Частота настройки радио (номер канала)
	 Vol_EE:	.BYTE 1 		;Сохранённая громкость
	 Fst_IR:	.BYTE 7 		;Нач. адрес кодов ДУ
;=============================================================================
;-------------------------Макросы---------------------------------------------
	.MACRO DELAY					;Пауза 1 цикл около 32 мс
		ldi time,@0					;Загрузить счётчик циклов таймера Т0
 		tst time					;Ждать, пока таймер0 отмотает R21 в 0
		brne PC-1					;
	.ENDMACRO
	
	.MACRO LOAD_ADDR 		 		;Загрузка числа в 16 бит регистр
		ldi @0l,low(@1)   			;Пример: LOAD_ADDR Z,12365
		ldi @0h,high(@1)  			;
	.ENDMACRO
;=============================================================================
	.CSEG				;Начало программного сегмента
	.ORG 0 				;Векторы прерывания
	rjmp RESET			;Reset
	reti				;Внешнее прерывание INT 0
	reti				;Внешнее прерывание INT 1
	reti				;Прерывание Таймера/счётчика 2 по совпадению
	rjmp OVF_T2			;Прерывание Таймера/счётчика 2 по переполнению
	rjmp IR_INT			;Прерывание Таймера/счётчика 1 по захвату 
	reti				;Прерывание Таймера/счётчика 1 по совпадению в канале А
	reti				;Прерывание Таймера/счётчика 1 по совпадению в канале В
	reti				;Прерывание Таймера/счётчика 1 по переполнению
	rjmp OVF_T0			;Прерывание Таймера/счётчика 0 по переполнению
	reti				;Передача по SPI завершена
	reti				;Прерывание UART приём завершён 
	reti				;Прерывание UART регистр данных пуст
	reti				;Прерывание UART передача завершена
	reti 				;Прерывание АЦП преобразование завершено
	reti				;Прерывание EEPROM готово
	reti				;Прерывание от аналового компаратора
	reti    			;Прерывание от последоват-го 2-проводного интерфейса
	reti				;Готовность SPM
	
;============================================================================
RESET:		; Прерывание по сбросу, стартовая инициализация
		; Инициализация стека
		LDI 	Temp, LOW(RAMEND)
		OUT 	SPL, Temp
		LDI 	Temp, HIGH(RAMEND)
		OUT 	SPH, Temp
		CLI							; Глобальный запрет прерываний
;-----------------------------------------------------------------------------
		;Настройка порта B
		ldi temp,0b00000010			;Настраиваем разряды порта В
		out DDRB,temp   			;
		ldi temp,0b11111101			;
		out PORTB,temp   			;Записать в порт В
			;Настройка порта С, к кот. подключен радиомодуль и делитель напр. бат.
	 	ldi temp,0b00000000			;Настраиваем разряды порта РС
		out DDRC,temp   			;
		ldi temp,0b11111111			;
		out PORTC,temp   			;Подключить подтягивающие резисторы
		
			;Настройка порта D, к кот. подключен LCD-индикатор
	 	ldi temp,0b11111111			;Настраиваем разряды порта РD
		out DDRD,temp				;
		ldi temp,0b00000100			;
		out PORTD,temp				;Записать в порт D 
		
			;Инициализация таймеров
		;Период счёта таймера:
		; (1/8000000)*1024)*256=0,032768 с = 32,768 мс для делит. 1/1024
		clr temp					;
		out TCNT0,temp				;Обнуляем счётный регистр таймеров
		out TCNT2,temp				;
		ldi temp,0b00000101			;Включаем таймер 0 с предделителем 1/1024
		out TCCR0,temp				;
		
		ldi temp,0b01100101			;Разрешаем прер. по переполнению таймеров
		out TIMSK,temp				; и по захвату таймера 1
	;	ldi temp,0b10000101			;Включаем таймер 1 с предделителем 1/1024
	;	out TCCR1B,temp				; и с прерыванием по спаду с подавлением шума
		
		sei                         ;Глобально разрешить прерывания
		
;=================== Основная программа ==================================
		clr temp					;
		sts prev_num,temp			;Очистить яч. хранения кода цифры
		;Включение подсветки дисплея
		ldi temp,Tm_l				;Загрузить время
		mov R7,temp					; действия подсветки
		sbi PORTD,PD6				;Включить подсветку дисплея
		ldi temp,0b00000111			;Включаем таймер 2 с предделителем 1/1024
		out TCCR2,temp				;
		;Инициализация LCD дисплея
		rcall lcd_init				;Инициализация LCD дисплея
		;Переход в режим обучения
		sbis PINC,PC1				;Если  кнопка V- нажата,-
		rjmp teaching				; перейти в режим обучения
		
;===========================================================================
; 	Переписать коды кнопок пульта ДУ из EEPROM  в ОЗУ
;----------------------------------------------------------------------------
copy_ee:
  		ldi temp,7				;Загр. число байтов
  		mov flg_tch,temp		; в счётчик байтов
  		LOAD_ADDR Y,Fst_IR		;Загр. в Y нач. адр.хран. кодов ДУ в EEPROM
  		LOAD_ADDR Z,Key_plt		;Загр. в Z нач. адр.хран. кодов ДУ в ОЗУ
  		
        out EEARH,YH   			;Загружаем ст. адрес EEPROM
Erlop:	sbic EECR,EEWE   		;Ждем готовности EEPROM
        rjmp Erlop				;
        out EEARL,YL   			;Загружаем мл. адрес EEPROM
        inc YL					;Подготовка следующего адреса
        sbi EECR,EERE   		;Передаём строб чтения _
        in temp,EEDR   			;Чтение данных в рег. R16
        st Z+,temp				;Сохранить принятый байт в ОЗУ
        dec flg_tch				;Считаем байты
        brne ERlop				;Если не все байты переписаны- повторить
 		
;===========================================================================
		ldi r16,0x0					;Будем писать в первой строке
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		;Вывод верхней строки на дисплей
		LOAD_ADDR Z,Mesg_1*2		;Загр. в Z адрес сообщ. "     MHz     "
		rcall LCD_WriteString		;Вывод строки на дисплей
		;Вывод нулевого уровня сигнала
		ldi r16,0x0					;Адрес знакоместа в LCD
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		ldi r16,2					;Код уровня 0
		rcall LCD_WriteData			;Вывод символа на LCD
		ldi r16,2					;Код уровня 0
		rcall LCD_WriteData			;Вывод символа на LCD
;------------------------------------------------------------------------------
		;Инициализация радиомодуля RDA5807М
		rcall IIC_init				;Инициализация интерфейса I2C
		ldi YL,LOW(Fst_EE)			;Загр. в Y адр.хран. последней станции
		clr YH						;
		ldi temp,15					;Установить начальное
		sts volume,temp				; значение громкости = 15
		rcall Rd_St					;Прочитать частоту и громкость из EEPROM
		cpi freq,210				;Частота не должна быть >210,
		brlo PC+2					; т.к. 210+870=1080,
		clr freq					; иначе freq =0
		DELAY 20					;Пауза 656 мс
		lds temp,volume				;Взять из ОЗУ значение громкости
		andi temp,0x0F				;Маскировать старшие биты
		mov R10,temp				;
		;Опознавание и индикация частоты и названия станции
		rcall Prnt_F				;Вывод частоты на дисплей
		rcall rekog					;п/пр опознавания станции
		;Установка конфигурации и настройка тюнера
		rcall ch_st					;Конфигурация RDA5807
		DELAY 20					;Пауза 656 мс
		;Очистка регистров
		clr R2						;Очистить кнопочный буфер
		clr flg_tch					;Очистить счётчик команд ДУ
		clr R8						;Очистить регистр режима поиска
		clr R10						;Очистить счётчик длит. нажатия кнопок
		clr freq					;Очистить регистр частоты
		clr temp3					;Очистить регистр R19
		clr R12						;Очистить принятый
		clr R13						; код от пульта ДУ
		clr ir_flag					;Очистить регистр флагов ДУ
		ser temp					;temp = 0xFF
		out TIFR,temp				;Сброс флагов прерывания таймеров
		out GIFR,temp				;Сброс флагов внешних прерываний
		ldi temp,(1<<PSR10)			;Сброс предделителя таймеров Т0 и Т1
		out SFIOR,temp				;
		
		sbis PINC,PC2				;Если  кнопка CH+ нажата,-
		inc R8						; установить режим автопоиска
		
;--------------------Главный цикл----------------------------------------------
main: 		;Проверка буфера приёма от пульта ДУ
		sbrc ir_flag,crd			;Если принята команда от ДУ
		rcall IR_comand				; выполнить принятую команду
		;Чтение клавиатуры
		in temp,PINC				;Чтение клавиатуры
		com temp					;Инвертировать байт
		andi temp,0x0F				;Маскировать старшие биты
		mov r1,temp					;Здесь в R1 пин нажатой кнопки
		
		;Подтверждение нажатия кнопки (антидребезг)
		DELAY 1						;Пауза 32 мс 
		tst R2						;Если кнопки не нажаты,
		breq main					; вернуться в начало
		
		;Включение подсветки дисплея
		ldi temp,Tm_l				;Загрузить время
		mov R7,temp					; действия подсветки
		sbi PORTD,PD6				;Включить подсветку дисплея
		ldi temp,0b00000111			;Включаем таймер 2 с предделителем 1/1024
		out TCCR2,temp				;
		
				;Регулировка громкости
reg_V:	sbrc R2,0					;Если нажата кнопка V+
		rcall vol_up				; перейти к увеличению громкости
		sbrc R2,1					;Если нажата кнопка V-
		rcall vol_dw				; перейти к уменьшению громкости
		sbrc R2,0					;Если нажата кнопка V+
		rjmp m_loop					;
		sbrc R2,1					;Если нажата кнопка V-
		rjmp m_loop					;
		
		;Подсчёт времени нажатия кнопки
		cp r1,r2					;Ждём отпускания кнопки, а
		breq PC+2					; пока нажата кнопка -
		rjmp long					;
		inc R10						; считаем время нажатия кнопки
		rjmp main					;
		
		;Проверка длительности нажатия кнопки
long:	mov temp,R10				;Проверить длительность нажатия
		cpi temp,50					;Если нажатие короткое -
		brmi lng_ts					; перейти к выполн. коротких команд,
		rcall mode_s				; иначе - установить регистр длинных команд
		
lng_ts:	tst R8						;Проверка режима настройки на канал
		breq hand					;НЕТ - к ручному выбору канала
		
		;Автопоиск
avto:	sbrc R2,2					;Если нажата кнопка CH+
		rcall freq_Up				; перейти к поиску вверх
		sbrc R2,3					;Если нажата кнопка CH-
		rcall freq_Down				; перейти к поиску вниз
		rjmp m_loop					;
		
		;Ручной перебор станций
hand:	sbrc R2,2					;Если нажата кнопка CH+
		rcall St_Up					; перейти к выбору следующей станции
		sbrc R2,3					;Если нажата кнопка CH-
		rcall St_Down				; перейти к выбору предыдующей станции
		
m_loop: clr R2						;Очистить кнопочный буфер
		clr R1						;Очистить кнопочный буфер
		clr R10						;Очистить счётчик длит. нажатия кнопок
		rjmp main					;Вернуться в начало цикла
		;
;===========================================================================
;		Цикл назначения сигналов ДУ (обучение)
;---------------------------------------------------------------------------
teaching: 		;Вывод сообщения
			ldi r16,0x0					;Адрес знакоместа в LCD
			rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
			LOAD_ADDR Z,Messg6*2		;Загр. в Z адрес сообщ. "Next:V+ Skip:CH-"
			rcall LCD_WriteString		;Вывод строки на дисплей
			clr flg_tch					;Очистить счётчик команд ДУ
			
kei_in:		;Чтение клавиатуры
			sbrc ir_flag,crd			;Если принята команда от ДУ
			rcall IR_memr				; запомнить принятую команду
			
			in temp,PINC				;Чтение клавиатуры
			com temp					;Инвертировать байт
			andi temp,0x0F				;Маскировать старшие биты
			mov r1,temp					;Здесь в R1 пин нажатой кнопки
			
			;Подтверждение нажатия кнопки (антидребезг)
			DELAY 1						;Пауза 32 мс 
			tst R2						;Если кнопки не нажаты,
			breq kei_in					; вернуться в начало
			
			;Включение подсветки дисплея
			ldi temp,Tm_l				;Загрузить время
			mov R7,temp					; действия подсветки
			sbi PORTD,PD6				;Включить подсветку дисплея
			ldi temp,0b00000111			;Включаем таймер 2 с предделителем 1/1024
			out TCCR2,temp				;
			
			;Выполнение действия по нажатию на кнопку
			sbrc R2,0					;Если нажата кнопка V+
			rjmp cod_rsv				; принять код от ДУ
			sbrc R2,3					;Если нажата кнопка CH-
			rjmp copy_ee				; идти в главный цикл
			rjmp t_wyh					; ожидать команду от ДУ
			
cod_rsv:	ldi r16,0x40				;Будем писать во второй строке
			rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
			LOAD_ADDR Z,Messg14*2		;Загр. в Z адрес сообщ.
			rcall LCD_WriteString		;Очистить нижнюю строку
			ldi r16,0x40				;Будем писать во второй строке
			rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
			LOAD_ADDR Z,(Messg7*2-18)		;Загр. в Z адрес сообщ. "Channel+"
			;Вычисление адреса сообщения о вводимой команде
			inc flg_tch					;Увеличить счётчик команд ДУ
			push flg_tch				;
			adiw Z,18					;Увеличить адрес на 1 строку
			dec flg_tch					;
			brne PC-2					;
			pop flg_tch					;
			rcall LCD_WriteString		;Вывод на дисплей названия команды
			DELAY 20					;Пауза
			
now_c:		mov temp,flg_tch			;
			cpi temp,8					;Если обучение окончено, -
			brne t_wyh					; идти на выход
			clr flg_tch					;Очистить счётчик команд ДУ
			rjmp copy_ee				; идти в главный цикл
			
  t_wyh:	clr R2						;Очистить кнопочный буфер
			clr R1						;Очистить кнопочный буфер
			rjmp kei_in					; вернуться в начало
			
;===========================================================================
;	Подпрограмма записи кода команды в EEPROM
;---------------------------------------------------------------------------
  IR_memr:	tst flg_tch					;Если счётчик команд = 0
  			breq rsv_end				; ничего не делать
  			sbrc ir_flag,rpt			;Если флаг повтора установлен, -
			rjmp rsv_end				; ничего не делать
			rcall Mig_1					;Мигнуть светодиодом
			;Вычислить адрес нужной ячейки EEPROM
			LOAD_ADDR Y,(Fst_IR-1)		;Загр. в Y нач. адр.хран. кодов ДУ в EEPROM
			add YL,flg_tch				;Прибавить текущий номер команды
			brsh PC+2 					;
			inc YH 						;
			;Сохранить принятый код в EEPROM
			cli
			sbic EECR,EEWE				;Ждём очистки бита
			rjmp PC-1					; разрешения записи EEWE
			out EEARH,YH				;Записываем ст. байт адреса ЕЕПРОМ
			out EEARL,YL				;Записываем мл. байт адреса ЕЕПРОМ
			out EEDR,code_L				;Записываем код команды
			;	out EEDR,code_H				;Для симулятора
			sbi EECR,EEMWE				;Установить флаг разрешения записи
			sbi EECR,EEWE				;Установить бит разрешения
			sei
			
			;Вывод кода команды на дисплей
			ldi r16,0x4D				;Будем писать во второй строке
			rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее

			;Вывод кода команды на дисплей в десятичном виде
			mov temp,code_L				;Скопировать принятый код
			;	mov temp,code_H				;Для симулятора
			ldi R17,0					;
			rcall bin16_dec5			;Преобразовать в 10-ный вид
			ldi YL,LOW(decnum+2)		;Загр. в X нач. адр. числа в ОЗУ
			clr YH						;
			;
			ld temp,Y					;Прочитать ст. разряд кода
			rcall LCD_WriteHexDigit		;Вывести ст. разряд кода на LCD
			ld temp,-Y					;Прочитать мл. разряд кода
			rcall LCD_WriteHexDigit		;Вывести мл. разряд кода на LCD
			ld temp,-Y					;Прочитать мл. разряд кода
			rcall LCD_WriteHexDigit		;Вывести мл. разряд кода на LCD
			
rsv_end:	clr ir_flag					;Очистить регистр флагов
			DELAY 4						;Пауза 133 мс 
			clr ir_flag					;Очистить регистр флагов
			clr code_L					;Очистить принятый код
			clr code_H					;
			ret
			
;===========================================================================
;	Подпрограмма обработки принятой команды от ДУ
;---------------------------------------------------------------------------
IR_comand:	rcall Mig_1					;Мигнуть светодиодом
			sbrs ir_flag,rpt			;Если флаг повтора установлен, -
			rjmp ir_store				;
			lds code_L,IR_code			; взять код команды из ОЗУ
			lds code_H,(IR_code+1)		; взять код команды из ОЗУ
			;Сохранить принятый код в ОЗУ
ir_store:	sts IR_code,code_L			;Сохранить мл. байт команды
			sts (IR_code+1),code_H		;Сохранить ст. байт команды
			;Проверка принятого кода
			tst code_L					;
			brne com_out				;Если принятый код = 0, -
			tst code_H					;
			brne com_out				;
			rjmp pr_end					; выйти из подпрограммы
			;
com_out:		;Исполнение принятой команды
			ldi temp,8					;Загр. число байтов
  			mov flg_tch,temp			; в счётчик байтов
  			LOAD_ADDR Y,Key_plt			;Загр. в Y нач. адр.хран. кодов ДУ в ОЗУ
  			
  			dec flg_tch					;Считаем команды
  			breq pr_end					;
			ld temp,Y+					;Взять из ОЗУ очередной код кнопки
			cp temp,code_L				;Сравнить с принятым кодом
			;	cp temp,code_H				;Для симулятора
			brne PC-4					;Продолжать сравнение, если не равно
			
;com_out:		;Исполнение принятой команды
			mov temp,flg_tch				;Взять номер команды
			;Команды управления настройкой на станции
			cpi temp,7					;Если это команда St_Up, -
			brne ir_com1				;
			tst R8						;Проверить режим настройки
			brne PC+3					;
			rcall St_Up					; выбрать следующую станцию
			rjmp pr_end					;  и выйти
			rcall freq_Up				; перейти к автопоиску вверх
			rjmp pr_end					; и выйти
			
ir_com1:	cpi temp,6					;Если это команда St_Down, -
			brne ir_com2				;
			tst R8						;Проверить режим настройки
			brne PC+3					;
			rcall St_Down				; выбрать предыдущую станцию
			rjmp pr_end					; и выйти
			rcall freq_Down				; перейти к автопоиску вниз
			rjmp pr_end					; и выйти
			
ir_com2:	cpi temp,5					;Если это команда AVTV
			brne ir_com3				;
			tst R8						;Проверить регистр длинных команд
			breq set_auto				;
			clr R8						;Установить режим перебора
			rjmp pr_end					; выйти
set_auto:	ldi temp,0xF0				;
			mov R8,temp					;Установить режим автопоиска
			rjmp pr_end					; выйти
 			
			;Команды управления звуком
ir_com3:	cpi temp,4					;Если это команда vol_up, -
			brne PC+3					;
			rcall vol_up				; выполнить команду vol_up
			rjmp pr_end					; выйти
			
			cpi temp,3					;Если это команда vol_dw, -
			brne PC+3					;
			rcall vol_dw				; выполнить команду vol_dw
			rjmp pr_end					; выйти
			
			cpi temp,2					;Если это команда MUTE -
			brne PC+3					;
			rcall vol_mute				; откл/вкл. громкость
 			rjmp pr_end					; выйти
 			
 			cpi temp,1					;Если это команда вкл/откл. подсветки -
			brne PC+3					;
			rcall light					; вкл/откл. подсветку
 			rjmp pr_end					; выйти
			
			;
pr_end:		clr ir_flag					;Очистить регистр флагов
			DELAY 4						;Пауза 133 мс 
			clr ir_flag					;Очистить регистр флагов
			clr code_L					;Очистить принятый код
			clr code_H					;
			ret
;===========================================================================
;		Подпрограмма отключения/подключения громкости
vol_mute:	cli
			lds temp,volume				;Взять знач. громкости из ОЗУ
			tst temp					;Если громкость не включена, -
			brmi vol_on					; идти к включению
			sts volume+1,temp			; сохранить текущее знач. громкости
			rcall mute					; отключить громкость
			ser temp					;
			sts volume,temp				; сохранить новое знач. громкости
			rcall type_vol				;Отобразить громкость на дисплее
			rjmp vol_mt					; к выходу
 vol_on:	lds temp,volume+1			;Взять предыдущее знач. громкости из ОЗУ
 			sts volume,temp				; сохранить новое знач. громкости
 			rcall wr_vol				; включить громкость
 vol_mt:	sei
 			ret
;===========================================================================
;		Подпрограмма включения/выключения подсветки
 light:		sbic PORTD,PD6				;
 			rjmp PC+3					;
			sbi PORTD,PD6				;Включить подсветку
			ret							;
			cbi PORTD,PD6				;Отключить подсветку
			
			ret
;===========================================================================
;		Подпрограмма мигания светодиодом
  Mig_1:	sbi PORTB,PB1				;Включить св. диод
			DELAY 4						;Пауза 133 мс 
			cbi PORTB,PB1				;Выключить св. диод
			
			ret
;===========================================================================
mode_s:		;Установка режима поиска Ручной/Авто
		DELAY 20					;Пауза
		clr R10						;Очистить счётчик длит. нажатия кнопок
		tst R8						;Проверить регистр длинных команд
		brne rev					;
		mov R8,R2					;Записать регистр длинных команд
		rjmp rev_end				;
rev:	clr R8						;Очистить регистр  длинных команд
		
rev_end:clr temp					;Очистить temp для возврата в меню
 		ret
;===========================================================================
freq_Up:	; Автонастройка в "+" на следующий канал
		cli							;Запретить прерывания
		clr R1						;Очистить кнопочный буфер
		inc freq					;Увеличить частоту
		cpi freq,210				;она не должна быть >210,
		brlo PC+2					; т.к. 210+870=1080,
		clr freq					; иначе freq =0
			;Вывод сообщения "Autotune"
		ldi r16,0x40				;Будем писать во второй строке
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		LOAD_ADDR Z,Messg3*2		;Загр. в Z адрес сообщения "Autotune"
		rcall LCD_WriteString		;Вывод строки на дисплей
		cbi PORTD,7					;Погасить стерео индикатор
			;Запись частоты и команды начать поиск
		ldi temp,0b10010011			;Рег(02H) норм.опер, DMUTE=0,стерео,бас,
									; SEEKUP=1, SEEK=1 (поиска вверх)
		mov r5,temp					;Загрузить конфиг-й байт
r_con:	rcall Config_RDA5807		;Запись конфигурации
		
nov_p:	DELAY 20					;Пауза
		rcall Read_RDA5807			;Чтение данных из RDA5807
		DELAY 5						;Пауза
		lds r17,seek_fm				;Взять байт сост. поиска
		sbrs r17,6					;STC=1? Если станция не найдена, -
		rjmp nov_p					; ожидать оконч. поиска
		
		rcall Read_RDA5807			;Чтение данных из RDA5807
		DELAY 5						;Пауза
		lds freq,read_fm			;Загрузить текущую частоту
		lds r17,seek_fm				;Взять байт сост. поиска
		sbrs r17,5					;SF=1? Если станция найдена, -
		rjmp st_tn					; завершить поиск
		cpi freq,210				;она не должна быть >210,
		brlo PC+2					; т.к. 210+870=1080,
		clr freq					; иначе freq =0
		rjmp r_con					; продолжить поиск
		
		rjmp st_tn					; завершить поиск
		clr temp					;Очистить temp для возврата в меню
		sei							;Разрешить прерывания
 		ret
;===========================================================================
freq_Down:		; Автонастройка в "-" на следующий канал
		cli							;Запретить прерывания
		clr R1						;Очистить кнопочный буфер
		dec freq					;Уменьшить частоту
		
			;Вывод сообщения "Autotune"
nastr:	ldi r16,0x40				;Будем писать во второй строке
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		LOAD_ADDR Z,Messg3*2		;Загр. в Z адрес сообщения "Autotune"
		rcall LCD_WriteString		;Вывод строки на дисплей
		cbi PORTD,7					;Погасить стерео индикатор
			;Запись частоты и команды начать поиск
		ldi temp,0b10010001			;Рег(02H) норм.опер, DMUTE=0,стерео,бас,
									; SEEKUP=0, SEEK=1 (поиска вниз)
		mov r5,temp					;Загрузить конфиг-й байт
rm_con:	rcall Config_RDA5807		;Запись конфигурации
		
nov_m:	DELAY 20					;Пауза
		rcall Read_RDA5807			;Чтение данных из RDA5807
		DELAY 5						;Пауза
		lds r17,seek_fm				;Взять байт сост. поиска
		sbrs r17,6					;STC=1? Если станция не найдена, -
		rjmp nov_m					; ожидать оконч. поиска
		
		rcall Read_RDA5807			;Чтение данных из RDA5807
		DELAY 5						;Пауза
		lds freq,read_fm			;Загрузить текущую частоту
		lds r17,seek_fm				;Взять байт сост. поиска
		sbrs r17,5					;SF=1? Если станция найдена, -
		rjmp st_tn					; завершить поиск
		tst freq					;Частота не должна быть =0
		brne PC+2					; т.к. 210+870=1080,
		ldi freq,210				; иначе freq =210
		rjmp rm_con					; продолжить поиск
		
		;Запись частоты и команды прекратить поиск
st_tn:	ldi temp,0b11010010			;Рег(02H) норм.опер, DMUTE=1,стерео,бас,
									; SEEKUP=1, SEEK=0 (поиска нет)
		mov r5,temp					;Загрузить конфиг-й байт
		rcall Config_RDA5807		;Остановка поиска
		
		sbrc r17,2					;Если стерео, -
		sbi PORTD,7					; зажечь стерео индикатор
		rcall type_lev				;Индикация уровня сигнала
		rcall type_vol				;Отобразить значение громкости на LCD
		rcall Prnt_F				;Вывод частоты на дисплей
		rcall rekog					;Опознавание станции
		clr temp					;Очистить temp для возврата в меню
		sei
 		ret
;===========================================================================
St_Up:			; Выбрать следующую станцию из FLASH
 		cli
		;Определение номера станции
		sts prev_ch,R9				;Сохранить в ОЗУ номер предыдущего канала
		inc R9						;Номер следующей станции
		mov temp,R9
 		cpi temp,Nst				;Номер не должен быть > Nst
 		brlo PC+2					;
 		clr R9						; иначе переход к первой станции
 		rjmp ch_seek				;
 		
;---------------------------------------------------------------------------
St_Down:	; Выбрать предыдующую станцию из FLASH
		cli
		;Определение номера станции
		sts prev_ch,R9				;Сохранить в ОЗУ номер предыдущего канала
 		dec R9						;Взять номер предыдущей станции,
 		tst R9						; если он > 0, перейти 
 		brpl PC+3					; к выбору адреса, иначе -
 		ldi temp,(Nst-1)			;  взять номер последней станции
 		mov R9,temp					;
 		rjmp ch_seek				;
 		
 ;===========================================================================
;	П/пр настройки радиоприёмника на заданную частоту
;---------------------------------------------------------------------------
 ch_seek: 		;Определение адреса хранения частоты и назв. радиостанции
 		cli
 		LOAD_ADDR Z,freq_st*2		;Загр. в Z нач. адр.хран. частот станций
 		tst R9						;
		breq st_set					;Если номер =0, адрес не изменять
 		push R9						;
		adiw Z,18					;Адрес хранения частоты и назв. станции
		dec R9						;
		brne PC-2					;
		pop R9						;
		
st_set: lpm freq,Z					;Прочитать частоту из FLASH
		adiw Z,1					;Сдвинуть адрес на название станции
		rcall Prnt_F				;Вывод частоты на дисплей
		rcall Prn_Ch				;Вывод на дисплей названия станции
		;Запись частоты и конфигурации
ch_st:	ldi temp,0b11010010			;Рег(02H) норм.опер, DMUTE=1,стерео,бас,
									;SEEKUP=1, SEEK=0 (поиска нет)
		mov r5,temp					;
		rcall Config_RDA5807		;Установка частоты р/ст
		
		cbi PORTD,7					;Погасить стерео индикатор
		DELAY 20					;Пауза
		rcall Read_RDA5807			;Чтение данных из RDA5807
		DELAY 5						;Пауза
		
		lds r17,seek_fm				;Взять байт сост. поиска
		sbrc r17,2					;Если стерео, -
		sbi PORTD,7					; зажечь стерео индикатор
		rcall type_lev				;Индикация уровня сигнала
		rcall type_vol				;Отобразить значение громкости на LCD
		
		clr temp					;Очистить temp для возврата в меню
		sei
		ret
		
;===========================================================================
;		П/пр опознавания станции
;---------------------------------------------------------------------------
rekog:	cli							;
		clr R9						;Очистить счётчик станций
		LOAD_ADDR Z,freq_st*2		;Загр. в Z нач. адр.хран. частот станций
rd_fr:	lpm temp,Z					;Прочитать частоту из FLASH
		cp temp,freq				;Сравнить с искомой, если это она, то -
		breq na_ind					; перейти к её индикации
		inc R9						;Подсчёт числа станций
		mov temp,R9					;Если подсчёт закончен, 
		cpi temp,Nst				; а станция не найдена, -
		brsh fall					; вывести сообщ. "Неизв. канал"
		adiw Z,18					;Взять адрес другой станции
		rjmp rd_fr					; продолжать опознавание
		
fall:	LOAD_ADDR Z,Mesg_2*2		;Загрузить адрес сообщ. "Неизв. канал"
		rjmp PC+2					;
		
na_ind:	adiw Z,1					;Сдвинуть адрес на начало сообщения
			;Вывод на дисплей названия станции
Prn_Ch:	ldi r16,0x40				;Будем писать во второй строке
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		rcall LCD_WriteString		;Вывод на дисплей названия станции
		sei
		
		ret
		
;===========================================================================
;			 П/пр чтения регистров RDA5807M
;---------------------------------------------------------------------------
Read_RDA5807:
		cli
		rcall IIC_Start			; Старт обмена
		ldi temp,0b00100001		; Адрес чтения 0х21
		rcall IIC_Send			; Отправка байта
		
		rcall IIC_Recieve		;(0АН) Принимаем первый байт
		sts seek_fm,temp		; Записать в ОЗУ сост. поиска
		
		rcall IIC_Recieve		;(0АL) Принимаем второй байт
		sts read_fm,temp		; Записать в ОЗУ частоту станции
		mov freq,temp			; Обновить значение частоты в регистре
		
		rcall IIC_Recieve_lst	;(0BН) Принимаем третий байт
		lsr temp				; Сдвинуть байт вправо
		sts level,temp			; Записать в ОЗУ уровень принимаемого сигнала
		
		rcall IIC_Stop			; Стоп обмена
		sei
		
		ret
		
;===========================================================================
;	 П/пр чтения данных из ЕЕПРОМ, (в YH:YL адрес ячейки ЕЕПРОМ)
;---------------------------------------------------------------------------
Rd_St:	
		cli
		LOAD_ADDR Y,Fst_EE			;Загр. в Y нач. адр.хран. частоты
		clr freq					;
Rd_EEF:	sbic EECR,EEWE				;Ждём очистки бита записи
		rjmp Rd_EEF					;
		out EEARH,YH				;Записываем ст. байт адреса ЕЕПРОМ
		out EEARL,YL				;Записываем мл. байт адреса ЕЕПРОМ
		sbi EECR,EERE				;Устанавливаем бит чтения
		in freq,EEDR				;Чтение частоты из яч. ЕЕПРОМ
		
		inc YL						;
Rd_EEV:	sbic EECR,EEWE				;Ждём очистки бита записи
		rjmp Rd_EEV					;
		out EEARH,YH				;Записываем ст. байт адреса ЕЕПРОМ
		out EEARL,YL				;Записываем мл. байт адреса ЕЕПРОМ
		sbi EECR,EERE				;Устанавливаем бит чтения
		in temp,EEDR				;Чтение громкости из яч. ЕЕПРОМ
		andi temp,0x0F				;Маскировать старшие биты
		sts volume,temp				;Записать в ОЗУ уровень громкости
		sei
		ret
;===========================================================================
; 			Загрузка байтов конфигурации модуля RDA5807
;---------------------------------------------------------------------------
Config_RDA5807: 
		cli
		push R16          	;сохраняем в стеке R16
    	in R16,SREG      	;
   		push R16         	;сохраняем в стеке SREG
		;Подготовка байтов для регистров установки частоты
;В рег.F_3H ст. 8 бит частоты. в рег.F_3L мл.2 бита част. & бит разр.настр.
		mov F_3H,freq				;
		clc							;Сбросить бит переноса
		clr F_3L					;
		lsr F_3H					;
		ror F_3L					;
		lsr F_3H					;
		ror F_3L					;
		ori F_3L,0x10				;Добавить бит разрешения настройки
		; Загрузка байтов конфигурации модуля RDA5807
		rcall iic_start				;Старт обмена
		ldi temp,0x20				;Адрес последовательной записи в RDA5807
		rcall IIC_Send				;Передать командный байт
		
		mov temp,r5					;Передать байты конфигурации -
		rcall IIC_Send				;Рег(02Н) = r5
		ldi temp,0b10000101			;Рег(02L):  SKMODE=1 Fclk=32.768kHz,
		rcall IIC_Send				; RDS_EN=0,NEW_METHOD=1, Power Up
		
		mov temp,F_3H				;Рег(03H) Ст. 8 бит частоты
		rcall IIC_Send				;
		mov temp,F_3L				;Рег(03L) Мл.2 бита част. & бит разр.настр.
		rcall IIC_Send				;	87–108 MHz	шаг 100 КГц
		
		ldi temp,0					;Рег(04H)
		rcall IIC_Send				;
		ldi temp,0					;Рег(04L)
		rcall IIC_Send				;
		
		ldi temp,0b10001000 		;Рег(05H)- прер., уровень сигн. поиска
		rcall IIC_Send				;
		lds temp,volume				;Рег(05L) Взять из ОЗУ значение громкости
		tst temp					;Если звук выключен,  -
		brpl PC+2					;
		ldi temp,0					; установить нулевой уровень громкости
		ori temp,0b10000000			;
		rcall IIC_Send				;Установить громкость приёмника
		
		rcall iic_stop 				;Стоп обмена
		rcall Wr_EEF				;Записываем номер канала в ЕЕПРОМ
		rcall Wr_EEV				;Записываем значение громкости ЕЕПРОМ
		
		pop R16          		;восстанавливаем из стека SREG
    	out SREG,R16     		;
    	pop R16          		;восстанавливаем из стека R16
		sei
		ret
;===========================================================================
;		Запись номера канала в ЕЕПРОМ, в YH:YL адрес ячейки ЕЕПРОМ
;---------------------------------------------------------------------------
Wr_EEF:	
		cli
		ldi YL,LOW(Fst_EE)			;Загр. в Y нач. адр.хран.
		clr YH						;Обнулить ст. байт указателя адреса
		sbic EECR,EEWE				;Ждём очистки бита
		rjmp PC-1					; разрешения записи EEWE
		out EEARH,YH				;Записываем ст. байт адреса ЕЕПРОМ
		out EEARL,YL				;Записываем мл. байт адреса ЕЕПРОМ
		out EEDR,freq				;Записываем частоту станции
		sbi EECR,EEMWE				;Установить флаг разрешения записи
		sbi EECR,EEWE				;Установить бит разрешения
		sei
		
		ret
; ========================================================================
;		Запись уровня громкости в ЕЕПРОМ, в YH:YL адрес ячейки ЕЕПРОМ
;-------------------------------------------------------------------------
Wr_EEV:	
		cli
		ldi YL,LOW(Vol_EE)			;Загр. в Y нач. адр.хран.
		clr YH						;Обнулить ст. байт указателя адреса
		lds temp,volume				;Взять из ОЗУ значение громкости
		sbic EECR,EEWE				;Ждём очистки бита
		rjmp PC-1					; разрешения записи EEWE
		out EEARH,YH				;Записываем ст. байт адреса ЕЕПРОМ
		out EEARL,YL				;Записываем мл. байт адреса ЕЕПРОМ
		out EEDR,temp				;Записываем значение громкости
		sbi EECR,EEMWE				;Установить флаг разрешения записи
		sbi EECR,EEWE				;Установить бит разрешения
		sei
		
		ret
; ========================================================================
		;Увеличение громкости
vol_up:	clr R1				;
		lds temp,volume		;Взять из ОЗУ значение громкости
		cpi temp,15			;Сравнить с максимальной
		breq vol_st			;Не увеличивать, если максимум
		;Увеличить громкость
		tst temp			;
		brpl PC+3			;Если звук был выключен, то
		clr temp			; только включить его,
		rjmp PC+2			; а не увеличивать
		inc temp			;
		sts volume,temp		;Сохранить в ОЗУ
vol_st:	rcall wr_vol		;Записать громкость в RDA5807m
		DELAY 10			;Пауза 320 мс
		clr temp			;Очистить temp для возврата в меню
		ret
;========================================================================
		;Уменьшение громкости
vol_dw:	clr R1				;
		lds temp,volume		;Взять из ОЗУ значение громкости
		tst temp			;Если значение гр. уже < 0, то
		brmi PC+2			; не уменьшать его, иначе -
		dec temp			; уменьшить громкость и
		sts volume,temp		; сохранить в ОЗУ
		tst temp			;Проверить новое значение громкости
		brpl vol_mi			;Если значение гр.  < 0, то
		rcall mute			; перейти к выключению громкости
		rcall type_vol		;Отобразить громкость на дисплее
		rjmp PC+2			;
		
vol_mi:	rcall wr_vol		;Записать громкость в RDA5807m
		DELAY 10			;Пауза 320 мс
		clr temp			;Очистить temp для возврата в меню
		ret
		
;========================================================================
; П/пр выключения громкости в RDA5807M по адресу произвольного доступа 0x22
;-------------------------------------------------------------------------
mute:	cli
		rcall iic_start				;Генерация условия "Старт"
		ldi temp,0x22				;Адрес прямого доступа
		rcall IIC_Send				;Передать командный байт
		
		ldi temp,0x02				;Номер регистра (02H) для записи
		rcall IIC_Send				;Передать номер регистра
		
		ldi temp,0b10010010			;Рег(02H): норм.опер, DMUTE=0,стерео,бас,
		rcall IIC_Send				;SEEKUP=1, SEEK=0 (поиска нет)
		ldi temp,0b10000101			;Рег(02L):  SKMODE=1 Fclk=32.768kHz,
		rcall IIC_Send				; RDS_EN=0,NEW_METHOD=1, Power Up
		
		rcall iic_stop 				;Генерация условия "Стоп"
		sei
		
		ret
; ========================================================================
; П/пр записи громкости в RDA5807M по адресу произвольного доступа 0x22
;-------------------------------------------------------------------------
wr_vol:	; Запись уровня громкости модуля RDA5807
		cli
		;Включить громкость
		rcall iic_start				;Генерация условия "Старт"
		ldi temp,0x22				;Адрес прямого доступа
		rcall IIC_Send				;Передать командный байт
		
		ldi temp,0x02				;Номер регистра (02H) для записи
		rcall IIC_Send				;Передать номер регистра
			
		ldi temp,0b11010010			;Рег(02H): норм.опер, DMUTE=1,стерео,бас,
		rcall IIC_Send				;SEEKUP=1, SEEK=0 (поиска нет)
		ldi temp,0b10000101			;Рег(02L):  SKMODE=1 Fclk=32.768kHz,
		rcall IIC_Send				; RDS_EN=0,NEW_METHOD=1, Power Up
		
		rcall iic_stop 				;Генерация условия "Стоп"
		;Установить заданное  значение громкости
		rcall iic_start				;Генерация условия "Старт"
		ldi temp,0x22				;Адрес прямого доступа
		rcall IIC_Send				;Передать командный байт
		
		ldi temp,0x05				;Номер регистра (05H) для записи
		rcall IIC_Send				;
		
		ldi temp,0b10001000  		;Рег(05H) - прер., уровень сигн. поиска
		rcall IIC_Send				;
		lds temp,volume				;Рег(05L) Взять из ОЗУ значение громкости
		ori temp,0b10000000			;
		rcall IIC_Send				;Записать громкость в RDA5807m
		
		rcall iic_stop 				;Генерация условия "Стоп"
		rcall type_vol				;Отобразить громкость на дисплее
		sei
		
		ret
;===========================================================================
;		П/пр вывода уровня громкости на LCD дисплей
;---------------------------------------------------------------------------
type_vol:	;Установить координаты вывода на LCD
		ldi r16,13					;
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		LOAD_ADDR Z,Messg4*2		;Загр. в Z адрес сообщ. "    "
		rcall LCD_WriteString		;Заполнинть пробелами место под громкость
		ldi r16,12					;
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		ldi r16,0					;
		rcall LCD_WriteData			;Вывод символа динамика на LCD
		ldi r16,13					;
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		
		lds temp,volume				;Взять из ОЗУ значение громкости
		tst temp					;Если звук выключен,  -
		brpl PC+3					;
		clr temp					; и показать на дисплее уровень 00,
		rjmp prbr					; иначе, - показывать уровень
		inc temp					; на 1 больше
		
  prbr:		;Преобразовать громкость в 10-чный вид
		andi temp,0x1F				;Маскировать старшие биты
		clr R17						;
		rcall bin16_dec5			;Преобразовать в 10-чный вид
		ldi YL,LOW(decnum)			;Загр. в X нач. адр. числа в ОЗУ
		clr YH						;
		inc YL						;Адрес ст. разряда числа в ОЗУ
			;Вывод уровня громкости на LCD дисплей
		ld temp,Y					;Прочитать ст. разряд громкости
		rcall LCD_WriteHexDigit		;Вывести ст. разряд громкости на LCD
		ld temp,-Y					;Прочитать мл. разряд громкости
		rcall LCD_WriteHexDigit		;Вывести мл. разряд громкости на LCD
		
		ret
		
;===========================================================================
;		П/пр вывода уровня сигнала на LCD дисплей
;---------------------------------------------------------------------------
type_lev:
		cli
		;Установить координаты вывода на LCD
		ldi r16,0x0					;Адрес знакоместа в LCD
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		
		lds temp,level				;Взять из ОЗУ значение уровня сигнала
		
		cpi temp,51					;Порог 4-го уровня
		brmi PC+6					;
		
		;Вывод 4-го уровня сигнала
		ldi r16,4					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		ldi r16,6					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		rjmp tp_lev					;Отобразить и выйти из п/пр
		
		cpi temp,38					;Порог 3-го уровня
		brmi PC+6					;
		
		;Вывод 3-го уровня сигнала
		ldi r16,4					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		ldi r16,5					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		rjmp tp_lev					;Отобразить и выйти из п/пр
		
		cpi temp,25					;Порог 2-го уровня
		brmi PC+6					;
		
		;Вывод 2-го уровня сигнала
		ldi r16,4					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		ldi r16,2					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		rjmp tp_lev					;Отобразить и выйти из п/пр
		
		cpi temp,12					;Порог 1-го уровня
		brmi PC+6					;
		
		;Вывод 1-го уровня сигнала
		ldi r16,3					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		ldi r16,2					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		rjmp tp_lev					;Отобразить и выйти из п/пр
		
		;Вывод 0-го уровня сигнала
		ldi r16,2					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		ldi r16,2					;Код уровня
		rcall LCD_WriteData			;Вывод символа на LCD
		
tp_lev:	sei
		ret

;===========================================================================
; П/пр обслуживания внешнего прерывания от ИК приёмника протокола NEC
;---------------------------------------------------------------------------
	.def code_L = R12			;Мл. байт принятого кода
	.def code_H = R13			;Ст. байт принятого кода
	.def ir_bit = R14			;Счётчик принятых бит
	.def ir_flag = R20 			;Регистр флагов приёма команды от ДУ
		;Биты регистра флагов ir_flag
		.equ rsv  = 0			;Флаг приёма команды от ДУ
		.equ rd	  = 1			;Флаг чтения принимаемых битов
		.equ rpt  = 2			;Флаг повторения соманды
		.equ crd  = 3			;Флаг окончания приёма соманды
	.equ IR_imp_0 = 7			;Для t=1,125 мс х=1,125*8000/1024 = 8,79
	.equ IR_imp_1 = 15			;Для t=2,25 мс х=2,25*8000/1024 = 17,58
	.equ IR_start = 71			;Стартовый импульс х=9*8000/1024 = 70.3
	.equ IR_com	  = 36			;Начальный импульс команды х=4,5*8000/1024 = 35,16
	.equ IR_rpt	  = 18			;Начальный импульс повтора х=2,25*8000/1024 = 17,58
 ;	.equ IR_code  = 0x8A 		;Адрес хранения в ОЗУ принятого кода пульта ДУ
;---------------------------------------------------------------------------
 IR_INT:
 		push temp          		;сохраняем в стеке temp
    	in temp,SREG      		;
   		push temp         		;сохраняем в стеке SREG
   		;
   		sbrc ir_flag,crd		;Если принятая команда не отработана,
   		rjmp IR_wyh				; выйти
		tst ir_flag				;Если флаг приёма не установлен,
		brne PC+2				; перейти к его установке
		rjmp IR_st				;
		
		;Исключение случайных импульсов
		in temp,TCNT0			;Прочитать текущее знач. счётчика Т0
		cpi temp,8				;Сравнить с мин. знач  имп.
		brlo IR_err				;Если temp < 8, - выйти
		
		cpi ir_flag,0b00000011 ;Если флаги приёма и чтения установлены, -
		breq IR_in				; перейти к вводу команды
		sbrc ir_flag,rd			;Если уст. флаг чтения, проверить нач. имп.
		rjmp nach				; иначе, - проверить стартовый импульс
		
		;Проверка длины стартового импульса 9 ms
		in temp,TCNT0			;Прочитать текущее знач. счётчика Т0
		cpi temp,68				;Сравнить с мин. длит. имп. 68
		brlo  IR_err			;Если temp < 58, - выйти по ошибке
		cpi temp,74				;Сравнить с макс. длит. имп. 74
		brsh IR_err				;Если temp > 74, выйти по ошибке, иначе -
		ldi ir_flag,0b00000010 ; установить флаг чтения принимаемых битов
		rjmp IR_red				; и перейти к подготовке приёма бита
		
		;Проверка длины импульса повтора 2,25 ms
 nach:	in temp,TCNT0			;Прочитать текущее знач. счётчика Т0
		cpi temp,17				;Сравнить с мин. длит. имп. повтора 17
		brlo  IR_err			;Если temp < 17, - выйти
		cpi temp,21				;Сравнить с макс. длит. имп. повтора 21
		brsh PC+3				;Если temp > 21, идти к приёму команды,
		ldi ir_flag,0b00001100	; иначе - установить флаг повтора,
		rjmp IR_wyh				; и флаг окончания приёма и выйти
		;Проверка длины импульса команды 4,5 ms
 		cpi temp,30				;Сравнить с мин. длит. имп. команды 30
		brlo  IR_err			;Если temp < 30, - выйти
		cpi temp,39				;Сравнить с макс. длит. имп. команды 39
		brsh IR_err				;Если temp > 39, - выйти
		rjmp IR_rd				; перейти к установке флага чтения
		
		;Детектирование принятых посылок
IR_in:	in temp,TCNT0			;Прочитать текущее знач. счётчика Т0
		cpi temp,IR_imp_1		;Если принятый бит, это 1, то -
		brsh  IR_1				; записать 1, если temp >= IR_imp_1
		cpi temp,IR_imp_0		;Если принятый бит, это 0, то -
		brsh  IR_0				; записать 0, если temp >= IR_imp_0
		brlo IR_err				;  иначе - ошибка
		;Заполнение приёмного буфера
IR_0:	clc						;Сбросить перенос
		rjmp PC+2				;
IR_1:	sec						;Установить перенос
		ror code_H				;Задвинуть принятый бит в буфер
		ror code_L				;
		dec ir_bit				;Счётчик принятых бит
		brne IR_rd				;
		;Проверка принятого кода
		mov temp,code_L				;Взять инверсный код
		com temp					;Инвертировать
	;		mov temp,code_H		;Для симулятора
		sub temp,code_H				;Вычесть из прямого кода
		brne IR_err					;Если не 0, значит ошибка
IR_crd:	ldi ir_flag,0b00001000		;Установить флаг окончания приёма кода
		rjmp IR_wyh					;
		;Инициализация чтения новой посылки
IR_rd:	ldi ir_flag,0b00000011 			;Установить флаг приёма и чтения
IR_red:	ldi temp,(1<<ICNC1)+(0<<ICES1)	;Прерывание по спаду с подавлением шума
		out TCCR1B,temp					;
		ldi temp,0						;Перезагрузка таймера 0
		out TCNT0,temp					;
		ldi temp,(1<<PSR10)				;Сброс предделителя таймеров Т0 и Т1
		out SFIOR,temp					;
		ldi temp,0b00000101				;Включаем таймер 0 с предделителем 1/1024
		out TCCR0,temp					;
		rjmp IR_wyh						; выйти
		;Инициализация приёма команды пульта
IR_st:	sbr ir_flag,(1<<rsv)			;Установить флаг приёма
		ldi temp,0						;Перезагрузка таймера 0
		out TCNT0,temp					;
		ldi temp,(1<<PSR10)				;Сброс предделителя таймеров Т0 и Т1
		out SFIOR,temp					;
		ldi temp,32						;Инициализация счётчика
		;	ldi temp,8						;Для симулятора
		mov ir_bit,temp					; принятых бит
		ldi temp,(1<<ICNC1)+(1<<ICES1)	;Прерывание по фронту с подавлением шума
		out TCCR1B,temp					;
		rjmp IR_wyh						;
		;Очистка приёмного буфера при приёме случайного импульса
IR_err:	clr code_L						;
		clr code_H						;
		andi ir_flag,0b00001100			;
		ldi temp,(1<<ICNC1)+(0<<ICES1)	;Прерывание по спаду с подавлением шума
		out TCCR1B,temp					;
		
IR_wyh:	pop temp          				;восстанавливаем из стека SREG
    	out SREG,temp     				;
    	pop temp          				;восстанавливаем из стека temp
		reti
		
;===========================================================================
; П/пр обслуживания прерывания от таймера 0
;---------------------------------------------------------------------------
OVF_T0:	
		push temp          	;сохраняем в стеке temp
    	in temp,SREG      	;
   		push temp         	;сохраняем в стеке SREG
		;
		dec time			;Счётчик циклов
		
		tst r1				;Если ни одна кнопка не нажата
		breq vyh			; выйти из п/пр, иначе -
		mov r2,r1			; подтвердить код нажатой кнопки
		
vyh:	pop temp          	;Восстанавливаем из стека SREG
    	out SREG,temp     	;
    	pop temp          	;Восстанавливаем из стека temp
		
		reti
		
;===========================================================================
; П/пр обслуживания прерывания от таймера 2
;---------------------------------------------------------------------------
OVF_T2:	
		push temp3          ;сохраняем в стеке temp3
    	in temp3,SREG      	;
   		push temp3         	;сохраняем в стеке SREG
		;
podsv:	dec R7				;Если счёт времени не окончен, -
		brne wyh			; выйти из п/пр, иначе -
		cbi PORTD,PD6		;Погасить подсветку
		ldi temp3,0			;Выключаем таймер 2
		out TCCR2,temp3		;
		;
wyh:	pop temp3          	;восстанавливаем из стека SREG
    	out SREG,temp3     	;
    	pop temp3          	;восстанавливаем из стека temp3
		
		reti
		
;===========================================================================
;		Вывод частоты настройки на LCD дисплей
;---------------------------------------------------------------------------
 Prnt_F:		;Вывод на дисплей частоты настройки в МГц
		cli
		;Установить координаты вывода на LCD
		ldi r16,0x3					;Начальный адрес знакоместа LCD
		rcall LCD_SetAddressDD		;Установить адрес знака в LCD дисплее
		;Преобразовать число шагов в частоту
		clr r18						;
		ldi r17,HIGH(870)			;Загрузить нижнюю частоту
		ldi temp,LOW(870)			; диапазона: 870 МГц 
		add temp,freq				;Сложить с частотой настройки
		adc r17,r18					;
		;Преобразовать частоту в 10-чный вид
		rcall bin16_dec5			;Преобразовать в 10-чный вид
		ldi YL,LOW(decnum+3)			;Загр. в X нач. адр. частоты в ОЗУ
		clr YH						;
		;Вывод на дисплей
		ld r16,Y					;Прочитать ст. разряд частоты
		tst r16						; если он равен 0, то -
		brne PC+4					; пропустить вывод первого нуля
		ldi r16,0x20				;
		rcall LCD_WriteData			;Вывод на LCD пробела
		rjmp PC+2
		rcall LCD_WriteHexDigit		;Вывод на LCD сотен МГЦ
		ld r16,-Y					;
		rcall LCD_WriteHexDigit		;Вывод на LCD десятков МГЦ
		ld r16,-Y					;
		rcall LCD_WriteHexDigit		;Вывод на LCD единиц МГЦ
		ldI r16,0x2C				;
		rcall LCD_WriteData			;Вывод на LCD запятой
		ld r16,-Y					;
		rcall LCD_WriteHexDigit		;Вывод на LCD десятых долей МГЦ
		
		push ZL
		push ZH
		LOAD_ADDR Z,Messg5*2		;
		rcall LCD_WriteString		;Вывод сообщ. "MHz"
		pop ZH
		pop ZL
		
		sei
		ret
		
;===========================================================================
;Подпрограмма преобразования 2-байтового двоичного числа в 5-разрядное
; десятичное (десятичные коэффициенты d4…d0 хранятся в SRAM микроконтроллера)
;
; R17:R16 – двоичное преобразуемое число, лежащее в диапазоне (0…0xFFFF)
; R18,R19,R20 – вспомогательные регистры
; [YH:YL] – десятичное число после преобразования адресуется через YH:YL
; decnum – адрес десятичного числа в ОЗУ
;---------------------------------------------------------------------------
 bin16_dec5:
 	cli
 	push R18
 	push R19
 	push R20
     ldi   YL,low(decnum) 	;Заносим в указатель Y адрес
     ldi   YH,high(decnum) 	; начала массива чисел d0…d4 в ОЗУ
     ldi   R20,4    		;Инициализируем счётчик десятичных цифр
bd1: clr   R18      		;Обнуляем вспомогательный регистр R18
     ldi   R19,16   		;Инициализируем счётчик циклов при делении
bd2: lsl   R16      		;Здесь производится деление V(D)/10
     rol   R17      		; частное снова заносится в R17:R16,
     rol   R18      		; а остаток в R18 = di
     andi  R16,0xFE
     cpi   R18,10
     brcs  bd3
     subi  R18,10
     ori   R16,0x01
bd3: dec   R19
     brne  bd2
     st    Y+,R18   		;заносим все di в ОЗУ
     dec   R20
     brne  bd1      		;деление производится n-1 раз
     st    Y+,R16   		;заносим старший коэффициент dn-1 в ОЗУ
    pop R20
    pop R19
    pop R18
     sei
     ret
	
;===================================================================================
			;Строки сообщений для вывода на индикатор
  Mesg_1: .DB  ' ',' ',' ',' ',' ',' ',' ',' ','M','H','z',' ',' ',' ',' ',' ', 0, 0
  Mesg_2: .DB  'U','n','k','n','o','w',' ','c','h','a','n','n','e','l',' ',' ', 0, 0
  Messg3: .DB  ' ',' ',' ',' ','A','u','t','o','T','u','n','e',' ',' ',' ',' ', 0, 0
  Messg4: .DB  ' ',' ',' ',' ', 0, 0
  Messg5: .DB  'M','H','z', 0
  Messg6: .DB  'N','e','x','t',':','V','+',' ','S','k','i','p',':','C','H','-', 0, 0
  Messg7: .DB  'C','h','a','n','n','e','l','+',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg8: .DB  'C','h','a','n','n','e','l','-',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg9: .DB  'A','u','t','o','s','e','e','k',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg10: .DB  'V','o','l','u','m','e','+',' ',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg11: .DB  'V','o','l','u','m','e','-',' ',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg12: .DB  'M','u','t','e',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg13: .DB  'L','i','g','h','t',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
  Messg14: .DB  ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', 0, 0
	
;====================================================================================
;******************************************************************************
	.include "User_set.asm"					;Пользовательские настройки
	.include "I2C_hard_driver.asm"			;Аппаратная реализация интерфейса I2C
	.include "HD44780_driver.asm" 			;Библиотека LCD
	
;******************************************************************************

	.EXIT

