Прошивка и отладка STM32 в VSCode под Windows

от автора

На хабре уже есть немало информации об отладке МК в VSCode на Linux (тыц, тыц), также было написано как настроить тулчейн для работы под Windows в QT Creator, Eclipse, etc.

Пришло и моё время написать похожую статью, но для VS Code и под Widnows.

Инициализация проекта будет проводиться с помощью STM32CubeMX. Сборкой будет управлять CMake с тулчейном stm32-cmake. В качестве компилятора используется ARM GNU Toolchain. Тестовым стендом является NUCLEO-F446ZE.

Источниками вдохновения послужили:

Предисловие окончено, приступаем к настройке.

Установка необходимых утилит

Для удобства установки будем пользоваться пакетным менеджером Scoop.

Для его установки достаточно прописать в powershell следующие команды:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser; irm get.scoop.sh | iex

Добавим необходимые репозитории

scoop bucket add main scoop bucket add extras

И, наконец, сами программы:

scoop install meson scoop install ninja scoop install cmake scoop install llvm scoop install gcc-arm-none-eabi scoop install stlink scoop install openocd scoop install git

meson, ninja, cmake, llvm и gcc-arm-none-eabi используются для конфигурации и сборки проекта, stlink и openocd являются gdb-серверами, git необходим для подключения различных тулчейнов.

P.S.

Если у вас уже есть что-то из этого и вы можете вызвать его через консоль (т.е. программа добавлена в path) то советую либо убрать её из скрипта либо удалить у себя, и установить через scoop.

Настройка VS Code

Для работы потребуются установить в VS Code следующие расширения:

Инициализация проекта

Открываем CubeMX и создаем проект для нашей платы. Всю периферию оставляем настроенной по умолчанию.

В параметрах проекта (Project Manajer) выбираем Make в качестве тулчейна

Настройки Project Manajer
Настройки Project Manajer

В параметрах генератора кода указываем подключение бибилотек только в виде ссылок

 Настройки Code Generator
Настройки Code Generator

Настройка системы сборки

Открываем папку проекта в VS Code и вызываем терминал командой Ctr+~
Скачиваем stm32-cmake

git clone --recurse-submodules -j8 https://github.com/ObKo/stm32-cmake.git

Также потребуются файлы .clang-format, .clang-tidy , fetch_svd.cmake , и CMakeLists.txt из репозитория stm32-template. Для удоства клонируем его в соседнюю директорию.

git clone https://github.com/Dooez/stm32-template.git ../stm32-template

.clang-format, .clang-tidy необходимы LLVM, а fetch_svd.cmake используется для поиска файла описания регистров конкретного микроконтроллера.

Отредактируем CMakeLists.txt под наш проект.

Изменим переменную MCU на STM32F446ZE

set(MCU STM32F446ZE)

По умолчанию «кубик» инициализирует на плате NUCLEO-F446ZE USART3, USB_OTG_FS и несколько GPIO. Добавим библиотеки в проект, для этого необходимо для сборки прописать команду target_link_libraries. Также добавим библиотеку CMSIS и, для уменьшения размеров прошивки, Newlib Nano и NoSys

target_link_libraries(${PROJECT_NAME}     HAL::STM32::${MCU_FAMILY}::RCC     HAL::STM32::${MCU_FAMILY}::GPIO     HAL::STM32::${MCU_FAMILY}::UART     HAL::STM32::${MCU_FAMILY}::CORTEX     HAL::STM32::${MCU_FAMILY}::LL_USB     HAL::STM32::${MCU_FAMILY}::PCD     HAL::STM32::${MCU_FAMILY}::PCDEx     CMSIS::STM32::${MCU_MODEL}     STM32::Nano     STM32::NoSys )

Чтобы CMake мог увидеть файлы, сгенерированные кубиком, необходимо добавить их в Include Path и явно указать исполняемые c/cpp файлы.

