В статье "Пульт радиоуправления пистолетного типа" я рассказал, как сделал пульт управления для авто- судомоделей. Здесь я немного расскажу о том, какие алгоритмы я заложил в прошивку этого пульта.
Прошивки для приемника и передатчика написаны в Arduino IDE 1.5.5-r2. Никаких сторонних библиотек не входящих в базовую поставку IDE не требуется.
Скачать их исходные коды можно по следующим ссылкам:
Передатчик
Приемник

Биндинг


Режим биндинга для пульта запускается либо из меню, либо специальной кнопкой. Возврат в нормальный режим происходит либо при успешном завершении привязки, либо по кнопке «отмена». При отмене процедуры привязки пульт работает с теми настройками, которые у него были перед запуском биндинга.
В режиме биндинга пульт переключается в режим приема и устанавливает свой адрес в 254.254.254.254. В этом режиме он ожидает специального пакета с приемника. Формат пакета при этом должен быть следующим:

 байт  значение
 0 0xfa
1:4 4 байта адреса приемника


При приеме такого пакета, пульт запоминает адрес и отправляет обратно пакет следующего формата:


 Байт значение
 0  0x5d
 1:4 4 байта адреса приемника (подтверждение)
 5  Идентификатор привязки

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

Режим привязки на приемнике включается установкой перемычки на канал ch7 (по маркировке OrangeRx). Перемычку необходимо установить до включения питания. В этом случае приемник так же как и передатчик устанавливает свой адрес в 254.254.254.254. Потом при помощи генератора случайных чисел получаются 4 байта нового адреса для последующей работы в нормальном режиме. Далее он в цикле кратковременно переключается на передачу информации и отправляет свой свежесозданный адрес пульту. После каждой такой попытки следует пауза ожидания ответа.
При получении ответа с пульта приемник записывает в энергонезависимую память свой адрес и контрольный байт пульта. Далее эти же данные применяются в качестве текущих и приемник переходит в режим нормальной работы.

В прошивке пульта основной функцией режима привязки является void BindMode() в файле Model.ino. В прошивке приемника тем же целям служит void DoBind() из файла proto.ino.


Подключение кнопок


Кнопки подключаются к микроконтроллеру при помощи входного сдвигового регистра 74HC165 (интерфейс SPI). Для чтения состояния кнопок предусмотрена функция void buttonsRead() из файла menu.ino. Результатом ее выполнения является обновление двух переменных:

dState – байт текущего состояния кнопок;
dChanged – байт, указывающий на то, состояние каких кнопок изменилось.

Соответствие битов кнопкам следующее:


 7  6  5  4  3 2  1  0
Не используется  Не используется  Кнопка
«канал 4»
Кнопка
«bind»
Кнопка
«меню»
Кнопка
«отмена»
Кнопка
«+»
Кнопка
«-»


Подключение резисторов управления пропорциональными каналами


Резисторы пропорциональных каналов подключаются к линиям АЦП 4 и 5. На микроконтроллере эти выводы совмещены с выводами SDA и SDL интерфейса I2C. АЦП запускаем в режиме циклической оцифровки сигнала. Подробно я описывал тут. Соответствующие функции располагаются в файле analog.ino прошивки пульта.

Стоит заметить, что считывание происходит циклически не с двух, а с трех входов АЦП(4, 5, 6). Шестой вход в оранже не используется и фактически "висит в воздухе". Это не ошибка. Получаемое с него значение - "белый шум" - используется для целей генератора случайных чисел.

 

Калибровка


Калибровка органов пропорционального управления имеет достаточно простой алгоритм. Сначала предлагается установить ОУ в центральное положение и нажать кнопку "меню". По нажатию кнопки для каждого канала заполняется переменная Center. Далее предлагается переместить каждый ОУ в свои крайние положения. Во время перемещений будут вычеслены максимальное и минимальное значения для каждого канала. Следующее нажатие на кнопку "меню" приводит к записи калибровочных значений в энергонезависимую память микроконтроллера.

Реализация алгоритма калибровки находится в файле analog.ino в функции void ADC_Calibration().


Подключение дисплея


Наиболее подходящим для данного проекта оказался дисплей Arduino TFT. Он практически идеально вписался в имеющийся корпус. Главное же его достоинство – подключение по SPI. Это особенно актуально ввиду большого дефицита свободных пинов на микроконтроллере. Для работы с дисплеем использована библиотека TFT, входящая в Arduino IDE версии 1.0.5 и выше. SD-карта на дисплее пока не используется.


Подключение радиомодуля


За основу пульта был взят OrangeRx OpenLRS модуль приемника. Достаточно подробно я описал его тут. В двух словах - это микроконтроллер AVR atmega328 с трансивером HopeRF RFM22B. За что точно практически сразу захотелось применить хирургическое вмешательство к китайцам, так это за то, что rfm'ка распаяна на программный SPI. Ввиду этого имеет место быть серьезный дефицит свободных пинов. Неудобно, но не смертельно...

