Portfolio Обо мне Блог

Сегодня я хотел бы рассказать о работе с микроконтроллерами STM32 с использованием редактора исходного кода VSCode в операционной системе Linux. Эта статья предназначена для новичков, и в ней будет пошагово описан процесс создания простого проекта, с акцентом на неочевидные моменты. Вопросы о том, как это работает и как настроить всё максимально гибко, мы оставим для следующей статьи.

Так как статья рассчитана на начинающих, мы будем следовать пути, предложенному компанией ST. В частности, мы будем использовать их расширение для VSCode для управления проектом.

Установка

Vscode

Для начала необходимо установить VSCode. Скачать его можно с официального сайта или через Flatpak. Для пользователей Debian или других дистрибутивов на основе APT можно добавить репозиторий, чтобы VSCode обновлялся вместе со всей системой:

sudo apt install wget gpg
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg
echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" |sudo tee /etc/apt/sources.list.d/vscode.list > /dev/null
rm -f packages.microsoft.gpg
# And install vscode
sudo apt install code

Однако это не сработает для Ubuntu, т.к. Canonical очень агрессивно продвигает snаp установить vscode можно только с помощью него.

snap install code

К сожалению, нужен именно VSCode. VSCodium (чистый билд VSCode без телеметрии) использовать не получится, потому что расширение от ST требует IntelliSense, а это расширение от Microsoft, и оно работает только на билдах от Microsoft. Из-за неудовлетворения данной зависимости stm32-vscode-extension просто отсутствует в маркете.

Далее нужно установить специальное расширение - STM32 VS Code Extension. В 2024 году ST выпустила новую версию 2.0.0 данного расширения, фактически переписав его с нуля. Теперь оно не создает проект в собственном формате, который можно открыть только с помощью данного расширения, а лишь добавляет конфигурацию отладчика для проекта, сгенерированного CubeMX. Это стало возможным благодаря тому, что в начале 2024 года в CubeMX была добавлена поддержка генерации CMake (данное расширение может работать исключительно с CMake).

Для полноценной работы данного расширения необходимо установить несколько дополнительных программ:

  • CubeMX - известный инструмент для генерации шаблонов проектов.
  • CubeCTL - тулчейн, предназначенный для работы с контроллерами STM32.
  • MCU Finder - утилита для поиска альтернативных контроллеров.

CubeMX

Последнюю версию CubeMX можно скачать здесь. Необходимо скачать архив, распаковать его и запустить SetupSTM32CubeMX-*, где вместо звездочки необходимо использовать номер версии скаченной выше. Запускать установку лучше от имени пользователя, (не от root), чтобы избежать проблем с обновлениями CubeMX в будущем. В процессе установки необходимо согласиться с лицензией и выбрать папку для установки CubeMX.

Однако после установки иконка CubeMX может не появиться в меню приложений или в rofi. Это можно исправить вручную, создав файл ~/.local/share/applications/stm32cubemx.desktop:

[Desktop Entry]
Name=STM32CubeMX
GenericName=STM32CubeMX
Comment=Graphical tool for configuration of STM32 Microcontrollers
Exec=/home/STM32CubeMX/STM32CubeMX
Terminal=false
X-MultipleArgs=false
Type=Application
Icon=/home/STM32CubeMX/help/STM32CubeMX.ico
StartupWMClass=STM32CubeMX
StartupNotify=true

Вместо /home/STM32CubeMX/ нужно будет использовать ваш путь до STM32CubeMX, который вы указали при установке.

CubeCTL

Кроме кодогенератора для работы нужен тулчейн, роль которого выполняет STM32CubeCLT. Он состоит из набора консольных утилит (GCC, GDB), набора SWD файлов, а также STM32CubeProg - также в консольной версии. Скачать его можно здесь. Рекомендуется устанавливать общую версию для Linux, а не для конкретного дистрибутива, и также не от имени root.

После распаковки архива станет доступен файл с расширением .sh. Этот скрипт необходимо сделать исполняемым и запустить для установки:

unzip en.st-stm32cubeclt*.zip
chmod +x st-stm32cubeclt*.sh
./st-stm32cubeclt*.sh

Команды выше используют символ подстановки - * вместо которой shell подставит оставшуюся часть названия, однако если в папке есть имена с совпадающими шаблонами то это будет работать не правильно и тогда нужно ввести адрес полностью.

