- Лоботрясы

Поиск
Перейти к контенту

Главное меню:

Датчики и модули

Читать в Яндекс.Подписках



    Радиомодули RF. Часть 2 - Первые опыты
Просмотров: 5427
     Со времени публикации первой части статьи я уже успел вдоволь наиграться с RF-радиомодулями, и теперь спешу поделиться своими результатами :-D
     Дабы не изобретать велосипед и не наступать на грабли, набившие лоб не одному радиолюбителю, я предварительно ознакомился с особенностями сопряжения приёмника и передатчика, связанными (эти особенности) с низкой помехоустойчивостью и наличием хаотичного сигнала на выходе приёмника в отсутствие приёма данных.
     Хотя на форумах многие и склоняются к использованию манчестерского кодирования, как самого оптимального для передачи данных через радиомодули, я решил для начала попробовать связать их посредством обычного UART. Если бы не вышло, тогда начал бы думать над более сложным протоколом. Естественно, мимо меня не прошла и библиотека VirtualWire – стандарт де-факто для связи RF-радиомодулей для Ардуины. Данная библиотека, разработанная неким Mike McCauley (не русский, походу…) предлагает связь приёмника и передатчика на основе программной реализации UART, причём в примере скорость связи составляет какие-то магические 2000 попугаев (бит за секунду), что явно не соответствует стандартной скорости UART (кто не помнит, это: …600, 1 200, 2 400, 4 800…) Дело тут вот в чём. Для того, чтобы при передаче (и приёме) сигнала передатчик и приёмник не теряли между собой уверенную связь, нужно добиться определённого баланса между чередой логических нулей и единиц (много логических 0 подряд это плохо – приёмник подумает, что мы уже ничего не передаём, и начинает выдавать всякий шум. И много логических 1 это тоже плохо – падает чувствительность приёмника… Т.е., иными словами, происходит рассинхронизация приёмника с передатчиком.). Поэтому автор решил преобразовывать полубайт (ниббл) полезного сигнала так, чтобы получить равный баланс логических «1» и «0».  Для этого каждый полубайт он увеличивает с 4-х до 6, используя специальную таблицу перекодировки, и уже закодированный байт содержит, как не сложно догадаться, 12 бит. И передаёт всё энто дело с определённой скоростью на вход передатчика. Приёмное устройство занимается обратной перекодировкой и получением исходного байта (байтов) полезных данных.
     Идея хорошая, и по отзывам разработчика обеспечивает надёжную связь между приёмником и передатчиком. Аналогично и манчестерское кодирование обеспечивает постоянную смену логического уровня сигнала и уверенный приём данных.
     Тем не менее, я решил проверить, насколько всё плохо при использовании радиомодулей в качестве обычного радиоудлинителя UART.
     Я не стал пока придумывать хитрую структуру сохранения баланса логических единиц и нулей, обеспечивающих стабильную работу сверхрегенеративного приёмника, однако я перенял идею подачи начальной преамбулы из нескольких 0x55 (0b01010101 – т.е. обычное чередование нулей и единиц) для устойчивой подстройки приёмника под частоту передатчика. Также перенял с VirtualWire и стартовую последовательность 0xb38, по наличию которой микроконтроллер приёмника начинает принимать (записывать) полезные данные с передатчика. Рассматривая в качестве подопытного кролика пульт с несколькими командами управления, я не стал заморачиваться с проверкой контрольной суммы переданного кода, просто наверняка обеспечив его (кода) передачу несколько раз подряд. Пяти раз хватает с головой :-D
 
     В общем, структура передачи данных у меня приняла такой вид:
