среда, 21 августа 2013 г.

Приёмник ИК команд от китайского пульта.

Продолжая эпопею со светодиодной подсветкой, добрался таки до управления с ИК пульта.

Схема подключения приёмника:

Скетч fritzing




Поскольку тестовый проект делается на Ардуино, всё просто — подключаем библиотеку IRremote и радуемся. С оговоркой, что она требует предоставить ей D3 выход arduino и перепрограммирует Timer2. И всё бы ничего, но во-первых, планируется таки переделать всю эту поделку на "голой" ATmega328, а во-вторых, код библиотеки добавляет около 8кб кода! "А оно мне надо, такая универсальность?", — подумалось мне и я начал поиски менее объёмного кода…

Минимальные наблюдения показали, что пульты от китайских светодиодных контроллеров работают с NEC протоколом, который и было решено найти. Несколько дней вялых поисков готового решения закончились тем, что пришлось сесть и почти самостоятельно написать своё. Приведённый ниже код — тестовый кусочек для ардуино, но без изменений может быть перенесён в простой gcc. Arduino-специфичны там только установка режима входов и вывод отладочных результатов в последовательный порт. Для работы используется аппаратное прерывание INT0 (вход D2 arduino) и Timer2.

Основой послужил пример с easyelectroincs и описание протоколов с petro-ewLab.com, потребовавшееся для его доработки. Существующий пример использовал нужный мне для других дел таймер и не обеспечивал отлов сигнала "повтор". Заодно в отладочном коде приведён пример проверки корректности приёма — сравнение прямого и инверсного значений принятых байтов, раз уж они передаются в протоколе.

// IR test
// Receive NEC codes.

#include <Arduino.h>
#include <avr/interrupt.h>

#define IR 2
// PD2
#define IR_PIN _BV(2)

// PB5
#define LED_PIN _BV(5)

volatile uint8_t started=0;
volatile uint32_t code=0;
volatile uint32_t buf=0;
// timer timeout prescaler ~150ms
#define NEC_MAXLEN  20
// bit maxtime counter ~ 1.216ms @ 16MHz/1024 -> "0" level pulse
#define PULSE_THRESH  19
// "repeat" code
#define RPT_THRESH 38
// "start" code
#define START_THRESH 62


ISR(INT0_vect) {
  uint8_t d;
  if (started) {
    d = TCNT2 - NEC_MAXLEN;
    buf <<= 1;
    if (d > PULSE_THRESH) buf |= 1;
    if (d > RPT_THRESH && d < START_THRESH) {
      PORTB |= LED_PIN;
      buf = 0xFFFFFF;
    }
  }
  else {
    started = 1;
    TIMSK2 = _BV(TOIE2); // start timer
  }

  TCNT2 = NEC_MAXLEN;
}

ISR(TIMER2_OVF_vect) {
  code = buf;
  buf = 0;
  started = 0;
  //  if (code==0) TIMSK2 &= ~_BV(TOIE2); // stop timer
  TCNT2 = NEC_MAXLEN;
}

void resume_ir() {
  TIMSK2 |= _BV(TOIE2);
  TCNT2 = NEC_MAXLEN;
}

void setup() {

  pinMode(IR_PIN, INPUT);
  pinMode(13, OUTPUT);

  asm("cli");
  // setup INT0
  EICRA |= _BV(ISC01) | _BV(ISC00); // rising edge as interrupt
  EIMSK |= _BV(INT0); // enable INT0

    // setup Timer2
  TCCR2A = 0; // overflow mode 
  TCCR2B = _BV(CS20) | _BV(CS21) | _BV(CS22); // clock 1024 -> 64us
  TCNT2 = 0;
  asm("sei");

  Serial.begin(115200);
  Serial.println("== test IR receiver==");
  PORTB |= LED_PIN;
  delay(1000);
  PORTB &= ~LED_PIN;
}


void loop() {
  if (code) {
    byte a = (code >> 16) & 0xFF;
    byte c1 = (code >> 8) & 0xFF;
    byte c2 = code & 0xFF;
    if (code == 0xFFFFFF) Serial.println("*repeat*");
    else {
      Serial.print("addr: ");
      Serial.print(a, HEX);
      Serial.print(", code: ");
      Serial.print(c1, HEX);
      if (c1 | c2 == 0xFF) Serial.println(" OK");
      else {
        Serial.print(" BROKEN (");
        Serial.print(c1, HEX);
        Serial.print(" <> ~");
        Serial.print(c2, HEX);
        Serial.println(")");
      }
    }
    code = 0;
    PORTB &= ~LED_PIN;
    resume_ir();
  }
  delay(10);
}

Комментариев нет:

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