Реализация ИК протокола NEC на ATmega

d307e2c43bae48c3a6972f256d030a79.jpg Данный протокол уже много где описан. Я хочу показать и подробно описать свою реализацию на конкретном микроконтроллере. Мне было необходимо принимать сигнал с пульта RGB — такого, как на картинке. Его система команд приведена внизу статьи.Краткий экскурсКаждый пакет протокола NEC состоит из стартовой последовательности — импульса длиной 9 мс и паузы длиной 4,5 мс. Дабы не грузить вас теоретическими рисунками, покажу реальные скриншоты с логического анализатора.9873107c73a04ec9a44e3873838c3593.jpgСам протокол основан для кодировании нулей и единиц длиной паузы. Начало каждого бита определяется импульсом длиной 560 мкс (одновременно этот импульс сигнализирует о конце предыдущего бита). Длина следующей за импульсом паузы определяет логическое значение бита. Так длина логического нуля получается 1.12 мс, а логической единицы 2.25 мс. Соответственно в реальной ситуации значения немного разнятся.

981e732c891b4b62a6f63656f3d67a38.jpg306a5056d5724d79b71fa23549198a05.jpg

Пакет состоит из стартовой последовательности, адреса и команды. В стандартной версии протокола пакет всегда имеет фиксированную длительность, так как адрес и команда передаются как в прямом, так и в инверсном виде.

e4e562f12bf0422eb3c6dcaa2afbb5aa.jpg

При удержании кнопки посылка повторно не передаётся. Вместо этого каждые 110 мс передаётся специальный код повтора длительностью 11,25 мс.

bbc7cc7b809542ff8f60c629c8577066.jpg

Программа Я не принимал адрес, так как он мне не нужен. В случае необходимости, вы легко сможете доработать программу. В качестве микроконтроллера была выбрана ATmega32 с 16МГц кварцем. Следовательно, все временные интервалы рассчитаны для 16Мгц. Для реализации протокола нам понадобится таймер для отсчета времени и внешнее прерывание по ниспадающему фронту. Таймер настроен с делителем 1024, один такт 1024/16МГц = 64 мкс, прерывание по переполнению 64 мкс * 256 = 16 мс (что заведомо больше любого из битов в пакете, это нам пригодится). Начальная инициализация и макросы старт/стоп таймера выглядят так. #define StopT0 TIMSK &= ~(1 << TOIE0); //макрос для старта таймера #define StartT0 TIMSK |= (1 << TOIE0); //макрос стоп таймера SREG|= (1<<7); //Global Interrupt Enable GICR|= (1<

#pragma vector=TIMER0_OVF_vect __interrupt void TIMER0_interrupt (void) //переполнение Т0 16 мс, что больше любого из кусков пакета { if (firstT ==0) { firstT = 1; } /* Первое прерывание возникает при его запуске, оно нам не надо. Даже если не возникает, временной интервал просто увеличится до 32 мс. */ else { startC = 0; //ждем новой команды firstT = 0; //для следующего «первого» запуска StopT0; //стоп таймер } } Прерывание по ниспадающему фронту INT0. Тут считаем биты и анализируем время, пройденное с последнего прерывания. По величине времени TCNT0 легко понять, какой бит это был. В конце обработчика обнуляем, чтобы начать отсчет сначала. #pragma vector=INT0_vect __interrupt void INT0_interrupt (void) { if (startC == 0) { //первое прерывание newC = 0; //флаг новой команды startC = 1; //начали принимать StartT0; //старт Т0 } else { //один отсчет таймера равен 64 мкс, все значения проверяеются на неком временном диапазоне

if (TCNT0>0xD2 & TCNT0<0xFF){ //13,5 мс СТАРТ бит (диапазон 13,4 ... 16,3 мс) i = 32; //количество бит ожидаемой посылки }

if (TCNT0>0×07 & TCNT0<0x16){ //1,12мс НОЛЬ (диапазон 0,45 ... 1,41 мс) if ((i>0) & (i<9)) Command1 &= ~(1<<(i-1)); //запись бита в прямую if ((i>8) & (i<17)) Command |= (1<<(i-9)); //и инверсную команды i--; } if(TCNT0>0×19 & TCNT0<0x28){ //2,25мс ЕДИНИЦА (диапазон 1,60 ... 2,56 мс) if ((i>0) & (i<9)) Command1 |= (1<<(i-1)); //запись бита в прямую if ((i>8) & (i<17)) Command &= ~(1<<(i-9)); //и инверсную команды i--; }

if (TCNT0>0xA9 & TCNT0<0xB8){ //11,25мс повтор команды (диапазон 10,8 ... 11,8 мс) newC = 1; //повтор команды } if (i==0) { //все биты приняты StopT0; //стоп таймер newC = 1; //это новая команда startC = 0; //ждем новой команды firstT = 0; //для следующего "первого" запуска таймера } } TCNT0 = 0; //обнуляем счетчик } В основной программе анализируем флаг newC, не забыв обнулить его. Ну и дальнейшая обработка команды. while(1) { if (newC) { //есть команда? newC = 0; //теперь нет if (Command == Command1) { //равны ли прямая и инверсная? switch (Command) { //Обработка команд case 0x5F: { } //Up case 0xDF: { } //Down //........... } } } } Система команд А вот и сама система команд для пульта ED618 (покупаю их на dx.com):6273cd1a902740329042daeb5de23e8a.jpg

Для других таких же RGB пультов система команд может быть другая. У меня есть точно такой же пульт, достался мне от какого-то покупного контроллера RGB, так там система команд весьма отличается. Считывайте и смотрите сами. Я, например, принимал команду и скидывал ее по UART’у на комп.

Алгоритм сам по себе не сложный, надеюсь кому-то пригодится. У меня уже сделано не одно устройство на основе пульта с таким протоколом, все работает хорошо. В следующей статье расскажу про реализацию протокола светодиодов ws2812b, если захотите конечно.

© Habrahabr.ru