Пара копеечных модулей на 433 МГц и два восьминогих микроконтроллера превращаются в полноценный пульт дистанционного управления по радио — без ИК-луча, без прямой видимости и без единой микросхемы кодера-декодера.
Что представляет собой конструкция
Проект показывает, как на дешёвых передающем и приёмном радиомодулях диапазона 433 МГц собрать беспроводную систему дистанционного управления на пять каналов. Всю работу по формированию и разбору посылки берут на себя два микроконтроллера PIC12F1822 — по одному в передатчике и в приёмнике. Модули стыкуются практически с любым микроконтроллером: у них минимум выводов и никакой внутренней логики, они просто переносят логические уровни с цифрового вывода одного контроллера на вход другого.
Список компонентов предельно короткий:
- микроконтроллер PIC12F1822 (два экземпляра — в передатчике и приёмнике);
- передающий радиомодуль 433 МГц или 315 МГц;
- приёмный радиомодуль 433 МГц или 315 МГц.
Модули выпускаются и на 433 МГц, и на 315 МГц — схема и программы от этого не меняются, важно лишь, чтобы передатчик и приёмник были на одну частоту.
Протокол обмена: NEC поверх радиоканала
Данные от передатчика к приёмнику идут по протоколу NEC — тому самому, что применяется в инфракрасных пультах. Здесь он работает не по ИК-лучу, а по радиоканалу, но кодирование остаётся прежним.
NEC использует кодирование битов длительностью паузы (pulse distance encoding). Элементарный импульс всегда один и тот же — 562,5 мкс, а различаются интервалы между импульсами:
- Логический 0 — импульс 562,5 мкс, затем пауза 562,5 мкс; полное время передачи бита 1125 мкс (562,5 x 2);
- Логическая 1 — импульс 562,5 мкс, затем пауза 1687,5 мкс (562,5 x 3); полное время передачи бита 2250 мкс (562,5 x 4).
Именно поэтому приёмнику достаточно измерить длительность паузы после импульса, чтобы понять, какой бит пришёл, — сравнение с порогом решает всё.
Передатчик: схема и программа
В передающей части пять кнопок, каждая отправляет в эфир свой код. Кнопки подключены к выводам RA0, RA1, RA2, RA3 и RA4, поэтому эти линии конфигурируются как входы, а RA5 — как выход, с него сигнал идёт на вход данных радиомодуля. Внешние резисторы подтяжки не нужны: включаются внутренние подтягивающие резисторы PIC12F1822 строкой port_a_pullups(0x1F);. Тактируется контроллер от внутреннего генератора.
Пяти кнопкам соответствуют коды: 0x40BF00FF — 0x40BF807F — 0x40BF40BF — 0x40BF20DF — 0x40BFA05F.
Единицы и нули выдаются в эфир простым переключением вывода: передача единичного уровня — output_high(PIN_A5);, нулевого — output_low(PIN_A5);. Посылка начинается со стартовой преамбулы: импульс 9 мс и пауза 4,5 мс, которые формируются так:
// Send 9ms pulse
output_high(PIN_A5);
delay_ms(9);
// Send 4.5ms space
output_low(PIN_A5);
delay_us(4500);
Далее идут 32 бита данных, начиная со старшего, и завершающий (стоповый) бит. В коде для единицы выдерживается пауза 1680 мкс, для нуля — 560 мкс, импульс в обоих случаях 560 мкс.
Полный текст программы передатчика на CCS C:
// NEC protocol 433MHz RF remote control transmitter using PIC12F1822 CCS C code
#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#use delay(clock=32000000)
#use fast_io(A)
void send_signal(unsigned int32 number){
int8 i;
// Send 9ms pulse
output_high(PIN_A5);
delay_ms(9);
// Send 4.5ms space
output_low(PIN_A5);
delay_us(4500);
// Send data
for(i = 0; i < 32; i++){
// If bit is 1 send 560us pulse and 1680us space
if(bit_test(number, 31 - i)){
output_high(PIN_A5);
delay_us(560);
output_low(PIN_A5);
delay_us(1680);
}
// If bit is 0 send 560us pulse and 560us space
else{
output_high(PIN_A5);
delay_us(560);
output_low(PIN_A5);
delay_us(560);
}
}
// Send end bit
output_high(PIN_A5);
delay_us(560);
output_low(PIN_A5);
delay_us(560);
}
void main(){
setup_oscillator(OSC_8MHZ | OSC_PLL_ON); // Set internal oscillator to 32MHz (8MHz and PLL)
output_a(0);
set_tris_a(0x1F); // Configure RA5 pin as output and others as inputs
port_a_pullups(0x1F); // Enable internal pull-ups for pins RA0,RA1,RA2,RA3 & RA4
while(TRUE){
while(!input(PIN_A0)){
send_signal(0x40BF00FF);
delay_ms(500);
}
while(!input(PIN_A1)){
send_signal(0x40BF807F);
delay_ms(500);
}
while(!input(PIN_A2)){
send_signal(0x40BF40BF);
delay_ms(500);
}
while(!input(PIN_A3)){
send_signal(0x40BF20DF);
delay_ms(500);
}
while(!input(PIN_A4)){
send_signal(0x40BFA05F);
delay_ms(500);
}
}
}
Пока кнопка удерживается, посылка повторяется с интервалом 500 мс — это задержка delay_ms(500); внутри цикла ожидания отпускания.
Приёмник: схема и программа
В приёмной части к микроконтроллеру подключены пять светодиодов и приёмный радиомодуль; выход данных модуля заведён на вывод RA3 (в программе он объявлен как RF_Receiver). Каждый светодиод управляется своей кнопкой передатчика. Контроллер тоже работает от внутреннего генератора, а вывод MCLR сконфигурирован как обычный цифровой вход — это задаёт фьюз NOMCLR.
Для измерения длительности импульсов и пауз используется Timer1, настроенный так, чтобы инкрементироваться каждую микросекунду при тактовой частоте 32 МГц:
SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8);
Приём начинается с проверки стартовой преамбулы. Программа обнуляет таймер, ждёт, пока на входе держится высокий уровень, и затем проверяет, попало ли измеренное время в допустимое окно. Если нет — посылка бракуется, функция возвращает FALSE:
// Check 9ms pulse (remote control sends logic high)
SET_TIMER1(0);
while(input(IR_Sensor) && (count < 9500))
count = GET_TIMER1();
if( (count > 9499) || (count < 8500))
return FALSE;
Аналогично проверяется пауза 4,5 мс — с окном от 4000 до 4999 мкс:
// Check 4.5ms space (remote control sends logic low)
SET_TIMER1(0);
count = 0;
while((!input(IR_Sensor)) && (count < 5000))
count = GET_TIMER1();
if( (count > 4999) || (count < 4000))
return FALSE;
Дальше в цикле читаются 32 бита: сначала контролируется импульс (допустимое окно 500…649 мкс), затем измеряется пауза. Решение о значении бита принимается по порогу 1 мс: если пауза длиннее — записывается единица, если короче — ноль. Собранный код накапливается в переменной rf_code, начиная со старшего разряда.
Полный текст программы приёмника на CCS C:
// NEC protocol 433MHz RF remote control receiver using PIC12F1822 CCS C code
#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#use delay(clock=32000000)
#use fast_io(A)
#define RF_Receiver PIN_A3
unsigned int32 rf_code;
short nec_remote_read(){
unsigned int16 count = 0;
unsigned int8 i;
// Check 9ms pulse (RF remote control sends logic high)
SET_TIMER1(0);
while(input(RF_Receiver) && (count < 9500))
count = GET_TIMER1();
if( (count > 9499) || (count < 8500))
return FALSE;
// Check 4.5ms space (RF remote control sends logic low)
SET_TIMER1(0);
count = 0;
while((!input(RF_Receiver)) && (count < 5000))
count = GET_TIMER1();
if( (count > 4999) || (count < 4000))
return FALSE;
// Read RF message (32 bits)
for(i = 0; i < 32; i++){
SET_TIMER1(0);
count = 0;
while(input(RF_Receiver) && (count < 650))
count = GET_TIMER1();
if( (count > 649) || (count < 500))
return FALSE;
count = 0;
SET_TIMER1(0);
while((!input(RF_Receiver)) && (count < 1800))
count = GET_TIMER1();
if( (count > 1799) || (count < 400))
return FALSE;
if(count > 1000) // If space width > 1ms
bit_set(rf_code, (31 - i)); // Write 1 to bit (31 - i)
else // If space width < 1ms
bit_clear(rf_code, (31 - i)); // Write 0 to bit (31 - i)
}
return TRUE;
}
void main(){
setup_oscillator(OSC_8MHZ | OSC_PLL_ON); // Set internal oscillator to 32MHz (8MHz and PLL)
output_a(0);
set_tris_a(8);
SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8);
while(TRUE){
while(!input(RF_Receiver)); // Wait until RF_Receiver pin raises
if(nec_remote_read()){
if(rf_code == 0x40BF00FF)
output_toggle(PIN_A0);
if(rf_code == 0x40BF807F)
output_toggle(PIN_A1);
if(rf_code == 0x40BF40BF)
output_toggle(PIN_A2);
if(rf_code == 0x40BF20DF)
output_toggle(PIN_A4);
if(rf_code == 0x40BFA05F)
output_toggle(PIN_A5);
}
}
}
Основной цикл ждёт появления высокого уровня на входе RF_Receiver и вызывает функцию разбора посылки. Если код принят без ошибок, он сравнивается с пятью известными значениями, и соответствующий вывод переключается командой output_toggle: нажатие кнопки на передатчике меняет состояние светодиода на противоположное. Обратите внимание, что вывод RA3 занят приёмным модулем (его направление задаёт set_tris_a(8);), поэтому светодиоды сидят на RA0, RA1, RA2, RA4 и RA5.