Почему так сурово, спросите вы. Во-первых, чтобы что-то хорошо освоить, необходимо начинать с основ. Я не хочу, чтобы мой читатель бездумно щелкал клавишами клавиатуры набирая текст очередной супер-программы для устройства, не понимая, как работает устройство. Stm32 гораздо более сложный микроконтроллер по сравнению, например с atmega8 — atmega328 (микроконтроллером, установленным на самой популярной плате серии arduino). Во-вторых, я люблю сам разбираться в любом деле с нуля, и можно сказать, данная статья — это заметки для меня в будущем, чтобы открыть и вспомнить некоторые нюансы.
Да, я забыл еще сказать, что разработку буду вести под Linux. Подойдет любой дистрибутив, например, у меня это Arch Linux. Для ubuntu процесс установки необходимых утилит я постараюсь описать в следующих частях. Можете попробовать Windows, MacOS, но для этого вам самим придется разобраться, как установить необходимые утилиты для компиляции и прошивки.
Первое, что вам нужно сделать, это приобрести плату для разработки на основе контроллера stm32f103. У меня это blue pill:
Еще одна вещь, необходимая для старта, это программатор st-link:
Плату blue pill и программатор я приобрел на aliexpress, заплатив 200 руб. за все вместе.
Второе, что необходимо сделать, это скачать набор для компиляции кода под arm GNU GCC.
Для arch linux необходимо поставить пакет gcc-arm-none-eabi:
yaourt -Syy arm-none-eabi-gcc
Далее нам понадобится утилита st-link для работы с одноименным программатором st-link2:
yaourt -Syy stlink
Теперь давайте попробуем подключить нашу плату к компьютеру через программатор.
Соединяем программатор с платой blue pill в таком порядке:
- Подключить к пину GND (ground — земля, пина два, возьмите любой, например, 4-й) на программаторе провод (желательно следовать некоторым стандартам, для земли используйте черный или синий) и подключите к пину на плате подписанному GND;
- Подключить пин SWCLK (clock — синхронизация) на программаторе к пину SWCLK на плате;
- Подключить пин SWDIO (IO — ввод/ввод) на прогамматоре к пину SWIO на плате;
- И наконец, пин 3,3V на программаторе соедините с пином 3,3V на плате.
Пока все очень просто. Теперь подключаем программатор в USB порт компьютера, открываем терминал и проверяем, что устройство успешно определилось в системе:
dmesg
При этом на самой плате загорится два диода, один красный должен гореть постоянно, что сигнализирует о том, что питание подается, второй зеленый, должен мигать. Это работает прошивка по-умолчанию.
Теперь давайте проверим характеристики нашей демо-платы. Для этого в терминале запускаем команду st-info из установленного до этого пакета stlink:
На выбор можем посмотреть:
--version — текущая версии утилиты st-info
--flash — выведет информацию о размере flash-памяти программ микроконтроллера, в моем случае это 0x10000 (65536 байт)
--sram — объем статической памяти — 0x5000 (20480 байт)
--descr — описание — F1 Medium-density device
--pagesize — размер страницы памяти — 0x400 (256 байт)
--hla-serial — "\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31"
--probe — Found 1 stlink programmers
serial: 303030303030303030303031
openocd: "\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31"
flash: 65536 (pagesize: 1024)
sram: 20480
chipid: 0x0410
descr: F1 Medium-density device
Из важного для нас — размер flash-памяти и размер статической памяти, а также стоит запомнить что у нас устройство Medium-density.
Не следует начинать разработку без документации под рукой. Во-первых следует скачать с официального сайта Reference Manual. В нем полное описание всей периферии, регистров периферии микроконтроллера. Во-вторых, скачиваем Programmer Manual по той же ссылке. В нем узнаете о микропроцессоре семейства контроллеров STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3, его архитектуре, наборе команд.
Далее разберем, с чего вообще начинается исполнение программы на микроконтроллере.
- Наш микроконтроллер stm32f103c8 сразу после включения начинает считывать по адресу 0x08000000 (для удобства чтения я буду делить тетрады пробелом — 0x0800 0000) значение для регистра SP. SP (Stack pointer) — регистр указателя стека (стр. 15 Programmer Manual). Стек начинается с конца доступной RAM-памяти и растет “вверх”;
- По адресу 0x0800 0004 считывает значение в регистр PC — Program counter. Это значение — адрес точки входа в нашу основную программу, другими словами по адресу 0x0800 0004 flash должен лежать адрес C — функции main(), определенной нами далее;
- Микроконтроллер начинает выполнение программы.
Чтобы вычислить начальное расположение стека (значение для SP регистра), обратимся к мануалу Reference Manual на стр. 65. Там указано, что RAM начинается с адреса 0x2000 0000. Ранее мы определили, что у микроконтроллера объем RAM 20480 байт:
0x2000 0000 + 0x5000 = 0x2000 5000
То есть по адресу 0x0800 0000 мы должны поместить значение 0x2000 5000.
По адресу 0x0800 0004 мы должны положить указатель на начало нашей программы. Каждый указатель имеет размер 4 байта, значит следующий адрес за 0x0800 0004 во flash памяти будет 0x0800 0004 + 4 = 0x0800 0008. Это значение и необходимо поместить по адресу 0x0800 0004.
Так будет выглядеть начальный участок нашей прошивки:
+-------------+-------------+
| Адрес flash | Значение |
+-------------+-------------+
| 0x0800 0000 | 0x2000 5000 |
| 0x0800 0004 | 0x0800 0008 |
+-------------+-------------+
Теперь об одной особенности микроконтроллеров stm32. Дело в том, что формат команд для stm32 должен быть в Thumb представлении вместо стандартного ARM. Это значит, что при указании указателей мы должны прибавлять 1. Запомните это правило.
Хватит теории, пора переходить к практике. Надеюсь, вы еще не спите. Открывайте ваш любимый редактор кода, будем писать начальный файл для запуска нашего контроллера. Мы начнем с startup файла и он будет написан на ассемблере. Это будет единственный раз, когда я заставляю вас писать на скучном ассемблере, зато вы начнете понимать и “чувствовать” устройство изнутри.
Пишем в самом начале:
@stm32f103
Это комментарий на языке ассемблера, каждый комментарий начинается с символа @.
Далее указываем директивы ассемблеру
.syntax unified
@тип команд для stm32 - Thumb!
.thumb
@семейство процессора микроконтроллера cortex-m3
@(в этом можно убедиться из мануала)
.cpu cortex-m3
И далее коротенькая bootstrap-программа:
@указатель на вершину стека. Пишем без пробелов!
@.equ директива ассемблера это почти
@тоже что и define в C или на худой конец
@думайте, что это обычное присваивание переменной
.equ StackPointer 0x20005000
@.word - указываем, что здесь машинное слово - 4 байта
@по сути отсюда (0x0800 0000) процессор
@начинает свою работу после включения
.word StackPointer
@”кладем” указатель на начало основной программы.
@Reset в данном случае - метка, адрес точки входа.
@не забываем о том, что у нас набор команд Thumb,
@поэтому к указателю прибавляем единицу
.word Reset + 1
@метка Reset. Здесь мы встречаем первую, настоящую и
@единственную команду, которая нам понадобится на
@протяжении всего руководства. Это команда B -
@безусловный переход в системе команд ARM,
@аналог JMP в ассемблере для x86, или простыми
@словами goto в языках более высокого уровня.
@Аргумент команды B - это адрес безусловного перехода, в нашем случае мы пока
@указываем метку Reset, тем самым заводим процессор в бесконечный цикл.
Reset: B Reset
Программу целиком вы можете скачать по ссылке https://bit.ly/2rc7bcf
Сохраните ее под названием bootstrap.s.
А теперь давайте скомпилируем и прошьем нашу плату.
Прежде всего я покажу, как скачать прошивку по-умолчанию с вашей платы, ту, которая мигает светодиодом. Вдруг когда-нибудь пригодится.
Снова вставляем программатор с подключенной платой в usb и запускаем в терминале Linux команду:
st-flash read ./default.bin 0x08000000 0x10000
Здесь мы указываем, что хотим прочитать в файл default.bin flash-память начиная с адреса 0x08000000 и размером 0x10000 (64K), то есть всю flash-память.
st-flash — утилита для работы с прошивкой микроконтроллера, полное описание ее читайте в терминале: st-flash --help
.
После этого проверим, что прошивка корректно считалась. Загрузим ее вновь, перезаписывая старую.
st-flash write ./default.bin 0x08000000
Что означает записать default.bin в flash память контроллера начиная с адреса 0x08000000.
Выньте и снова вставьте программатор, на плате зеленый диод должен как и раньше мигать.
Теперь давайте скомпилируем нашу самописную прошивку. В терминале в той же директории, что и сохранили запустите:
arm-none-eabi-as -o bootstrap.o bootstrap.s
Здесь мы компилируем наш исходный файл в объектный код. Это еще не готовая прошивка, годная для заливки в микроконтроллер. Нам необходимо еще “указать” куда, по каким адресам размещать нашу программу. Этим занимается компоновщик. Мы воспользуемся самым популярным компоновщиком LD, который входит в поставку пакета arm-none-eabi-gcc. Подробнее о компоновщике и описание скрипта для компоновщика ld я расскажу в следующей части, когда мы перейдем к автоматической сборке нашей супер-простой прошивки. А пока просто скачайте этот маленький файл stm32f103.ld https://bit.ly/2HXIydu, и выполните команду:
arm-none-eabi-ld -o main.elf -T stm32f103.ld bootstrap.o
Этой командой мы компонуем наш объектный файл с помощью скрипта stm32f103.ld, на выходе получаем elf файл.
Чтобы окончательно подготовить исполнимый elf файл к прошиванию, выполним последнюю команду:
arm-none-eabi-objcopy main.elf main.bin -O binary
Здесь мы преобразуем elf файл в чистый бинарный формат, пригодный для заливки в нашу плату.
Итак, наша первая программа для контроллера stm32 готова! Прошиваем!
st-flash write ./main.bin 0x08000000
Поздравляю! Теперь микроконтроллер обречен на вечное выполнение безусловного перехода. До следующей встречи!
Комментариев нет:
Отправить комментарий