Когда пишут программу HelloWorld на C, обычно выводят текст “Hello, World!”. Но когда программа пишется для микроконтроллера, часто ограничиваются миганием светодиода, особенно для микроконтроллеров семейства STM32.

В статье я разберу, как можно вывести “Hello World” в программе для микроконтроллеров этого семейства.

Для микроконтроллера RP2040 вывести “Hello world” проще, чем для микроконтроллеров STM32, пишешь printf("Hello, world!\n"); и в CMakefiles.txt указываешь использовать USB или UART для вывода. Для STM32 ситуация другая — нужно больше действий от программиста.

Вывод отладочной информации не доступен «из коробки» и может потребовать дополнительной настройки: выбора UART, подключения USB-UART преобразователя или использования отладочного интерфейса ST-LINK/J-Link

Я использовал отладочную плату WeAct Studio на базе микроконтроллера STM32H750VBT6. Плата не имеет встроенного аппаратного отладчика ST-Link и не предоставляет виртуальный COM-порт для отладки, у отладочных плат Discovery и NUCLEO, поэтому я использовал китайские клоны отладчиков ST-Link и J-Link.

Разрабатывая простую программу “Hello world” на микроконтроллере STM32, можно узнать много нового, систематизировать и улучшить свои знания.

Примеры, приведённые в статье, доступны в моём GitHub репозитории

Начнём с краткой теории.

STM32

После RP2040 разработка для STM32 кажется гораздо сложнее.

Если у RP2040 это один чип, то STM32 объединяет множество чипов от простых Cortex-M0 (серия F0/G0) до Cortex-M7 (серия H7) с частотами под 480 МГц, DSP и аппаратным ускорением графики. Под каждую задачу подбирают свой чип STM32.

Разработка для STM32 может вестись на трёх уровнях: CMSIS, LL и HAL.

  • CMSIS — прямой доступ к регистрам. Максимальная скорость и минимальный размер кода, но нужно наизусть знать тысячестраничный Reference Manual.

  • LL (Low-Layer) — лёгкая обёртка из inline-функций над регистрами. Быстро и прозрачно, но привязано к конкретному чипу.

  • HAL (Hardware Abstraction Layer) — тяжёлый высокоуровневый API. Позволяет легко переносить код между разными STM32, но расплачиваться приходится раздутым размером прошивки и медлительностью.

Перед началом работы с микроконтроллером в программном коде его необходимо сконфигурировать: настроить периферию и тактовые частоты для неё.

Главный шок после RP2040 — это дерево тактирования (Clock Tree). На RP2040 частоты настраиваются парой строчек. В STM32 нужно вручную связать цепочку из кварцев (HSI/HSE), множителей PLL и делителей Bus Prescalers. Поэтому обычно конфигурацию выполняют при помощи STM32CubeMX с последующей генерацией кода под разные IDE или системы сборки.

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

Для меня это выглядит неуклюже, так как код содержит визуальный мусор, который отвлекает. Но видимо, от Legacy здесь не откажешься.

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

Высокий порог входа и «монструозная» экосистема STM32 — это плата за промышленный стандарт, гибкость и возможность подобрать чип точно под бюджет и задачи проекта.

Что необходимо для сборки, компиляции и отладки кода для STM32

Естественно, что нужен сам микроконтроллер, вернее, нужна отладочная плата с микроконтроллером.

Как я говорил ранее, я использовал плату WeAct Studio. Вы можете использовать её или какую-нибудь другую плату на базе STM32, например, так называемые Blue Pill и Black Pill.

Моя отладочная плата на базе STM32H750VBT6
Моя отладочная плата на базе STM32H750VBT6

Как и для любых других микроконтроллеров, кроме самого микроконтроллера, для разработки под STM32 необходимы:

  1. Кросс-тулчейн.

  2. Система сборки.

  3. Интегрированная среда разработки.

  4. SDK и библиотеки.

  5. Программатор.

  6. Аппаратный отладчик.

  7. USB-UART преобразователь.

  8. Сервер отладки.

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

Чтобы лучше понять, что будет рассказано, далее приведу две схемы:

Процесс получения прошивки для микроконтроллера
Процесс получения прошивки для микроконтроллера
Типовая архитектура подключения инструментов разработки к микроконтроллеру (MCU) для прошивки, отладки и мониторинга данных
Типовая архитектура подключения инструментов разработки к микроконтроллеру (MCU) для прошивки, отладки и мониторинга данных

Они будут справедливы для многих микроконтроллеров (MCU) и STM32 тоже.

Кросс-тулчейн

Тулчейн (toolchain) — это набор программных инструментов, используемых для создания исполняемого файла из исходного кода для конкретной программно-аппаратной платформы.

В этот набор обязательно входят компилятор, ассемблер и компоновщик (линкер), а также могут входить стандартные библиотеки (например, libc), отладчик (debugger) и утилиты для анализа кода.

Кросс-тулчейн (Cross Toolchain) отличается от тулчейна тем, что с помощью него собирают бинарный код, предназначенный для другой платформы, отличной от той, на которой разрабатывают.

Система сборки

В отличие от тулчейна (который просто транслирует код), система сборки (например, Make, CMake, Ninja, MSBuild) автоматизирует весь процесс.

Вы описываете конечную цель (target), исходные файлы и зависимости, а система сама решает, в каком порядке и с какими параметрами вызывать компилятор и компоновщик.

Система сборки (Build System) управляет процессом через три ключевых понятия:

  • Цели (Targets): Что именно нужно получить. Это может быть исполняемый файл, статическая/динамическая библиотека или даже промежуточный этап (например, генерация документации).

  • Зависимости (Dependencies): Файлы или другие цели, которые необходимы для создания текущей цели. Например, чтобы собрать исполняемый файл app, сначала нужно скомпилировать файлы main.o и utils.o, а для них, в свою очередь, нужны исходники main.c, utils.c и заголовочные файлы .h.

  • Артефакты: Результаты сборки (объектные файлы, библиотеки и сам итоговый исполняемый файл).

