Драйвера радиомодулей (приёмник)
Приёмник это устройство, на выводе которого появляется напряжение, если антенна что-то выловила на резонансной частоте. Если передаётся человеческий голос, то напряжение будет пропадать и появляться с частотой человеческого голоса. Если провод с так скачущим напряжением положить на магнит, то и сам провод начнёт прыгать с частотой звуковой волны.
В случае же цифровой передачи едёт включение сигнала на какое-то время а потом его выключение. Провод с этим включающимся и выключающимся сигналом подключается к какому-то пину микроконтроллера. И микроконтроллер должен как-то реагировать на эти включения и выключения. Микроконтроллер, как и компьютер «научен» получать сигналы от периферических устройств и как-то на них реагировать. Получение таких сигналов (об изменении состояний, например, с «выключено» на «включено») называется прерываниями. А действия, которые выполняет после этого компьютер – программами – обработчиками прерываний.
В своё время меня очень озадачил этот термин «прерывание», но пришлось привыкнуть. Видимо, имелось в виду, что если что-то такое случилось, то компьютер должен прерваться на какую-то реакцию на возникшую ситуацию.
Обработчики прерываний можно писать вручную. В компьютерах это делают редко, потому что программы – обработчики – часть уже написанной операционной системы, установленной на компьютер. А микроконтроллеры это маленькие компьютеры без понятных функций. К ним можно подключить что угодно, но и самостоятельно надо писать, как реагировать на поведение подключенного устройства.
При подключении передатчика надо написать такое «заклинание»:
mySwitch.enableReceive(0); // Receiver on inerrupt 0 => that is pin #2
Русский перевод комментария: «Приёмник на прерывание 0. что соответствует пину №2».
При этом последовательно запускаются две функции:
void RCSwitch::enableReceive(int interrupt) {
this->nReceiverInterrupt = interrupt;
this->enableReceive();
}
void RCSwitch::enableReceive() {
if (this->nReceiverInterrupt != -1) {
RCSwitch::nReceivedValue = NULL;
RCSwitch::nReceivedBitlength = NULL;
attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE);
}
}
Внутренней переменной nReceiverInterrupt присваивается номер пина. А потом функцией attachInterrupt к событию изменение состояния CHANGE пина с номером this->nReceiverInterrupt привязываемся обработчик прерывания handleInterrupt.
Теперь при каждом появлении и пропадании сигнала от передатчика (при каждом CHANGE) будет вызываться функция с именем handleInterrupt. Вот её начало и конец:
void RCSwitch::handleInterrupt() {
static unsigned int duration;
static unsigned int changeCount;
static unsigned long lastTime;
static unsigned int repeatCount;
long time = micros();
duration = time - lastTime;
if (duration > 5000 && duration > RCSwitch::timings[0] - 200 &&
duration < RCSwitch::timings[0] + 200) {
repeatCount++;
changeCount--;
if (repeatCount == 2) {
if (receiveProtocol1(changeCount) == false){
if (receiveProtocol2(changeCount) == false){
if (receiveProtocol3(changeCount) == false){
//failed
}
}
}
repeatCount = 0;
}
…
ELSE {
RCSwitch::timings[changeCount++] = duration;
lastTime = time;
}
Первое, что делает эта функция – узнает текущее время в микросекундах, сравнивает с временем предыдущего своего запуска и узнаёт, сколько времени длился период включенного или выключенного состояния. Если прошло достаточно много времени, все считанное передаётся функциям receiveProtocolX(), которые и будут определять похоже это на переданное значение или нет.
Если времени прошло мало, то в массив временных промежутков timings[] записывается следующий временной промежуток.
Все функции проверки соответствия протоколу одинаковы. Для первого протокола (начало функции):
bool RCSwitch::receiveProtocol1(unsigned int changeCount){
unsigned long code = 0;
unsigned long delay = 350;
unsigned long delayTolerance = 350 * 60 * 0.01;
for (int i = 1; i<changeCount ; i=i+2) {
if ( // Проверка “0” this->transmit(1,3);
RCSwitch::timings[i] > delay-delayTolerance // Сигнал д.б = delay
&& RCSwitch::timings[i] < delay+delayTolerance
&& RCSwitch::timings[i+1] > delay*3-delayTolerance // Пауза д.б = 3* delay
&& RCSwitch::timings[i+1] < delay*3+delayTolerance)
{
code = code << 1;
}
else if ( ( // Проверка “1” this->transmit(3,1);
RCSwitch::timings[i] > delay*3-delayTolerance // Сигнал д.б = 3*delay
&& RCSwitch::timings[i] < delay*3+delayTolerance
&& RCSwitch::timings[i+1] > delay-delayTolerance // Пауза д.б = delay
&& RCSwitch::timings[i+1] < delay+delayTolerance)
{
code+=1;
code = code << 1;
}
else {
// Failed - Ошибка
i = changeCount;
code = 0;
}
}
…
delay – базовая длина, delayTolerance – допустимое отклонение длины сигнала. А дальше идёт проверка всей полученной последовательности на соответствие этой длины и этого отклонения. Если что-то вылезло за диапазон, последовательность считается ошибкой.
Просто для иллюстрации, я отправляла значения 1 (0000 0001) и 2 (0000 0010) и смотрела какие приходили последовательности. Вот такие, например:
7-й бит (=1) в значении «2» все-таки уложился в диапазон, потому двойка опознана.
Сразу, кстати, видно максимальное количество информации на этом дисплее (Поворот картинки дисплея на 180 градусов - display.setRotation(2);)
А это – единица:
… информация о 8-м бите которой на экране уже не поместилась. Но видимо она тоже попала в нужный диапазон.
Что мне осталось добавить? Передатчик всегда отправляет сигнал по одному проводу, и приёмник тоже получает по одному проводу. От передатчика требуется передавать на нужной частоте, пока он включен, а от приёмника довить свою резонансную частоту. Описанный драйвер подойдёт для любых устройств, которые выполняют эти функции. А если приёмник ещё сам будет хоть как-то чистить сигнал от шумов, может и не придётся делать 10 отправок.
Но вернусь к этому модулю:
https://amperkot.ru/spb/catalog/radiomodul_nrf24l01_pa__lna_s_antennoy-24118355.html
Протухли они за полгода. Не работают. Есть у меня какое-то смутное воспоминаете о том, что год назад полечило последовательное подключение всех пинов к земле, но так это не должно работать.
Почему я думаю, что производители знают, что продают заведомый брак? Потому, что протокол SPI никак не соответствует нуждам такого устройства. Этот протокол не имеет к радиопередаче никакого отношения. По нему передаются данные от модуля микроконтроллеру. Но тут нет ни больших объемов данных ни даже одновременного приёма с передачей (приемник выключается, передача включается и наоборот). А тут даже не SPI. Тут ещё минимум один провод подключить надо.
Единственное, зачем это все может быть надо – чтобы спрятать за всем этим избыточным барахлом неработающий модуль. Или быстро ломающийся. Или очень нестабильный. Уже не важно.
|