Наш канал в telegram

Программа для самодельного ИК-приемника, протокол RC-5 (манчестерское кодирование), независимая от стабильности и точности генератора приёмника

Программа, рассмотренная в этой статье, разработана специально для самодельного ИК-приемника сигналов дистанционного управления на контроллере ATTiny13. Если вы измените аппаратную часть (например, будете использовать другой порядок ног), то программу так же нужно будет переделать.

Представленная ниже программа родилась после того, как выяснилось, что внутренние генераторы контроллеров не обладают достаточной точностью и стабильностью, для того, чтобы всегда одинаково и без ошибок отсчитывать необходимые длительности битов и полубитов. В основе её работы лежат свойства манчестерского кодирования (а в протоколе RC-5 используется именно оно), дающие отличную возможность синхронизировать приёмник и передатчик не взирая на эти мелкие проблемы.

Подробнее о манчестерском кодировании и его способностях к синхронизации написано в отдельной статье, поэтому здесь я скажу вкратце. Информация о том, что первые два бита передаваемого кода 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» — низкий.

Алгоритм:

Алгоритм работы самодельного ИК-приёмника (протокол RC5)

Итак, пусть в аппаратной части мы имеем:

входы: 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
;---------------------------------------------------------

[свернуть]

Скачать готовую прошивку и asm-файл

Комментарии 3

Добавить комментарий