Программа, рассмотренная в этой статье, разработана специально для самодельного ИК-приемника сигналов дистанционного управления на контроллере ATTiny13. Если вы измените аппаратную часть (например, будете использовать другой порядок ног), то программу так же нужно будет переделать.
Представленная ниже программа родилась после того, как выяснилось, что внутренние генераторы контроллеров не обладают достаточной точностью и стабильностью, для того, чтобы всегда одинаково и без ошибок отсчитывать необходимые длительности битов и полубитов. В основе её работы лежат свойства манчестерского кодирования (а в протоколе RC-5 используется именно оно), дающие отличную возможность синхронизировать приёмник и передатчик не взирая на эти мелкие проблемы.
Подробнее о манчестерском кодировании и его способностях к синхронизации написано в отдельной статье, поэтому здесь я скажу вкратце. Информация о том, что первые два бита передаваемого кода RC-5 равны единице (стартовые биты), позволяет нам, во-первых, засечь длительность полубита, и, во-вторых, рекуррентно определять последующие биты.
Рекурсия здесь такая: Если следующий бит отличается от предыдущего, то в начале передачи следующего бита не будет изменения состояния сигнала, а в следующий раз сигнал изменится только в середине передачи следующего бита (сигнал после середины предыдущего бита изменится 1 раз, через интервал, равный длительности целого бита). Если следующий бит повторяет предыдущий, то изменение состояния сигнала в начале передачи следующего бита — будет, а потом будет ещё второе изменение состояния сигнала — в середине бита (то есть сигнал после середины предыдущего бита изменится 2 раза, через интервалы, равные половине бита). Чтобы было понятнее — смотрите на картинку справа.
То есть, можно засечь интервал между первыми двумя изменениями состояния, а дальше — сравнивать последующие интервалы с этим эталоном (он будет равен длительности полубита). Причём, если интервал получился в 2 раза больше эталона, — значит мы принимаем бит, инвертированный по отношению к предыдущему, а если интервал получился равным эталону, — значит мы принимаем такой же бит, как и предыдущий (кроме того, в последнем случае нам нужно пропустить один интервал). В результате этих действий мы восстановим все 14 передаваемых бит.
Для того, чтобы определить, когда нужно пропустить один интервал в нашей программе мы будем использовать пользовательский флаг T в регистре SREG. То есть, если этот флаг установлен, — мы перезапускаем таймер, но очередной бит не записываем.
Чтобы исключить влияние нестабильности генератора приёмника, — все измеренные интервалы, которые ближе к одинарному эталону, чем к удвоенному эталону, будем считать равными одинарному эталону, а все интервалы, которые ближе к удвоенному эталону, чем к одинарному эталону, будем считать равными удвоенному эталону. Определить к какому значению ближе текущий интервал можно очень просто. Для этого достаточно вычесть из текущего измеренного значения 1,5 эталонных интервала (3/4 длительности бита). Если полученное значение будет отрицательным — значит текущее значение ближе к одинарному интервалу, если положительным — значит к двойному.
На этом с вводной частью и описанием закончим и перейдём уже наконец к самой программе и алгоритму.
Программа реализует следующие функции:
1) Принимает и декодирует 14 бит протокола RC-5, передаваемых манчестерским кодом (старшие 8 бит помещаются в регистр MBC, младшие 6 бит — в регистр LBC).
2) Инвертирует состояние одного из четырёх светодиодов, в зависимости от номера принятой команды (номер системы при этом игнорируется).
Прежде чем разбираться с программой, советую ещё раз посмотреть описание самого протокола RC-5.
Не забывайте, что выход ILMS1836 — инвертированный, т.е. для него состояние «OFF» — это высокий уровень на выходе, а состояние «ON» — низкий.
Алгоритм:
Итак, пусть в аппаратной части мы имеем:
входы: PB3 — вход ИК-приемника
выходы: PB2, PB1, PB0, PB4 — выходы на транзисторы управления светодиодами.
RESETвнешне подтянут к питанию; используется внутренний генератор.
Запрограммированные фьюзы: SPIEN, SUT0, CKSEL0 (для внутреннего генератора на 9,6 МГц).
.device ATtiny13 .include "tn13def.inc" .list ;-- определяем свои переменные .def w=r16 ; это будет наш аккумулятор .def MBC=r17 ; старший байт посылки .def LBC=r18 ; младший байт посылки .def Sch=r19 ; счётчик принятых бит .def C_time=r20; текущее значение таймера .def H_time=r21; 3/4 бита ;-- определяем константы .equ Sys=29 ; номер системы .equ CMD_1=1 ; 000001 код команды=1 .equ CMD_2=2 ; 000010 код команды=2 .equ CMD_3=3 ; 000011 код команды=3 .equ CMD_4=4 ; 000100 код команды=4 ;-- Используемые регистры ; SPL - указатель вершины стека ; ACSR - управление компаратором ; DDRB - направление работы ног ; PORTB - выходы порта ; PCMSK - разрешение прерываний на отдельных входах ; GIMSK - общее разрешение прерываний по входам ; GTCCR - сброс предделителя ; TCCR0B - управление таймером ; TIMSK0 - прерывания от таймера ;-- начало программного кода .cseg .org 0 rjmp Init ; переход на начало программы ;-- вектора прерываний reti ; INT0 rjmp Pch ; Pin Change rjmp Timer ; Timer reti ; EEPROM reti ; comparator reti ; timer compare match A reti ; timer compare match B reti ; watchdog reti ; ADC ;-- начало программы Init: ldi w,RAMEND ; устанавливаем указатель вершины out SPL,w ; стека на старший байт RAM sbi ACSR,7 ; выключаем компаратор ldi w,0b00010111 ; определяем входы и выходы порта out DDRB,w ldi w,0b11101000 ; включаем подтягивающие резисторы out PORTB,w ; и определяем начальное состояние выходов ;-- разрешить прерывания от таймера -- ;-- (пока он не запущен - прерывания всё равно не будет) -- ldi w,0b00000010 out TIMSK0,w ; разрешить прерывания от таймера ;-- Обнуляем счётчик и регистр флагов Reset: in w,PORTB ; читаем порт clr Sch clt ; сбрасываем флаг Т ;-- разрешить прерывание от входов ldi w,0b00100000 out GIMSK,w ; разрешаем прерывание от входов ;-- разрешить прерывания на входе PB3 Start: ldi w,0b00100000 out GIFR,w ; сбросить флаг прерываний от входов ldi w,0b00001000 out PCMSK,w ; разрешаем прерывания на входе PB3 sei ; разрешить глобальные прерывания Scan: nop rjmp Scan ;-- ОБРАБОТЧИК ПРЕРЫВАНИЯ ПО ВХОДУ ;-- инициализируем счётчик, адр.бита, пров.рег., адр.рег.записи Pch: tst Sch ; счётчик =0? brne Next Nachalo: sbic PINB,3 ; если на входе низкий уровень - пропускаем команду reti ; это не приём - выходим ldi w,0b00000001 out GTCCR,w ; сбросить предделитель ldi w,0b00000100 out TCCR0B,w ; запустить таймер с делителем на 256 sec ; поднять carry flag rol LBC rol MBC inc Sch reti Next: in C_time,TCNT0 ; читаем значение таймера clr w out TCNT0,w ; перезапускаем таймер cpi Sch,1 ; Sch=1? brne Next2 Next1: mov H_time,C_time lsr C_time ; находим четверть бита add H_time,C_time ; находим 3/4 бита set ; ставим флаг пропуска (T в регистре SREG) sec rol LBC rol MBC inc Sch ; увеличиваем счётчик reti Next2: brts Not_Signed_FRont Signed_Front: cp C_time,H_time brsh Long_time Short_time: set ; установить признак пропуска clc sbrc LBC,0 ; пропустить, если прошлоне значение = 0 sec rjmp Zapis_bita Long_time: clc ; принимаемый бит инвертирован, по отношению к последнему sbrs LBC,0 ; пропустить, если прошлое значение = 1 sec Zapis_bita: rol LBC rol MBC inc Sch cpi Sch,14 breq rab_chast reti Not_Signed_Front: clt reti ;-- ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ ТАЙМЕРА Timer: clr w out TCCR0B,w ; выключить таймер pop w ; очищаем стек pop w rjmp Reset ; ресетимся ;-- РАБОЧАЯ ЧАСТЬ ;-- Проверка номера системы и команды rab_chast: clr w out TCCR0B,w ; выключить таймер out TCNT0,w ; сбросить таймер pop w ; очищаем стек pop w clc rol LBC ; сдвигаем всё, кроме номера команды в MBC rol MBC clc rol LBC rol MBC lsr LBC ; выравниваем номер команды младшим битом к lsr LBC ; младшему биту байта in w,PORTB ; читаем порт B cpi LBC,CMD_1 breq Button1 cpi LBC,CMD_2 breq Button2 cpi LBC,CMD_3 breq Button3 cpi LBC,CMD_4 breq Button4 rjmp Thats_all Button1: ldi Sch,0b00000001 eor w,Sch ; инвертируем нулевой бит out PORTB,w rjmp Thats_all Button2: ldi Sch,0b00000010 eor w,Sch ; инвертируем первый бит out PORTB,w rjmp Thats_all Button3: ldi Sch,0b00000100 eor w,Sch ; инвертируем второй бит out PORTB,w rjmp Thats_all Button4: ldi Sch,0b00010000 eor w,Sch ; инвертируем четвёртый бит out PORTB,w Thats_all: rcall Pause rjmp Reset Pause: ldi w,0xFF Pause_cikl: nop nop nop nop nop dec w brne Pause_cikl ret ;--------------------------------------------------------- |
не работает в proteus посылаю данные из pattertn generator никакой реакции
Сочувствую, в proteus вообще много чего не работает. Но для начала проверьте всё ещё раз: фьюзы, данные, которые посылает pattern generator и т.д.
Что он, кстати, генерирует, какой паттерн, правильно ли там выставлен номер системы?
В реальности всё работает.
Я новичек, мне многое не понятно, и вопрос как проверить фьзы в протеус? возможно вы знаете?))