add_executable(${PROJECT_NAME}  Core/Src/main.c Core/Src/stm32f4xx_it.c  )   target_include_directories(${PROJECT_NAME} PRIVATE  ${CMAKE_CURRENT_SOURCE_DIR}  ${CMAKE_CURRENT_SOURCE_DIR}/Core/Inc  ${CMAKE_CURRENT_SOURCE_DIR}/Core/Src )

main.c содержит, собственно, функцию main(), а в stm32f4xx_it.c находится функция, которая считает количество срабатываний SysTick, без которой не будут работать такие функции как HAL_Delay()

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

target_compile_options(${PROJECT_NAME} PUBLIC -Os -fno-exceptions -fno-rtti)

Настройка проекта под VS Code

Нажимаем сочетание клавиш Ctrl+Shift+P и в появившейся строке находим
Preferences: Open Workspace Settings (JSON)

В создашемся файле .vscode/settings.json указаны параметры для расширений и корректного отображения кода. Пишем:

{   "cmake.generator": "Ninja",   "cmake.configureEnvironment": {     "CMAKE_EXPORT_COMPILE_COMMANDS": "on"   },   "C_Cpp.default.intelliSenseMode": "gcc-arm",   "cortex-debug.gdbPath": "arm-none-eabi-gdb",   "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" }

Далее по тому же сочетанию находим Tasks: Configure Task и выбираем cmake build

В создашийся файл .vscode/tasks.json добавляем задания для прошивки и очистки памяти микроконотроллера с помощью st-flash. Итоговый файл tasks.json выглядит следующим образом:

{   "version": "2.0.0",   "tasks": [     {       "type": "cmake",       "label": "CMake: build",       "command": "build",       "targets": [           "ALL_BUILD"       ],       "problemMatcher": [],       "group": "build"     },     {       "type": "shell",       "label": "flash",       "command": "st-flash",       "args": [         "--reset",         "write",         "${input:workspaceFolderForwardSlash}/build/${workspaceFolderBasename}.bin",         "0x8000000"       ],       "options": {         "cwd": "${workspaceFolder}/build"       },       "dependsOn": "CMake: build",       "problemMatcher": [],       "group": {         "kind": "build",         "isDefault": true       },       "detail": "Builds project and flashes firmware."      },     {       "type": "shell",       "label": "erase",       "command": "st-flash",       "args": [         "--connect-under-reset",         "erase"       ],       "detail": "mass erase of chip"     }   ],      "inputs": [     {       "id": "workspaceFolderForwardSlash",       "type": "command",       "command": "extension.commandvariable.transform",       "args": {         "text": "${workspaceFolder}",         "find": "\\\\",         "replace": "/",         "flags": "g"       }     }   ] }
Также при желании можно добавить команду для прошивки с помощью OpenOCD

Для STM32F4 она выглядит следующим образом

{       "type": "shell",       "label": "flash-openocd",       "command": "openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c 'program ${input:workspaceFolderForwardSlash}/build/${workspaceFolderBasename}.bin verify reset exit' ",       "dependsOn": "CMake: build",       "problemMatcher": [],       "group": {         "kind": "build",         "isDefault": true       },       "detail": "Builds project, connects to the openOCD server and flashes new firmware."     }

Далее необходимо сконфигурировать расширение CMake для VS Code

Нажимаем сочетание клавиш Ctrl+Shift+P и в появившейся строке находим
CMake: Configure и выбираем конфигурацию под arm-none-eabi

После конфигурации автоматически сгенерируется файл .vscode/launch.json, рассмотрим его поподробнее:

{   "configurations" :    [     {       "cwd" : "${workspaceRoot}",       "device" : "STM32F446ZE",       "executable" : "${workspaceRoot}/build/${workspaceFolderBasename}.elf",       "name" : "Cortex Debug (generated)",       "preLaunchTask" : "CMake: build",       "preRestartCommands" : [ "load", "enable breakpoint", "monitor reset" ],       "request" : "launch",       "runToEntryPoint" : "main",       "servertype" : "stutil",       "showDevDebugOutput" : "raw",       "svdFile" : "${workspaceRoot}/build/_deps/st-svd-archive-src/STM32F4_svd_V1.8/STM32F446.svd",       "type" : "cortex-debug"     }   ],   "version" : "0.2.0" }

