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

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

  • клавиатура на 60 клавиш
  • дисплей с разрешением в 384x136, 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-программатор.



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



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

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

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

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

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

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



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

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

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

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


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



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

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

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