;Игорь Васильевич Ильин (IVI) г.Таганрог. 2010г. 
;Программа для устройства на ATtiny 13A, обеспечивающее дистанционное включение,
;выключение или сброс компьютера с помощью мобильного телефона.  Fтакт = 9,6 МГц
;     Включение РС   - удерживать звонок  8-13 сек  в среднем 10 сек
;     Сброс РС       - удерживать звонок 17-23 сек  в среднем 20 сек
;     Выключение РС  - удерживать звонок 30-40 сек  в среднем 35 сек

.include  "C:\Program Files\Atmel\AVR Tools\AvrAssembler2\Appnotes\tn13Adef.inc"

;************************ Программа для ATtiny-13A ****************************
	.def temp = R25
	.def tmp0 = R24
	.def tmp1 = R23
	.def clk  = R22
	.def clok = R21
	.def time = R20
;---------------------------- ОБЯЗАТЕЛЬНОЕ ВСТУПЛЕНИЕ -------------------------
	.cseg
	.org    0

rjmp START  ; RESET			0 Общий сброс
	nop     ; EXT_INT0  	1 Внешнее прерывание 0
	nop     ; PCINT0	  	2 Прерыв. 0 по изменению состояния сигн. на выводах
	nop	 	; TIMER0_OVF  	4 Переполнение таймера/счетчика Т0
	nop     ; EE_RDY  		5 EEPROM готово
	nop     ; ANA_COMP  	6 Аналоговый компаратор
	nop     ; TIMER0_COMPA 	7 Совпадение А таймера/счетчика Т0
	nop     ; TIMER0_COMPA 	8 Совпадение B таймера/счетчика Т0
	nop		; WTD			9 Тайм-аут сторожевого таймера
	nop 	; ADC  		   10 Преобразование АЦП завершено

;-------------------------------  Подготовка  ---------------------------------
START:
; Настройка выводов порта В (вход/выход -> 0/1)
	ldi temp,low(RAMEND); инициализация стека
	out SPL,temp
	ldi temp,$07 	; порт В сконфигурирован: РВ0-РВ2 на выход, остальные 3	
	out DDRB,temp	; разряда - на вход
	ldi temp,$03	; устанавливаем на выходах РВ0 и РВ1 логические единицы
	out PORTB,temp	; а на выходе РВ2 логический ноль

; Предварительная инициализация АЦП
	ldi temp,$63	; установка внутр. ИОН=1,1В [р6], выравнивание результата
	out ADMUX,temp	; преобраз АЦП [p5], мультиплексор PC0/ADC0 [p2,p1.p0]
	ldi temp,$87	; разрешить АЦП [p7], одиночное преоб.[p5], запретить	
	out ADCSRA,temp	; прерыван.[p4], предделитель = Fтакт/16 [p1,p0]

;========================= ОСНОВНАЯ ПРОГРАММА =================================

ADC_0:		; проверяем наличие телефонного звонка
	rcall ADC_L		; результат сравнения c 0,88*Vref, если меньше-> звонка
	brsh ADC_0		; нет -> идем дальше , если звонок есть -> зацикливаемся

; Предварительная установка временных параметров
	ldi clok,10		; константа длительности паузы (сей час она равна 10 сек)
	clr clk			; обнуляем счетчик длительности паузы
	ldi time,6		; постоянная времени задержки пропорциональна 1 сек

ADC_S:		; ожидаем звонка по телефону
	rcall ADC_L		; результат сравнения с 0,88*Vref, если меньше-> звонка
	brlo ADC_S		; нет -> зацикливаемся, если звонок есть -> идем дальше
	
	in temp,PINB	; читаем сост. выв. порта В РВ4=0->РС -выкл, РВ4=1->РС -вкл
	sbrc temp,4		; анализируем разряд РВ4 если 0 clok=10 т.е. 10 сек
	ldi clok,17		; в противном случае clok=17 т.е. 17 сек

