Собственно идея написать эту статью как памятку себе любимому, ну может ещё кому пригодится пришла в голову год назад, после того как убил немало времени на это нехитрое занятие. Недавно оказалось, что проблема актуальна по сей день. Почему-то ни один из найденных вариантов сам по себе не помогает и данная статься является результатом обработки всей найденной информации. При решении вопроса, больше всего бесило - возьмите мой проект и будет вам счастье, а проекта там уже и нет... Такой подход я плохо переношу, поэтому и сам делать так не буду.
Всё ниже описанное является следствием моего личного опыта, и ни на какую истинность не претендует. Все советы рассчитаны не людей только решившихся на переход с AVR на STM32
Вопросы типа почему Linux, VSCode и прочее, думаю, освещения не требуют. Считаю, что все заинтересованные в вопросе, на эти мелочи давно нашли СВОЙ ответ. Однако отмечу, в Винде всё это тоже работает, проверено, и проекты спокойно переживают миграцию между машинами.
Пожалуй начнём!
Этап установки VSCode опущу, этого добра настролько навалом, что даже чересчур. Скажу только, что минимально нужно поставить модули C/C++ от Microsoft (считаю, что плагин от крупного автора имеет больше шансов на долгую жизнь), Cortex-Debug от marus25 (альтернатив пока нет) и Makefile Tools от Microsoft (анализирует Makefile и сам настраивает IntelliSense, давно ждал такую штуку, было хоть сам пиши).
Самый простой способ получить заготовку проекта - собрать его в CubeMX. Процесс интуитивно понятный, сотни раз описанный в сети, поэтому отвлекаться на него не буду. Отмечу пару важных моментов.
Нужно не забыть в настройках порта разрешить отладочную шину
Очень беспонтовая ошибка. По умолчанию все выводы переводятся на вход и после первой прошивки у Вас отваливается программатор. Так как у всех начинающих программатор - китайский минимальный клон, то никакого сброса для STM32 там нет и что дальше делать не всем понятно.
Решение довольно простое - нужно исправить досадную ошибку и запустить процесс программирования или стирания удерживая кнопку сброса. Когда программа будет выполнять повторы подключения, отпустить сброс. Получиться может не с первого раза, но обязательно получится. Ещё на днях подсказали способ - переключить джамперы загрузки на положение отличное от Flash, что бы не дать запуститься нашей косячной программе и переписать камень из такого положения (сам не пробовал).
Как и у меня в своё время, у большинства начинающих в распоряжении так называемая Синяя Пилюля, поэтому давайте включим выход к которому подключен светодиод (PC13).
Так как светодиод через резистор подключен к питанию, то вывод настроим в режим Open-Drain, хотя от Push-Pull хуже не будет.
Настройку тактировки мне приводить лень, пхать сюда огромную бессмысленную картинку не вижу смысла, а от уменьшенной смысла ещё меньше. К тому же CubeMX предлагает сам настроить все коэффициенты, да и для ручного режима всё более чем интуитивно понятно.
Поэтому завершаем работу с CubeMX задав имя проекта и обязательно переключив *Toolchain/IDE* на *Makefile*
После генерации проекта, в целевом каталоге получаем вот такой набор файлов.
Всё бы хорошо, но для отладки нам необходим SVD-файл, в нём информация для отладчика, необходимая для человеческого отображения регистров периферии, ядра и т.п.
Где взять этот файл? С одной стороны всё довольно быстро ищется, но не всегда это будут свежие версии. Лучше всего взять с сайта ST. Идём на st.com, дальше Products -> Microcontrollers & Microprocessors и ищем там свой проц. В нашем случае STM32 32-bit Arm Cortex MCUs -> STM32 Mainstream MCUs -> STM32F1 Series -> STM32F103
Тут можно найти всю возможную документацию по процессору, но сейчас нас интересует вкладка 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"); ставим точку останова, переходим "Запуск и Отладка" и запускаем всё это добро, нажав на зелёный треугольничек.
В Терминале побежали надписи процесса компиляции и через пару секунд всё остановится на вот такой картинке
ПОЗДРАВЛЯЮ, ВЫ В ОТЛАДКЕ!!!
Можете просто нажать F5 или значок проигрывания и наслаждаться мигающим светодиодом!
На этом у меня всё! Можно было бы ещё рассказать про настройку IntelliSense, но во-первых такого добра и так навалом, а во-вторых проблема уже частично решается плагином Makefile Tools, хоть пока и неидеально, но для начала хватает.
Комментарии (35)
stephanthe
26.10.2021 08:26+1Плагин PlatformIO к VSCode не рассматривали? Я поставил его, а от Куба полностью отказался.
jogick Автор
26.10.2021 08:31+6Я смотрел на PlatformIO, но решил отказаться. Очень не люблю когда на двадцать строк кода добавляется пол-мегабайта служебных файлов. И от меня прячется основная часть работы. По этой причине я в своё время отказался от AVR CodeVision, потом от AVR Studio, потом от Eclipse. Сам PlatformIO показался громоздким и избыточным. Опять же этомоё мнение и никого ни к чему не призываю.
stephanthe
26.10.2021 08:59+1Мне кажется, что куб прячет гораздо больше и добавляет в проект несколько мегабайт файлов. Причём я если при генерации проекта выбирал везде LL, и копировать только используемые файлы, он мне все равно в папку проекта кроме CMSIS накидывает и HAL, и main.c создаёт довольно не слабый. Может я конечно кубик готовить не умею. Я использую CMSIS, и всю инициализации пишу сам, а платформио библиотеки в проект не копирует, и вся папка проекта у меня килобайт на 60 выходит.
remzalp
26.10.2021 09:15А компилированный проект при этом тяжелый или сравнительно лёгкий?
Кубик про запас накидывает всё необходимоеjogick Автор
26.10.2021 09:18Если посмотрите Makefile, то там подключаются только те файлы исходников, которые используются, а дальше всё на совести gcc.
stephanthe
26.10.2021 10:57#include "stm32f4xx.h" #define BLUE (15) #define ORANGE (13) int main() { RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN | RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN; // для выбора порта прерывания обязательно включить тактирование контроллера системного конфигурирования RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; GPIOD->MODER |= GPIO_MODER_MODER13_0 | GPIO_MODER_MODER15_0; GPIOC->PUPDR |= GPIO_PUPDR_PUPDR6_0; // подтягивающий резистор к питанию EXTI->RTSR |= EXTI_RTSR_TR0; // прерывание по 0 линии по повышению (rising) EXTI->FTSR |= EXTI_FTSR_TR6; // прерывание по 6 линии по падению (falling) EXTI->IMR |= EXTI_IMR_MR0 | EXTI_IMR_MR6; // снимаем маскирование прерываний по линиям 0 и 6 SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PA; // прерывание по 0 линии с порта A SYSCFG->EXTICR[1] |= SYSCFG_EXTICR2_EXTI6_PC; // прерывание по 6 линии с порта C NVIC_SetPriority(EXTI0_IRQn, 15); NVIC_SetPriority(EXTI9_5_IRQn, 10); NVIC_EnableIRQ(EXTI0_IRQn); NVIC_EnableIRQ(EXTI9_5_IRQn); for(;;){ asm("NOP"); } } void EXTI0_IRQHandler(void) { EXTI->PR = EXTI_RTSR_TR0; GPIOD->ODR ^= (1 << BLUE); } void EXTI9_5_IRQHandler(void) { EXTI->PR = EXTI_RTSR_TR6; GPIOD->ODR ^= (1 << ORANGE); }
Мне не с чем сравнивать, я начинаю только. Сейчас разбираюсь с прерываниями. Два светодиода, две кнопки, два прерывания. Код в платформио выглядит так. Файл bin 884 байта.
remzalp
26.10.2021 11:34+1Бинарник вполне компактный. Минимальный размер. Можете еще утрамбовать, но там уже придётся руками адреса прописывать, плюс в ассемблер удариться.
stephanthe
26.10.2021 11:58Спасибо за совет, но думаю мне пока рано. Первый месяц в микроконтроллеры ударился. Но может через какое-то время и до ассемблера доберусь. Адреса вручную есть смысл? Ведь все определения в CMSIS задефайнены, и вроде на размер влиять не должны. Разве что функции работы с прерываниями заменить на запись в регистры.
remzalp
26.10.2021 12:25+1Не надо так увлекаться гипероптимизацией. Чаще всего если немного памяти не хватает - если нет специфических требований - проще взять контроллер постарше, благо они по пинам совместимые.
VelocidadAbsurda
27.10.2021 02:05Как вы оценили минимальность? Сам вот этот видимый код выше со включённой оптимизацией оттранслируется во что-то порядка 200 байт. А остальное — раздутые startup, SystemInit и прочие чудеса, заботливо добавляемые Кубом до вызова main, о существовании которых немало начинающих и не подозревает, т.к. их код туда не ссылается.
jogick Автор
26.10.2021 09:16Я тоже выкидываю из Кубовского проекта всё безбожно, меня от него интересуют только свежие библиотеки и начальная инициализация тактировки, HAL выпиливаю начисто.
DSarovsky
26.10.2021 17:12Мне кажется, странно предъявлять претензий к PlatformIO по поводу разрастания проекта и тут же использовать HAL, который тоже тащит кучу всего, причем прямиком в прошивку.
AKP1
26.10.2021 09:06+1Пробовали STM32CubeIDE? Или не рассматривали так как основано на eclipse? Просто всё-равно использовали CubeMX для конфигурации, а в обозначенной IDE он встроен
jogick Автор
26.10.2021 09:13STM32CubeIDE я смотрел когда тот ещё был совсем маленьким и запустить проект на Eclipse было проще чем на STM32CubeIDE. К тому же тогда его не было для Linux. Сейчас для меня основным набором являются Си для микроконтроллеров (STM8, STM32 AVR) и Python для служебных утилит. Если учесть что я перемещаюсь между Window, Debian и Raspberry (да, я использую RPI 4 8 Gb как нормальную машину) то полная кросплатформенность для меня важна.
remzalp
26.10.2021 12:26Уже пару лет как вполне хорошо с CubeID, посмотрите еще раз.
count_enable
26.10.2021 14:02+1Уже пару лет как всё очень плохо с STM32CubeIDE.
Оно медленное. Оно очень плохо работает с Makefile-проектами. Оно постоянно качает гигабайты обновлений.
Вы скачали CubeIDE и решили попрограммировать. Выбираете процессор или плату и оно начинает качать библиотеки - так мегабайт по 700. Поменяли семейство? Опять качать ВСЁ. Невозможно выбрать компоненты, оно скачает весь BSP, примеры, библиотеки для фат, фриртос, аудио, сенсорных экранов и ещё 100500 компоненотов.
А через пару недель прилетает обновление и пожалуйте качать заново библиотеки для старых проектов. С STM32CubeIDE не бывает так что его просто можно запустить и начать программировать. Впрочем, у других производителей ситуация не лучше, а наверно ещё хуже. При таком качестве бесплатного софта не удивительно что IAR прямиком из 1998 года продают за большие деньги и он народу нравится.
Punk_Joker
26.10.2021 17:13+1IAR комуто нравиться? Не знал этого.
count_enable
26.10.2021 19:04+2Интерфейс топорный, плюшек типа автокомплита и интеграции с гитом нету, но работает очень стабильно, быстро, и имеет кучу плагинов для отладки (например для ОСРВ). И конечно же нету добровольно-принудительных обновлений посреди рабочего дня и всплывающих "помощников". Он для тех кому ехать, а не шашечки.
jogick Автор
26.10.2021 21:38Не знаю как IAR, а вот с Keil'а пытаемся слезть на описанную в статье связку. Сейчас занимаемся корректировкой рабочего проекта.
Gerrero
27.10.2021 10:08+1Там вроде свой компилятор какой-то крутой плюс очень много всяких плюшек в виде плагинов.Очень мощная штука для серьезных вещей. Есдинственный "минус" - интерфейс.
grmile
26.10.2021 21:22По RPI как впечатления? Не тормозит? Намного дольше собирает?
jogick Автор
26.10.2021 21:28Смотря с чем сравнивать. Первая сборка идёт дольше, но потом пересобираются только изменённые файлы, а это один - два. Если учесть, что большую часть времени тупишь над котодом или смотришь результат в режиме отладки, то если время полной сборки будет пятнадцать секунд, а не десять - я переживу.
Всё остальное работает как и на большой машине.
grmile
27.10.2021 09:09Если сравнивать с настольником или лептопом. Когда сборка занимает десять секунд то разница небольшаяб у меня проект большой, сборка в один поток 5-6 минут, хотелось бы прикинуть во сколько раз дольше это будет на малине.
d_suslov
26.10.2021 16:29Идём Файл -> Настройки -> Параметры, откроется вкладка с настройками. Далее Текстовый редактор -> Шрифт и ищем надпись Изменить в settings.json.
Можно проще перейти в файл с настройками: ctrl+shift+P -> Open settings (JSON)
RollerBob
26.10.2021 21:28А ещё есть прекрасное расширение для VSCode STM32-for-vscode, которое из подготовленного кубом проекта и мэйкфайла конфигурирует task.json, settings.json, launch.json, c_cpp_properties.json. А также может сам скачать и настроить openocd и arm-none-eabi-gcc. Короче, сделать всё что описано в статье :)
jogick Автор
26.10.2021 21:36Не спорю, но зачем мне ещё один плагин, если я всё это могу и сам сделать за пять минут.
Что Вы будете делать, когда автору плагина надоест его поддерживать, а вы знать не знаете о той ерунде которую он делал? Уже не раз столкнулся с такой ерундой на Eclipse, когда программист решил не переписывать плагин под новую версию среды.
order227
27.10.2021 00:51Если плагин с открытым кодом, то проблем никаких нет - делаешь форк и спишь спокойно. Если код закрыт, то в топку
jogick Автор
27.10.2021 05:38Лично у меня не на столько много лишнего времени, что бы поддерживать форк чужой программы. Особенно, если учесть, что она мне не нужна.
И сейчас достаточно работы
Вы же не будете выпускать свой хлеб, только потому что ваша любимая пекарня решила закрыться
GennPen
Все гораздо проще. Перед подачей питания на контроллер (либо нажатием кнопки сброса) нужно BOOT0 подтянуть к VCC. Делается это соответствующей перемычкой или зажатием соответствующей кнопки. Контроллер переводится в режим DFU и его можно шить хоть через SWD, хоть через UART, хоть через USB (в F1 не поддерживается)
VelocidadAbsurda
А можно один раз в китайском "свистке" вывести наружу сигнал сброса (нога 18 контроллера) и забыть об этих мучениях навсегда.
GennPen
Не вижу смысла в подключении аппаратного ресета, когда программный нормально работает. А данный способ подъема нужен за очень редким исключением, когда забываешь первым делом включить SWD-интерфейс.
lamerok
насколько я помню, можно в настройках openocd указать сброс через сам отладчик
VelocidadAbsurda
Проблема в том, что самим пинам SWD программно могут быть назначены другие функции (по ошибке, как в примере, либо намеренно). В таком случае для установления связи по SWD нужно держать ядро в сбросе (тогда пины будут исполнять функцию по умолчанию - SWD). Для случая «по ошибке», как написали выше, действительно достаточно замкнуть nRST любым подручным средством. Однако для проекта с сознательно переназначаемым SWD это уже будут постоянные мучения и проще один раз допаять один провод в самом отладчике.