Сегодня я хотел бы рассказать о работе с микроконтроллерами 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
.
Первым делом необходимо выбрать контроллер:
После чего попадаем в основное меню конфигурации контроллера.
Тут все достаточно просто:
- В левом меню (1) выбираем конкретный блок периферийного устройства.
- В меню (2) выбираем, какие функциональные блоки данной периферии будут включены.
- В меню ниже (3) выбираем конкретные параметры периферии.
- В окне (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
. Не стоит бояться большого обилия окон с возможностью менять в них значения, на самом деле нам нужно не так многое оттуда.
Самое важное — проверить, что частоты кварца совпадают с реальным кварцем на плате (1). У меня не совпало, и я изменил 8 -> 24. Далее можно включить тактирование PLL
от кварца (2) и вывести сигнал с PLL
как тактовый для процессора и шин (3). После этого можно установить максимально возможную частоту работы ядра (4) для получения максимальной производительности.
Этого достаточно в 90% случаев. Сложности могут появиться, если для какого-то периферийного блока нужна особая частота, например для CAN. Тогда можно будет либо тактировать данную периферию от другой части PLL
(на скриншоте выше это PLLQ
или PLLP
), либо использовать внутренний генератор. Промежуточные значения CubeMX
постарается найти сам, и если у него это не выйдет, то тогда можно будет поперебирать их вручную.
После этого останется только настроить сам проект во вкладке Project Manager
. Необходимо выполнить следующие шаги:
- Указать имя проекта в поле
Project name
(1). - Задать путь к проекту в поле
Project Location
(2). - В разделе
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).
Сразу после установки плагин, скорее всего, будет требовать указать путь до папки с CubeCTL
. Нужно указать тот путь, который был выбран при установке.
Расширение позволяет выполнить ряд функций:
- Импортировать проект с
CMake
(2). Эта функция будет использована далее. - Запустить
CubeMX
(3). Эта опция просто запускает CubeMX, даже не открывая текущий проект. - Создать пустой проект (4). Эта команда запустит меню, где можно выбрать контроллер или отладочную плату, и будет сгенерирован чистый проект без
HAL
,LL
и дажеCMSIS
. Проект будет содержать только скрипт линкера, сборочный скриптCMake
иstartup file
на ассемблере. - Запустить
MCU Finder
(5), если он был установлен. - Обновить прошивку
stlink
.
Для дальнейшей работы нужно выбрать импорт проекта (2), указать путь до сгенерированного с помощью CubeMX проекта, после чего подтвердить импорт.
Эта функция сгенерирует папку .vscode
в корне проекта, которая содержит набор параметров для VSCode
.
Как понятно из описания выше, функция импорта проекта — единственная полезная из представленных, поэтому само меню не особо полезно. Чтобы скрыть иконку в правом меню, можно нажать на нее правой кнопкой мыши и выбрать Hide STM32 VS Code Extension
(вернуть ее можно через правый клик в любой области правого меню). Для получения функционала кнопки импорта проекта можно воспользоваться консолью, для чего нужно нажать Ctrl + Shift + P
и начать вводить stm32
:
После этого проект будет готов к работе. Однако для корректной работы линтера необходимо сконфигурировать проект. Это можно сделать через меню 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
:
Здесь вы можете столкнуться с проблемой, когда отладка просто не запускается и не отображается никаких сообщений об ошибках. Я столкнулся с этой проблемой на 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
Далее рассмотрим более детально сам процесс отладки:
- Локальные, статические, глобальные переменные и регистры процессора. Данное поле есть в любой IDE.
- Просмотр конкретных переменных. В этом поле можно выбрать конкретные переменные для просмотра, а также указать формат вывода, например
var,h
- будет выводить переменную в форматеhex
. Доступны форматыb
(в бинарном виде),o
(в восьмеричной системе счисления),c
иs
(строка). Также можно использовать преобразования типов C, например*var
- выведет то, что находится по адресу вvar
,*(char *)(0x2002ff64)
выведет 8-битное значение поRAW
адресу0x2002ff64
,*(uint32_t[10] *)(0x2002ff64)
- будет выводить массив из 10-ти элементов поRAW
адресу0x2002ff64
. - Стек вызовов. Из интересного - через
Right click -> Open Disassembly View
можно открыть дизассемблер конкретной функции. - Список брейкпоинтов.
- Поле для мониторинга глобальных переменных в реальном времени. Однако, чтобы эта возможность заработала, нужно в файл
.vscode/launch.json
вconfigurations
добавить следующее:
"liveWatch": {
"enabled": true,
"samplesPerSecond": 4 // частота опроса в секунду
}
- Просмотр значений регистров периферии с адресами, взятыми из SVD файлов, в
CubeCTL
.
Следующие окна доступны всегда, не только в режиме отладки, однако большинство из них нужно именно в процессе отладки, поэтому описывать я буду их тут.
- Problems - В это окно будут попадать все ошибки и предупреждения от компилятора или линтера. По хорошему, оно должно быть пустым.
- Terminal - В этом окне можно найти вывод консольных утилит, используемых для сборки и отладки -
CMake
иGDB server
. Также там можно создать свой терминал при необходимости. - Output - Вывод плагинов. В принципе, как и прошлое окно, не особо полезно.
- Memory - Данная вкладка позволяет читать большими кусками память, начиная с какого-то адреса, и сохранять для дальнейшего анализа. Например, для примера проекта выше можно указать адрес как
&press_count
и увидеть кусок памяти, находящийся за данным адресом.
- Xrtos - Позволяет мониторить таски при использовании
RTOS
. Однако для того чтобы это работало,RTOS
должна собираться с определенными параметрами, чтобы присутствовала необходимая дополнительная информация. - Debug console - Это консоль отладчика
GDB
. Можно напрямую вводить команды и смотреть результат. Например, командаset output-radix 16
приведет к тому, что все будет читаться в форматеHEX
, аset output-radix 10
вернет все как было. Такой же эффект можно получить, если нажать0x
в шапке окна (1), (2) или (5). - Serial monitor - Это serial консоль, встроенная в
vscode
. Однако для нее нужно установить отдельный плагин - ms-vscode.vscode-serial-monitor.
- Spell Checker - Это окно появится, если установить плагин
streetsidesoftware.code-spell-checker
, как описано выше. - Меню со стандартными действиями такими как остановка исполнения, продолжение, шаг вперед и т.д.
Собственно, этого достаточно для комфортной работы, однако я хотел бы отметить еще некоторые моменты:
- Вероятно, вам не всегда нужно, чтобы при запуске отладки программа останавливалась на
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 адресом.
Далее можно прописать 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!!!.
Ссылки
- VSCode.
- STM32 VS Code Extension.
- CubeMX docs.
- Project example.
- Git How To