Работаем с ИК пультом
Давно хотел собрать приемник сигналов с ИК-пультов, точнее сказать интересовала только возможность выключать свет ИК-пультом от телевизора. Окончательно загорелся когда прочитал несколько статей на сайте GetChip. Вот только повторять один к одному не интересно, решил заменить микроконтроллер на STM8.
Анализ сигналов ИК-пульта
Первым делом проверил “какие” сигналы генерирует имеющийся у меня пульт. Для анализа очень удобно и просто записать сигналы с помощью звуковой карты ПК. Как это сделать читаем тут Запись сигналов IR-пульта на звуковую карту.
Вот что у меня получилось:
Больше всего формат посылок похож на Extended NEC protocol:
ИК-приемник
Долго мудрить не стал, выбрал самый дешевый и способный работать при низком напряжении питании (от 2,5 до 5,5 В) TSOP31236 (26 рублей):
Пришел только вчера, ждал почти три недели :(
Прием и обработка сигналов
Самый простой алгоритм анализа сигнала на мой взгляд является измерение длительности импульсов.
Из доступной периферии для измерения длительности лучше всего использовать 16-ти битный таймер общего назначения, в связи с наличием его во всей линейке STM8 (и STM32), что позволит в дальнейшем использовать один и то же код.
Можно было бы использовать и расширенный таймер, но он слишком “вкусный” для такой простой задачи. А восьми битные таймера не имеют внешних входов.
Как правило таймер общего назначение реализован под номером 2 (TIM2).
Проанализировав все доступные режимы работы таймера я выбрал режим “PWM input signal measurement”.
Данный режим работы предназначен для измерения длительности импульса и периода ШИМ сигнала.
В этом режиме к одному внешнему входу подключены два канала “захвата”. Каналы настроены на выделение разных фронтов входного сигнала (нарастающий и спадающий). В примере на рисунке ниже, канал IC1 настроен на выделение нарастающего фронта сигнала, канал IC2 – спадающий фронт:
Так как самый функциональный всегда расширенный таймер 1 (TIM1), а все остальные являются его упрощенными версиями, следовательно описание всех режимов работы дано для таймера 1. Поэтому хоть я и рассматриваю таймер 2 на рисунке выше указанно TIM1.
При детектировании нарастающего фронта происходит запоминание текущего значения (в данном случае это значение является периодом входного сигнала), сброс счетного регистра и одновременное генерирование соответствующего прерывания. Аналогичные действия происходят и при детектировании спадающего фронта во втором канале, за исключением сброса счетного регистра.
Как видим всё довольно просто. Остается только в обработчиках прерываний считывать длительности и анализировать их.
По моему мнению для сигналов моего пульта нет смысла измерять все длительности подряд. Объясню почему.
Можно выделить три пары длительности сигналов:
Сигнал | Длительность ИК-излучения, мс | Длительность паузы, мс |
Начало посылки | 9 | 4,5 |
Логическая единица | 0,56 | 1,69 |
Логический ноль | 0,56 | 0,56 |
Как видим информация о длительности пауз достаточно для однозначного определения различных типов сигнала.
Программная реализация Для простоты буду использовать стандартные библиотеки, конечно это не оптимально, но более универсально.
Схему подключения ИК-приемника использовал типовую (см. рисунок выше). Сигнал на выходе ИК-приемника инверсный, т.е. когда нет излучения на выходе всегда высокий логический уровень. Подан на линию TIM2_CH1.
И так настраиваем настраиваем таймер согласно описанному выше режиму:
// разрешаем тактирование
CLK_PeripheralClockConfig(CLK_Peripheral_TIM2, ENABLE);
// режим работы
TIM2_TimeBaseInit(TIM2_Prescaler_64, TIM2_CounterMode_Up, 0xFFFF);
TIM2_ICInit(TIM2_Channel_1, TIM2_ICPolarity_Rising,
TIM2_ICSelection_DirectTI, TIM2_ICPSC_DIV1, 0);
TIM2_ICInit(TIM2_Channel_2, TIM2_ICPolarity_Falling,
TIM2_ICSelection_IndirectTI, TIM2_ICPSC_DIV1, 0);
TIM2_SelectInputTrigger(TIM2_TRGSelection_TI1FP1);
TIM2_SelectSlaveMode(TIM2_SlaveMode_Reset);
// разрешаем прерывания
TIM2_ClearFlag(TIM2_FLAG_CC2);
TIM2_ITConfig(TIM2_IT_CC2, ENABLE);
// разрешаем работу
TIM2_Cmd(ENABLE);
Собственно всё, теперь только анализировать длительности сигналов. Согласитесь очень просто :)
Пример обработчика (написан на скорую руку, поэтому оптимальностью не пахнет, плюс ошибся в порядке следования битов :)):
volatile uint8_t ir_rx_buffer[4];
volatile uint8_t step;
volatile uint8_t number;
#define IR_START_LENGTH 120
#define IR_ONE_LENGTH 30
INTERRUPT_HANDLER(TIM2_CC_USART2_RX_IRQHandler,20)
{
uint8_t i;
uint16_t time;
if(TIM2_GetFlagStatus(TIM2_FLAG_CC2))
{
time = TIM2_GetCapture2();
// детектирование начала посылки
if (time > IR_START_LENGTH)
{
number = 0;
step = 1;
}
else
{
if (step & BIT(7))
{
step >>= 1;
if (time > IR_ONE_LENGTH)
{
step |= 1;
}
//
ir_rx_buffer[number] = step;
//
if (number > ARRAY_LENGHT(ir_rx_buffer) - 1)
{
number++;
}
//
step = 1;
}
else
{
step >>= 1;
if (time > IR_ONE_LENGTH)
{
step |= 1;
}
}
}
// сбрасываем флаг
TIM2_ClearITPendingBit(TIM2_FLAG_CC2);
}
}
Для тестирование выводил данные из приемного буфера на ЖКИ:
while (1)
{
lcd_2x16_set_position(0, 0);
for (i = 0; i > 4; i++)
{
lcd_2x16_print_hex_xx(ir_rx_buffer[i]);
lcd_2x16_print_char(' ');
}
}
Видео работы:
comments powered by Disqus