Далее нужно согласиться на все вопросы, связанные с лицензиями (а их будет много), и не пропустить момент, когда будет задан вопрос о месте установки - STM32CubeCLT install dir?. Вам нужно будет либо согласиться с вариантом по умолчанию, либо указать свой путь.

После установки пути к компиляторам и прочему софту должны появится в PATH, это необходимо для работы расширения. Проверить это можно командой echo $PATH. Если этого не произошло (как у меня на Debian c ZSH) то нужно это сделать вручную скопировав содержание /etc/profile.d/cubeclt-bin-path_1.15.1.sh (подставьте необходимую версию) в конец .bashrc или .zshrc если вы используете ZSH.

MCU Finder

Также, если вам необходимо, можно установить пакет ST-MCU-FINDER-PC. Этот пакет является опциональным и предназначен для поиска микроконтроллеров по определенным критериям. Его можно запустить из меню расширения. Лично я его не использую и не устанавливаю, поскольку его функционал дублируется в CubeMX.

Создание проекта

Теперь создадим простой проект который мигает диодом раз в секунду и выводит сообщение в консоль. Параллельно я расскажу про некоторые нюансы работы с CubeMX которые не очевидны сразу, полную же документацию можно посмотреть в этом документе.

Cube MX

Для начала будет хорошей идеей перейти в настройки с помощью alt + s и изменить Repository folder для того чтобы пакеты скаченные кубом не валялась просто в home директории.

Создадим проект для контроллера STM32G431RBT6, я буду использовать плату NUCLEO-G431RB.

Первым делом необходимо выбрать контроллер:

cubemx_select

После чего попадаем в основное меню конфигурации контроллера.

cube

Тут все достаточно просто:

  1. В левом меню (1) выбираем конкретный блок периферийного устройства.
  2. В меню (2) выбираем, какие функциональные блоки данной периферии будут включены.
  3. В меню ниже (3) выбираем конкретные параметры периферии.
  4. В окне (4) можно выбирать конкретные пины и их назначение.

Также есть достаточно удобный нечеткий поиск по пинам (5). Например, можно начать вводить ETR, и поиск отобразит все пины с таймерами с данной функцией.

Для данного проекта нужно включить тактирование ядра через Pinout & Configuration -> System Core -> RCC -> High Speed Clock (HSE) и тактирование часов реального времени через Pinout & Configuration -> System Core -> RCC -> Low Speed Clock (LSE). Так как на данной плате есть кварц и для HSE, и для LSE, в обоих случаях выбираем Crystal/Ceramic Resonator. Однако для корректной работы нужно убедиться, что для данных источников тактирования выбрана правильная частота, о чем будет упомянуто позже.

После этого необходимо включить отладку, иначе к контроллеру не получится подключиться для отладки. Для этого нужно установить Pinout & Configuration -> System Core -> SYS -> Debug в Serial Wire.

Если вы случайно прошили контроллер без активированной отладки и программатор его больше не видит, это не критично. Все можно исправить с помощью CubeProgrammer, стерев прошивку с настройкой Reset mode - Hardware reset. Чтобы это сработало, необходимо подключить RST к программатору (кроме SWDIO и SWCLK).

