TinyRF – радиоуправление и обмен данными на ATttiny13

TinyRF - радиоуправление и обмен данными на ATttiny13 Самолеты

Ev1527

— это один из самых популярных аппаратных кодеров/декодеров для использования в различных радиоуправляемых устройствах (розетках, лампах, воротах, шлагбаумах). Интересен он в первую очередь тем, что использует очень простой ШИМ-подобный метод кодирования информации, заключающийся в том, что логические нули и единицы в сигнале кодируются разным соотношением длительности импульса и тишины:

TinyRF - радиоуправление и обмен данными на ATttiny13

Единичный бит кодируется последовательностью {3; 1}, т.е. сначала в течение 3-х некоторых отсчетов времени предается высокий уровень, а затем 1 отсчет времени — низкий. Нулевой бит имеет обратные значения {1; 3} — 1 высокий импульс и 3 низких. Длина единичного отсчета времени (импульса) у EV1527 составляет 350 микросекунд. Кроме того, перед данными передается особая преамбула, имеющая формулу {1; 31}:

TinyRF - радиоуправление и обмен данными на ATttiny13

Она позволяет приемнику отследить начало передачи данных и начать их принимать. В зависимости от реализации преамбула может передаваться как в начале, так и в конце посылки. Это не играет большой разницы, так как в целях повышения надежности посылки с сообщениями отправляются многократно (10 и более раз подряд). Кодированная данным протоколом единичная посылка из 8 бит двоичного числа 10101010 выглядит как-то так:

TinyRF - радиоуправление и обмен данными на ATttiny13

В стандартном исполнении посылки EV1527 имеют длину 3 байта (24 бит), но что мешает нам в личных целях использовать любой размер передаваемых данных? В идеальных условиях — ничто не мешает, но в реальности слишком длинные сообщения данным методом передавать не выйдет из-за помех и отсутствия какого-либо устранения ошибок.

) поэтому я решил реализовать именно его. Существует множество разновидностей таких протоколов, есть даже трехуровневые (tri-state) вариации, где бит помимо стандартных «0» и «1» может принимать третье значение «F» и называется

, но я реализовал только самый простой вариант — двоичный.

Вселенная на проводе

Как передать цифровой сигнал, состоящий из нулей и единиц, из точки A в точку B? Кажущаяся тривиальной задача на самом деле не является таковой, даже в случае передачи данных по физическому проводу. Если мы просто подадим обычный («сырой») цифровой сигнал на линию связи, то приемник на другом конце не сможет различать между собой отдельные биты в длинных последовательностях нулей и единиц. В проводных линиях связи самый простой способ решения проблемы — передача тактирующего сигнала по отдельному каналу:

TinyRF - радиоуправление и обмен данными на ATttiny13

Приемник читает значение очередного бита с линии данных только в момент получения тактового импульса, который генерируется передатчиком примерно в середине каждого бита, таким образом отпадает проблема синхронизации между передатчиком и приемником.

Но как быть, если провод для передачи данных один единственный? Для этого придумали различные методы кодирования сигнала, наиболее известным из которых является манчестерское кодирование:
TinyRF - радиоуправление и обмен данными на ATttiny13
Манчестерский код довольно прост в понимании и реализации: в нем нулевые и единичные биты исходных данных кодируются направлением изменения сигнала (0-1 или 1-0) в середине каждого бита. Тем не менее есть еще более простые способы передачи данных, особенно если не столь важна скорость.

Смотрите про коптеры:  Радиоуправляемые самолеты для начинающих

Пример №1 — управляем светодиодами

. Пример по сути представляет собой реализацию радиореле на 4 канала: передатчик последовательно передает 8 различных команд (4 на включение и 4 на выключение) с интервалом по 0.5 секунды между ними, приемник принимает и зажигает/гасит светодиоды, которых у него 4.

Схема приемника:
TinyRF - радиоуправление и обмен данными на ATttiny13
Для тестов я собрал его из подручных материалов на обрезке макетки:
TinyRF - радиоуправление и обмен данными на ATttiny13
TinyRF - радиоуправление и обмен данными на ATttiny13
Для увеличения дальности следует припаять к приемнику обрезок провода длиной 17.3 см. Скомпилированный с оптимизацией по размеру пример приемника занимает 636 байт (62% памяти микроконтроллера), причем довольно много весит сама логика проверки сообщений. Тем не менее, еще остается достаточно места на добавление каких-либо действий.Схема передатчика:
TinyRF - радиоуправление и обмен данными на ATttiny13
В качестве антенны — такой же кусок провода:
TinyRF - радиоуправление и обмен данными на ATttiny13
TinyRF - радиоуправление и обмен данными на ATttiny13
Пример работы:
TinyRF - радиоуправление и обмен данными на ATttiny13
Передатчик весит заметно скромнее — всего 286 байт (28%), что дает пространство для маневра и добавления целой кучи своей логики. Дальность уверенной связи — около 20 метров по прямой или в пределах одной комнаты, в первую очередь из-за шумности приемника. Кроме того, как выяснилось, приемник работает нормально только в узком диапазоне напряжений питания (4-5В), в остальных случаях безбожно шумит и не может обеспечить нормальный сигнал на выходе даже при передаче «в упор».