Среды разработки

Для разработки и отладки STM32 существует несколько сред разработки (IDE).

  • STM32CubeIDE: Официальная бесплатная среда от компании STMicroelectronics, основанная на Eclipse и GCC. STM32CubeIDE: считается базовым и самым надёжным выбором для старта.

STM32CubeIDE
STM32CubeIDE
  • VS Code (Visual Studio Code) с плагинами: Универсальный текстовый редактор, который превращается в мощную IDE с помощью расширений (например, PlatformIO или официального расширения STM32 VS Code Extension). Часто используется в связке с CMake и отдельно запущенным CubeMX для генерации кода.

VS Code
VS Code
  • CLion: Платная профессиональная IDE от JetBrains с поддержкой Embedded-разработки. Доступна бесплатная лицензия для образовательных целей или 30-дневный пробный период.

CLion
CLion

Существуют ещё платные профессиональные среды IAR Embedded Workbench и Keil MDK, но для “Hello World" подойдут и первые три.

В процессе подготовки статьи я обнаружил, что каждая из сред имеет свои особенности. При использовании клонов J-Link v9 и ST-Link не всегда удобно или возможно вывести отладочное сообщение в той или иной среде.

Замечание о кроссплатформенности: Изначально экосистема STM32 была жёстко привязана к Windows, но сегодня разработка на Linux и macOS стала полноценным стандартом. Вся разработка в рамках этой статьи выполнялась под Linux.

SDK и библиотеки

Чтобы не работать с регистрами микроконтроллера напрямую «по адресам в памяти», используются готовые библиотеки от производителя или сообщества:

  • CMSIS (Cortex Microcontroller Software Interface Standard): Абстракция ядра от ARM.

  • HAL / LL от STMicroelectronics: Официальные библиотеки. HAL предоставляет высокоуровневый API, а LL (Low-Layer) — быстрые inline-обёртки над регистрами.

  • LibOpenCM3: Альтернатива официальному SDK с чистым и лаконичным API.

Программаторы и аппаратные отладчики

Часто аппаратный отладчик (Debug Probe) совмещён с программатором (Programmer).

ST-Link (STMicroelectronics)

Так как мы выводим “Hello World” на микроконтроллере STM32, рассмотрим программаторы ST-Link первыми. Если вы используете отладочную плату STM32 NUCLEO или STM32 DISCOVERY, то программатор ST-Link уже распаян на ней.

Также существуют отдельные устройства: как оригинальные от STMicroelectronics, так и их китайские клоны. Главное отличие большинства дешёвых клонов — отсутствие поддержки вывода SWO (Serial Wire Output), что ограничивает возможности быстрой отладки и трассировки.

Китайский клон ST-Link V2
Китайский клон ST-Link V2

J-Link

J-Link — аппаратный отладчик от компании SEGGER, совместно с ПО для компьютера поддерживает множество различных микроконтроллеров и отладочных плат.

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

Китайский клон J-Link V9
Китайский клон J-Link V9

CMSIS-DAP / DAP-Link

DAP-Link — это референсная имплементация CMSIS-DAP с открытым программным кодом от ARM для аппаратных отладчиков.

Pico Debug Probe является примером ещё одной реализации CMSIS-DAP.

Так как у Pico Debug Probe используется RP2040, который не содержит модуля ITM, использование SWO невозможно.

Многие IDE для STM32 не содержат встроенной поддержки СMSIS-DAP, но если установить сервер отладки OpenOCD, то теоретически можно прошить и отладить код для STM32.

ST-Link и J-link это и физические устройства, и прошивка микроконтроллера. Имея прошивку, можно плату с микроконтроллером превратить в аппаратный отладчик и программатор.

Прошивки, поддерживающие стандарт CMSIS-DAP, также можно установить на микроконтроллеры и отладочные платы и получить из них программатор и аппаратный отладчик. Так, например, получается Pico Debug Probe из Raspberry Pi Pico.

USB-UART преобразователи (CP2102, FT232, CH340)

USB-UART преобразователь
USB-UART преобразователь

USB-UART преобразователь, наверное, самое простое и привычное устройство для получения диагностических сообщений от микроконтроллера. Если вы когда-нибудь разбирали роутер или телевизионную приставку, то видели нераспаянный 4 пиновый разъём на плате (TX, RX, GND, Vcc). Это и есть разъём для подключения преобразователя.

Пины TX и RX платы и преобразователя подключаются перекрёстно. TX платы с RX преобразователя, RX платы с TX преобразователя.

Для просмотра того, что выводит микроконтроллер на печать на компьютере, должен быть запущен эмулятор терминала, который подключён к виртуальному COM-порту (VCP), создаваемому драйвером USB-UART преобразователя.

Времена, когда нужно было искать и устанавливать драйвер в операционной системе вручную прошли. Но если у вас преобразователь на базе CH340 и операционная система Windows, вечер, потраченный на понимание, как же всё-таки загружаются драйвера в Windows, вам гарантирован.

Для просмотра сообщений, приходящих по UART, под Windows я обычно использую Putty, в Linux — picocom. Вам нужно знать параметры UART-протокола, который использует устройство (скорость передачи, используется ли проверка бита чётности (parity), количество стоп-бит). Если вы укажете их неправильно при старте эмулятора терминала, вы увидите мусор или вообще ничего.

Обычно используются три пина из четырёх: TX, RX и GND, а пин питание не используется, так как питание подаётся на устройство обычным образом.

Также нужно быть внимательным к используемым уровням логики. Может быть 1.8 В, 3.3 В, 5 В. Чтобы ничего не сжечь, они должны быть одинаковые. Узнать, какие уровни использует устройство можно, замерив напряжение на пине ТХ.

Обычно производители оставляют поддержку вывода диагностики по UART даже в потребительских устройствах.

UART можно успешно использовать для вывода “Hello, World”, но существуют другие способы, позволяющие это сделать.

Физические интерфейсы связи