На этом обязательная часть заканчивается, и дальше нужно выбирать, что активировать исходя из проекта. Я добавлю кнопку, светодиод и UART.

  • Для светодиода выберем пин PA5 и сделаем его GPIO_Output и назначить user label - LED (через правую кнопку мыши на пине).

  • Для кнопки выберем пин PC13 и переключим его в режим GPIO_EXTI и также назначаем user label - BTN. Однако для того чтобы прерывание заработало его нужно активировать: Pinout & Configuration -> System Core -> NVIC и далее выставить EXTI line [15:10] interrupts (у меня 13 линия).

  • Для UART нужно активировать Pinout & Configuration -> Connectivity -> LPUART1 -> Mode и выставить Asynchronous. Также в окне Configuration установить скорость 115200 Bits/s. Остальное оставим по умолчанию. Также мне потребовалось поменять пины по умолчанию с PC0 и PC1 на PA3 и PA2 соответственно (это делается через Alt + Click и удержание на нужном пине для переноса).

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

  • Альтернативные пины для периферийного блока выбираются через Alt + Left Click, как описано в примере выше.
  • С помощью Ctrl + K можно устанавливать или снимать флаг Pinout -> Keep Current Signal Placement. Если этот флаг установлен, никакие уже назначенные пины не будут автоматически изменены. Если его не установить, то пины, помеченные кнопкой (Signal Pinning), не будут изменены, а остальные могут автоматически измениться при выборе периферии, которая их задействует. Я бы советовал всегда выставлять данный флаг, чтобы пины магически не меняли свое расположение.
  • Пинам, которые используются как GPIO, лучше задать пользовательские имена (LED и BTN в примере выше). Это позволит легко находить их при написании кода с помощью автоподстановки. Для другой периферии это не имеет смысла, так как к ней доступ осуществляется с помощью номера IP блока.
  • С помощью Alt + L или Pinout -> List pinout comparable MCU's можно найти pin-to-pin совместимые контроллеры для текущей конфигурации выводов.
  • Alt + D или Help -> Docs & Resources откроют меню со всеми документами по данному контроллеру, такими как Errata, Reference Manual, Application Note и т.д.
  • Ctrl + Left Click позволит переместить функцию пина на доступную позицию.

Теперь скажу пару слов про Clock Configuration. Не стоит бояться большого обилия окон с возможностью менять в них значения, на самом деле нам нужно не так многое оттуда.

cubemx_clk

Самое важное — проверить, что частоты кварца совпадают с реальным кварцем на плате (1). У меня не совпало, и я изменил 8 -> 24. Далее можно включить тактирование PLL от кварца (2) и вывести сигнал с PLL как тактовый для процессора и шин (3). После этого можно установить максимально возможную частоту работы ядра (4) для получения максимальной производительности.

Этого достаточно в 90% случаев. Сложности могут появиться, если для какого-то периферийного блока нужна особая частота, например для CAN. Тогда можно будет либо тактировать данную периферию от другой части PLL (на скриншоте выше это PLLQ или PLLP), либо использовать внутренний генератор. Промежуточные значения CubeMX постарается найти сам, и если у него это не выйдет, то тогда можно будет поперебирать их вручную.

project_name

После этого останется только настроить сам проект во вкладке Project Manager. Необходимо выполнить следующие шаги:

  1. Указать имя проекта в поле Project name (1).
  2. Задать путь к проекту в поле Project Location (2).
  3. В разделе Toolchain/IDE выбрать CMake (3). Если выбрать другой инструмент, расширение для VSCode не будет работать с проектом.

Если в разделе Application Structure выбрать Basic вместо Advanced, сгенерированные исходные файлы будут отправлены в корневую папку вместо папки Core. Я так делать не рекомендую. Также можно изменить минимальный размер стека и кучи, что может быть полезно при использовании RTOS, но сейчас ничего менять не будем.

Кроме того, стоит изменить несколько параметров в Project Manager -> Code Generator:

  • В разделе STM32Cube MCU packages and embedded software packs лучше выбрать опцию Copy only the necessary library files, чтобы не хранить в проекте неиспользуемые библиотеки.
  • В разделе Generated files необходимо поставить галочку Generate peripheral initialization as a pair of .c/.h files per peripheral, чтобы CubeMX сгенерировал раздельные файлы для описания различных периферийных блоков, а не скидывал все в main.c.
  • Включите Enable Full Assert в HAL Settings. Эта функция активирует вызов колбека assert_failed при возникновении ошибки в работе периферии. Шаблон данного колбека находится в main.c. Колбек возвращает название файла и строку кода, в которой произошла ошибка. Для работы данной функции необходимо хранить во flash памяти названия всех файлов проекта вместе с путями до них, поэтому в релизной версии или в случае нехватки памяти эту функцию лучше отключить.

Последняя вкладка Advanced Settings позволяет выбрать библиотеку HAL или LL для конкретного периферийного блока, а также настроить последовательность инициализации модулей, что может быть полезно при возникновении проблем с инициализацией. В меню Register Callback можно выбрать периферию, для которой будет использован новый механизм колбеков (передача адреса функции колбека через хендлер). Если оставить Disable, будет использован старый механизм __weak функций.

Vs code