4 байта 0x55 – преамбула для получения устойчивой связи (подстройки) приёмника и передатчика;
0x5b38 – стартовое слово, сигнализирующее микроконтроллеру приёмника о начале приёма данных;
ADDRESS_ID – индивидуальный байт адреса приёмника (у меня предполагается наличие нескольких приёмников, управляемых одним передатчиком);
LENGHT  – один байт, показывающий длину передаваемого сообщения (кода) плюс этот же байт;
DATA – от одного до восьми байт данных.
 
     Опыты решил проводить на «своих в доску» AVR-ках. 
     Сначала о передатчике. Его собрал на ATtiny13 (коих у меня лежат около 40 штук – старые запасы :-)
     Схема – отряд простейшие (амёба, инфузория-туфелька…)
     Т.к. в этой букашке аппаратный UART отсутствует, то пришлось его сделать программным. В этом мне помог апноут AVR274 от ATmel. Вкратце – для формирования необходимых интервалов времени для передачи бит данных, используется таймер в режиме СТС – Clear Timer on Compare (счёт от нуля до значения ТОР (OCR0) с последующим прерыванием и обнулением счётного регистра). И чтобы получить нужную скорость (Baud Rate) интерфейса UART, необходимо записать в регистр сравнения (значение ТОР, до которого будет происходить счёт) результат расчёта данной формулы:
 
System Clock / (Baud Rate * Timer Prescaler) – 1
 
где System Clock – частота кварца;
     Timer Prescaler – коэффициент деления предделителя таймера.

     Тактовую частоту микроконтроллера я использовал 9,6 МГц, без внутреннего деления на 8.
     Фрейм UART состоит из 8 бит данных, 1 стартового бита и 1 стопового бита. Скорость UART я установил 2400 бод. А основные функции программного передатчика получились такие:
 
// Прототипы
void USART_putchar(char data);
void Send_Mess(char adress, char *str);
 
//===============================================================
//                              Режим СТС (Прерывание по совпадению OCR0A)
//===============================================================
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
    if(start_tx)
    {
        if(tx_bit_counter == 0)  // Если все биты переданы - отключаю таймер и устанавливаю на линии TX сигнал логической "1"
        {
            start_tx = 0;
            TX_HIGH;
        }       
        if(tx_bit_counter--)
        {
            if(tx_bufer & 1)    TX_HIGH;   
            else                TX_LOW;
            tx_bufer >>= 1;
        }               
    }   
    BUTTON_Scan();  // Опрос кнопок     
 
//===============================================================
//                                         Отправить по UART байт данных
//===============================================================
void USART_putchar(char data)
{        
    while(start_tx); //Нахожусь здесь, если уже установлен флаг передачи символа по UART (это актуально, если к ф-и происходит частое обращение
    //Формируем передаваемые 10 бит (добавляем стартовый и стоповый биты)
    tx_bufer = ((int)data << 1) | 0x200; //"(data<<1)" - т.е. 0-й бит (стартовый) равен 0, "| 0x200" - стоп бит равен 1
    tx_bit_counter = 10; // Передаю 10 бит  
    start_tx = 1; // Разрешить передачу байта данных
}
 
//===============================================================
//     Отправить по UART информационные данные. Здесь организована отправка преамбулы, 
//     слова синхронизации и самих данных
//===============================================================
void Send_Mess(char adress, char *str)
{
    char x;
    for(x=0; x<4; x++) // Преамбула
    {
        USART_putchar(0x55);
    }
    // Ключевое слово (стартовый символ)
    USART_putchar(0x5b);
    USART_putchar(0x38);
    // Адрес устройства
    USART_putchar(adress);
    // Отправить байт длины сообщения
    USART_putchar(strlen(str)+1);
    // Пошли сами данные
    while(*str)
    {
        USART_putchar(*str++);
    }   
}

     К основной программе подключена также библиотека кнопок, при кратковременном или длительном нажатии на которые в эфир отправляется одно из сообщений: "ab", "cd", "ef" или "gh". Режим сна в этом пульте организован не был, так как ставилась цель просто поиграться :-D
 
     Приёмник был сварганен на основе своей отладочной платы под ATmega32, и в итоге получил такой непритязательный вид:
     При поступлении и распознавании команды микроконтроллер зажигает определённый светодиод. Просто и сердито. Здесь я уже использовал аппаратный UART, на что у меня и была задумка.
     Принятые данные поступают в FIFO, и хотя в нём особой надобности и нет, но мне так удобнее :-)
     Приём и распознавание данных выполняется в обработчике прерывания по приёму символа по UART:
 
