Собственно идея написать эту статью как памятку себе любимому, ну может ещё кому пригодится пришла в голову год назад, после того как убил немало времени на это нехитрое занятие. Недавно оказалось, что проблема актуальна по сей день. Почему-то ни один из найденных вариантов сам по себе не помогает и данная статься является результатом обработки всей найденной информации. При решении вопроса, больше всего бесило - возьмите мой проект и будет вам счастье, а проекта там уже и нет... Такой подход я плохо переношу, поэтому и сам делать так не буду.
Всё ниже описанное является следствием моего личного опыта, и ни на какую истинность не претендует. Все советы рассчитаны не людей только решившихся на переход с AVR на STM32
Вопросы типа почему Linux, VSCode и прочее, думаю, освещения не требуют. Считаю, что все заинтересованные в вопросе, на эти мелочи давно нашли СВОЙ ответ. Однако отмечу, в Винде всё это тоже работает, проверено, и проекты спокойно переживают миграцию между машинами.
Пожалуй начнём!
Этап установки VSCode опущу, этого добра настролько навалом, что даже чересчур. Скажу только, что минимально нужно поставить модули C/C++ от Microsoft (считаю, что плагин от крупного автора имеет больше шансов на долгую жизнь), Cortex-Debug от marus25 (альтернатив пока нет) и Makefile Tools от Microsoft (анализирует Makefile и сам настраивает IntelliSense, давно ждал такую штуку, было хоть сам пиши).
Самый простой способ получить заготовку проекта - собрать его в CubeMX. Процесс интуитивно понятный, сотни раз описанный в сети, поэтому отвлекаться на него не буду. Отмечу пару важных моментов.
Нужно не забыть в настройках порта разрешить отладочную шину
![](https://habrastorage.org/getpro/habr/upload_files/abb/6dc/31c/abb6dc31c01c3d82413968f9272555b3.png)
Очень беспонтовая ошибка. По умолчанию все выводы переводятся на вход и после первой прошивки у Вас отваливается программатор. Так как у всех начинающих программатор - китайский минимальный клон, то никакого сброса для STM32 там нет и что дальше делать не всем понятно.
Решение довольно простое - нужно исправить досадную ошибку и запустить процесс программирования или стирания удерживая кнопку сброса. Когда программа будет выполнять повторы подключения, отпустить сброс. Получиться может не с первого раза, но обязательно получится. Ещё на днях подсказали способ - переключить джамперы загрузки на положение отличное от Flash, что бы не дать запуститься нашей косячной программе и переписать камень из такого положения (сам не пробовал).
Как и у меня в своё время, у большинства начинающих в распоряжении так называемая Синяя Пилюля, поэтому давайте включим выход к которому подключен светодиод (PC13).
![](https://habrastorage.org/getpro/habr/upload_files/e4a/cb1/99f/e4acb199f39222d7c3b927dfd0cc84ab.png)
Так как светодиод через резистор подключен к питанию, то вывод настроим в режим Open-Drain, хотя от Push-Pull хуже не будет.
Настройку тактировки мне приводить лень, пхать сюда огромную бессмысленную картинку не вижу смысла, а от уменьшенной смысла ещё меньше. К тому же CubeMX предлагает сам настроить все коэффициенты, да и для ручного режима всё более чем интуитивно понятно.
Поэтому завершаем работу с CubeMX задав имя проекта и обязательно переключив *Toolchain/IDE* на *Makefile*
![](https://habrastorage.org/getpro/habr/upload_files/ac0/b2a/d7a/ac0b2ad7a87d918cc369412ccf3b4bf9.png)
После генерации проекта, в целевом каталоге получаем вот такой набор файлов.
![](https://habrastorage.org/getpro/habr/upload_files/17a/23c/f01/17a23cf01d0e24ec8a53a093775ab8c8.png)
Всё бы хорошо, но для отладки нам необходим SVD-файл, в нём информация для отладчика, необходимая для человеческого отображения регистров периферии, ядра и т.п.
Где взять этот файл? С одной стороны всё довольно быстро ищется, но не всегда это будут свежие версии. Лучше всего взять с сайта ST. Идём на st.com, дальше Products -> Microcontrollers & Microprocessors и ищем там свой проц. В нашем случае STM32 32-bit Arm Cortex MCUs -> STM32 Mainstream MCUs -> STM32F1 Series -> STM32F103
![](https://habrastorage.org/getpro/habr/upload_files/311/def/ef9/311defef92a8b2c59645acbb4d1b5c47.png)
Тут можно найти всю возможную документацию по процессору, но сейчас нас интересует вкладка CAD Resources. На ней находим HW Model, CAD Libraries & SVD -> System View Description. Скачиваем найденый архив. Для ленивых ссылка на страницу. В архиве найдёте несколько svd-файлов. Нас сейчас интересует - STM32F103.svd. Не долго думая кидаем его в папку с проектом.
Пришло время запустить VSCode!!! Лично я предпочитаю в нужном каталоге дать команду code ., а там каждому своё... Программа спросит, доверяете ли вы авторам этого каталога, придётся сказать, что доверяете. Makefile Tool проанализирует Makefile, о чём выдаст много текста в панели, и настроит IntelliSense. Ещё не так давно приходилось его настраивать вручную, не сложно, но не интересно.
Не забываем сохранить Рабочую Область!!!
Теперь немного дополняем Makefile - добавляем ещё одну цель prog
prog: $(BUILD_DIR)/$(TARGET).elf
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "program build/$(TARGET).elf verify exit reset"
Небольшое пояснение. Скрипты interface/stlink-v2.cfg и target/stm32f1x.cfg позволяют openocd узнать через какой адаптер и с кем он должен работать. Сами скрипты распологаются в каталоге scripts с установленным openocd (для Винды) или в каталоге /usr/share/openocd в Linux. Приведённый пример работает для китайского клона ST-Link v2 и Синей Пилюли. Если у Вас другой программатор или камень, то необходимые файлы найдёте в тех же каталогах.
Собственно зачем нужна эта байда? Иногда нужно записать программу в процессор не запуская отладку - просто записать и посмотреть, что произойдёт. Вот за это и отвечает -c "program build/$(TARGET).elf verify exit reset
Следующим этапом я настраиваю git, но это по вкусу. Ну как настраиваю, создаю файл *.gitignore* ну и инициализацию репозиторий. Пример .gitignore
DSP/
Templates/
Examples/
NN/
/build/
*.log
*.d
.mxproject
*.ioc
по списку поясню только каталог build - в него производится сборка проекта, поэтому чтобы не отвлекаться на него и случайно не нагадить в коммит его и игнорим. Логи любит создвать Makefile Tools, они мне тоже ни к чему.
Если очень чешутся руки, то на данном этапе уже можно в терминале VSCode дать команду `make prog`, соберётся Ваш пустой проект и запишется в процессор. Процесс сборки будет отображаться здесь же в терминале, а об успешной записи Вам сообщит openocd примерно вот так
...
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : clock speed 1000 kHz
Info : STLINK V2J37S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.169781
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000c74 msp: 0x20005000
** Programming Started **
Info : device id = 0x20036410
Info : flash size = 64kbytes
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
shutdown command invoked
Желающие почесали руки, продолжаем. В каталоге .vscode проекта нужно создать файл tasks.json примерно такого содержания
{
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"group": "build",
"command": "make",
"problemMatcher": []
},
{
"label": "Clean",
"type": "shell",
"group": "build",
"command": "make clean",
"problemMatcher": []
},
{
"label": "Write firmware",
"type": "shell",
"command": "make prog",
"problemMatcher": []
}
]
}
Собственно здесь объявляются три задачи: сборка проекта, очистка рабочего каталога и прошивка контроллера. Можно конечно и здесь расписать все команды с параметрами, раньше так и делал, но потом стало лень дублировать команды, следить что бы они были одинаковыми в Makefile и тут... Ну в общем остановился на таком варианте. Работает же...
Теперь создаём launch.json c вот таким содержимым
{
"version": "0.2.0",
"configurations": [
{
"name": "openocd",
"type": "cortex-debug",
"request": "launch",
"cwd": "${workspaceRoot}",
"servertype": "openocd",
"executable": "./build/Habr.elf",
"svdFile": "STM32F103.svd",
"configFiles": [
"interface/stlink-v2.cfg",
"target/stm32f1x.cfg"
],
"preLaunchTask": "Build"
}
]
}
Страшная штука. Собственно "name" может иметь любое значение, оно влияет только на отображение. Поле "executable" должно иметь значение файла в который собирается ваша программа, с указанием пути от каталога проекта. У Вас оно своё, поэтому вместо Habr должно быть то, чему у вас равно TARGET = в Makefile. В "svdFile" прописываем svd файл нашего процессора. Если вы его положили не в корень проекта, то нужно указать путь от каталога проекта. В "configFiles" указываем те самые скрипты, которые мы добавляли в Makefile для прошивки контроллера. И последний момент "preLaunchTask": "Build" - запуск задачи сборки программы из tasks.json, что бы было чего прошивать и отлаживать.
А теперь момент без которого всё это добро не работает - необходимо сообщить программе, где лежат бинарники openocd и gdb, причём не пути к каталогам с ними, а именно сами программы. Сделать это нужно в глобальном файле settings.json, пока так стабильно работает. Так как сам этот файл слегка закопан, к тому же, в Linux и Windows находится в разных местах, то я для себя нашёл простой способ его открыть. Идём Файл -> Настройки -> Параметры, откроется вкладка с настройками. Далее Текстовый редактор -> Шрифт и ищем надпись Изменить в settings.json. В открывшийся файл нужно добавить следующие строчки
"cortex-debug.armToolchainPath": "usr/bin",
"cortex-debug.openocdPath": "/usr/bin/openocd",
"cortex-debug.gdbPath": "/usr/bin/gdb-multiarch",
"cortex-debug.armToolchainPath" - просто путь к каталогу в котором ваш *arm-none-eabi-gcc*, вот тут просто каталог
"cortex-debug.openocdPath" - полный путь к бинарнику openocd, для винды будет заканчиваться на .exe
"cortex-debug.gdbPath" - полный путь к бинарнику gdb котрым Вы будете пользоваться
Ну и финишная прямая.
В файле main.c чуть-чуть меняем функцию main() на вот такое
int main(void)
{
uint32_t tempTick;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
__ASM("NOP");
while (1)
{
tempTick = HAL_GetTick();
while (HAL_GetTick() - tempTick < 500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);
tempTick = HAL_GetTick();
while (HAL_GetTick() - tempTick < 500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
}
}
Честно говоря страшнее кода придумать с ходу не смог. Хочу просто показать, что ЭТО ВСЁ может жить.
Теперь на __ASM("NOP"); ставим точку останова, переходим "Запуск и Отладка" и запускаем всё это добро, нажав на зелёный треугольничек.
![](https://habrastorage.org/getpro/habr/upload_files/72a/1f4/23a/72a1f423a40540a7529f8f3a02ec8574.png)
В Терминале побежали надписи процесса компиляции и через пару секунд всё остановится на вот такой картинке
![](https://habrastorage.org/getpro/habr/upload_files/f4a/f96/ede/f4af96ede057acdd757540c1068af026.png)
ПОЗДРАВЛЯЮ, ВЫ В ОТЛАДКЕ!!!
Можете просто нажать F5 или значок проигрывания и наслаждаться мигающим светодиодом!
На этом у меня всё! Можно было бы ещё рассказать про настройку IntelliSense, но во-первых такого добра и так навалом, а во-вторых проблема уже частично решается плагином Makefile Tools, хоть пока и неидеально, но для начала хватает.