Для взаимодействия микроконтроллера с аппаратным отладчиком или программатором используется несколько аппаратных интерфейсов:

  • JTAG,

  • SWD,

  • UART.

Последний интерфейс не обязательно используется для подключения к аппаратному отладчику. Микроконтроллер можно подключить к компьютеру через USB-UART преобразователь.

  • JTAG — классический многопроводный интерфейс (TDI, TDO, TMS, TCK).

  • SWD (Serial Wire Debug) — двухпроводная альтернатива от ARM (SWDIO, SWCLK).

  • UART (Universal Asynchronous Receiver-Transmitter) — текстовая связь (TX, RX).

Методы вывода текстовой отладочной информации

Semihosting

Sеmihosting — самый медленный метод вывода отладочных сообщений, так как при выводе строки или символа процессор микроконтроллера прерывает свою работу и ожидает обработки компьютером.

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

У меня упорно не хотел работать semihosting при использовании сервера отладки J-Link совместно с аппаратным отладчиком J-Link. При использовании сервера отладки STLink и аппаратного отладчика STLink всё работало как положено. Мне удалось добиться работы semihosting с аппаратным отладчиком JLink, используя сервер отладки OpenOCD.

SWO (Serial Wire Output)

На микроконтроллерах с ядрами Сortex M3 и старше доступен метод вывода отладочных сообщений через отдельный пин SWO. В архитектуру встроен специальный модуль трассировки ITM (Instrumentation Trace Macrocell), который позволяет выводить отладочные сообщения через один выделенный пин SWO (Serial Wire Output). На более младших ядрах вроде Cortex-M0 и Cortex-M0+ этот функционал отсутствует. Для включения ITM нужно правильно сконфигурировать микроконтроллер. Также нужно правильно настроить частоту для SWV.

SWO поддерживается аппаратными отладчиками ST-Link и J-Link. Но китайские клоны ST-Link могут не содержать пина SWO. У меня именно такой.

На пин SWO информация выводится с использованием асинхронного протокола. SWO очень напоминает работу пина TX в UART. Когда SWO корректно сконфигурирован и находится в состоянии IDLE, на пине логическая единица.

На пин SWO может выводиться не только текстовые отладочные сообщения, но и другая отладочная информация.

SEGGER RTT (Real Time Transfer)

Если у вас аппаратный отладчик J-Link можно воспользоваться фирменной технологией от SEGGER, которая называется RTT (Real Time Transfer).

UART

UART — самый привычный способ вывода отладочной информации. Большинство устройств оставляют пины UART (TX, RX, GND, VCC) куда выводится отладочная информация при загрузке устройства.

Хотя многие аппаратные отладчики содержат пины TX и RX и создают виртуальный COM-порт для взаимодействия с устройством по UART, просмотреть отладочную информацию можно и без аппаратного отладчика. Достаточно подключить UART-USB адаптер.

При использовании UART в STM32, его необходимо настроить в CubeMX.

Практическая часть: Создаём проекты в STM32CubeMX и STM32CubeIDE

UART для вывода Hello, World

  1. Запускаем STM32CubeMX.

  2. Выбираем свою модель микроконтроллера или отладочной платы:

    Выбор модели микроконтроллера
    Выбор модели микроконтроллера
  3. Создаём проект в STM32CubeMX, нажав Start Project.

  4. Выполняем конфигурирование UART-интерфейса. Обратите внимание, какие значения у Basic Parameters, и какие пины используются для выбраного UART-интерфейса. Это понадобится позже для подключения UART-адаптера.

    Конфигурирование UART-интерфейса
    Конфигурирование UART-интерфейса
  5. Заходим на вкладку Clock Configuration и соглашаемся на автоматическое конфигурирование.

  6. На вкладке Project Mananager заполняем поля Project Name и Project Location. Выбираем значение CMake в поле Toolchain/IDE.

  7. На вкладке Code Generator ставим флажок Generate peripheral initialization as pair of '.c/.h' files per peripheral.

  8. Нажимаем кнопку GENERATE CODE.

  9. Запускаем STM32CubeIDE и создаём новый Workspace.

  10. Импортируем проект в STM32CuberIDE:

  11. Выбираем свою модель микроконтроллера:

  12. Собираем проект.

  13. Создаём конфигурацию для отладки.

  14. STM32CubeMX сгенерировал большинство кода за нас. Нам нужно подключить заголовочный файл stdio.h в файле main.c

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    /* USER CODE END Includes */
    
  15. Определяем функцию _write:

    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    int _write(int file, char *ptr, int len)
    {
        HAL_UART_Transmit(&huart3, (uint8_t *)ptr, len, HAL_MAX_DELAY);
        return len;
    }
    /* USER CODE END 0 */
    
  16. Вызываем функцию printf():

    /* USER CODE BEGIN WHILE */
    while (1)
    {
    /* USER CODE END WHILE */
    
    /* USER CODE BEGIN 3 */
          printf("UART Hello world \n");
          HAL_Delay(1000);
    }
    /* USER CODE END 3 */
    
  17. Перед запуском нужно подключить аппаратный отладчик и USB-UART преобразователь к микроконтроллеру и компьютеру. Я использовал J-Link, который может параллельно работать как USB-UART преобразователь.

  18. Запускаем отладку. Результат можно посмотреть в эмуляторе терминала, подключённому к виртуальному COM-порту. Такой эмулятор терминала можно или создать в STM32CubeIDE или запустить командой:

    sudo picocom -b 115200 /dev/ttyACM1
    

    где ttyACM1 — имя виртуального COM-порта в Linux.

Semihosting для вывода Hello, World

