Наш магазин на eBay Наш магазин на AliExpress Наш канал в 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

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