Гаджеты стремительно врываются в нашу жизнь. Чуть менее, чем все из них имеют на борту контроллер отличной от Intel’овской архитектуры x86, которую даже консерваторы стали называть умирающей. И если раньше программировать такие устройства могли только настоящие гуру своего дела, то теперь планка квалификации снизилась, а возможности самих контроллеров многократно увеличились.

Докажем, что разработка embedded (то есть встроенного) софта из-под GNU/Linux не только возможна, но даже более удобна, чем из-под Windows.

 

Введение

Зоопарк архитектур и устройств на них огромен, и поначалу во всем этом многообразии очень легко запутаться. Чтобы не охватывать все одновременно, можно пойти по пути наименьшего сопротивления: начать изучать что-либо одно. Предположим, у тебя на руках есть устройство, бывшее в прошлой жизни каким-нибудь маршрутизатором или контроллером сбора данных. Тогда можно поставить себе задачу научиться программировать это самое устройство: компилировать под него софт и прошивать. Если с ним натренироваться, хак всех остальных девайсов пойдет по накатанной, ибо на определенном уровне абстракции все становится одинаковым. Система GNU/Linux обычно сама умеет отделять модули для разных архитектур, и часто портирование программы с одного устройства на другое сводится к замене одной строчки в Make-файле.

Теперь о том, что нужно знать, прежде чем бросаться за программирование и прошивку того или иного девайса. Само собой, нужен некий опыт написания и компилирования хоть каких-нибудь программ на Си. Было бы нелишним уметь писать Make-файлы и знать, какие куски библиотек прилинковываются к исполняемым файлам в Unix-системах. Также приветствуется моральная готовность после очередной перепрошивки устройства получить голый кусок железа вместо дышащего жизнью Linux-box’а.

Кроме добавления софта в уже работающий на девайсе Linux, не менее интересно работать также с «голым» контроллером (так называемый bare metal), когда во внутренней памяти устройства нет ничего, кроме загрузчика. Такой вариант развития событий мы также рассмотрим.

 

Конкуренция

В наше время, когда программисты железа уже не кодят на машинном уровне и не пользуются примитивными программаторами, а возможности самих микроконтроллеров резко скакнули вверх, наше светлое дело строителей коммунизма помогает нам бороться за свободу на фронте embedded-софта! Ура, товарищи!

Если серьезно, то современные производители стали поставлять со своей продукцией какой-то невнятный виндово-кнопочно-тыкательный софт, позволяющий индусам от программизма не убирать руку с мышки. Казалось бы, рай настал, удобно? Но, как это ни странно, программирование контроллеров из-под линукса оказывается в разы удобнее! То ли сказывается хакерское прошлое этой ОС, то ли ее кроссплатформенность, но факт – работать здесь комфортнее. На черном экране консоли работа замечательно автоматизируется и освобождает программиста от поиска «той самой кнопки» в очередном GUI-интерфейсе для очередного МК.

