В предыдущей статье про ИК канал обмена данными был рассмотрен приемник. Это наиболее востребованная задача, т.к. излучателем являются, как правило, пульты ДУ от различной бытовой радиоаппаратуры. Вместе с тем интересно посмотреть как в библиотеке IRremote реализована передача сигнала.
Для указанной библиотеки есть стандартный пример для передатчика IRsendDemo.
#include <IRremote.h> IRsend irsend; void setup() { Serial.begin(9600); } void loop() { if (Serial.read() != -1) { for (int i = 0; i < 3; i++) { irsend.sendSony(0xa90, 12); // Sony TV power code delay(40); } } }
Простой пример реализует посылку по ИК каналу команды включения ТВ (видимо) при каждом приеме символа по последовательному интерфейсу. При этом инфракрасный светодиод должен быть подключен к pin3 Arduino (это непременное условие для указанной библиотеки). Анодом к pin3, катодом — через токоограничивающий резистор — на землю. Подключение ИК диода ничем не отличается от подключения к Arduino обычного светодиода и подробно рассмотрено ранее.
Кроме метода sendSony(), в библиотеке реализован ряд алгоритмов кодирования применяемых другими фирмами. В файле библиотеки IRremote.cpp есть методы:
void IRsend::sendNEC(unsigned long data, int nbits) void IRsend::sendSony(unsigned long data, int nbits) void IRsend::sendRaw(unsigned int buf[], int len, int hz) void IRsend::sendRC5(unsigned long data, int nbits) void IRsend::sendRC6(unsigned long data, int nbits) void IRsend::sendPanasonic(unsigned int address, unsigned long data) void IRsend::sendJVC(unsigned long data, int nbits, int repeat) void IRsend::sendSAMSUNG(unsigned long data, int nbits)
Практически, на «все случаи жизни»…
Рассмотрим наиболее универсальную процедуру sendRaw()
void IRsend::sendRaw(unsigned int buf[], int len, int hz) { enableIROut(hz); for (int i = 0; i < len; i++) { if (i & 1) { space(buf[i]); } else { mark(buf[i]); } } space(0); // Just to be sure }
Сначала, собственно, включается режим передачи ИК: процедура enableIROut(hz) включает ШИМ на выводе 3 (используется таймер 2) — тем самым осуществляется модуляция сигнала с заданной (hz — частота модуляции в килогерцах) частотой. Затем из буфера buf[] выбираются значения длительностей импульсов (в микросекундах) и четные значения передаются импульсом (mark() заданной в массиве длины), а нечетные — паузой (space() так же заданной длины). Таким образом, создав массив buf длиной len, можно передать практически любой код через импровизированный ИК-передатчик.
Процедуры mark(), space() и enableIROut() доступны для использования в скетче, так что можно реализовать передачу любого «самодельного» кода. При работе с библиотекой надо помнить, что таймер 2 — ЗАНЯТ и прерывания от него либо блокируются, либо используются «не нами». И с выводом 3 надо быть аккуратным…
void IRsend::mark(int time) { // Sends an IR mark for the specified number of microseconds. // The mark output is modulated at the PWM frequency. TIMER_ENABLE_PWM; // Enable pin 3 PWM output if (time > 0) delayMicroseconds(time); } /* Leave pin off for time (given in microseconds) */ void IRsend::space(int time) { // Sends an IR space for the specified number of microseconds. // A space is no output, so the PWM output is disabled. TIMER_DISABLE_PWM; // Disable pin 3 PWM output if (time > 0) delayMicroseconds(time); } void IRsend::enableIROut(int khz) { // Enables IR output. The khz value controls the modulation frequency in kilohertz. // The IR output will be on pin 3 (OC2B). // This routine is designed for 36-40KHz; if you use it for other values, it's up to you // to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.) // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B // controlling the duty cycle. // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A) // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin. // Disable the Timer2 Interrupt (which is used for receiving IR) TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt pinMode(TIMER_PWM_PIN, OUTPUT); digitalWrite(TIMER_PWM_PIN, LOW); // When not sending PWM, we want it low // COM2A = 00: disconnect OC2A // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted // WGM2 = 101: phase-correct PWM with OCRA as top // CS2 = 000: no prescaling // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A. TIMER_CONFIG_KHZ(khz); }