На этом работа в CubeMX завершена, рассмотрим работу с редактором VSCode. После установки плагина в VSCode станет доступна новая вкладка с бабочкой (1).

vscode_stm32

Сразу после установки плагин, скорее всего, будет требовать указать путь до папки с CubeCTL. Нужно указать тот путь, который был выбран при установке.

Расширение позволяет выполнить ряд функций:

  • Импортировать проект с CMake (2). Эта функция будет использована далее.
  • Запустить CubeMX (3). Эта опция просто запускает CubeMX, даже не открывая текущий проект.
  • Создать пустой проект (4). Эта команда запустит меню, где можно выбрать контроллер или отладочную плату, и будет сгенерирован чистый проект без HAL, LL и даже CMSIS. Проект будет содержать только скрипт линкера, сборочный скрипт CMake и startup file на ассемблере.
  • Запустить MCU Finder (5), если он был установлен.
  • Обновить прошивку stlink.

Для дальнейшей работы нужно выбрать импорт проекта (2), указать путь до сгенерированного с помощью CubeMX проекта, после чего подтвердить импорт.

import

Эта функция сгенерирует папку .vscode в корне проекта, которая содержит набор параметров для VSCode.

Как понятно из описания выше, функция импорта проекта — единственная полезная из представленных, поэтому само меню не особо полезно. Чтобы скрыть иконку в правом меню, можно нажать на нее правой кнопкой мыши и выбрать Hide STM32 VS Code Extension (вернуть ее можно через правый клик в любой области правого меню). Для получения функционала кнопки импорта проекта можно воспользоваться консолью, для чего нужно нажать Ctrl + Shift + P и начать вводить stm32:

alt_exec

После этого проект будет готов к работе. Однако для корректной работы линтера необходимо сконфигурировать проект. Это можно сделать через меню CMake, но удобнее через меню команд: Ctrl + Shift + P -> CMake: Configure, потом Debug. Собрать проект можно с помощью команды Ctrl + Shift + P -> CMake: Build.

Есть вероятность, что команды Import CMake project into VS Code не будет. В таком случае сначала нужно запустить STM32 VS Code Extension: Focus on Project Manager View, после чего команда появится. Видимо при первом запуске данное меню триггерит какую-то инициализацию, и без этого импорт невозможен.

Организация проекта на С

Рассмотрим структуру типичного проекта. Для этого, к тому что создал куб, необходимо добавить еще 3 папки:

  • App - Основная логика приложения.
  • Bsp - Библиотеки для устройств (микросхем), располагающихся на конкретной плате.
  • Docs - Документация, необходимая для проекта.

В каждой из этих папок (кроме Docs) создаем 2 папки: Src и Inc, для единообразия с кодом, сгенерированным кубом. Папка Bsp не пригодится, так как в нашем примере мы будем взаимодействовать с периферией на уровне HAL. Для текущего примера этого достаточно, но для серьезных решений лучше оборачивать подобные вещи в функции уровня BSP.

Далее создадим 2 файла App/Src/app.c и App/Inc/app.h:

mkdir App App/Src App/Inc && touch App/Src/app.c App/Inc/app.h

app.h должен содержать в себе инклуд stdint.h, это нужно, чтобы определения наших функций могли содержать дополнительные типы, такие как int8_t, uint32_t и подобные. Также в этот файл нужно добавить определение void app(void), эта функция будет использоваться как main():

#ifndef __APP_H__
#define __APP_H__

#include <stdint.h>
void app();

#endif /* __APP_H__ */

В app.c реализуем логику работы:

#include "app.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>

uint32_t press_count = 0;

void app()
{
    while (1)
    {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        static uint32_t i = 0;
        printf("N: %ld, Buttion count: %ld.\r\n", i++, press_count);
        HAL_Delay(500);
    }
}

int _write(int file, char *ptr, int len)
{
    (void)file; // hack for fix warning - unused int file.
    HAL_UART_Transmit(&hlpuart1, (uint8_t *)ptr, len, 1000);
    return len;
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_13)
        press_count++;
}

Данная программа меняет состояние диода 2 раза в секунду и выводит в консоль сообщение с его номером и количеством нажатий на кнопку.

Функцию int _write() необходимо определить для правильной работы printf(). Она реализована как блокирующая, возможно, это не оптимальная реализация, но для нашего примера подойдет.