Библиотека совместима с некоторыми другими микроконтроллерами из серии ATtiny, один из них — ATtiny85, который известен тем, что используется в Arduino-совместимой плате Digispark. Это сильно упрощает эксперименты, потому что его можно прошить через обычный USB порт из Arduino IDE без всяких программаторов.

Я портировал пример мигания светодиодами в скетч для заливки в Digispark с той лишь разницей, что вместо управления 4-мя внешними диодами в нем переключается только 1 светодиод, встроенный в Digispark (висит на пине PB1). Важно отметить, что в скетчах не нужно указывать частоту в F_CPU, потому что Arduino IDE подставляет ее сама при компиляции.

Схема приемника и передатчика:
TinyRF - радиоуправление и обмен данными на ATttiny13
Пример работы:
TinyRF - радиоуправление и обмен данными на ATttiny13
В приведенном примере представлен простой и немного избыточный, но действенный способ проверки целостности коротких сообщений. Фактически размер команды — 1 байт, вторым байтом в сообщении отправляется инвертированное значение первого:

{ 11, (uint8_t) ~11 }

Приемник предварительно проверяет корректность полученной команды, пытаясь сравнить на равенство:

if (data_buffer[0] == (uint8_t)(~data_buffer[1])) {...}

В случае ошибок при приеме равенство не выполняется, и команда игнорируется. Такой способ проверки достаточно надежен, я поэкспериментировал и не заметил ложных срабатываний в течение нескольких дней непрерывной работы, но в реальных ответственных сценариях использования стоит усилить защиту дополнительными проверками.

Пример №2 — приемник и передатчик в одном флаконе

. Библиотека довольно компактная и построена таким образом, что использует одни и те же настройки таймера как для приема, так и для передачи сообщений, плюс на время передачи сообщений прерывания по портам отключаются. Благодаря этому коды приема и передачи сообщений можно уместить на одном микроконтроллере, главное подключить приемник и передатчик к разным пинам. Следующий пример демонстрирует простейший эхо-репитер и работает следующим образом:

Смотрите про коптеры:  Квадрокоптер WLToys Q303 (обзор, видео, фото), цена 4600 руб

Схема:

TinyRF - радиоуправление и обмен данными на ATttiny13

Скомпилированный код весит всего 576 байт (56% памяти ATtiny13), что даже меньше, чем в примере управления светодиодами. Как вы догадываетесь, с помощью этого репитера можно увеличить дальность работы предыдущего примера, если разместить его между приемником и передатчиком (предварительно уменьшив время задержки до 250-500 мс).

Версия для Digispark:
TinyRF - радиоуправление и обмен данными на ATttiny13
Чтобы сделать пример более осязаемым предлагаю на примере скетча для Digispark немного видоизменить код повторителя, сделав из него «инвертор»: при получении команды на включение светодиода (11, 22, 33, 44) он будет с задержкой 250 мс передавать в эфир обратную ей команду на выключение (55, 66, 77, 88):

#define TRF_RX_PIN          PB0 // Приемник на PB0
#define TRF_TX_PIN          PB1 // Передатчик на PB1
#define TRF_DATA_SIZE       2   // 2-байтовая команда

#include "tinyrf.h"
#include <util/delay.h>

// Команды включения
byte commands[4] = {11, 22, 33, 44};
// Обратные команды выключения
byte inv_commands[4][TRF_DATA_SIZE] = {
  { 55, (byte) ~55 },
  { 66, (byte) ~66 },
  { 77, (byte) ~77 },
  { 88, (byte) ~88 }
};

void setup() {
  trf_init();
}

void loop() {
  // Получена новая команда
  if (trf_has_received_data()) {
    // Извлекаем данные
    byte data[TRF_DATA_SIZE];
    trf_get_received_data(data);

    // Проверяем корректность
    if (data[0] == (byte)(~data[1])) {
      // Ищем обратную команду и отправляем
      for (byte i = 0; i < 4; i  ) {
        if (commands[i] == data[0]) {
          _delay_ms(250);
          trf_send(inv_commands[i]);
        }
      }
    }

    // Разрешаем прием следующей команды
    trf_reset_received();
  }
}

И если раньше переключение светодиодов происходило так:
TinyRF - радиоуправление и обмен данными на ATttiny13
То со включенным находящимся рядом «инвертирующим повторителем» оно происходит так:
TinyRF - радиоуправление и обмен данными на ATttiny13
Т.е. повторитель принимает «включающие» команды одновременно с основным приемником и с небольшой задержкой перебивает их своими «выключающими».

Примеры использования

От примеров теоретических предлагаю перейти к примерам практическим. В первую очередь помимо микроконтроллеров нам понадобятся передатчики и приемники, я взял самые дешевые на 433.92 МГц:

