Возможно, вы очень хотели заняться чем-нибудь, что связано с чипами, прошивками, возможно даже использовать новые знания для домашних поделок — мало ли вещей, которые можно автоматизировать, начиная от контроллера солнечной батареи и заканчивая автоматом запуска бензоагрегата? При этом, не сильно погружаясь в особенности операционных систем Linux / Windows и не используя сильно избыточные платы, такие как Raspberry Pi, BeagleBoard и подобные. Тогда добро пожаловать сюда: здесь мы запускаем микроконтролллер STM32, чистый «bare metal», никакой операционки, железо на расстоянии вытянутой руки и богатая собственная периферия.
В данной статье мы покажем то, чего не хватает в других публикациях в инете: не погружаясь в детали, схватим весь процесс разработки, начиная от простейшей программы Hello World и заканчивая ее прошивкой в контроллер и отладкой. Ну и конечно, как дань традиции, помигать светодиодом — без этого Hello World для микроконтроллеров получится совсем не солидным.
В этой статье не будет длинных листингов кода, которыми так славятся ресурсы по STM32, и не будет утомляющих скриншотов STM32CubeMX. Все будем описывать текстом — емко и кратко.
Набираем инструменты
Нам понадобятся приложения:
- STM32CubeMX — конфигуратор;
- текстовый редактор, например vim (категорически рекомендую освоить его);
- ST-LINK, которое состоит из двух утилит: st-flash для прошивки STM32 и st-util для отладки, или точнее st-util это настоящий gdb сервер;
- комплект для кросс-компиляции arm-none-eabi.
Сразу примечание: берем кросс-компилятор именно с «none», поскольку опять таки bare metal и другие варианты типа arm-linux-eabi нам не подходят, потому как подразумевают наличие операционной системы со всем набором сопутствующих библиотек, которые у нас будут отсутствовать напрочь.
Теперь у вас есть первая самостоятельная работа — найти и поставить эти приложения. Дальше я предполагаю, что на десктопе у вас стоит Linux, и изложение будет идти именно в этом ключе. Впрочем и для Windows процедуры будут отличаться не сильно.
Вторая самостоятельная работа — приобретение оборудования (хвала Али Экспрессу!). Нам понадобятся:
- плата с микроконтроллером STM32F103;
- программатор и отладчик St Link V2, похожий на флешку;
- миниатюрная платка адаптера UART — USB.
Цена вопроса за все про все — около 500р, ждать не больше месяца.
Да, чуть не забыл: закажите еще набор проводков с окончаниями «мама»: их удобно надевать на пины плат, получится очень аккуратно.
Roadmap
Окинем взором поле предстоящей деятельности и наметим наши шаги в крупную клетку. Именно в крупную, поскольку как мы договорились, в детали сильно погружаться не будем. На определенном этапе отсутствие именно этой крупной клетки сильно мешало мне, когда я застревал в многостраничных мануалах — деревьях, за которыми не видно леса. А этот лес у нас получится следующим.
Первым делом мы начнем использовать STM32CubeMX. Куб — это конфигуратор, который создает все файлы проекта со своей структурой каталогов. Если мы с самого начала знаем чего хотим, запустить его можно только один раз и потом про него забыть. Проект будет создан, мы будем менять код в программе, но структура останется. Еще раз — Куб это не среда разработки, а скажем так генератор шаблонов. Тем не менее сгенерированный им проект — вполне рабочий.
Сразу скажу, что Куб умеет формировать проекты для IDE разных типов. Мы, как поклонники vim и хардкора, никакими IDE пользоваться не будем — зачем нам эти бестолковые графические прослойки, заслоняющие реализацию? Редактор и Makefile — вот и все что нам нужно.
После того как структура проекта создана, мы внесем в исходники нечто похожее на вывод Hello World: должно же быть и какое-то наше участие в проекте. Сильно заморачиваться не будем, от нас потребуется буквально пара строчек. Сразу назовем наш проект сочно и звучно — hardcore.
После этого проект готов. Запускаем make и компилируем его, в результате чего создастся директория build и там появятся интересующие нас файлы: hardcore.bin — прошивка и hardcore.elf — исполняемый файл, который ценен тем что содержит отладочную информацию.
Следующим этапом прошиваем микроконтролллер:
1 |
st-flash write build/hardcore.bin 0x08000000 |
и после этого наша программа начнет работать. Мигающий на плате светодиод мы увидим и так, а чтобы посмотреть на заветную строчку Hello World которую выдает наше приложение, на компе нужно будет запустить терминал (конечно, если вы не забыли подключить адаптер UART — USB):
1 |
minicom -D /dev/ttyUSB0 |
Мы также можем начать отладку нашей программы — например, пройти ее пошагово, для этого запускаем st-util и потом отладчик arm-none-eabi-gdb.
Общая картина ясна, дело за подробностями.
Связи
Опишем как все это соединяется друг с другом, не забывая стыки железа и софта.
Во первых, нужно соединить программатор / отладчик St Link V2 (который я для краткости далее буду называть донглом) и плату микроконтроллера. Донглу нужно две линии — по одной, двунаправленной он будет передавать и принимать данные, по другой — выдавать сигнал синхронизации. Плюс земля и питание платы: это удобно, не нужно подключать к плате источник питания, оно будет от донгла через USB разъем компа.
В комплекте к донглу идет как раз четырехжильный кабель, которым мы и воспользуемся. Подключаться нужно к 20-пиновому JTAG разъему платы микроконтроллера. Схема подключения:
1 2 3 4 5 6 |
STLINK ST32 2 ----- SWDIO ----- 7 4 ----- SWCLK ----- 9 6 ----- +3.3V ----- 1 8 ----- GND ----- 20 |
Вставляем донгл в USB, и если мы все сделали правильно, на плате загорится светодиод питания, и — надо же! начнет мигать другой светодиод — кто-то за нас позаботился о прошивке тестовой программы. Ну ничего, мы ее все равно сотрем, когда будем записывать свою.
Если вы решили запитать плату STM32 от внешнего источника питания, контакт +3.3В подсоединять не нужно. И помните золотое правило: прежде чем подавать питание все «земляные» контакты должны быть уже соединены!
Проверим наличие связи с платой:
1 2 3 4 5 6 7 8 |
$ st-util st-util 1.4.0-41-ge147a8e 2018-08-01T19:12:57 INFO usb.c: -- exit_dfu_mode 2018-08-01T19:12:57 INFO common.c: Loading device parameters.... 2018-08-01T19:12:57 INFO common.c: Device connected is: F1 Medium-density device, id 0x20036410 2018-08-01T19:12:57 INFO common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x10000 bytes (64 KiB) in pages of 1024 bytes 2018-08-01T19:12:57 INFO gdb-server.c: Chip ID is 00000410, Core ID is 1ba01477. 2018-08-01T19:12:57 INFO gdb-server.c: Listening at *:4242... |
Если выходит сообщение такого типа, что устройство обнаружено, то все в порядке.
Мы использовали последовательную линию для работы с JTAG и нужно не забыть сказать об этом потом конфигуратору Куб, иначе никаких прошивок потом не получится. Сразу говорю — если вы попали на неправильный параметр, прошили плату и сбили эту настройку, это лечится длительным нажатием Reset платы )
Во-вторых, нам нужно соединить плату STM32 и терминал в компе, чтобы посылать на терминал строчку Hello World. Для этого соединим пин PB6 платы и пин Rx адаптера UART — USB, а также свяжем их «земляные» контакты. Почему именно этот пин? Забегая вперед — именно его нам выдаст Куб когда мы будем настраивать UART. Схема соединений:
1 2 3 4 |
UART ST32 2 Rx --------- PB6 (Tx) 4 GND ------------ GND |
Подключаем кабелем адаптер ко второму разъему USB, Линукс увидит его как устройство /dev/ttyUSB0. К этому устройству мы и подключаем терминал, как было показано выше.
Куб
Вот так с этими микроконтроллерами — столько возни из-за двух строчек кода. Запускаем Куб, и он первым делом потребует от нас указать, с каким микроконтроллером мы работаем. Если вы не ошиблись с заказом на Ali Express, то смело указывайте STM32F103C8Tx, в противном случае берите лупу и читайте обозначение на корпусе чипа. Далее быстро вспоминаем, какие две вещи мы должны проделать с конфигуратором.
Во первых, настройка коннектора JTAG. Открываем пункт SYS и устанавливаем Debug в состояние Serial Wire: это наш последовательный интерфейс связи платы с донглом St-Link V2.
Во-вторых, настраиваем UART: открываем USART1 и ставим Mode Asynchronous. Больше ничего делать не нужно. После этой манипуляции на вкладке Configuration появится кнопка USART1, где можно менять параметры порта. Заметим, что на схеме чипа появится метка у пина PB6: он будет назначен как передающий (Tx).
Каждый раз, когда в Кубе вы будете включать функцию, связанную с внешним миром, он будет задействовать пины (по своему усмотрению) и отмечать их зеленым цветом.
Вот в принципе и все. Заходим в Project/Settings, даем нашему проекту имя hardcore и в выпадающем меню Toolchain / IDE выбираем Makefile, как и договаривались — никаких IDE!
Единственное, что осталось, это сконфигурировать пин для мигания светодиодом. На моей плате светодиод уже разведен на пин PC13, поэтому кликаем на этот пин на изображении в Кубе и выбираем GPIO_Output. Если вы решите подключить светодиод к произвольному пину, точно также выберите его, только смотрите чтобы назначение не конфликтовало с другими пинами (Куб следит за этим). Не забудьте только токоограничивающий резистор и соблюдать правильную полярность.
Нажимаем на кнопку с шестеренкой, и наш проект готов: он появится в папке hardcore по тому маршруту, который вы указали в настройках.
Исходные файлы созданы, это вполне рабочий проект. Мы можем запустить make из директории hardcore, после этого исходники откомпилируются, скомпонуются и во вновь созданном каталоге build появятся исполнительные файлы hardcore.elf и hardcore.bin. Файл bin — это прошивка контроллера, файл в elf формате это исполнительный формат Linux. Конечно, исполнять его никто нигде не собирается — он нужен лишь постольку, поскольку в нем содержится отладочная информация: нумерация строк кода, имена переменных, функций и так далее. Нюанс: на самом деле собирается hardcore.elf, а потом уже из него утилитой objcopy извлекается прошивка hardcore.bin, избавляя последнюю от всего лишнего.
Та часть структуры проекта, которая представляет для нас интерес, лежит в каталогах Src/ и Inc/. Остальное трогать не нужно. Куб рассматривает созданное им дерево файлов и каталогов как свою безраздельную собственность, поскольку ему добавлять или удалять куски файлов в зависимости от того, как мы будем менять конфигурацию проекта. Он милостиво разрешает нам писать свой код в места обозначенные как /* USER CODE BEGIN */ и гарантирует, что трогать их не будет. Поэтому запускаем vim, открываем Src/main.c и ищем такой кусок в районе while(1).
Vim
Наверное несколько странно что после инициализации железа программа в main.c входит в бесконечный цикл ) А чего вы ждали, операционной системы нет, соответственно нет планировщика, и чем будет заниматься контроллер в холостом режиме — полностью наша забота. Поэтому после while(1), строго не выходя за рамки установленные нам Кубом, вставляем строчки кода таким образом:
1 2 3 4 5 6 7 8 9 |
/* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } |
Первая строчка будет переключать светодиод, вторая — создавать задержку между переключениями в полсекунды, итого светодиод будет мигать с частотой раз в секунду.
Поскольку помимо помигивания светодиодом мы решили выводить настоящую текстовую строку, сделаем это командой
1 |
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello World\r\n", 13, 0xFFFF); |
где 13 — длина нашей строки, подсчитанная вручную (вот ведь, всплыла чертова дюжина).
Команду можно вбить перед задержкой.
Когда выше я сказал, что можно запустить make, я несколько слукавил. Makefile для работы не хватает информации о том, где расположены все инструменты для сборки — toolchain. Обычно для этого задают переменную окружения GCC_PATH, но у меня на машине несколько разных компиляторов, поэтому я указал эту переменную в самом Makefile. Выглядит это так:
1 |
GCC_PATH = /opt/arm-none-eabi/bin |
Ваши инструменты могут быть в другом месте, у меня они находятся под каталогом /opt.
Теперь запускаем make, и наша прошивка готова. Результаты работы — объектные и исполняемые файлы создаются в каталоге build.
Прошивка микроконтроллера STM32
Надеюсь, к этому моменту вы осилили соединение донгла St-Link V2 и платы микроконтроллера. Вставляем St-Link V2 в usb и начинаем прошивку.
Прошивать будем из командной строки. Если вы в папке hardcore, запускайте команду:
1 |
$ st-flash write build/hardcore.bin 0x8000000 |
программатор выведет на экран результаты своей работы и если все нормально, завершит торжествующей фразой jolly good.
Признаком хорошего тона будет выполнять прошивку по команде make install. Для этого добавим в конец Makefile пару строк:
1 2 |
install: st-flash write $(BUILD_DIR)/$(TARGET).bin 0x8000000 |
Внимание: перед st-flash должна быть табуляция а не пробелы! make весьма строго относится к этому, и если вы напутаете это может быть источником трудно распознаваемой ошибки.
Теперь процесс прошивки будет выглядеть как make install. Со временем вы можете сами дополнить Makefile, чтобы по одной этой команде также выполнялась сборка.
Очистка всего проекта с удалением каталога build выполняется соотвествующей командой make clean.
Запуск
После прошивки микроконтроллер запускается (если он притормозил, дайте пинка кнопкой reset на плате), и наблюдайте мигающий светодиод.
Дальше, если вы не накосячили с подключением UART, после подключения адаптера вы увидите устройство /dev/ttyUSB0. Usb теперь стало стандартным последовательным устройством unix, поэтому с ним можно работать с терминальной программой. Запускаем ее:
1 |
$ minicom -D /dev/ttyUSB0 |
Настройки терминала менять не нужно. Значение скорости 115200 по умолчанию подходит, также подходят установки количества бит и признака четности.
Если все сделано правильно, адаптер будет подмигивать на каждую передаваемую строку, а на экране вы увидите строчки «Hello World» каждую секунду.
На самом деле, вы сделали большой шаг к тому чтобы с комфортом работать с микроконтроллером, поскольку теперь у вас появилась возможность выводить в окно то что происходит внутри.
Отладка
До сих пор поклонники IDE наблюдали за нами со сжатыми зубами, дожидаясь момента когда нам надо будет отлаживать программу. Вот тут они и выкинут свой главный козырь: а где подсветка строки, точки останова, отображение переменных? На самом деле, в реальной жизни, все это гораздо проще реализуется из командной строки. Перед глазами нет ничего лишнего, а только то на чем необходимо сконцентрировать внимание.
Перед тем как двинуться дальше, сделаем небольшой экскурс в кроссплатформенную отладку. Если вы пользовались знаменитым отладчиком gdb на своей станции, то скорее всего для вас было само собой разумеющимся то, что результаты отладки и само исполнение программы происходит на одной и той же машине. Однако, в общем случае это не так. Для нашей кроссплатформенной системы мы запускаем отладчик на своей PC Ubuntu, а программа работает на микроконтроллере STM32. Как все это должно быть состыковано вместе?
Все это уже предусмотрено в gdb. Мы даже не подозреваем о том, насколько это мощное приложение. В нашем случае мы будем использовать возможности работы отладчика в режиме клиент — сервер. Клиент — это приложение gdb на нашей машине, а сервер… занавес открывается… в качестве сервера будет выступать наш донгл St-Link V2. Не забываем о том, что помимо программатора это также gdb — сервер!
Принцип отладки выглядит следующим образом. Донгл St-Link V2 работает с платой микроконтроллера по интерфейсу JTAG и имеет возможность запускать и останавливать исполнение программы аппаратным способом. Все таки будет приятно знать, что когда мы соединяли его проводками, мы получили не только программатор но еще оказывается и отладку! С другой стороны, gdb сервер донгла запускается утилитой st-util и начинает прослушивать порт 4242 на нашей машине (значение порта по умолчанию можно поменять в командной строке). Мы со своей стороны запускаем отладчик gdb также на нашей машине, и указываем ему приконнектиться к gdb серверу донгла. С этого момента запущенный нами gdb становится клиентом, и все команды которые мы будем вводить в командной строке, будут исполняться St-Link V2.
До этого момента для краткости изложения я говорил про приложение gdb, но на самом деле конечно же мы используем не родной отладчик Ubuntu, а кроссплатформенный отладчик который идет в составе toolchain:
1 |
/opt/arm-none-eabi/bin/arm-none-eabi-gdb |
Общий принцип теперь ясен, переходим к деталям. Чтобы не плодить консоли, запускаем gdb сервер донгла St-Link V2 в фоновом режиме:
1 |
$ st-util & |
Запускаем в этой же консоли кроссплатформенный отладчик
1 |
$ /opt/arm-none-eabi/bin/arm-none-eabi-gdb |
или просто arm-none-eabi-gdb, если вы включили маршрут /opt/arm-none-eabi/bin в переменную окружения PATH. Отладчик выводит приглашение (gdb) и переходит в интерактивный режим.
Первым делом цепляемся к gdb-серверу донгла:
1 |
(gdb) target remote localhost:4242 |
С этого момента появляется возможность задать специальные команды для сервера через ключевое слово monitor; первое что сделаем — это сбросим и остановим микроконтроллер:
1 |
(gdb) monitor reset halt |
Сразу замечу, что с помощью команды monitor можно также выполнить прошивку, но мы этого делать не будем, потому что уже используем более понятный способ. Далее, загружаем отладочную информацию:
1 |
(gdb) file build/hardcore.elf |
Чтобы не вводить эти команды каждый раз при запуске отладчика, поместите их в файл debug.txt и запускайте в командной строке
1 |
$ /opt/arm-none-eabi/bin/arm-none-eabi-gdb -x debug.txt |
Подчеркну еще раз, как мы используем elf и bin файлы в кроссплатформенной отладке: bin файл не нужен отладчику, поскольку он уже прошит в контроллере. Из elf файла ему нужна только отладочная информация, код из elf — файла не запускается.
После того как мы подключились к gdb серверу, отладка идет в обычном режиме. Ставим точку останова на функции main() и запускаем на выполнение:
1 2 |
(gdb) b main (gdb) cont |
Точку останова можно поставить и на номер строки (включите отображение номеров строк в vim).
Осмотримся:
1 |
(gdb) list |
Продолжим выполнение в пошаговом режиме:
1 |
(gdb) n |
или в полном написании next. Если мы хотим зайти в функцию, вместо next используем step. Чтобы выйти из функции не дожидаясь пошагового выполнения, задаем finish.
Краткая памятка команд отладчика:
1 2 3 4 5 6 7 8 |
break: ставим точку останова на функцию или строку next: пошагово, не заходя в функции step: пошагово, заходим в каждую функцию по дороге finish: выходим из функции с выполнением оставшегося кода continue: идем пока не встретится точка останова until: идем до номера строки backtrace: смотрим, как мы здесь оказались: что было до этого list: смотрим исходник там, где остановились |
Если вы что-то подзабыли, смело просите о помощи, например так:
1 |
(gdb) help watch |
и отладчик скажет, что команда watch EXPRESSION обеспечивает остановку программы если значение выражения EXPRESSION поменяется. В отладчике очень много таких полезных фишек, пользуйтесь ими!
И последнее. Запускайте отладчик с ключом -tui чтобы сразу видеть на экране исходник своей программы. И больше вам не нужны никакие IDE ) Более того, сейчас вы точно представляете себе, как все эти куски взаимодействуют вместе. Иначе все тонкости реализации были скрыты за фасадом какого-нибудь тормознутого Eclipse.
Успехов в ваших начинаниях!
Отличная статья! Емко и доходчиво.
Ранее встречался сайт с тематикой STM32 http://ziblog.ru/category/mcu/stm32/
где описание начиналось с подготовки Make файла вручную, что немного усложняло процес вхождения.
STM32CubeMX призван сэкономить много времени. 🙂
Если не секрет что планируете реализовать на STM32?
Интересный сайт. Пригодится тем кто не использует HAL, все описано очень подробно.
STM32 вначале появился вынужденно в одном потенциальном проекте, где есть жесткое требование использовать только отечественную комплектацию. Из того, что можно использовать и что выпускает например Миландр, STM32 подошел как ближайший аналог. Точнее, Миландр выпускает аналоги STM32F103, пока неизвестно, с какой степенью приближенности.
Поскольку я наткнулся на этот микроконтроллер и у меня есть привычка опробовать различные технологии, чтобы знать их возможности и ограничения, я взял его поиграться.
По ходу процесса выяснилось, что есть определенный рынок (системы запуска бензоагрегатов, АВР, электрические автоматы работающие по программе, инверторы для систем отопления), в котором хорошо ориентируются люди с электрической специализацией, но микроконтроллер это для них уже сложно. Поэтому есть еще поле для бытового применения )
Извените за глупый вопрос.
ARM tool chain (arm-none-eabi) использовали с этого сайта:
https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
или другой ARM tool chain?
Нормальный вопрос )
Я брал toolchain из ветки какой-то IDE на базе Eclipse, точно не помню. Но думаю что по вашей ссылке находится тоже самое
Вспомнил. У меня стоял System Workbench AC6 на базе Eclipse, и в его недрах я нашел arm-none-eabi. Позже я отказался от использования IDE, а ветку с toolchain скопировал отдельно в /opt и в Makefile’s давал ссылки на эту ветку. У меня она выглядит следующим образом:
$ tree -L 2 arm-none-eabi/
arm-none-eabi/
├── arm-none-eabi
│ ├── bin
│ ├── include
│ ├── lib
│ └── share
├── bin
│ ├── arm-none-eabi-addr2line
│ ├── arm-none-eabi-ar
│ ├── arm-none-eabi-as
│ ├── arm-none-eabi-c++
│ ├── arm-none-eabi-c++filt
│ ├── arm-none-eabi-cpp
│ ├── arm-none-eabi-elfedit
│ ├── arm-none-eabi-g++
│ ├── arm-none-eabi-gcc
│ ├── arm-none-eabi-gcc-6.3.1
│ ├── arm-none-eabi-gcc-ar
│ ├── arm-none-eabi-gcc-nm
│ ├── arm-none-eabi-gcc-ranlib
│ ├── arm-none-eabi-gcov
│ ├── arm-none-eabi-gcov-dump
│ ├── arm-none-eabi-gcov-tool
│ ├── arm-none-eabi-gdb
│ ├── arm-none-eabi-gdb-py
│ ├── arm-none-eabi-gprof
│ ├── arm-none-eabi-ld
│ ├── arm-none-eabi-ld.bfd
│ ├── arm-none-eabi-nm
│ ├── arm-none-eabi-objcopy
│ ├── arm-none-eabi-objdump
│ ├── arm-none-eabi-ranlib
│ ├── arm-none-eabi-readelf
│ ├── arm-none-eabi-size
│ ├── arm-none-eabi-strings
│ └── arm-none-eabi-strip
├── CHANGES
├── lib
│ ├── gcc
│ ├── libcc1.so -> libcc1.so.0.0.0
│ ├── libcc1.so.0 -> libcc1.so.0.0.0
│ └── libcc1.so.0.0.0
└── share
├── doc
└── gcc-arm-none-eabi
Для удобства отладки с gdb удобно использовать svd файлы описания процессора. Подходящее описание https://illumium.org/node/137
Интересная возможность. Надо будет поэкспериментировать
гм.. а на асме кто нить пишет для stm32? а то я тут себе замутил целый редактор, с возможностью отладки по st-link (китайскому).. правда программа под win
Наверняка пишут, кому нужно поближе к железу