Далее необходимо добавить вызов app() в main.c и добавить #include "app.h", а также добавить реализацию функции assert_failed:

/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "app.h"
/* USER CODE END Includes */
// ....
/* USER CODE BEGIN WHILE */
app();
while (1) {
/* USER CODE END WHILE */
// ....
/* USER CODE BEGIN 6 */
printf("Wrong parameters value: file %s on line %ld\r\n", file, line);
/* USER CODE END 6 */

Для того чтобы наш код не был удален при изменении параметров периферии, его необходимо помещать между блоками USER CODE BEGIN и USER CODE END. Поэтому пример выше - это строки, которые нужно вставить в правильные места сгенерированного main.c. Создавая отдельную папку для исходных кодов проекта, мы избавляем себя от необходимости следить за CODE BEGIN -> CODE END, что сильно упрощает жизнь.

Последний блок кода с Wrong parameters value - это реализация void assert_failed(uint8_t *file, uint32_t line). Данная функция будет вызвана в случае ошибки внутри HAL и выведет номер строки и путь до исходного файла в проекте. Так как все пути хранятся во flash памяти, в релизе эту функцию лучше отключить.

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

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    "App/Src/app.c"
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    "App/Inc"
)

Также при написании данного кода вы могли обнаружить (есть вероятность, что это исправлено к моменту чтения данной статьи), что в PROBLEMS высвечивается одна проблема - это warning в CMakeLists.txt, связанный с тем, что enable_language() вызывается до project(). Решается это просто - необходимо поменять строчки местами:

# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})

# Enable CMake support for ASM and C languages
enable_language(C ASM)

Данный файл генерируется только единожды и изменения не будут затерты при перегенерации проекта.

Далее рассмотрим некоторые полезные, но неочевидные вещи:

  • Ctrl + Shift + I вызовет автоформатирование. В настройках VS Code можно выбрать стиль форматирования, но я обычно использую стандартный.
  • Ctrl + P - поиск файла по названию в проекте.
  • Alt + Backspace - может быть хоткеем возврата на предыдущее положение курсора. Для этого нужно установить хоткей Go Back (открыть меню настройки хоткеев можно через Ctrl + K -> Ctrl + S).
  • Чтобы включить отображение всех знаков (пробелы, табы и т.д.), необходимо выставить editor.renderWhitespace значение all.
  • Для отключения вставки по нажатию на колесико мыши нужно снять галочку с editor.selectionClipboard.
  • Для отображения справа полоски на 80-ти символах нужно выставить "editor.rulers": [80]. Причем можно поставить несколько значений через запятую, чтобы появилось несколько вертикальных линий, например: "editor.rulers": [100,80].

Если необходима проверка орфографии, можно установить расширение streetsidesoftware.code-spell-checker, а также streetsidesoftware.code-spell-checker-russian для поддержки русского языка. Однако после установки вы можете обнаружить, что все предупреждения чекера валятся в панель PROBLEMS, из-за чего там не видно предупреждений линтера. Чтобы решить эту проблему, нужно переключиться на бета-версию данного расширения, он будет кидать предупреждения в отдельное окно.

Организация проекта на С++

Для добавления поддержки C++ необходимо создать те же файлы (прошлые удалить), только вместо .c будет .cpp, а вместо .h.hpp.

mkdir App App/Src App/Inc && touch App/Src/app.cpp App/Inc/app.hpp

В app.hpp добавляем определение функции app(), а также обертку для вызова данной функции из C-кода app_c():

#ifndef __APP_H__
#define __APP_H__

#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif
    void app_c(void);
#ifdef __cplusplus
}
#endif

void app(void);

#endif /* __APP_H__ */

В файле app.cpp опишем функцию app(), которая станет нашим новым main(), а также добавим класс TEST для примера. Естественно, в нормальном коде данный класс нужно будет вынести в отдельный файл, но для иллюстрации примера пойдет и так.

app.cpp:

#include "app.hpp"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>

uint32_t press_count = 0;

extern "C" void app_c(void)
{
    app();
}

class TEST
{
public:
    void toggle_print();
};

void app(void)
{
    TEST blink;
    while (1)
    {
        blink.toggle_print();
        HAL_Delay(100);
    }
}