TinyRF - радиоуправление и обмен данными на ATttiny13

Купить сразу пару можно

. Это самые примитивные и дешевые модули, поэтому ждать от них каких-либо выдающихся характеристик не приходится, но для демонстрации вполне сгодятся.

Программная реализация

Благодаря своей примитивности метод кодирования/декодирования легко реализуется программно и занимает очень мало места. Настолько мало, что код приемника и передатчика можно уместить в ATtiny13 с 1024 байтами памяти, и еще останется место для какой-нибудь полезной работы (!) Я не буду подробно расписывать код, он довольно короткий и хорошо задокументирован. Исходники и примеры доступны

и распространяются под MIT лицензией. Как вы догадываетесь, вся библиотека — это один лишь заголовочный файл

Процесс отправки и приема сообщений завязан на аппаратном таймере (у ATtiny13 он один единственный), вычисление настроек таймера выполняется на этапе компиляции с учетом тактовой частоты микроконтроллера, поэтому в проекте обязательно должна быть объявлена директива F_CPU с указанием тактовой частоты в герцах (например, 1.

Смотрите про коптеры:  Радиоуправляемый самолет на ардуино » Изобретения и самоделки

Таймер (а точнее тактовый генератор) у ATtiny может быть очень кривым, поэтому при каких-либо проблемах с приемом/передачей в первую очередь необходимо попробовать подкрутить F_CPU, а в идеале — откалибровать генератор. С этим я столкнулся, когда проверял правильность отсчета времени таймером. Так по мнению ATtiny13, работающего на частоте 4.8МГц, выглядит меандр с шириной импульса 350 микросекунд:
TinyRF - радиоуправление и обмен данными на ATttiny13
Поначалу я грешил на вычислительные затраты, но при понижении до частоты 1.2 МГц ситуация наоборот улучшилась:
TinyRF - радиоуправление и обмен данными на ATttiny13
Разгадка оказалась проста — у многих версий ATtiny13 внутри стоят 2 разных тактовых генератора: на 9.6 МГц и 4.8 МГц, причем первый более-менее откалиброван с завода, а второй — как получится. Частоты 1.2 МГц и 0.6 МГц соответственно получаются с помощью деления на 8, поэтому погрешности сохраняются. Тем не менее, как показали эксперименты, разница в 50мкс оказалась несущественной, поэтому прием/передача практически всегда работают нормально без лишних калибровок и настроек.

Обобщенный пример передачи сообщений:

// Шаг 1: задаем параметры

#define F_CPU           1200000UL   // Тактовая частота МК в Гц
#define TRF_TX_PIN      PB1         // Пин, к которому подключен передатчик (если не указано - используется PB0)
#define TRF_DATA_SIZE   2           // Размер сообщения в байтах (если не указано - используются 3 байта)
#define TRF_RX_DISABLED             // Исключить код приемника для экономии места

// Шаг 2: подключаем библиотеку

#include "tinyrf.h"

// Шаг 3: вызываем инициализацию
// Если ваш код не меняет настройки прерываний и таймера, то вызвать инициализацию можно один раз при запуске
// иначе следует вызывать инициализацию каждый раз перед тем, как хотим отправить сообщение

trf_init();

// Шаг 4: готовим сообщение и отправляем его

uint8_t message[TRF_DATA_SIZE] = { 123, 234 };
trf_send(message);

Обобщенный пример приема сообщений:

// Шаг 1: задаем параметры

#define F_CPU           1200000UL   // Тактовая частота МК в Гц
#define TRF_RX_PIN      PB0         // Пин, к которому подключен приемник (если не указано - используется PB1)
#define TRF_DATA_SIZE   2           // Размер сообщения в байтах (если не указано - используются 3 байта)
#define TRF_TX_DISABLED             // Исключить код передатчика для экономии места

// Шаг 2: подключаем библиотеку

#include "tinyrf.h"

// Шаг 3: вызываем инициализацию
// Если ваш код не меняет настройки прерываний и таймера, то вызвать инициализацию можно один раз при запуске
// иначе следует вызывать инициализацию каждый раз перед тем, как хотим начать принимать сообщения

trf_init();

// Шаг 4: проверка наличия нового сообщения

if (trf_has_received_data()) {

    // Шаг 5: подготовка буфера и чтение в него сообщения

    uint8_t data_buffer[TRF_DATA_SIZE];
    trf_get_received_data(data_buffer);

    // Шаг 6: проверка корректности полученного сообщения и выполнение требуемых действий
    if (data_buffer[0] == 123 && data_buffer[1] == 234) {
        ...
    }

    // Шаг 7: сброс флага наличия сообщения для приема следующего

    trf_reset_received();
}

Как видно, некоторые параметры можно конфигурировать с помощью директив, можно даже частично перенастроить сам протокол. Полный список доступных директив можно найти здесь

Оцените статью
Радиокоптер.ру
Добавить комментарий

Adblock
detector