//===============================================================
//                                     USART Процедура прерывания по приёму
//===============================================================
interrupt [USART_RXC] void usart_rx_isr(void)
{
    char data;
    static unsigned long int rec_temp_buf=0; // Буфер для обработки принятых байт и синхронизации сигнала
    data = UDR; // Сохраняю принятый символ
    rec_temp_buf <<= 8; // Записываю его в приёмный буфер
    rec_temp_buf |= data;
    if (start_symbol) // Пошёл приём полезных данных
    {
        if(check_address) // Если адрес совпал
        {
            if(!data_count) size_rx_data = data; // Первый байт данных - длина принимаемого сообщения (его не сохраняю в буфере)
            if((size_rx_data >= MIN_LEN_SIZE)&&(size_rx_data <= MAX_LEN_SIZE)) // Если размер полезных данных в допустимых пределах
            {
                if(data_count) // Пропускаю байт длины сообщения (записываю только байты, идущие после него)
                {
                    rx_buffer[rx_write] = data;
                    if (++rx_write == RX_BUFFER_SIZE) rx_write=0;
                    if (++rx_counter == RX_BUFFER_SIZE) { rx_counter=0; rx_write=0; rx_read =0; } // На всякий пожарный
                }
                if(++data_count >= size_rx_data) //Если принято все количество size_rx_data полезных данных
                {
                    res_recieve++; //Строка принята! Если res_recieve больше 1, то скорость приёма данных больше скорости обработки принятой строки!
                    start_symbol = 0;
                    data_count = 0;
                    check_address = 0;
                }
            }
            else
            {
                start_symbol = 0;
                rec_temp_buf = 0;
            }
        }
        // Проверяем адрес (какому устройству предназначена команда :-)
        if((data == ADDRESS_ID_1)&&(!check_address)) check_address = 1;
        else if((data != ADDRESS_ID_1)&&(!check_address))
        {
            start_symbol = 0;
            rec_temp_buf = 0;
        }
    }
    if(!start_symbol) // Ищу символ синхронизации (ключевое слово)
    {
        if ((rec_temp_buf &0xFFF) == 0xb38) // Если 0xb38 найден
        {
            start_symbol = 1;
            data_count = 0;
        }
    }
}
 
     Работоспособность программы проверялась на расстоянии 30 метров (на увеличение расстояния повлияла обычная человеческая лень, не летние погодные условия и пивной катарсис…), и несмотря на центр города и обилие автолюбителей с их сигналками, показала положительный результат. Но это на скорости 2400 бод. На скорости 4800 бод приёмник некоторое время лениво принимал часть команд, а затем отказался подавать какие-либо признаки жизни – похоже сказалось техническое ограничение радиомодулей по скорости передачи :-( По итогам экспериментов, при одноразовой передаче команды приёмник её улавливал где-то в 90…95% случаев (сказывалось отсутствие синхронизации в самом начале передачи из-за "проглоченного" приёмником первого (первых) байта/бита передачи), поэтому пятикратной (дублированной) передачи команды вполне достаточно для уверенного приёма.
     Подводя итоги скажу, что отсутствие баланса между логическими сигналами самого кода данных не особенно сильно повлияло на правильный приём сообщений. Естественно, при передаче большего числа данных, данный показатель может заметно и ухудшиться. Но тем не менее, небольшие сообщения с гарантией их поступления и декодирования приёмником, передавать напрямую через UART можно.


     Скачать:

Опубликовано 27.01.2016
©Igoryosha, 2016
 
 
Назад к содержимому | Назад к главному меню