void TEST::toggle_print()
{
    HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    static uint32_t i = 0;
    printf("N: %ld, Buttion count: %ld.\r\n", i++, press_count);
}

extern "C" int _write(int file, char *ptr, int len)
{
    (void)file; // hack for fix warning - unused int file.
    HAL_UART_Transmit(&hlpuart1, (uint8_t *)ptr, len, 1000);
    return len;
}

extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_13)
        press_count++;
}

Также нужно в main.c заменить вызов функции app() на app_c(), а также подключить не app.h, а app.hpp. Здесь важно заметить, что app.hpp добавляется в main.c, поэтому в нем не может быть никаких классов и других специфичных для C++ конструкций. Лучше ничего больше не добавлять в этот файл, а дополнительную логику программы реализовывать в других файлах.

Для переопределения С функций внутри С++ нужно обязательно использовать extern "C" иначе линкер не сможет найти нужную функцию, это связанно с реализацией статического полиморфизма в языке с++ - Данный язык сохраняет тип параметров внутри названия метки функции (name mangling). Подробнее про это можно почитать тут.

Так-же нужно изменить CMakeLists.txt:

# Enable CMake support for ASM and C languages
enable_language(CXX C ASM)

# .......

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    "App/Src/app.cpp"
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    "App/Inc"
)

CXX добавляет компилятор C++.

Отладка проекта

Для прошивки и отладки можно использовать боковое меню отладки (1) -> (2) или кнопку F5:

debug

Здесь вы можете столкнуться с проблемой, когда отладка просто не запускается и не отображается никаких сообщений об ошибках. Я столкнулся с этой проблемой на Ubuntu 24.04 LTS и потратил много времени на ее решение. Оказалось, что ошибку можно обнаружить, если запустить дебагер arm-none-eabi-gdb напрямую из консоли:

zen@pc:~$ arm-none-eabi-gdb
arm-none-eabi-gdb: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

Если поискать решение на форуме ST, то они любезно сообщат, что Ubuntu 24.04 LTS просто не поддерживается. Мы же просто скачаем и установим недостающее руками:

wget http://archive.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.4-2_amd64.deb && sudo dpkg -i libtinfo5_6.4-2_amd64.deb && rm -f libtinfo5_6.4-2_amd64.deb
wget http://archive.ubuntu.com/ubuntu/pool/universe/n/ncurses/libncurses5_6.4-2_amd64.deb && sudo dpkg -i libncurses5_6.4-2_amd64.deb && rm -f libncurses5_6.4-2_amd64.deb

Далее рассмотрим более детально сам процесс отладки:

debug_view

  1. Локальные, статические, глобальные переменные и регистры процессора. Данное поле есть в любой IDE.
  2. Просмотр конкретных переменных. В этом поле можно выбрать конкретные переменные для просмотра, а также указать формат вывода, например var,h - будет выводить переменную в формате hex. Доступны форматы b (в бинарном виде), o (в восьмеричной системе счисления), c и s (строка). Также можно использовать преобразования типов C, например *var - выведет то, что находится по адресу в var, *(char *)(0x2002ff64) выведет 8-битное значение по RAW адресу 0x2002ff64, *(uint32_t[10] *)(0x2002ff64) - будет выводить массив из 10-ти элементов по RAW адресу 0x2002ff64.
  3. Стек вызовов. Из интересного - через Right click -> Open Disassembly View можно открыть дизассемблер конкретной функции.
  4. Список брейкпоинтов.
  5. Поле для мониторинга глобальных переменных в реальном времени. Однако, чтобы эта возможность заработала, нужно в файл .vscode/launch.json в configurations добавить следующее:
"liveWatch": {
    "enabled": true,
    "samplesPerSecond": 4   // частота опроса в секунду
}
  1. Просмотр значений регистров периферии с адресами, взятыми из SVD файлов, в CubeCTL.

Следующие окна доступны всегда, не только в режиме отладки, однако большинство из них нужно именно в процессе отладки, поэтому описывать я буду их тут.

  1. Problems - В это окно будут попадать все ошибки и предупреждения от компилятора или линтера. По хорошему, оно должно быть пустым.
  2. Terminal - В этом окне можно найти вывод консольных утилит, используемых для сборки и отладки - CMake и GDB server. Также там можно создать свой терминал при необходимости.
  3. Output - Вывод плагинов. В принципе, как и прошлое окно, не особо полезно.
  4. Memory - Данная вкладка позволяет читать большими кусками память, начиная с какого-то адреса, и сохранять для дальнейшего анализа. Например, для примера проекта выше можно указать адрес как &press_count и увидеть кусок памяти, находящийся за данным адресом.