CLOCK:		; задержка защищающая от ложных звонков (звонок менее 10/17 сек.)
	rcall DELAY					; в зависимости от режима: включение/сброс
	inc clk 		; увеличиваем счетчик длительности паузы на единичку
	cp clk,clok		; сравниваем с константой длительности паузы
	brne CLOCK		; если не 0 - время паузы не вышло, повторяем цикл 
	clr clk			; время вышло - обнуляем счетчик длительности паузы

;-------------------------------  Работаем с РС  ------------------------------
	
ADC_E:		; если звонок еще есть перходим к включению или сбросу РС
	rcall ADC_L		; результат сравнения с 0,88*Vref, если меньше-> звонка
	brlo ADC_S		; нет -> возврат к ADC_S, если звонок есть -> идем дальше
	
	in temp,PINB	; читаем сост. выв. порта В РВ4=0->РС -выкл, РВ4=1->РС -вкл
	sbrc temp,4		; анализируем разряда РВ4 если 0 - пропуск следующей команды 
	rjmp PC_RESET	; сбрасываем компьютер

; включение РС
	ldi clok,5		; константа длительности паузы (сей час она равна 5 сек)
	clr clk			; обнуляем счетчик длительности паузы
	ldi time,6		; постоянная времени задержки пропорциональна 1 сек

CLOCK_ON:	; защита от ложных звонков (10сек < время звонка < 15сек)
; пауза в 5 сек.
	rcall DELAY	
	inc clk 		; увеличиваем счетчик длительности паузы на единичку
	cp clk,clok		; сравниваем с константой длительности паузы
	brne CLOCK_ON	; если не 0 - время паузы не вышло, повторяем цикл 
	clr clk			; время вышло - обнуляем счетчик длительности паузы
; остался ли звонок?    т.е 10сек < время звонка < 15сек	
	rcall ADC_L		; результат сравнения c 0,88*Vref, если меньше-> звонка
	brsh ADC_0		; нет -> идем дальше , если звонок есть -> возврат в начало
; нажимаем кнопку включения РС и удерживаем ее 1,5 сек
	ldi time,9		; увеличиваем постоянную времени задержки до 1,5 сек
	cbi PORTB,1		; сбраываем РВ1 (включение РС) в 0 
	rcall DELAY		; удерживаем этот 0 в течении 1,5 секунд
	sbi PORTB,1		; возвращаем РВ1 (включение РС) в 1
	rcall DELAY		; удерживаем этот 0 в течении 1,5 секунд
	in temp,PINB	; читаем сост. выв. порта В РВ4=0->РС -выкл, РВ4=1->РС -вкл
	sbrc temp,4		; анализируем разряда РВ4 если 0 - повторяем нажатие кнопки
	rjmp ADC_0		; идем ожидать новых звонков
; повкорно нажимаем кнопку включения РС и удерживаем ее 3 сек
	ldi time,18		; увеличиваем постоянную времени задержки до 3 сек
	cbi PORTB,1		; сбраываем РВ1 (включение РС) в 0 
	rcall DELAY		; удерживаем этот 0 в течении 3 секунд
	sbi PORTB,1		; возвращаем РВ1 (включение РС) в 1
	rcall DELAY		; удерживаем этот 0 в течении 3 секунд
	rjmp ADC_0		; идем ожидать новых звонков

PC_RESET:	; сброс или принудительное выключение РС
	ldi clok,12		; устанавливаем задержку clok=12 т.е. на 12 сек
	clr clk			; обнуляем счетчик длительности паузы
	ldi time,6		; постоянная времени задержки пропорциональна 1 сек

CLOCK_OFF:	; задержка для селекции синалов RESET и OFF
	rcall DELAY	
	inc clk 		; увеличиваем счетчик длительности паузы на единичку
	cp clk,clok		; сравниваем с константой длительности паузы
	brne CLOCK_OFF	; если не 0 - время паузы не вышло, повторяем цикл 
	clr clk			; время вышло - обнуляем счетчик длительности паузы
; остался ли звонок?	
	rcall ADC_L		; определяем наличие звонка
	brsh PC_OFF		; нет -> дальше RESET, если звонок есть -> PC OFF				
