Простой универсальный декодер ИК ДУ


Использование ИК ДУ пультов от бытовой техники достаточно популярно для управления различными самодельными устройствами от различных умных выключателей света, систем умного дома и до игрушек и роботов. Существует большое разнообразие протоколов ИК ДУ пультов и способов кодирования сигнала. Предлагаю один очень простой способ обработки ИК сигналов, работающий с большинством распространённых протоколов.

В сети опубликовано большое количество различных программных декодеров для приема команд с пультов ДУ, большинство заточены под какой-то определенный протокол, например, RC-5, RC-6, Sony, Nec и т.д., но есть и универсальные. Со специализированными декодерами всё понятно, они хорошо работают (только) со своими пультами, но по этой-же причине их применение ограниченно. Универсальные алгоритмы, как правило, либо сравнивают длительности импульсов и пауз между ними по таблице, либо производят выборку по таблице контрольных точек. Это требует относительно большого объёма памяти для хранения кодов кнопок.
А нужно-ли для уверенного детектирования определенной команды ее в точности разбирать по определенному протоколу, или точно сопоставлять длительности всех импульсов/пауз? В общем-то не нужно. Минимальная длительность импульсов ограниченна используемой несущей частотой. Например, для ИК приёмников серий TSOP17xx (где xx — шесущая частота КГц) минимально детектируемый импульс — 15 периодов тактовой частоты ~0.42 мс. Минимальная длительность импульсов в посылках пультов примерно вдвое больше и составляет около 0.85 мс. Увеличивать длительность импульсов больше этой величины не имеет смысла, так как на отправку команды будет уходить слишком много времени. Тоже самое касается пауз между импульсами. На практике, разница между длинами импульсов разных пультов ДУ меньше чем в в два раза, это позволяет отказаться от точного измерения длительности импульсов и пауз и характеризовать их только как «короткие» и «длинные» (стартовые импульсы нас не интересуют). То есть нам нужно запоминать один бит на фронт и спад.

Демонстрационное устройство
Схема демонстрационного устройства очень проста — контроллер AtTiny26 с минимальной обвязкой, приёмник TSOP1736 и знакосинтезирующий дисплей. Оно принимает от пулта и отображает полученный код на ЖК дисплее. На диодах vd1, vd2 и конденсаторах С1, С2 собрана схема накачки заряда (charge pump) для получения отрицательного напряжения контраста ЖК дисплея — мне попался экземпляр для работы при низких температурах, и ему нужно где-то минус 1.5 вольта на выводе CONTR относительно земли.

Для замера длительностей импульсов используется таймер и внешнее прерывание, но стем-же успехом можно использовать таймер с модулем захвата.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "lcd.h"

// пороговое значение для сравнения длинн импульсов и пауз
static const IrPulseThershold = 9;// 1024/8000 * 9 = 1.152 msec
// определяет таймаут приема посылки
// и ограничивает максимальную длину импульса и паузы
static const uint8_t TimerReloadValue = 150;
static const uint8_t TimerClock = (1 << CS02) | (1 << CS00);// 8 MHz / 1024

volatile struct ir_t
{
// флаг начала приема полылки
uint8_t rx_started;
// принятый код
uint32_t code,
// буфер приёма
rx_buffer;
} ir;

static void ir_start_timer()
{
TIMSK = (1 << TOIE0);
TCNT0 = 0;
TCCR0 = TimerClock;
}

// когда таймер переполнится, считаем, что посылка принята
// копируем принятый код из буфера
// сбрасываем флаги и останавливаем таймер
ISR(TIMER0_OVF0_vect)
{
ir.code = ir.rx_buffer;
ir.rx_buffer = 0;
ir.rx_started = 0;
if(ir.code == 0)
TCCR0 = 0;
TCNT0 = TimerReloadValue;
}

// внешнее прерывание по фронту и спаду
ISR(INT0_vect)
{
uint8_t delta;
if(ir.rx_started)
{
// если длительность импульса/паузы больше пороговой
// сдвигаем в буфер единицу иначе ноль.
delta = TCNT0 — TimerReloadValue;
ir.rx_buffer <<= 1;
if(delta > IrPulseThershold) ir.rx_buffer |= 1;
}
else{
ir.rx_started = 1;
ir_start_timer();
}
TCNT0 = TimerReloadValue;
}

static inline void ir_init()
{
GIMSK |= _BV(INT0);
MCUCR |= (1 << ISC00) | (0 <<ISC01);
ir_start_timer();
}

char buf[10];

int main()
{
PORTA|=_BV(0) | _BV(1);
DDRA=0;
DDRB|=_BV(PB3) | _BV(PB5);
PORTB|=_BV(PB5);

ir_init();
lcd_init();
sei();
lcd_puts("IR Lcd");

for(;;)
{
// если ir.code не ноль, значит мы приняли новую комманду
// ir.code будет сохранять свое значение до следующего переполнения таймера
if(ir.code)
{
// конвертируем код в строку и выводим на дисплей
ultoa(ir.code, buf, 16);
lcd_clear();
lcd_puts((buf));
}
// дёргаем ногу, на которой у нас зарядовая подкачка для контраста дисплея
PORTB^=_BV(PB3);
}
}

Декодер предельно прост, компактен и эффективен, но тем не менее работает с большинством протоколов ИК пультов.

Исходник к статье


Источник

Оставить комментарий

Вы можете использовать следующие теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>