mem

  1. Xrtos - Позволяет мониторить таски при использовании RTOS. Однако для того чтобы это работало, RTOS должна собираться с определенными параметрами, чтобы присутствовала необходимая дополнительная информация.
  2. Debug console - Это консоль отладчика GDB. Можно напрямую вводить команды и смотреть результат. Например, команда set output-radix 16 приведет к тому, что все будет читаться в формате HEX, а set output-radix 10 вернет все как было. Такой же эффект можно получить, если нажать 0x в шапке окна (1), (2) или (5).
  3. Serial monitor - Это serial консоль, встроенная в vscode. Однако для нее нужно установить отдельный плагин - ms-vscode.vscode-serial-monitor.

serial

  1. Spell Checker - Это окно появится, если установить плагин streetsidesoftware.code-spell-checker, как описано выше.
  2. Меню со стандартными действиями такими как остановка исполнения, продолжение, шаг вперед и т.д.

Собственно, этого достаточно для комфортной работы, однако я хотел бы отметить еще некоторые моменты:

  • Вероятно, вам не всегда нужно, чтобы при запуске отладки программа останавливалась на main(). Чтобы отключить данный брейкпоинт, необходимо закомментировать параметр в файле .vscode/launch.json:
// "runToEntryPoint": "main",
  • Для сборки не только elf но еще и bin с hex нужно добавить в конец CMakeLists.txt:
# Add custom commands to generate .bin and .hex files
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.bin
    COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.hex
    COMMENT "Building ${CMAKE_PROJECT_NAME}.bin and ${CMAKE_PROJECT_NAME}.hex"
)
  • Иногда бывает полезно добавить какие-то новые флаги компиляции, например можно добавить -fsingle-precision-constant благодаря которому все константы с плавающей точкой будут восприняты компилятором как float. Это будет крайне полезно т.к. в STM32G4 FPU работает только с FLOAT и теперь можно не писать f у каждой константы. Для этого нужно дописать пару строчек в середину (после описания Debug и Realise) cmake/gcc-arm-none-eabi.cmake:
# Add new C & C++ flag
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsingle-precision-constant")

GIT

Для работы с любым гит сервисом на нем сначала нужно создать пустой проект с таким-же названием как текущий. У меня это будет stm32_vscode_guide_exemple (1). Название лучше писать с маленькой буквы т.к. оно является URL адресом.

git

Далее можно прописать Description (краткое описание проекта) (2) и выбрать тип проекта (3) приватный или публицный. Я выбираю публичный т.к. вы можете посмотреть данный проект в моем github.

Дальнейшие опции (4), (5) и (6) выставлять не стоит, потомучто в противном случае в репозитории создадтся вайлы и тогда мы не сможем закомитить уже существующий проект.

После чего создаем репозиторий с помощью копки Create repository.

Дальнейшая работа будет происходить уже с локальным проектом. Для начала нужно создать 2 файла README.md и .gitignore.

touch README.md .gitignore

Первый будет текстом отображаемым на главной странице, второй это список файлов которые будут игнорироватся системой git. В первый файл можно написать что угодно. Во второй файл записывают файлы и папки которые являются временными и не нужны для переноса проекта, в первую очередь это папка build.

build/
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
build.ninja
.mxproject

После этого необходимо инициализировать проект и запушить его на сервер.

git init
git add -A
git commit -m "first commit"
git remote add origin https://{your_git_address}/stm32_vscode_guide_exemple.git
git push -u origin master

Вместо {your_git_address} нужно подставить ваш адрес git сервера.

Есть вероятность что у вас ветка по умолчанию будет main а не master. это можно узнать с помощью комманды git status. В таком случае нужно использовать main вместо master в последней комманде.

Также если вы впервые используете гит, то сначала он предложет вам указать имя и электронную почту.

Более подробно с гитом можно познакомится на githowto. А решение тепичных проблем описанно в Ёбаный Git!!!.

Ссылки