[Перевод] Пишем OTA-загрузчик для ATmega128RFA1 (в составе устройства Smart Response XE)

ieebszdo1m1pcoy6jx1gvxww3ao.jpeg

Всё началось с приобретения автором на вторичном рынке интересного устройства — Smart Response XE (краткое описание). Предназначено оно для школ: каждый ученик в классе получает по девайсу, похожему на электронную записную книжку или переводчик девяностых, учитель задаёт вопрос, и ученики набирают на клавиатурах устройств ответы, поступающие по радиоканалу (802.15.4) в приёмник, подключённый к учительскому ПК.

Поддержка этих устройств прекращена несколько лет назад, и то, что школы закупали по 100–200 долларов за штуку, теперь всплывает на eBay по 10 и меньше. «Железо» там ну очень подходит для гиковских опытов:

— клавиатура на 60 клавиш
— дисплей с разрешением в 384×136, 2 бита на пиксель — аналогично БК, CGA, но 4 не цвета, а градации яркости
— микроконтроллер ATmega128RFA1 (128 кБ флеш-памяти, 4 кБ ПЗУ, 16 кБ ОЗУ, приёмопередатчик стандарта 802.15.4)
— внешняя (по отношению к микроконтроллеру, а не всему устройству) флеш-память на 1 мегабит (128 килобайт) с интерфейсом SPI
— отсек для 4 элементов ААА

По названию микроконтроллера понятно, что он относится к семейству AVR, а значит, сделать устройство Arduino-совместимым — задача более чем тривиальная…

Из новости на Hackaday автор узнал, что это уже сделали (по этой же ссылке рассказано, что куда подключать), получив возможность запускать игры для Arduboy:

Но автора больше интересует возможность не поиграть на устройстве, а изучить:

— флеш-память с последовательным интерфейсом SPI
— загрузчики для AVR
— стандарт 802.15.4

Автор начал с написания библиотеки (GPL v3), позволяющей инициализировать дисплей, выводить текст и прямоугольники, а также получать доступ к флеш-памяти с интерфейсом SPI. Затем он начал придумывать идеи практического использования устройства: карманный VT-100-совместимый терминал, многопользовательские игры. Переделав три девайса, он решил «научить» их получать скетчи «по воздуху». Что было бы не только интересно, но и очень удобно: корпус устройства каждый раз открывать трудно, а под крышкой батарейного отсека находятся только отверстия, позволяющие подключать к плате JTAG-программатор.

dstbzns76vsm4pfjogzjsyo9mla.jpeg

Этого достаточно чтобы залить загрузчик Arduino, но не скетч — последовательный порт туда не выведен, без вскрытия корпуса всё равно не обойтись. Также линии TX0 и RX0 первого последовательного порта совмещены с линиями опроса матрицы клавиатуры, а именно — теми, по которым идёт опрос функциональных клавиш по сторонам от дисплея. Но что поделать — автор соорудил вот что:

8fxa-qo6dgerkd3ghgh4g-tbjak.jpeg

Туда он вывел линии JTAG, и теперь батарейный отсек открывать не обязательно. А чтобы можно было заливать и скетчи, вывел на этот же разъём и оба последовательных порта, добавив также выключатель, потому что при установленных батарейках устройство по-другому физически выключить невозможно.

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

Arduino IDE для заливки скетчей использует программу avrdude. Она взаимодействует с микроконтроллером по протоколу STK500, позволяющему передавать файлы в обе стороны. Он плохо совместим с каналами, где возможны переменные задержки, искажение и потеря данных. Если в последовательном канале что-то отходит или шуршит, можно сойти с ума в поисках причины. Один раз автор промучился полдня, пока не понял, что дело в в плохом кабеле, а также капризном преобразователе интерфейса CP2102. Даже микроконтроллер со встроенным преобразователем интерфейса, например, ATmega32u4, может иногда так «шалить». Каждый пользователь Arduino замечал, что ошибки при заливке скетчей не так уж редки. Иногда запись проходит нормально, а при проверочном считывании обнаруживается ошибка. Это не значит, что ошибка была при записи — сбой был при чтении. А теперь представьте, что при работе «по воздуху» будет происходить то же самое, но намного чаще.

Перепробовав разные способы преодоления этой проблемы, автор придумал следующее. У устройства есть 128-килобайтная флеш-память с интерфейсом SPI — принимаем данные по проводам (помним, что один девайсы с разъёмом на боку у автора уже есть), используем эту память как буфер, и по радиоканалу отправляем данные в другое устройство. Такой привет от Cybiko.