Иногда дела с подобным софтом не так хороши, но знающие люди запускают его через Wine, или же просто реверсят его (пример: реверс USB-программатора для Silabs C8051: http://ec2drv.sourceforge.net). Одним словом, несмотря на некоторую ориентированность производителей на Windows-пользователей, жить и в GNU-мире можно.

Итак, какой же софт должен иметь под рукой начинающий embedded-программист?

 

Редакторы и среды

Как и в программировании для «большого брата», самое главное – это удобство рабочего места. Чтобы файлики в проекте были отсортированы, а компилятор вызывался с нужными параметрами, существуют среды программирования (или IDE – Integrated Desktop Environment). В общем смысле, нам подойдет любая среда программирования, умеющая базово понимать хотя бы язык Си и вызывать внешний компилятор. Но некоторые из них я подчеркну особо.

Code::Blocks (www.codeblocks.org). Это очень удобная и простая среда, не перегруженная наворотами. Автоматически подцепляет все найденные компиляторы и отладчики, подсвечивает синтаксис и функции, поддерживает внешние make-файлы и импортирование проектов из других IDE. В этой среде удобно программировать подо что угодно, в том числе и под микроконтроллеры, чем я в ней и занимался последние пару лет. Несмотря на то, что последнее обновление программы на сайте значится от февраля 2008 года, она все же развивается, и регулярные билды выкладываются в svn.

Eclipse (www.eclipse.org). Монстр, написанный на Java, который поддерживает все на свете, а что не поддерживает – легко дополняет плагинами. Если ты уже программируешь на этой платформе, то переучиваться необязательно – контроллеры кодятся и тут.

Vim/Emacs. Любители консоли с полпинка прикрутят к своему любимому редактору нужные скрипты для компиляции и прошивки.

Блокнот. Так уж повелось, что в какой-то момент программисты забивают на использование той или иной среды разработки и начинают писать в обычном текстовом редакторе. Гномосеки отдадут предпочтение gedit, кедерасты – kate. Оба редактора подсвечивают синтаксис и немного поддерживают автодополнение. Если ты не боишься компилирования в голой консоли, то возможностей блокнотика тебе хватит.

 

Компиляторы

Пройдемся по софту, переводящему человеческий язык Си в машинные коды. Ведь нет компилятора – нет и программы.
GNU GCC. На первом месте, конечно, компилятор GCC, который используется везде в Linux-мире. Под какую архитектуру он бы ни собирал код, можно быть уверенным, что синтаксис будет совместимым, код – переносимым, а коровы – надоены. Я настоятельно рекомендую использовать в работе именно его, даже несмотря на то, что некоторые коммерческие и сторонние компилеры могут быть эффективнее.

SDCC или Small Devices C Compiler. Создан для сборки прошивок под простые процессоры типа Intel MCS51, AVR, HC08, PIC и Z80. В параллельном мире конкуренцию ему может составить тулчейн для 8051 от Keil.

Проприетарные компиляторы. Тот же Keil, например. Многие умельцы используют его через Wine и не жалуются. Что ж, тоже вариант.
Прочие. Понятно, что выбор вышеперечисленными софтинами не ограничивается. Поиск по Сети покажет множество компилеров под малоизвестные или забытые архитектуры, и использовать их – дело вкуса или необходимости.

 

Компиляция

Компилятор у нас есть, теперь надо им что-нибудь собрать, например, под ARM-архитектуру. GCC предлагает для таких целей два «таргета»: arm-linux и arm-gcc. Если вкратце, то отличаются они друг от друга тем, что первый нужен для сборки софта под Linux, а второй – под голое железо. Это не совсем правильное объяснение, но для первого раза вполне подойдет.

Для начала разберемся в том случае, если устройство, под которое ты кодишь, уже имеет свой Linux на борту, и тебе надо лишь написать под него свою софтину.

Как известно, все Юниксы очень дружелюбны, просто очень разборчивы в друзьях. Я к тому, что при наличии Linux на основной машине написание софта становится сущим кайфом. Если твоя программа не использует сторонних библиотек, то компиляция сводится к простому переопределению компилера. Напишем программку:

$ vi hello.c

#include <stdio.h>
int main (){
printf("Hello world!\n");
}

Теперь скомпилируем:

$ arm-linux-gcc -o hello hello.c
$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1, statically linked, not stripped

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

Если проект имеет Make-файл, то компилятор можно переназначить до вызова программы make. Вот так:

$ CC=arm-linux-gcc make

А если наличествует скрипт configure, то, например, так:

$ CC=arm-linux-gnu-cc ./configure --host=arm

А что же с более сложными проектами, использующими сторонние библиотеки? Ведь если они присутствуют на PC’шнике и собраны под PC-архитектуру, то кросс-компилятор откажется их использовать! Мало того, он даже не найдет эти файлы. Можно вспомнить, как такие проблемы решаются на Большом Брате. Если там не хватает какой-нибудь либы, то команда типа «apt-get install libncurses5-dev» обычно решает проблему поиска заголовков и бинарников для библиотеки. Так вот, в случае с другими архитектурами можно сделать точно так же, а именно – поискать уже готовые и заранее скомпилированные либы. Проект Debian, скорее всего, имеет на своих зеркалах все более-менее популярное. Хотя, если для тебя использование готовенького – читерство, то логично, что все нужные библиотеки тоже надо компилировать самому.

Разумеется, версии и реализации качаемых библиотек должны быть одинаковы с теми, что на устройстве, или хотя бы не сильно отличаться друг от друга. Например, многие embedded-линуксы имеют у себя на борту крошечную библиотеку uclibc вместо «взрослой» и большой glibc. Они не особо совместимы между собой, поэтому софт должен собираться в паре с той либой, что установлена на девайсе. То есть, это тянет за собой весь тот самый Dependency Hell, так знакомый олдовым линуксоидам, да еще усугубленный необходимостью иметь «по второму экземпляру» всего кроссплатформенного на хост-машине.

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

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

$ ls
startup.S main.c sdram.lds
$ arm-elf-gcc -Os -march=armv4t -c -o startup.o startup.S
$ arm-elf-gcc -Os -march=armv4t -c -o main.o main.c
$ arm-elf-gcc -T"sdram.lds" -s -Os -march=armv4t -nostartfiles -nostdlib -o firmare.elf startup.o main.o
$ arm-elf-objcopy --strip-debug --strip-unneeded firmware.elf -O binary firmware.bin

Объясню, что здесь написано, чтобы было не так страшно. Здесь происходит сборка некой прошивки из двух исходных файлов, один из которых написан на ассемблере, а другой – на Си. Параметр "-Os" указывает на необходимость оптимизировать размер бинарника, а "-march=armv4t" – на оптимизацию под конкретное процессорное ядро ARMv4.

Далее, получившиеся объектные файлы мы связываем в общий бинарник, настоятельно рекомендуя линкеру не использовать его собственные загрузочные файлы и стандартную библиотеку. Они нам не нужны. Теперь про опцию "-Tsdram.lds". Файлы типа lds – это так называемые линкерные скрипты. Дело в том, что компилятор, собирая проект, должен знать структуру памяти, в которой будет работать прошивка. Даже для программ, собираемых под родной Линукс на x86-архитектуре, gcc достает откуда-то из недр своих каталогов нужный линкерный скрипт и применяет его. Только это незаметно для программиста. Здесь же, отринув все мирское, мы вынуждены составлять собственный lds, а это особый дзен.

Четвертая команда также вызывает вопросы у неподготовленного программиста. Здесь все дело в том, что, когда запускается софт на той или иной ОС, то перед началом работы ядром вызывается загрузчик, сканирующий программу в формате ELF (или PE – неважно), размещающий ее в нужном месте памяти и передающий управление в точку старта. Но ведь на нашей железке пока что нет никаких ELF-загрузчиков, а есть только голое процессорное ядро, тупо выполняющее машинные инструкции! Вот поэтому из получившегося ELF-файла мы выдираем бинарный код, не имеющий никаких оберток. Именно в таком виде он и будет жить в оперативке нашего устройства, и именно он пригоден для заливки во флеш.

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

 

Шьем

Понятное дело, одной только компиляции программы для начала ее использования недостаточно. Теперь ее надо как-нибудь залить на целевое устройство. Хорошо, если там уже стоит нечто линуксоподобное. Тогда для запихивания в девайс обычно достаточно нескольких телодвижений. Например, пересобрать jffs2-образ файловой системы или же просто скопировать получившуюся софтину через Ethernet/Xmodem/SD-card/что-угодно. Подробности в таком случае подскажет Гугл или мои предыдущие статьи.

А что, если устройство представляет собой голую железяку? Тут все немного сложнее. Сложность состоит в том, что тот или иной девайс шьется по-разному и предоставляет для этого разные интерфейсы (например, JTAG или I2C). Здесь уже надо курить документацию на плату и процессор.

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

Openocd (http://openocd.berlios.de) – утилита, предназначенная для перепрошивки ARM и MIPS устройств через интерфейс JTAG. Поддерживает пару десятков программаторов и внушительное количество процессорных ядер. Must have.

Далее, поддержку C2 или JTAG-интерфейсов имени SiLabs через местный программатор осуществляет linux-патч по ссылке http://wiki.enneenne.com/index.php/Silicon_C2_Interface или отдельная софтина Ec2drv (http://ec2drv.sourceforge.net).
Также существует Eep24c – программа для заливки бинарного кода в I2С-совместимые микросхемы постоянной памяти EEPROM. Работает через спаянный на коленке LPT-программатор.

Программки avrdude/avrprog/uisp/dfu-programmer тем или иным способом заливают прошивку на популярные нынче контроллеры Atmel AVR.

Все вышеперечисленное находится в репозиториях популярных дистрибутивов, поэтому установка не вызывает проблем.

Как видно, энтузиастами написана огромная куча софта под любой более-менее распространенный микроконтроллер. И даже если производитель поставляет Windows-программку для внутрисхемного программирования или отладки, не поленись найти свободный аналог. Он есть.

 

Облегчаем жизнь

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

Для начала, напоминаю про проект Emdebian (emdebian.org), имеющий у себя в репозиториях уже готовые компиляторы и библиотеки под множество архитектур. Также много всякого софта есть и в оригинальном Debian, который, как мы помним, кроссплатформеннее некуда. Даже Ubuntu (о, ужас!) уже давно портирована на некоторые не-x86 платформы и, следовательно, имеет скомпилированные для этого пакеты.

Если же среди готовых инструментов не оказалось нужной тебе комбинации компилятор/библиотека, то от участи собирать все вручную избавит скрипт crosstool-ng (http://ymorin.is-a-geek.org/projects/crosstool). По словам самих разработчиков, довольно сложно перечислить все возможности этой программы, и я с ними согласен. От количества поддерживаемых таргетов берет оторопь, и вот тут уж точно найдется все нужное тебе для кросс-компиляции.

 

__exit()

Сборка софта под железо – сложное и очень интересное занятие, требующее усидчивости. Мало что сравнится с кайфом, когда наблюдаешь мигающий по твоей программе светодиодик, только вот подготовка к этому может отнять очень много сил. Главное – не бросать все на полпути, и иметь в виду, что линукс сильно помогает в деле embedded-кодинга.

Большинство существующих на рынке отладочных плат уже имеют у себя на борту загрузчик той или иной степени куцеватости. Пример – знаменитый U-boot. Так вот, подобные программы позволяют прошивать устройство без всяких программаторов через USB или последовательный порт (Xmodem). У них хватает ума положить прошивку по определенному адресу во флеш, а также скопировать в память и передать ей управление. Довольно часто подобные загрузчики живут на отдельной микросхеме памяти, до которой не добраться, кроме как через JTAG. Получается некая защита от дурака: если у человека хватило квалификации убить такое, то хватит и восстановить.

Энтузиасты-китайцы создали крайне дешевое Linux-устройство с кучей возможностей – FriendlyARM (http://friendlyarm.net). Его разновидность, Mini2440, создан на базе контроллера Samsung s3c2440 и имеет сенсорный LCD-экран. Купить такое для экспериментов несложно, а продается он за 150 баксов.

 

INFO

JTAG – стандарт для внутрисхемного программирования и отладки всяческой электроники. Если девайс не самый примитивный, то этот интерфейс на нем наверняка разведен.

 

WWW

Оставить мнение

Check Also

Эхо кибервойны. Как NotPetya чуть не потопил крупнейшего морского перевозчика грузов

Российское кибероружие, построенное на утекших у АНБ эксплоитах, маскировалось под вирус-в…