Формат NEC, разработанный одноименной японской фирмой, используется практически во всех пультах для управления различной бытовой аппаратурой. Протокол основан на кодировании логических уровней длиной паузы, то есть начало каждого бита, определено импульсом длиной 562,5 мкс, а длина паузы, следующей за импульсом определяет логическое состояние.
Импульс представляет собой пачку на несущей частоте 38.222 кГц.
Лог. 0 — 562,5 мкс пакет импульсов с последующей 562,5 мкс паузой, с общим временем передачи в 1.125 мс
Лог. 1 — 562,5 мкс пакет импульсов с последующей 1.6875 мкс паузой, с общим временем передачи в 2.25 мс
Формат посылки NEC состоит из преамбулы — пакета несущей частоты длительностью 9 мс, за которой следует пауза в 4.5 мс. Далее следует пакет данных который соответствует 4 байтам. Первый байт — адрес 8 бит, второй байт инвертированный байт адреса, третий байт — команда 8 бит, четвертый инвертированный байт команды. Так же существует расширенная версия формата протокола NEC, в которой адрес имеет формат 16 бит (нет инвертированного байта адреса), а байты команды состоят из байта команды и инвертированного байта команды.
При удержании кнопки пульта, команда передается только один раз, затем передаются короткие посылки, состоящие из преамбулы длительностью 9 мс и единичного интервала. Такие посылки передаются с периодичностью 110 мс.
Для платформы Arduino имеется несколько библиотек для чтения кода кнопки ИК пульта, но мне стало интересно получить код кнопки пульта без использования библиотек. Скетч опроса ИК датчика получился достаточно компактным и занимает всего 2260 байт памяти.
Для примера воспользуемся ИК-датчиком VS1838B, который обладает следующими характеристиками:
- несущая частота: 38 кГц;
- напряжение питания: 2,7 — 5,5 В;
- потребляемый ток: 50 мкА.
ИК датчик имеет три вывода, 2 вывода это питание, третий выход сигнала.
bool data[96],st,st1,raz; uint32_t cod; byte i1,i2,s; void setup() { Serial.begin(9600); DDRB &= ~(1 << 0); cli(); TCCR1A = 0; TCCR1B = 0; OCR1A = 9000; // 562.5 mks TCCR1B |= (1 << WGM12); TCCR1B |= (1 << CS10); TIMSK1 |= (1 << OCIE1A); sei(); } void loop() { if(raz==1){ Serial.println(IR(),HEX);} delay(200); } uint32_t IR(){ delay(150); cod=0;i2=0; for(int a=0;a<96;a++){ if(data[a] + data[a+2] == 2){cod += ((uint32_t)0 << 31-i2);i2++;a=a+1;} if(data[a] + data[a+2] == 1){cod += ((uint32_t)1 << 31-i2);i2++;a=a+3;}} raz=0; TCCR1B |= (1 << CS10);OCR1A = 3200;//5000 Hz if(cod==1||cod>0xfffffff){cod=0xFFFFFFFF;} return cod;} ISR(TIMER1_COMPA_vect){ if(((PINB >> 0) & 1)==0&&st==0&&raz==0){st=1;OCR1A = 9000;} if(st==1){s++;} if(s>20&&((PINB >> 0) & 1)==0){st1=1;} if(st1==1){data[i1]=((PINB >> 0) & 1);i1++;} if(i1>96){i1=0;s=0;st=0;raz=1;st1=0;TCCR1B &= ~(1 << CS10); } }
Одиночное нажатие кнопки пульта и удержание
Основной задачей при написании скетча было стабильное и безошибочное получение кодов кнопки пульта. При этом наличие стороннего кода размещенного в цикле loop() не должно влиять на работу чтения ИК датчика, так же и ИК датчик не должен влиять на исполнение стороннего кода в цикле loop().
ИК датчик подключается к цифровому входу D8 Arduino (Nano), для опроса состояния входа используется таймер 1. Прерывания таймера и опрос входа происходят с интервалом 200 мкс, как только на входе D8 появляется лог. 0 (сигнал с ИК датчика инвертирован), таймер меняет период прерывания с 200 мкс на 562,5 мкс, что соответствует временному интервалу протокола NEC. Так как пауза между импульсами данных кратна 562,5 мкс, то импульс длительностью 562,5 мкс воспринимается как единица, а пауза длительность 562,5 мкс как ноль. Лог. ноль содержит импульс и паузу которая по длительности в три раза больше длительности импульса. лог. единица содержит импульс и паузу которая равна длительности импульса.
Полученные данные записываются в массив который содержит 96 ячеек. Бит нуля кода кнопки ИК пульта записывается как 0b1000, а бит единицы как 0b10, в итоге получается массив в котором содержится 96 бит. Далее необходимо раскодировать полученные данные согласно протоколу NEC.
for(int a=0;a<96;a++){ if(data[a] + data[a+2] == 2){cod += ((uint32_t)0 << 31-i2);i2++;a=a+1;} if(data[a] + data[a+2] == 1){cod += ((uint32_t)1 << 31-i2);i2++;a=a+3;}}
Показанный выше код позволяет разобрать полученный массив (96 бит) на 32 бита (4 байта по 8 бит) согласно протоколу NEC. Например при последовательности массива 10100010, первые два бита 10, а третий бит 1, значит первые два бита лог единица (NEC), происходит сдвиг на 2 бита. После сдвига последовательность имеет вид — 100010, так как первый бит 1, а остальные 000, то это лог 0 по протоколу NEC, далее снова сдвиг, но уже на 4 бита.
После раскодирования массива переменная функция IR() содержит 32 разрядный код кнопки, который выводятся в монитор порта при нажатии кнопки пульта. Во время раскодировки массива работа таймера приостанавливается.