Функции для работы с rfm находятся в файлах SoftSPI.ino и rfm22.ino. Необходимые переменные и дефайнсы вынесены в файл config.h.


Настройки моделей (хранение их в EEPROM)

 

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

 Наименование  Тип данных Назначение  Область EEPROM
 Center uint16_t  Нейтральное положение органа управления  Область калибровочных значений
 MaxVal  uint16_t Крайнее правое положение органа управления Область калибровочных значений
 MinVal  uint16_t Крайнее левое положение органа управления Область калибровочных значений
 chValue  uint16_t Текущее положение органа управления  
 ExpLeft  uint16_t Максимальное отклонение управляющего сигнала влево Область настроек модели
 ExpRight  uint16_t Максимальное отклонение управляющего сигнала вправо  Область настроек модели
Trimm  uint8_t Смещение нейтрального положения Область настроек модели
 Revers  uint8_t Реверс канала Область настроек модели

Все дискретные каналы описываются двумя переменными с размерностью в один байт:


 Наименование Тип данных  Назначение Область EEPROM
dChannels  uint8_t Текущее состояние кнопок  
 dFix  uint8_t Режим работы кнопок  Область настроек модели

Каждому дискретному каналу в этих переменных соответствует один бит ( с первого по четвертый).
Для кнопок возможны два режима работы:
1. Без фиксации – при нажатой кнопке значением соответствующего канала является логическая единица, при отпущенной – логический ноль.
2. С фиксацией – каждое нажатие кнопки переключает состояние соответствующего канала с логического ноля на логическую единицу и обратно. Т.е. для включения канала требуется одно кратковременное нажатие на кнопку. Выключение при этом производится повторным нажатием.

Так же для каждой модели сохраняется еще 4 байта адреса ее приемника и контрольный байт.

Прошивка пульта рассчитана на хранение в энергонезавиломой памяти микроконтроллера настроек для восьми моделей. Именно столько их уместилось в списке на одном экране меню. Памяти хватило бы и на 16, но ввиду природной лени изощеряться со скроллингом в меню я не захотел...

void SetModelDefaults() - сброс настроек модели в исходное состояние. Параметры привязки при этом не сбрасываются.

void LoadModelInfo() - загрузка настроек выбранной модели в оперативную память.

void SaveModelInfo() - сохранение настроек в энергонезависимую память.

Все функции располагаются в файле Storage.ino.

 

Алгоритм микширования

 

Фактически это алгоритм приведения сигнала с органа пропорционального управления к заданному диапазону значений. Базовым диапазоном при этом является диапазон значений от 1000 до 2000 с нейтральным значением 1500. Так же используется диапазон реальных значений, полученных при калибровке пульта.
  1. Если ADC_Value больше Center, то считать ОУ отклоненным вправо. Выполнить приведение ADC_Value к диапазону значений 1500:2000
  2. Если ADC_Value меньше Center, то считать ОУ отклоненным влево. Выполнить приведение ADC_Value к диапазону значений 1000:1500
  3. Если 1 и 2 не выполняются, то считать ADC_Value равным 1500
  4. Если для канала включен реверс, то «перевернуть» его значение в рамках диапазона 1000:2000 и изменить принятое отклонение в 1/2 на противоположное.
  5. Если ОУ отклонен вправо, то привести его значение к диапазону 1500 : 1500 + ExpRight
  6. Если ОУ отклонен влево, то привести его значение к диапазону 1500 + ExpLeft : 1500
  7. Полученное значение сдвинуть на Trimm - 128

Таким образом имеется возможность задавать кривую изменения пропорционального сигнала по трем точкам.

Функционал реализован в файле Model.ino в void UpdateDigiChannels() и в файле analog.ino в void ADC_Update(uint8_t indx, uint16_t ADC_Value).


Меню

 

В случае, когда dChanged показывает наличие изменения состояния и текущий экран не является основным, вызывается функция void ButtonPress(). Она реализует основной обработчик меню.

Описание пунктов меню живет в структуре
typedef struct {
String menuname;   //отображаемое на экране наименование пункта
byte ParentID;       //указатель на экран меню, на котором оно прорисовывается
byte Children;        //количество подпунктов
byte LineID;          //строка на экране, на которой отображается пункт
} tMenuItem;

tMenuItem MenuItems[MENU_ITEMS_COUNT];  // массив всех элементов меню.

Последние 8 пунктов этого массива соответствуют восьми моделям в памяти пульта. Остальные инициализируются функцией void LoadMenu(). Функция void UpdateScreen(byte ss) отвечает за перерисовку экранов меню. Функция void CallMenuFunc() - вызов действия, связанного с выбранным пунктом. Функция void UpdateSelection() - перерисовка рамки текущего пункта меню без перерисовки всего экрана(чтоб исключить моргание экрана при навигации).


В ближайших планах - реализация FHSS и передача телеметрии.