;RESET - сброс РС
	ldi time,12		; удваеваем постоянную времени задержки до 2 сек
	cbi PORTB,0		; сбраываем РВ1 (сброс РС) в 0 
	rcall DELAY		; удерживаем этот 0 в течении 2 секунды
	sbi PORTB,0		; возвращаем РВ1 (сброс РС) в 1	
	rjmp ADC_0		; идем в начало программы ожидать звонок

PC_OFF:			;OFF - выключение РС
	cbi PORTB,1		; сбраываем РВ1 (включение РС) в 0 
	rcall DELAY		; удерживаем этот 0 в течении 1 секунды
	sbi PORTB,1		; возвращаем РВ1 (включение РС) в 1
	rcall DELAY		; удерживаем этот 0 в течении 1 секунды
; пауза на 1,5 мин - гарантированное время выключения
	ldi clok,90		; константа длительности паузы (сей час она равна 1,5 мин)
	clr clk			; обнуляем счетчик длительности паузы
	ldi time,6		; постоянная времени задержки пропорциональна 1 сек	

CLOCK_15:		; пауза на 1,5 мин
	rcall DELAY	
	inc clk 		; увеличиваем счетчик длительности паузы на единичку
	cp clk,clok		; сравниваем с константой длительности паузы
	brne CLOCK_15	; если не 0 - время паузы не вышло, повторяем цикл 
	clr clk			; время вышло - обнуляем счетчик длительности паузы
; анализ состояния вкл/выкл РС
	in temp,PINB	; читаем сост. выв. порта В РВ4=0->РС -выкл, РВ4=1->РС -вкл
	sbrs temp,4		; анализируем разряда РВ4 если 1 - пропуск следующей команды
	rjmp ADC_0		; если выключен идем в начало ожидать новых звонков
;RESET - сброс РС
	ldi time,12		; удваеваем постоянную времени задержки до 2 сек
	cbi PORTB,0		; сбраываем РВ1 (сброс РС) в 0 
	rcall DELAY		; удерживаем этот 0 в течении 2 секунды
	sbi PORTB,0		; возвращаем РВ1 (сброс РС) в 1
	rcall DELAY		; удерживаем этот 0 в течении 2 секунды	

OFF_PC:		; принудительное выключение РС
	cbi PORTB,1		; сбраываем РВ1 (включение РС) в 0 
	in temp,PINB	; читаем сост. выв. порта В РВ4=0->РС -выкл, РВ4=1->РС -вкл
	sbrc temp,4		; анализируем разряда РВ4 если 0 - пропуск следующей команды 
	rjmp OFF_PC		; продолжаем держать кнопку выкл
	sbi PORTB,1		; возвращаем РВ1 (включение РС) в 1
	rjmp ADC_0		; если выключен идем в начало ожидать новых звонков
	
;============================ ПОДПРОГРАММЫ ====================================

ADC_L:		; Проверка наличия телефонного звонка
	sbi ADCSRA,ADSC	; запускаем одно преобразование АЦП -> ADSC в единицу !!!!! 
	
ADC_L1:		; ждем окончания преобразования
	in temp,ADCSRA	; читаем регистр состояния и управления АЦП 
	andi temp,$40	; анализируем состояние бита ADSC [p6], если 0-преобразован
	brne ADC_L1		; окончено, если 1 продолждаем ждать окончания (циклимся) 	
	in temp,ADCH	; читаем ст. байт результата преобразования
	subi temp,$B9	; сравниваем результат с 0,88*Vref = 0,8B
; индикация звонка	
	brsh LED_ON		; если больше - звонок есть - включаем индикаторный LED
	cbi PORTB,2		; сбраываем РВ2 в 0 (выключение индикаторного LED )
	ret

LED_ON:		; включение светодиода индицируещего наличие звонка
	sbi PORTB,2		; возвращаем РВ2 в 1 (включение индикаторного LED)		
	ret
;..............................................................................

DELAY:		; Задержка (определяется переменной "time") 
	mov temp,time	; единица переменной "time" пропорциональна 0,163 сек
DEL_1:
	ldi tmp0,$FF
DEL_2:
	ldi tmp1,$FF
DEL_3:
	dec tmp1
	brne DEL_3
	dec tmp0
	brne DEL_2
	dec temp
	brne DEL_1
	ret