После написания кода для работы с радиоканалом, а также шрифта, загрузчик стал длиннее 4 килобайт. Поэтому значение HFUSE пришлось поменять с 0xDA to 0xD8. Теперь загрузчик может быть длиной до 8 килобайт, а начальный адрес стал равен 0×1E000. Это отражено в Makefile, но должно быть учтено и при заливке загрузчика с помощью avrdude.

Приёмопередатчик стандарта 802.15.4 в ATmega128RFA1 изначально предназначен для работы по протоколу ZigBee, который довольно сложен, поэтому автор решил вместо этого просто передавать пакеты. Это в ATmega128RFA1 реализовано аппаратно, так что кода потребуется немного. Также для простоты автор решил использовать фиксированный канал, не давая выбрать его даже вручную. Стандарт 802.15.4 поддерживает 16 каналов с номерами от 11 до 26. Они довольно забиты, некоторые также перекрывают каналы WiFi (красным обозначены каналы ZigBee, синим, зелёным и жёлтым — WiFi).

nnvxfnxaptwvpgbllq60zkfh2bq.png

Оказалось, что наименее подвержены помехам от WiFi каналы 15 и 26. Второй из них автор и выбрал. Дисклеймер: переводчик не знает, разрешено ли так упрощать ZigBee. Может быть, стоит ещё немного попрограммировать и реализовать его полностью?

На первом из устройств надо реализовать конечный автомат, передающий данные по протоколу STK500. В большинстве своём передаваемые и принимаемые сообщения самодостаточны, но некоторые привязаны к прошедшим по каналу ранее. Описание диалога приведено здесь.

Важной составляющей этого диалога является передача пакетов, предназначенных для записи во флеш-память устройства назначения. У простых микроконтроллеров семейства AVR размер страницы составляет 128 байт, но у ATmega128RFA1 — 256. А у той флеш-памяти, которая подключается по протоколу SPI, он такой же. Программа в первом устройстве при заливке скетча не передаёт его сразу во второе, а пишет его в эту память. Когда же Arduino IDE проверяет правильность записи, ему отправляют то, что туда записалось. Теперь надо передать полученные данные по радиоканалу во второе устройство. При этом переключение с приёма на передачу и обратно происходит довольно часто. Протокол STK500 безразличен к задержкам, но не терпит потери данных (странно, а выше сказано, что задержки на передаче данных тоже сказываются). А потери при беспроводной передаче неизбежны. В ATmega128RFA1 встроена аппаратная реализация повторных запросов при сомнениях в правильности передачи, но автор решил реализовать то же самое программно самостоятельно. Он разработал протокол, при котором в одну сторону проходит намного больше данных, чем в другую.

Он неидеален, но всё работает. 256-байтная страница разбивается на четыре сегмента, каждый из которых передаётся по радиоканалу в виде пакета. Пакет вмещает до 125 байт данных плюс один байт — длина и два — CRC. Так что фрагменты длиной по 64 байта вместе с номерами страниц и сегментов (от 0 до 3) туда помещаются. В принимающем устройстве предусмотрена переменная, позволяющая отследить, сколько сегментов принято, и когда приходят все четыре, на передающее устройство идёт подтверждение, что принята вся страница. Нет подтверждения (CRC не совпало) — отправляем всю страницу заново. Скорость при этом получается даже больше, чем при передаче по кабелю. Смотрите:

Но вообще-то, надо бы предусмотреть удобный способ подключения к устройствам кабеля для заливки скетчей и по нему. Например, поместить внутрь такой преобразователь интерфейсов на CP2102, как на фото, и приклеить его к плате так, чтобы он выдерживал усилие при подключении и отключении Micro USB-кабеля.

vdqje7zvkvnkzoggl7nvoezzzry.jpeg

Также в нём есть 3,3-вольтовый стабилизатор (и как его применить в устройстве с 6-вольтовым питанием — если только там есть такой же стабилизатор, и можно добавить два диода, чтобы автоматически выбирать, от какого из них будет питаться устройство). С платы преобразователя интерфейса надо выпаять все три светодиода, иначе они будут дополнительно нагружать батарейки при работе от них, а также мешать опросу клавиатуры и работе с флеш-памятью с интерфейсом SPI.

Преследование цели оказалось даже интереснее, чем её достижение (и не надо того анекдота про автобус). Автор узнал много нового о загрузчиках для AVR, флеш-памяти с интерфейсом SPI, протоколе STK500 и стандарте 802.15.4.

Весь остальной код в дополнение к описанной выше библиотеке — здесь, и он тоже под GPL v3. Твиттер автора — здесь.

© Habrahabr.ru