Хочу сразу оговориться:
1. Статься из разряда «how to…». Пишу только потому что просили и чтоб не выглядеть голословным.
2. Мне столь простое устройство не нужно. Поэтому корпусом и подбором компонентов не заморачиваюсь. Делаем в виде прототипа.
3. Платформа Arduino. Ее уровень абстракции является наиболее подходящим для изучения школьниками (класса с 6-го). Собственно из моего учебного курса и взята большая часть описываемых примеров.
4. Статья скорее как затравка к изучению этой темы. Я не буду сильно углубляться и рассказывать все с азов. Это будет слишком объемно и не влезет в рамки статьи. В конце дам список ресурсов для самостоятельного изучения.
Постановка задачи
Хотим получить электронную начинку 4-х канального пульта пропорционального радиоуправления для… каждый сам решает для чего.
Формализуем наше «хочу»:
Вход – два двухосевых джойстика.
Выход – PPM сигнал для подключения к вч-модулю (или к компьютеру для симулятора).
Введение
Сердцем нашей конструкции будет микроконтроллер семейства mega фирмы Atmel, помещенный на плату с минимальной обвязкой и с зашитым в него специальным загрузчиком для программирования по последовательному порту. Собственно это и есть Arduino. Подробно можно почитать на официальном сайте проекта Arduino.cc или на его русскоязычном клоне Arduino.ru. Там желающие найдут всю необходимую информацию о том, какие бывают разновидности Arduino, как заливать прошивку, познакомятся с основами языка, смогут свободно скачать среду программирования… Там все по-русски и без затей. Никаких заумных инсталляторов, никаких сложных сред. Небольшое меню и две-три кнопки на тулбаре – все что нужно для того чтобы начать.
Джойстик. В его конструкции за прошедшие ндцать лет кардинально ничего не изменилось. Два потенциометра с перпендикулярными осями – вот и весь джойстик. Перемещение ручки по некоторой оси приводит к перемещению скользящего контакта соответствующего переменного резистора. Подключим их по следующей схеме(левый рисунок). Получится ни что иное как обыкновенный делитель напряжения(схема справа). Так для каждого положения ручки управления R1 и R2 будут свои. При этом R1 + R2 = const (номинал резистора). Т.е. для каждого положения ручки на оси на входе микроконтроллера будет свое напряжение в интервале от 0 до VCC(напряжения питания).
В джойстиках используются обычно резисторы номиналом в 5Ком. Но точность номинала при таком подключении не особо важна. Это может быть и 4, и 6 и 10Ком.
Где взять джойстики? Ну как-то так сложилось, что все самое интересное и нужное можно найти в пультах Nintendo Wii. Нет, можно конечно сделать самому... Но на сегодня, как мне кажется, самый простой и дешевый вариант – купить джойстики управления для Turnigy 9XR прямо тут на Паркфлаере. Лично я закупил их с десяток для различных поделок и экспериментов.
Левый стик управления в сборе - для передатчика Turnigy 9XR, (Mode 2)
Правый стик управления в сборе - для передатчика Turnigy 9XR, (Mode 2)
Где взять Arduino и какую выбрать для данной задачи? Поскольку задача крайне проста, плату можно брать любую. Самая маленькая и дешевая – Arduino Mini с atmega168 на борту. Правда к ней надо еще взять USB-to-TTL для программирования или Kingduino USB to serial UART interface Я взял Arduino Nano c atmega328 (потому что под руку подвернулась именно она) и шилд типа такого для удобства подключения периферии.
Получение положения джойстиков
Подключаем каждый из четырех резисторов джойстиков по выше указанной схеме. При этом у Arduino задействуем контакты A0-A3. Эти контакты подключены к АЦП микроконтроллера и могут быть запрограммированы как аналоговые входы. Более менее подробно о программировании аналоговых входов я писал тут. Повторяться не буду.
Пишем прошивку. Я предумышленно разбил код на три файла:
TX.ino – основной файл. Он содержит главные точки входа – функции setup() и loop().
Config.h – в этом файле в дефайнсах определяется подключение периферии
Analog.ino - собственно код работы с АЦП.
Принцип простой. Настраиваем регистры микроконтроллера так, чтобы АЦП непрерывно выполнял преобразования. По завершении каждого преобразования срабатывает прерывание ISR(ADC_vect) и выполняется соответствующий код. Нам остается только переключать входы к АЦП и запоминать результат в массиве. Время на одно преобразование порядка 26мкс. Если Вы внимательно посмотрите на мой код, то заметите, что я беру результат каждого второго преобразования. Все дело в том, что АЦП работает независимо от центрального ядра. Пока выполняется код в прерывании и переключаются входы АЦП уже делает следующее преобразование. Как следствие – недостоверный результат. Поэтому я и беру каждое второе… В итоге получим массив из четырех элементов, значения которых должны изменяться пропорционально движению джойстиков в интервале от 0 до 1023. Вот архив (TX1.rar) с кодом примера.
Формируем выходной сигнал
Теперь нам надо сформировать массив канальных импульсов. Сделаем для этого такую функцию:
void UpdateChannels()
{
for (uint8_t i = 0; i<CHANNELS_COUNT; i++)
{
Channels[i] = map(AnalogValues[i], 0, 1023, 1000, 2000);
}
}
Здесь мы заполняем массив значений каналов попутно приводя полученные с АЦП значения к интервалу 1000-2000мкс. Это будут наши канальные импульсы. Осталось по этому массиву сформировать выходной сигнал. Для этого воспользуемся таймером.
У 168-ой /328-ой меги есть три таймера/счетчика, работающих от основного задающего генератора. Два из них имеют разрядность 16 и один – 8. Будем использовать один из 16-ти разрядных таймеров.
Сначала его надо настроить. Во-первых при частоте 16МГц установим делитель на 8, чтоб таймер отсчитывал половинки микросекунд:
TCCR1B |= (1<<CS11);
Каждый из таймеров имеет два компаратора и может генерить прерывания по совпадению значений. Разрешим эти прерывания для нашего таймера:
TIMSK1 = (1<<OCIE1A) | (1<<OCIE1B);
Компаратор А настроим на длину фрейма – 20мс
OCR1A = 100000;
Компаратор В будем использовать для формирования канальных импульсов.
Выводить импульсы будем на 13-й вывод Arduino.
В прерывании компаратора А напишем код инициализации нового канального фрейма:
ISR (TIMER1_COMPA_vect)
{
ppm_cur = 0;
OCR1B = PPM_PAUSE;
TCNT1 = 0;
PPM_on;
}
Фрейм начинается с паузы. Ее длину мы и запишем в соответствующий регистр компаратора В.
В прерывании компаратора В будет следующий код:
ISR (TIMER1_COMPB_vect)
{
if (PPM_0){
PPM_off;
OCR1B = TCNT1 + PPM_PAUSE;
ppm_cur++;
}
else {
PPM_on;
if(ppm_cur <= CHANNELS_COUNT)
OCR1B = TCNT1 + (Channels[ppm_cur-1]<<1);
else {
OCR1B = 0;
}
}
Если на выходе был сигнал, то дальше должна быть пауза. Устанавливаем соответствующее значение на выходе и в регистре таймера. Попутно увеличим на 1 счетчик каналов.
Если на выходе была пауза, то установим на выходе сигнал. А в регистр таймера запишем его длину. Если это синхропауза, то длина будет до конца фрейма и в регистр запишем 0.
Вроде бы как все. Загружаем в контроллер, подключаем к вч-модулю, на выход приемника вешаем серы… Работает!
Вот только есть одно НО. Сервы работают не в полном диапазоне. В чем проблема?
На самом деле все элементарно. Точность резисторов посредственная, механическая часть тоже ширпотреб. В итоге наша изначальная схема делителя преобразуется к такой:
И сопротивления R1 и R4 как раз и дают эту погрешность. Ну да не проблема. Введем калибровку. Для этого подключим кнопку. Алгоритм будет примитивный:
Первый джойстик влево вниз, держать, нажать кнопку.
Первый джойстик вправо вверх, держать, нажать кнопку.
Второй джойстик влево вниз, держать, нажать кнопку.
Второй джойстик вправо вверх, держать, нажать кнопку.
Так мы заполним два массива минимальных и максимальных значений для каждого аналогового входа. Вот только каждый раз калибровать – как-то неудобно. Ок. У нашего контроллера есть энергонезависимая память – запишем туда.
Теперь все работает как надо. Код по ссылке (TX2.rar) .
Так же не сложно увидеть, что число каналов легко увеличить до восьми, добавив еще органы управления – «крутилки» (резисторы) и тумблеры. Далее можно усложнить функцию UpdateChannels() - добавить туда микшеры. Можно подключить дисплей и написать меню сервисных функций… Итог – пульт а-ля Turnigy 9XR своими руками. Если не будет хватать выводов или памяти контроллера – возьмите по-мощнее. Atmega 2560 сможет реализовать практически все Ваши идеи и фантазии.
В заключении обещанные ссылки:
1. Arduino.cc – официальный сайт платформы
2. Arduino.ru – русскоязычный сайт платформы
3. http://rc-master.ucoz.ru/publ/20-1-0-85 - достаточно неплохой справочник по микроконтроллерам Atmel
4. Улли Соммер - Программирование микроконтроллерных плат ArduinoFreeduino – 2012
5. Arduino. Блокнот программиста. Brian W. Evans – справочник языка. Есть русский перевод.
6. Гололобов В. - С чего начинаются роботы. О проекте Arduino для школьников (и не только) – 2011
Будут вопросы – задавайте. Постараюсь ответить.
Делать фотографии и снимать видео ради всего этого не вижу смысла - времени нет, да и лениво.
Оборудование, на котором тестировал(за исключением компьютера):
http://rc-master.ucoz.ru/publ/10-1-0-40
Спасибо! Очень професионально.
Пора в РОССИИ свои "примочки" делать, жалко что тема заглохнет, здесь мало тех кто это понимает.
Под товарами раньше была полезная информация, теперь один флуд остался...
Практически все люди, чьи статьи было интересно читать, писать тут перестали. Да и я тоже скорее всего больше не буду...
Кому будет интересен мой опыт - http://rc-master.ucoz.ru - только полезная информация, ничего лишнего. Флуд удаляю сразу наместе. Спамерам - пожизненный бан. Если кто-то, что-то захочет выложить, то я сначала это прочитаю. И главное - никаких денег, голый энтузиазм.
Добавил Ваш сайт в закладки.
Собственные аксперементы пока ограничиваются движениями сервой и т.п. В планах было использовать ардуину мини для стабилизации полета, но отмазка одна нехватка времени. На работе только в инете можно посидеть втихоря часок другой, принесешь железки косо смотреть начинают.
Я скажу вот что: подобную вещь для симулятора делать смысла нет. Проще купить готовый.
НО. Это база, которой есть реальное применение. Один близкий мне человек (очень далекий от нашего хобби) занимается монтажем и обслуживанием разных гидросистем - полив, водоснабжение, отопление и пр. Он попросил меня сделать робота для инспектирования труб, проходящих в труднодоступных местах: узкие галереи, подвалы, подполье... По сути радиоуправляемая машинка с ФПВ.
Требование - простота и быстрота развертывания и использования, удобство транспортировки и противоударность.
Имеющиеся пульты не подошли по причине неудобства, хрупкости и наличия кучи ненужных и непонятных настроек (для его целей).
Было решено сделать все самому. Транспортировка в двух чемоданчиках: один для робота, второй - пульт. Развертывание - открыть крышку, щелкнуть тумблер. Управление и видео в одном корпусе.
Вот для такого и нужна подобная основа...
Пульт там гораздо сложнее описанного выше. Да и на стороне "модели" тоже не все так просто.
К весне авось соберу...
Вот это в отличии от пещерных конструкций прошлого века действительно серьезно. Огромное спасибо за такой материал. За последние пару месяцев это,наверное, единственный материал что меня всерьез заинтересовал. И это несмотря на то,что самодельный пульт я делать не собираюсь.
PS вот только будет очень обидно если этот материал наберет голосов гораздо меньше чем пещерные(никому не нужные) радиолюбительские конструкции.
Но судя по раскладу оценок так тому и быть.
Давайте уже о PPM.... По коду есть что добавить:
-Может кто обьяснит зачем регистры таймеров в такие значения выставили
- Принцип формирования PPM при помощи прерываний
- как с 4 канального PPM перейти к 8 канальному
Ну и если кто код еще более понятно разжует было бы здорово
Нормальным людям теперь не спросить не обсудить ....
Кстати, в коде косяк. Пока чистил лишние комменты в коде пропустил одну строчку. Хуже то, что я ее раскоментировал.
TCCR1B = (1<<CS10); УДАЛИТЬ!!! Иначе делитель будет на 1024
Один компаратор отсчитывает длинну фрейма
Второй - прописывает импульсы на выход. Писать следующий импульс или паузу определяется по состоянию пина. Если он включен, выключаем а время следующего прерывания ставим как текущее + пауза. Иначе берем следующий канал, включаем пин и пишем следующее время прерывания как текущее + длинна канального импульса.
//#define CHANNELS_COUNT 4
#define CHANNELS_COUNT 8
Что такое "void setup()" здесь объяснять не буду. Слишком много времени понадобится. Да и все это проще в книжках прочитать.
PORTB |= (1<<5)
У atmega168 все пины объединены в порты по 8 в каждом. Именуются они буквами (см распиновку).
Выше указанная запись говорит о следующем: записываем единицу в разряд порта B(5-й пин) что соответствует номеру контакта Arduino D13. Логическое или здесь говорит о том, что остальные пины порта не трогаем.
Задавайте конкретные вопросы - попробую разжевать.
Управляется так же как серва.