svdFile – путь до файла, который необходим, чтобы просматривать регистры периферии МК

Картинка

"preLaunchTask": CMake: build – компилирует проект перед прошивкой МК.

preRestartCommands – отправляет команды через GDB при нажатии на кнопку перезапуска отладки

Скрипт fetch_svd.cmake по умолчанию использует в качетсве GDB-сервера stutils. Примеры конфигурации под OpenOCD и JLink можно посмотреть на вики cortex-debug в приложенных ссылках.

Переходим к коду (наконец-то)

Не мудрствуя лукаво, пойдём мигать светодиодом. (и ещё немного поиграемся с выделением памяти). Изменим main() следующим образом

#include "stdlib.h"  uint8_t* data; int main(void) {   HAL_Init();   SystemClock_Config();   MX_GPIO_Init();   MX_USART3_UART_Init();      /* USER CODE BEGIN 2 */   data = new uint8_t[16];   uint8_t const_data[16];      for(int i = 0; i < 16; i++){     data[i] = i+1;     const_data[i] = i+1;   }   /* USER CODE END 2 */    /* Infinite loop */   while (1)   {     HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);     HAL_Delay(50);   } }

Компиляция проекта осуществляется нажатием клавиши F7 либо сочетанием Ctrl+Shift+B. Так как ранее мы в launch.json указали сборку перед прошивкой, то нам будет достаточно нажать F5 и перейти сразу к отладке. Рассмотрим интерфейс:

Отладочный GUI в VS Code
Отладочный GUI в VS Code

Первая кнопка осуществляет программный сброс (software reset) устройства
Вторая запускает код (горячая клавиша F5)
Третья, четвёртая и пятая – «шаг» вперёд к следующей функции, «шаг» вперёд к следующей инструкции (т.е. с погружением) и выполнение код до выхода из функции.
Шестая клавиша осуществляет пересборку проекта и перезапуск прошивки.
А седьмая останавливает отладку.

Окно слева содержит следующие разделы:

Отладочный GUI в VS Code
Отладочный GUI в VS Code
  • Cortex Registers – регистры процессора

  • Cortex Peripherals – регистры периферии (например, там можно смотреть и изменять состоянием регистров GPIO и мигать светодиодом с помощью мышки, хехе)

  • Breakpoints – список выставленных прерываний. Отмечу, что у разных микроконтроллеров и отладчиков допустимо различное число брейкпоинтов (Например, у ST-Link V2.1 их всего 6)

  • В CallStack можно посмотреть очередь вызова (вплоть до main, что логично)

  • Раздел Variables позволяет просматривать как локально объявленные переменные, так и глобальные, например uwTick, показывающую количество милисекунд от момента запуска МК

  • В Memory View можно посмотреть в любой доступный раздел памяти МК

Адреса недоступные к прочтению (по причине отсутствия в этом мире на данном МК) показаны тильдой
Адреса недоступные к прочтению (по причине отсутствия в этом мире на данном МК) показаны тильдой
Рассмотрим возможности Watch Window (и заодно сравним его с Keil MDK)

Массив const_data был объявлен статически, и его можно посмотреть просто по названию, тут всё как везде

А теперь попробуем посмотреть содержимое динамически выделенного массива:

Здесь, так же как и везде, дебаггер отобразит лишь первый элемент (в кавычках можно увидеть содержимое до первого \0 ). Однако, в отличие от, например, Keil MDK, мы можем явно указать, как именно следует воспринимать данный указатель:

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

Также мы можем переопределить этот указатель написав, например, такой запрос:
*(uint16_t*)data@8
Тогда в Watch Window будет показано отображение массива типа short, а не uchar


ссылка на оригинал статьи https://habr.com/ru/post/713432/