Вывод с использованием Semihosting немного сложнее, так необходимо выполнить несколько неочевидных действий.

  1. Создаём и импортируем проект из STM32CubeMX, как мы делали ранее.

  2. Создаём и настраиваем конфигурацию отладки, выбрав отладчик STLink.

  3. Добавляем опцию для компоновщика в файл CMakeLists.txt

    target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
     --specs=rdimon.specs
    )
    
  4. Добавляем библиотеку rdimon в файле CMakeLists.txt

    # Add linked libraries
    target_link_libraries(${CMAKE_PROJECT_NAME}
       stm32cubemx
       # Add user defined libraries
       rdimon
    )
    
  5. Чтобы проект смог собраться, нам нужно исключить из компиляции файл c заглушками syscalls.c, который сгенерировал STM32CubeMX, так как rdimon добавляет свои.

    set_source_files_properties(Core/Src/syscalls.c PROPERTIES HEADER_FILE_ONLY TRUE)
    
  6. В файле main.c подключаем заголовочный файл stdio.h:

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    /* USER CODE END Includes */
    
  7. В файле main.c добавляем объявление функции initialise_monitor_handles

    /* USER CODE BEGIN PFP */
     extern void initialise_monitor_handles(void);
     /* USER CODE END PFP */
    
  8. Не забываем её вызвать в начале функции main():

    int main(void)
    {
    
      /* USER CODE BEGIN 1 */
      initialise_monitor_handles();
      /* USER CODE END 1 */
    ...
    
  9. Используем обычную функцию printf() для вывода с использованием semihosting.

    while (1)
    {
       /* USER CODE END WHILE */
       /* USER CODE BEGIN 3 */
       printf("Hello World from Semihosting!\n");
       HAL_Delay(1000);
    }
    

SWO для вывода Hello, World

  1. Создаём и импортируем проект из STM32CubeMX, как мы делали ранее.

  2. Создаём и настраиваем конфигурацию отладки, выбрав отладчик J-Link.

  3. В конфигурации отладки активируем SWO.

  4. В файле main.c подключаем заголовочный файл stdio.h:

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    /* USER CODE END Includes */
    
  5. Определяем функцию SWO_PrintString():

    /* USER CODE BEGIN 0 */
    void SWO_PrintString(const char *str) {
      while (*str) {
      ITM_SendChar(*str++);
      }
    }
    /* USER CODE END 0 */
    
  6. Для вывода сообщений вызываем функцию SWO_PrintString():

       /* USER CODE BEGIN WHILE */
       while (1)
       {
       /* USER CODE END WHILE */
    
       /* USER CODE BEGIN 3 */
          SWO_PrintString("Hello world from SWO\n");
          HAL_Delay(1000);
       }
       /* USER CODE END 3 */
    
  7. Чтобы увидеть вывод, необходимо отобразить окно SWV ITM Data Console:

  8. Запускаем отладку.

  9. В SWV ITM Data Console активируем мониторинг, нажав на кнопку с красным кружочком:

  10. Нажимаем на кнопку с изображением инструментов и активируем Stimulus port 0:

  11. Наблюдаем вывод.

RTT для вывода Hello, World

  1. Создаём и импортируем проект из STM32CubeMX, как мы делали ранее.

  2. Создаём и настраиваем конфигурацию отладки, выбрав отладчик J-Link.

  3. В терминале клонируем библиотеку RTT:

       git clone https://github.com/SEGGERMicro/RTT.git
    
  4. В директорию Core/Src копируем два файла из библиотеки RTT – SEGGER_RTT.c и SEGGER_RTT_printf.c.

  5. В директорию Core/Inc копируем два файла из библиотеки RTT - SEGGER_RTT.h и SEGGER_RTT_ConfDefaults.h.

  6. Директории Core/Inc создаём пустой файл SEGGER_RTT_Conf.h

    touch  SEGGER_RTT_Conf
    
  7. В файле CMakeLists.txt добавляем исходные файлы RTT:

    # Add sources to executable
    target_sources(${CMAKE_PROJECT_NAME} PRIVATE
       # Add user sources here
       Core/Src/SEGGER_RTT.c
       Core/Src/SEGGER_RTT_printf.c
    )
    
  8. В файле main.c подключаем заголовочный файл SEGGER_RTT.h:

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include "SEGGER_RTT.h"
    /* USER CODE END Includes */
    
  9. Выводим текст:

    /* USER CODE BEGIN WHILE */
    while (1)
    {
     /* USER CODE END WHILE */
    
     /* USER CODE BEGIN 3 */
         SEGGER_RTT_printf(0, "Hello World from SEGGER!\n");
         HAL_Delay(1000);
    }
    /* USER CODE END 3 */
    
  10. Запускаем отладку.

  11. Запускаем из консоли JLinkRTTViewer:

С чем я столкнулся при подготовке примеров

Маркетологи не зря хлеб едят, но когда гордо заявлено о поддержке той или иной фичи, всё-таки нужно самому смотреть, что и как реализовано.

Например:

  1. Мне не удалось вывести “Hello World” средствами semihosting, используя сервер отладки J-Link. Пробовал все доступные версии для моей операционной системы, похоже на то, что в ПО J-Link здесь присутствует долгоживущий баг, по крайней мере, для ОС Linux.

  2. Я не обнаружил простого способа отладки с использованием semihosting для аппаратного отладчика ST-Link в CLion.

  3. Я долго не мог вывести в CLion отладочное сообщение средствами SWO для аппаратного отладчика J-Link, пока не обновил ПО J-Link на компьютере на самую свежую версию.

Заключение

На первый взгляд Hello World для микроконтроллера кажется совсем простой задачей. Но в случае STM32 за привычным printf() скрывается целая цепочка: микроконтроллер должен быть правильно настроен, сообщение — перенаправлено на выбранный интерфейс, а компьютер — получить и отобразить его.

В этой статье мы посмотрели на несколько способов сделать это: обычный UART, semihosting, SWO/ITM и SEGGER RTT. У каждого способа есть свои особенности, преимущества и ограничения. UART прост, понятен и не требует полноценного аппаратного отладчика. SWO позволяет получать отладочную информацию через интерфейс отладки, не занимая обычный UART. RTT предлагает удобный способ обмена данными с микроконтроллером при использовании J-Link. Semihosting прост с точки зрения программы, но сильно зависит от отладчика и при этом заметно влияет на выполнение программы.

На практике оказывается, что выбор способа вывода зависит не только от возможностей микроконтроллера, но и от используемого аппаратного отладчика, его прошивки, сервера отладки и среды разработки. Поэтому одинаковый проект может вести себя по-разному в STM32CubeIDE, VS Code или CLion и с разными ST-Link или J-Link.

Именно поэтому Hello World для STM32 — хороший учебный пример. Он позволяет не просто увидеть строку в терминале, а разобраться, что происходит между вызовом printf() и появлением символов на экране. За этим простым примером обнаруживается довольно большой пласт технологий: периферия STM32, UART, SWD, SWO, ITM, отладочные серверы и инструменты разработки.

После этого printf() уже не выглядит магией. Становится понятно, куда именно отправляется сообщение и какие компоненты обеспечивают его появление в терминале.

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

© 2026 ООО «МТ ФИНАНС»

Комментарии (65)


  1. x89377
    29.09.2026 14:38

    Когда же иссякнет эта река знаний ?


    1. dimao79
      29.09.2026 14:38

      Никогда. С недавних пор в эту реку начали вливаться множество новых притоков.

      "Напиши статью, где будет сравнение Хелловорлда для STM и RPi, включи в нее обзор сред разработки, отладчиков, программаторов, включи побольше максимально простых и детальных примеров."


      1. zambezi9
        29.09.2026 14:38

        Если бы редактор разрешал схлопывать фрагменты, то почему нет. Я вот спросил элэлэмку для похожей платы: “in VS Code PlatformIO Arduino framework what would be typical “Hello World” code for WeAct STM32H750VBT6 board with small built-in OLED-display 0.96?”

        Ответила всего двумя файлами platformio.ini и main.cpp. Карл, два небольших “кода” и эта неслабая плата уже что-то делает. Я взял ее (с дисплеем) всего за 1200р недавно на одном из маркетов. 480 MHz. Автор не очень удачно спросил и его элэлэмка нагрузила чуть ли не “bare-metal” подходом, когда то же можно проще сделать и в Ардуино фрэймворке.

        platformio.ini
        
        [env:weact_stm32h750vbt6]
        platform = ststm32
        board = genericSTM32H750VB
        framework = arduino
        
        ; Upload and Debug settings
        upload_protocol = dfu ; or stlink / jlink depending on your setup
        
        ; Library Dependencies
        lib_deps =
            adafruit/Adafruit SSD1306 @ ^2.5.9
            adafruit/Adafruit GFX Library @ ^1.11.9
        
        
        
        main.cpp
        
        #include <Arduino.h>
        #include <Wire.h>
        #include <Adafruit_GFX.h>
        #include <Adafruit_SSD1306.h>
        
        // Screen dimensions
        #define SCREEN_WIDTH 128
        #define SCREEN_HEIGHT 64
        
        // Hardware I2C Pin Definitions for WeAct STM32H750VBT6
        #define OLED_SDA PB9
        #define OLED_SCL PB8
        #define OLED_RESET -1 // Shared reset pin or unused (-1)
        #define OLED_ADDRESS 0x3C
        
        // Initialize SSD1306 display instance
        Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
        
        void setup() {
          // Initialize serial output for debugging
          Serial.begin(115200);
        
          // Configure custom I2C pins for the STM32 Arduino core
          Wire.setSDA(OLED_SDA);
          Wire.setSCL(OLED_SCL);
          Wire.begin();
        
          // Initialize display with 3.3V internal charge pump
          if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
            Serial.println(F("SSD1306 allocation failed"));
            for (;;); // Don't proceed, loop forever
          }
        
          // Clear display buffer
          display.clearDisplay();
        
          // Configure text formatting
          display.setTextSize(1);              // 1:1 pixel scale
          display.setTextColor(SSD1306_WHITE); // Draw white text
          display.setCursor(0, 0);             // Start at top-left corner
          
          // Render "Hello World" message
          display.println(F("WeAct STM32H750"));
          display.println(F("----------------"));
          display.setTextSize(2);              // Larger font for main text
          display.setCursor(0, 20);
          display.println(F("Hello,"));
          display.println(F("World!"));
        
          // Push buffer to display
          display.display();
        }
        
        void loop() {
          // Blink an onboard LED or update screen elements dynamically if needed
        }


        1. RigelGL
          29.09.2026 14:38

          STM используют чтобы уйти от тормозов Ардуино и получить бо́льший контроль над мк.


        1. CrashLogger
          29.09.2026 14:38

          Только это была бы статья про Arduino, а не про STM32. Приведенный выше код main.cpp не содержит ничего специфичного для STM32 и будет выглядеть так же для любой платы, которую поддерживает Arduino IDE.


    1. aabzel
      29.09.2026 14:38

      @artyomsoft

      Добавьте, пожалуйста, в источники мои тексты на эту же тему.

      Настройка ToolChain(а) для Win10+GCC+С+Makefile+ARM Cortex-Mx+GDB https://habr.com/ru/articles/673522/

      Сборка прошивки STM32 компилятором IAR при помощи GNU Make скрипта (IAR+Make=CI/CD) https://habr.com/ru/articles/1010484/

      STM32 + GCC + CMake + Win10
      https://habr.com/ru/articles/1019118/

      CLI через Segger J-Link RTT на ARM Cortex-M (или однопортовая лапароскопия) https://habr.com/ru/articles/1018168/

      SoftSerial: Программный UART на STM32
      https://habr.com/ru/articles/973392/

      Почему Нам Нужен UART-Shell? (или Добавьте в Прошивку Гласность) https://habr.com/ru/articles/694408/


    1. LinkToOS
      29.09.2026 14:38

      Когда же иссякнет эта река знаний ?

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


    1. dlinyj
      29.09.2026 14:38

      И чем плоха эта река? Я вот никогда дел не имел с STM32, и начал вкатываться в неё. При этом портировал ядра, писал драйвера, работал с AVR и lpc2103. Но STM32 просто какая-то адовая наркомания и нормального гайда, так чтобы он был актуален и по шагам не нашёл. Очень сложно и запутанно. Я понимаю, что разный HAL, что там разве что щи не варит и невозможно всё объять.

      Мне чтобы помигать светодиодом пришлось пару вечеров ставить STM32CubeMX, затем разбираться как генерировать проект. Непонятно зачем нужно для STM32 отдельное IDE, если даже блокнот прекрасно справляется. Потом разбирался как ставить компилятор, как генерировать Makefile, как шить под линукс.

      Вроде всё просто и понятно (когда знаешь примерно что должно быть), но вкатиться в это сходу очень сложно.


      1. Qwest_Prozto
        29.09.2026 14:38

        Есть проблема у STM, что официальные способы собирать прошивку в целом не особо адекватны, и все пытаются делать так как им удобнее.
        CubeMX страшная фигулина с кривой автогенерацией кода и прекрасным предложением писать код в секциях.
        HAL еще большая катастрофа с ООП без ООП т.е. буквально код на C, который пытается выглядеть как C++
        При этом все эти минусы важны только для уже сформировавшихся разрабов, для новичка вполне сносные инструменты


        1. NutsUnderline
          29.09.2026 14:38

          зато удобно сделать набросок мышкой и прикинуть как оно вообще работает, посмотреть куда какие байты надо совать, опять же карта клоков наглядная ... а уж потом - хоть табличку [105000]{адрес,значение}


          1. Qwest_Prozto
            29.09.2026 14:38

            Ну CubeMX у меня установлен и иногда пригождается как раз узнать делители на частоту и номера доступных таймеров


        1. aabzel
          29.09.2026 14:38

          CubeMX страшная фигулина с кривой автогенерацией кода

          Да. Именно так.


      1. RTFM13
        29.09.2026 14:38

        del


    1. aabzel
      29.09.2026 14:38

      Когда же иссякнет эта река знаний ?

      Добавьте в ссылки аналогичные тексты других авторов,
      которые уже освещали эту тему.

      @andreyzaostrovnykh
      STM32. Процесс компиляции и сборки прошивки https://habr.com/ru/companies/timeweb/articles/793152/
      @andreyzaostrovnykh STM32. Подготавливаем среду разработки в Linux https://habr.com/ru/companies/timeweb/articles/787328/
      @romanshuvalov ARM-микроконтроллеры STM32F. Быстрый старт c STM32-Discovery https://habr.com/ru/articles/128734/?ysclid=muoitpw2i874730498
      @Butaforsky STM32 Часть 1. Как поморгать светодиодиком https://habr.com/ru/articles/694184/
      @DimKa_exe Жизнь без CubeMX: Первая прошивка для STM32, имея под рукой только блокнот https://habr.com/ru/articles/1022976/


  1. TheHost
    29.09.2026 14:38

    чет как-то сложно, я через програмулину для ардуино прошивал, просто подключив к USB


    1. artyomsoft Автор
      29.09.2026 14:38

      Используя все четыре способа?


      1. TheHost
        29.09.2026 14:38

        Ну в плане 4 способа? Заголовок - hello world, а текст как будто программирование микро контроллера из 2000х. Сейчас все намного проще делается.

        У вас же не голый чип, а с интерфейсом на плате, по сути просто подключив его к компьютеру у вас и сериал порт будет и все остальное. А вы зачем-то вооружились программатором и прочим и называете это hello world.


        1. artyomsoft Автор
          29.09.2026 14:38

          Я рассказал про 4 возможных способа получить текстовые сообщения от микроконтроллера на хосте.

          Если вы говорите про подкючение по USB отладочной платы WeAct Studio к компьютеру - то не будет сериал порта и всего такого у STM32. Это не Raspberry Pi Pico.

          В случае STM32 должна быть плата Nucleo или Disovery. Они содержат в себе отладчик/програматор ST-Link и UART доступен через USB.

          На моей плате, как и на известных Blue Pill и Black Pill такой возможности нет. Нужен программатор для заливки прошивки и USB-UART адаптер как минимум для того, чтобы видеть, что мы вывeли на UART.


          1. vojlk
            29.09.2026 14:38

            Если быть точнее, не USB-UART, а USB-TTL. Потому что на плате не распаян приёмопередатчик

            И программатор по факту не нужен. Можно прошивать через встроенный загрузчик и stm32flasher. Плюс сразу удобно делать трасировку данных в терминалку по usart1

            При таких габаритах платы это в несколько раз удобнее, потому что дополнительная лапша на столе очень мешает отладке


            1. artyomsoft Автор
              29.09.2026 14:38

              По USB-UART адаптером (преобразователем) я имею ввиду устройство на базе CP2102, CH340 и им подобным.

              Может я неправильно понимаю архитектуру, но как данные из usart1 попадут в USB без дополнительного чипа?

              Но отдадить с точками останова и пошагово не получится же вашим способом?


              1. vojlk
                29.09.2026 14:38

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

                как данные из usart1 попадут в USB без дополнительного чипа

                Никак, так же через преобразователь.


            1. NutsUnderline
              29.09.2026 14:38

              USB-UART, а USB-TTL. 

              смешение физики и логики вижу тут я

              Можно прошивать через встроенный загрузчик

              в основном уже по usb dfu если речь про stm32. только старые по типу f103 не содержат возможности usb во встроенном загрузчике


              1. vojlk
                29.09.2026 14:38

                смешение физики и логики вижу тут я

                Есть такое дело. Профдеформация, привык по старинке такие адаптеры usb to ttl, если на плате не разведен приёмопередатчик под rs422/485.

                в основном уже по usb dfu

                Согласен, но если это готовое изделие, нужно писать программный переход в dfu, потом этот обработчик в нестираемую область убрать. У меня изделия без usb и очень удобно в кабеле сделать переключатель и сразу одним махом прошивать и потом через этот же разъём данные в терминалку прокидывать


            1. LinkToOS
              29.09.2026 14:38

              не USB-UART, а USB-TTL

              Эта штука помогает реализовать виртуальный COM-порт в компьютере. Так что по сути это плата расширения портов компьютера, которая по чистой случайности (из-за изобретения USB) втыкается не во внутренний слот, а в разъем USB.

              А процессор подключается к ней без преобразователя уровня (приемопередатчика), потому что "а зачем?".


    1. zambezi9
      29.09.2026 14:38

      Меня тоже минуснули за то что написал, что все на самом деле проще. Это нормально. Есть авторы, которые считают разумным поделиться знаниями с массами как проще и быстрее теперь можно делать то, что раньше было недоступно из-за высокого по трудоемкости порога. Идеалисты, епт. Они верят во что-то там непонятное.

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


      1. artyomsoft Автор
        29.09.2026 14:38

        Получается, лучше вообще ничего не писать?


        1. NutsUnderline
          29.09.2026 14:38

          итоговые плюсы покажут ;) хорошо стурктурированный и читаемый материал - гораздо лучше чем клочки инфы по закоулочкам интернета. но проверенный на практике и очищенный от галюцинаций. Как ИИ так и живого автора материала.


      1. CrashLogger
        29.09.2026 14:38

        С Arduino вы никуда не вкатитесь. При трудоустройстве ее лучше вообще не упоминать)


  1. LinkToOS
    29.09.2026 14:38

    На вкладке Project Mananager заполняем поля Project Name и Project Location. Выбираем значение CMake в поле Toolchain/IDE

    Почему CMake, если дальше используется STM32CubeIDE? В чем плюсы? Создаются какие-то дополнительные файлы для тонкой настройки проекта?


    1. artyomsoft Автор
      29.09.2026 14:38

      Потом проект проще будет открыть в СLion, если вдруг захотите в нем работать


      1. artyomsoft Автор
        29.09.2026 14:38

        СLion мне даже больше понравился, но с ним не получилось бы использовать все четыре способа вывода текста на имеющихся у меня сейчас устройствах для отладки и разработки. Ну или сложный конфиг для запуска сервера отладки городить. У СLion хуже поддержка серверов отладки.


    1. dlinyj
      29.09.2026 14:38

      А нафига это всё надо? Если есть Makefile и любимая IDE, хоть тот же прекрасный vim...


      1. LinkToOS
        29.09.2026 14:38

        Потому что просто. CubeMX + CubeIDE. Все сразу работает без лишних настроек. Для новичков лучший вариант.

        А профи могут и без CubeMX обойтись. Им вообще кю-инициализатор периферии не нравится, кроме clock config.


        1. dlinyj
          29.09.2026 14:38

          Погодите, не поймите меня не правильно, вопрос без вызова. Скорее реально хочу понять.

          Я новичок совершенно, буквально разок помигал светодиодом. Но мне понравилась идея инициализации HAL через CubeMX, а дальше я просто сгенерировал проект с Makefile и Visual Studio Code правил то что мне нужно, собирал и шил просто в косноли по классике.

          Может я что-то упускаю?


          1. artyomsoft Автор
            29.09.2026 14:38

            Но дебажить же проще в IDE? Чем в GDB. Xoтя что-то в дебаге в GDB есть


            1. dlinyj
              29.09.2026 14:38

              Ну вот, пример :). Но vscode может же в связке с gdb работать


              1. artyomsoft Автор
                29.09.2026 14:38

                А VS Code не IDE?

                Но я думаю для дебага микроконтроллеоров нужно будет плагины ставить


                1. dlinyj
                  29.09.2026 14:38

                  Да не, gdb он одинаковый для всего, особенно если через телнет.


            1. aabzel
              29.09.2026 14:38

              Но дебажить же проще в IDE? Чем в GDB. Xoтя что-то в дебаге в GDB есть

              Зачем Вам эта пошаговая отладка из-под IDE?
              Если ваш микроконтроллер поддерживается Segger Jlink (а все STM32 поддерживаются), то делайте пошаговую отладку в Segger Jlink Ozone.
              Ozone стартует мгновенно и просто летает!

              Вот методички про то как это всё настроить и запустить

              Программирование плат Nucleo отладчиком J-Link https://habr.com/ru/articles/1060054/ Отладка STM32 программатором J-LINK по SWD https://habr.com/ru/articles/995996/


              1. dlinyj
                29.09.2026 14:38

                Прмедленее, я записываю :)

                Если вы посмотрите стоимость jlink оригинала, то будете неприятно удивлены


                1. aabzel
                  29.09.2026 14:38

                  Никто не покупает оригиналы. Покупайте клон jlink v12 на алиекспресс максимум за 4к руб и все у вас будет работать.


                  1. dlinyj
                    29.09.2026 14:38

                    С ними там свои проблемы, к сожалению, если не домашний проект.


                    1. aabzel
                      29.09.2026 14:38

                      Нет никаких проблем. Везде где я работал используют клоны jlink для коммерческих разработок. Более того используют ещё десятки и десятки клонов переходников PCAN (usb -can преобразователи) .


                      1. artyomsoft Автор
                        29.09.2026 14:38

                        А они не могут окирпичиться, если вдруг обновишь их прошивку?


                      1. aabzel
                        29.09.2026 14:38

                        Да такие случаи были.

                        А они не могут окирпичиться, если вдруг обновишь их прошивку?

                        В таких случаях надо откатываться на более старую прошивку и можно снова работать.


                      1. NutsUnderline
                        29.09.2026 14:38

                        прошивка не прошивка, но вот с обновлениями софта на ПК оный софт становится что то подозревать и ругаться. вопрос с руганием решен в случае windows и macos но не очень понятно что делать в случае linux. например не обновлять


                      1. NutsUnderline
                        29.09.2026 14:38

                        Везде где я работал используют клоны jlink

                        не забываем что есть места где нонче все все должно быть отечественное, из реестра. причем в такие места на aliexpress не вроде как и покупают, да и фирма segger официально не продала бы.


              1. artyomsoft Автор
                29.09.2026 14:38

                Спасибо


          1. LinkToOS
            29.09.2026 14:38

            Есть люди, которые свой путь в программировании начинают с микроконтроллеров. У них еще никакая среда программирования не развернута. Или те кто делал только приложения для ПК, например в Microsoft Visual C#. Вариантов много.


            1. dlinyj
              29.09.2026 14:38

              Понял, нет причин использовать.


              1. LinkToOS
                29.09.2026 14:38

                Переходить на CubeIDE повода нет, если процесс уже настроен в другой среде. Это тот же eclipse, только настроенный для stm.


                1. dlinyj
                  29.09.2026 14:38

                  А в чём смысл этой настройки, я не понимаю?


                1. aabzel
                  29.09.2026 14:38

                  Более того stm32cubeide ещё и дольше прогружается при старте и больше тормозит, чем просто голый eclipse.

                  Переходить на CubeIDE повода нет, если процесс уже настроен в другой среде. Это тот же eclipse, только настроенный для stm.

                  Да . Именно так.


    1. NutsUnderline
      29.09.2026 14:38

      а можно и не использовать. сгенерировать набор нужных либ, заголовков и make/cmake к ним - а дальше в командой строке + текстовый редактор или любимая IDE


  1. voldemar_d
    29.09.2026 14:38

    ИМХО, как-то это всё сложновато, если это "руководство для новичка". Я вот подумываю заняться написанием чего-нибудь для STM32, но после Arduino как-то рука не поднимается такую кучу всего осваивать (для меня это хобби, а не работа).

    Возможно, сейчас меня закидают чем-нибудь, но можно же и в Arduino IDE для STM32 писать. Например, здесь и тут пишут:

    Программная поддержка STM32 в Arduino IDE называется STM32duino. Она реализована в виде пакета поддержки и загрузчика, прошиваемого в плату. В среду она добавляется как обычно: сначала указывается ссылка в настройках, потом устанавливается пакет из Менеджера плат.

    Для новичка этого недостаточно будет?


    1. artyomsoft Автор
      29.09.2026 14:38

      Для новичка лучше, наверное, Raspberry Pi Pico и RP2040 или ESP32, а не STM32


      1. voldemar_d
        29.09.2026 14:38

        С этим я не спорю. Я имею ввиду "для новичка, желающего начать работать именно с STM32".


        1. artyomsoft Автор
          29.09.2026 14:38

          Ну если быстро получить результат и мотивацию для дальнейшего изучения, то, наверное, да.


    1. RigelGL
      29.09.2026 14:38

      STM после Ардуино ощущается как глоток свежего воздуха, всё под контролем, вся периферия настраивается, программа для настройки периферии (CubeMX) есть.

      Что может предложить Ардуино? Недо pinMode и тормозной digitalWrite. А как ставится высокомное состояние в Ардуино? pinMode(INPUT), digitalWrite(HIGH) - что за бред? В то время как в stm режимов GPIO больше 3 и вместо хака с pinMode, digitalWrite в коде будет указан конкретный тип из enum.


      1. Qwest_Prozto
        29.09.2026 14:38

        Справедливости ради вы сравниваете фреймворк ардуины и HAL STM, хотя то и то может работать без этой шушеры на самописных фреймворках. Хотя их наличие у новичка не совсем реалистично.


        1. RigelGL
          29.09.2026 14:38

          Справедливости ради вы сравниваете фреймворк ардуины и HAL STM,

          Новичок на STM скорее всего будет использовать HAL, получив полный контроль над периферией.

          хотя то и то может работать без этой шушеры на самописных фреймворках. Хотя их наличие у новичка не совсем реалистично.

          Согласен, писать в регистры не похоже на Arduino-way с шильдиками и готовыми библиотеками.

          В то время как HAL генерируется в проекте, позволяя посмотреть реализацию, Ардуино предлагает максимально упрощённую среду разработки.


      1. voldemar_d
        29.09.2026 14:38

        Тормозной digitalWrite можно заменить на прямую запись в регистры, тогда побыстрее будет. Другое дело, что у многих Arduino тактовая частота МК низкая, и они просто медленные сами по себе.

        А как ставится высокомное состояние в Ардуино? pinMode(INPUT), digitalWrite(HIGH) - что за бред? 

        вместо хака с pinMode, digitalWrite в коде будет указан конкретный тип из enum

        Согласен, что лучше явное включение режима, но это, ИМХО, не такая уж страшная проблема, как малое количество памяти, низкая тактовая частота и др. ИМХО, на всё имеет смысл смотреть с точки зрения решаемых задач и баланса между возможностями и сложностью их использования.


        1. RigelGL
          29.09.2026 14:38

          Тормозной digitalWrite можно заменить на прямую запись в регистры, тогда побыстрее будет.

          Теоретически в ~110 раз, если писать PORTA = 0b...;

          Другое дело, что у многих Arduino тактовая частота МК низкая, и они просто медленные сами по себе.

          но это, ИМХО, не такая уж страшная проблема, как малое количество памяти, низкая тактовая частота и др.

          Попытка притянуть STM под Ардуино выглядит как добровольный отказ от большинства аппаратных фич STM и топтание на месте.

          ИМХО, на всё имеет смысл смотреть с точки зрения решаемых задач и баланса между возможностями и сложностью их использования.

          Ардуино решает задачу привлечения новичков в embedded (и решает отлично), но Ардуино нужно вовремя перерасти.


          1. voldemar_d
            29.09.2026 14:38

            Так ведь и более крутые уровни есть - разработка на FPGA, например, вплоть до плат PCIx. А вот "нужно" или нет - ИМХО, каждый решает сам.


      1. aabzel
        29.09.2026 14:38

        Что может предложить Ардуино? Недо pinMode и тормозной digitalWrite.

        Для AVR есть codeVisionAVR, который умеет генерить код по настройкам в gui.