Программирование STM32 в Linux — задача вполне решаемая, хотя и требует некоторой начальной настройки. В отличие от Windows, где доминируют IDE вроде Keil или STM32CubeIDE, в Linux вы получаете полный контроль над инструментами и можете выбрать подход по душе: от "голого" C до Rust.
Ниже показаны простые шаги по быстрому старту на примере STM32F103C8T6 (BluePill). Вот что вам понадобится и какие есть пути.
[Базовый набор инструментов]
Для начала работы в Linux (на примере Ubuntu) потребуется установить кросс-компилятор и утилиты для прошивки. Стандартный набор:
gcc-arm-none-eabi: кросс-компилятор для ARM Cortex-M. binutils-arm-none-eabi: ассемблер, линкер и другие бинарные утилиты. gdb-multiarch: отладчик для разных архитектур. stlink-tools или openocd: для прошивки через ST-Link.
В Linux у вас есть несколько вариантов, от простого к сложному:
1. Arduino Framework (STM32duino): самый быстрый старт, если вы знакомы с Arduino. Можно использовать PlatformIO в VS Code или настроить Arduino IDE с ядром STM32. Вы получаете функции вроде pinMode() и digitalWrite(), но с ограничениями по контролю.
2. Bare-Metal (на регистрах): полный контроль над железом без библиотек. Вы напрямую управляете регистрами RCC, GPIO и другими. Идеально для понимания, как работает микроконтроллер, и дает самый компактный код. Например, мигание светодиодом может занимать всего 440 байт.
3. libopencm3: библиотека с открытым исходным кодом, которая предоставляет удобные функции для работы с периферией, но без «тяжеловесности» HAL от ST. Хороший компромисс между контролем и удобством.
4. Rust: современный и безопасный язык. Настройка включает установку rustup, целей для ARM Cortex-M (thumbv7m-none-eabi для STM32F103) и инструментов вроде probe-rs для прошивки.
[Bare-Metal: мигание светодиодом (Blinker)]
Ниже показан простой пример для микроконтроллера STM32F103C8T6 на плате BluePill для понимания процесса программирования без библиотек.
Скрипт линкера (linker script) — это файл, который сообщает линкеру, как расположить код и данные в памяти микроконтроллера. Он определяет области памяти (Flash, RAM) и распределяет по ним секции: вектор прерываний, код программы, константы, инициализированные и неинициализированные данные.
Для STM32F103C8T6 типичная карта памяти:
Flash: 64 КБ, начиная с адреса 0x08000000 RAM: 20 КБ, начиная с адреса 0x20000000
Примечание: в сообществе есть готовые скрипты для STM32F103C8T6, например, в репозиториях на GitHub [5, 6], посвящённых bare-metal программированию. Файл STM32F103C8Tx_FLASH.ld также генерируется STM32CubeMX при создании проекта.
Для работы команды st-flash` вам нужен программатор ST-Link, подключённый к плате Blue Pill по интерфейсу SWD. Напрямую через USB-порт Blue Pill эту команду выполнить нельзя.
Схема подключения ST-Link к Blue Pill. Вам понадобятся 4 провода (плюс опционально пятый для Reset). Подключение к 4-контактному разъёму SWD на Blue Pill:
ST-Link
Blue Pill (SWD)
Назначение
3.3V
3.3V
Питание +3.3В.
GND
GND
Земля, общий провод.
SWDIO
DIO (PA13)
Данные SWD.
SWCLK
CLK (PA14)
Тактирование SWD.
NRST
R (Reset)
Аппаратный сброс (опционально, но рекомендуется).
Важный нюанс: обычно не рекомендуется подавать питание на Blue Pill одновременно от ST-Link и от USB. Выберите один источник: либо ST-Link (подаёт 3.3V), либо USB-порт. Для прошивки обычно достаточно питания от ST-Link.
Программная настройка в Linux. Прежде чем запускать st-flash, убедитесь, что система видит программатор и у вас есть права на доступ к USB-устройству.
1. Проверьте подключение. Подключите ST-Link к компьютеру. Выполните в терминале:
lsusb | grep -i st-link
Вы должны увидеть устройство с ID `0483:3748` (или похожим).
2. Настройте права доступа, если они еще не настроены (udev rules). Если st-flash выдаёт ошибку `No ST-Link detected` или `Failed to connect`, скорее всего, у вашего пользователя нет прав на USB-устройство.
Для других версий (например, ST-Link/V2-1 от плат Nucleo или V3) ID продукта будет другим (374b и 374e соответственно).
Сохраните файл, перезагрузите правила и переподключите ST-Link:
sudo udevadm control --reload-rules sudo udevadm trigger
3. Проверьте связь с микроконтроллером:
Команда ниже покажет, видит ли ST-Link подключённую плату Blue Pill:
st-info --probe
Вы должны увидеть информацию о чипе (например, `Device connected is: F1 Medium-density device`).
4. Запуск прошивки. После всех подключений и настройки выполните команду:
st-flash write main.bin 0x8000000
Будет выведено что-то наподобие:
$ st-flash write main.bin 0x8000000
st-flash 1.8.0
2026-09-10T09:18:54 INFO common.c: STM32F1xx_MD: 20 KiB SRAM,
64 KiB flash in at least 1 KiB pages.
file main.bin md5 checksum: 1e8cd4107ce0a1137684211ff09bfed,
stlink checksum: 0x00004ee7
2026-09-10T09:18:54 INFO common_flash.c: Attempting to write
236 (0xec) bytes to stm32 address: 134217728 (0x8000000)
-> Flash page at 0x8000000 erased (size: 0x400)
2026-09-10T09:18:54 INFO flash_loader.c: Starting Flash write for VL/F0/F3/F1_XL
2026-09-10T09:18:54 INFO flash_loader.c: Successfully loaded flash loader in sram
2026-09-10T09:18:54 INFO flash_loader.c: Clear DFSR
1/1 pages written
2026-09-10T09:18:54 INFO common_flash.c: Starting verification of write complete
2026-09-10T09:18:54 INFO common_flash.c: Flash written and verified! jolly good!
2026-09-10T09:18:54 INFO common.c: Go to Thumb mode
Нажмите на кнопку RESET платы, и зеленый светодиод, подключенный к порту PC13, начнет мигать.
- Ошибка `Can not connect to target`: чаще всего это плохой контакт или неправильное подключение SWDIO/SWCLK. Проверьте провода. Также может помочь добавление флага `--connect-under-reset`:
- Ошибка `Unknown memory region`: иногда возникает, если предыдущая прошивка отключила или переназначила выводы сигналов SWD. Решение: переведите джампер BOOT0 в положение `1`, сбросьте плату, попробуйте прошить заново, а после успеха верните BOOT0 в `0`.
- Программа не запускается: проверьте, что джамперы BOOT0 и BOOT1 установлены в `0` (запуск из Flash).
[Прошивка с помощью USB-загрузчика]
Для этого потребуется сначала один раз записать в микроконтроллер специальную программу-загрузчик (bootloader) [2].
Важный нюанс: встроенного USB DFU нет. Многие ожидают, что STM32F103C8T6, как и некоторые другие модели STM32, имеет встроенный в системную память USB DFU-загрузчик. Однако это не так.
Встроенный USB DFU есть только у линеек STM32F105/107. У STM32F103C8T6 (и у всей линейки F103) такого заводского загрузчика нет. Поэтому, если просто подключить плату по USB-кабелю и замкнуть BOOT0 в 1, она не определится как DFU-устройство.
Однако есть решение: пользовательский USB-загрузчик. Чтобы получить возможность прошиваться через USB, в микроконтроллер нужно один раз (через ST-Link) записать пользовательский bootloader. После этого обновлять прошивку можно будет обычным USB-кабелем.
Распространённые варианты таких загрузчиков:
1. STM32duino bootloader (Maple) — классический выбор для Blue Pill. Он создаёт виртуальный последовательный порт (CDC ACM) и позволяет загружать прошивку через dfu-util или среду Arduino IDE [7]. 2. USB DFU Bootloader (Rust/RTIC) — пример загрузчика на Rust, который занимает 16 КБ Flash и позволяет обновлять прошивку через стандартную утилиту dfu-util [8]. 3. USB Mass Storage Bootloader — загрузчик, который после включения эмулирует USB-флешку (FAT32). Вы просто перетаскиваете на неё .hex-файл, и прошивка обновляется [9].
Как это работает (на примере STM32duino)
1. Установка загрузчика: с помощью ST-Link (как вы уже делали) вы прошиваете в Blue Pill бинарник загрузчика. Обычно он располагается по адресу 0x08000000 и занимает небольшой объём (например, 2 КБ).
2. Настройка платы: устанавливаете перемычки BOOT0 и BOOT1 в определённое положение. Для некоторых версий загрузчика это нестандартная комбинация (BOOT0=0, BOOT1=1). Для большинства других — стандартная для входа в режим загрузки (BOOT0=1, BOOT1=0) .
3. Подключение и прошивка: перезагружаете плату, подключив её к ПК через USB. В системе появляется виртуальный COM-порт или DFU-устройство с определённым VID/PID. После этого можно использовать `dfu-util` для загрузки новой прошивки .
4. Возврат в рабочий режим: после прошивки нужно установить BOOT0 в `0` (GND) и нажать Reset. Микроконтроллер запустит вашу основную программу.
Что важно помнить:
- Вы потеряете часть Flash-памяти: загрузчик занимает место в начале памяти (например, первые 16 КБ из 64 КБ). Соответственно для основного кода останется меньше места, и его придётся размещать со смещением (например, по адресу 0x08004000) (см. врезку далее для примера загрузчика STM32duino bootloader). - Нужна первоначальная прошивка загрузчика через ST-Link: это разовое действие, без него USB-загрузчик не появится. - Не все загрузчики одинаковы: у разных загрузчиков могут отличаться адреса, способ входа в режим и утилиты для прошивки.
Чтобы ваш код запускался с загрузчиком STM32duino (Maple), его нужно собирать со смещением адреса начала Flash. По умолчанию код компилируется для запуска с адреса 0x08000000, но загрузчик занимает это место, поэтому ваша программа должна располагаться выше.
Вот ключевые моменты, которые нужно изменить:
1. Адрес запуска и объем зарезервированной памяти
Современный загрузчик STM32duino занимает 8 КБ памяти и размещается по адресу 0x08000000. Соответственно, ваша программа (скетч) должна быть скомпилирована для запуска с адреса 0x08002000.
Это оставляет для вашего кода 56 КБ из 64 КБ доступной Flash-памяти.
2. Что нужно изменить в проекте
Если вы используете стандартный STM32duino core для Arduino IDE, он сам управляет этими настройками через меню выбора платы и метода загрузки. При выборе "BluePill F103C8" и метода загрузки "Maple DFU Bootloader 2.0" в Arduino IDE, все необходимые смещения (включая `VECT_TAB_OFFSET = 0x2000`) применяются автоматически.
При ручной сборке (Makefile / свой скрипт линкера) необходимо внести три изменения:
1. Скрипт линкера (.ld): нужно изменить начало Flash.
2. Векторная таблица прерываний: необходимо указать смещение для таблицы векторов в коде.
В system_stm32f1xx.c или в main.c нужно установить SCB->VTOR. Часто используется макрос VECT_TAB_OFFSET:
// Для работы с загрузчиком Maple #define VECT_TAB_OFFSET 0x2000
SCB->VTOR=FLASH_BASE|VECT_TAB_OFFSET;
Это говорит микроконтроллеру, где искать таблицу векторов прерываний после запуска.
3. Адрес прошивки: при загрузке бинарного файла через dfu-util или st-flash (для теста) указывайте адрес 0x08002000.
[Важный нюанс совместимости]
Существует два возможных адреса для пользовательского кода, в зависимости от версии загрузчика:
- 0x08002000 — для современного 8-килобайтного загрузчика (используется по умолчанию сейчас). - 0x08005000 — для старого 20-килобайтного загрузчика из оригинального Maple IDE. Этот адрес поддерживается для обратной совместимости.
Современный загрузчик при старте сначала проверяет наличие кода по адресу 0x08002000 и, если его там нет, может переключиться на проверку 0x08005000. Поэтому, если ваш код не запускается, стоит проверить, по какому именно адресу его ожидает загрузчик.
[Измененный код программы Blinker для загрузчика STM32duino]
Загрузчик STM32duino (Maple) занимает первые 8 КБ Flash (адреса 0x08000000 – 0x08001FFF), поэтому ваша программа должна начинаться с 0x08002000.
Изменений потребуется ровно два: в скрипте линкера и в startup.c. В main.c менять ничего не нужно.
1. Скрипт линкера: stm32f103c8t6.ld
Меняем только блок `MEMORY` — начало Flash и её длину.
Остальные секции не трогаем — они автоматически привяжутся к новому `ORIGIN(FLASH)`.
2. startup.c: добавляем установку SCB->VTOR
Загрузчик Maple перед передачей управления вашей программе оставляет свою векторную таблицу в начале Flash. Ваш код должен перенаправить процессор на свою таблицу по адресу 0x08002000. Делается это через регистр SCB->VTOR (Vector Table Offset Register).
/* 4. Если main() вернулся — зацикливаемся */ while(1){}
}
voidDefault_Handler(void){ while(1){}
}
Что здесь важно:
SCB_VTOR = FLASH_BASE | VECT_TAB_OFFSET; — эта строка должна выполняться в самом начале Reset_Handler, до любых прерываний. Иначе, если прерывание сработает до переключения, процессор уйдёт по адресу из таблицы загрузчика и, скорее всего, зависнет.
VECT_TAB_OFFSET = 0x2000 — смещение 8 КБ от начала Flash. Это ровно тот адрес, где линкер размещает вашу векторную таблицу.
3. Файл main.c менять не нужно. Код мигания светодиодом остаётся прежним. Единственное, что стоит помнить: теперь ваша программа стартует не с 0x08000000, а с 0x08002000, и перед `main()` загрузчик делает небольшую паузу (обычно ~2 секунды, ожидая сигнала от хоста на загрузку новой прошивки).
В выводе у секции `.isr_vector` должен быть адрес 0x08002000, а не 0x08000000. Если это так — скрипт линкера применён корректно.
Прошивка: два способа. Вариант A, через ST-Link (для теста):
Прошейте бинарник по новому адресу вручную:
st-flash write main.bin 0x8002000
И не забудьте сначала прошить сам загрузчик Maple по адресу 0x8000000, иначе стартовать будет нечему.
Вариант B, через USB и dfu-util (штатный путь):
1. Установите dfu-util, если она еще не установлена:
sudo apt install dfu-util
2. Переведите плату в режим загрузчика (перемычка BOOT0 в `1`, BOOT1 в `0`, нажмите Reset).
3. Подключите USB-кабель к Blue Pill.
4. Проверьте, что устройство видно:
dfu-util -l
Вы должны увидеть устройство с `Vendor ID 0x1eaf` и `Product ID 0x0003` (это VID/PID Maple).
5. Залейте прошивку:
dfu-util -a 2 -d 1eaf:0003 -D main.bin
Ключ `-a 2` указывает альтернативный интерфейс для загрузки в Flash (у Maple их два: `-a 1` — RAM, `-a 2` — Flash).
Полезно знать:
- Обратная совместимость со старым Maple: если у вас загрузчик старого образца (20 КБ), адрес смещения будет 0x5000. Если после прошивки плата не стартует — попробуйте заменить VECT_TAB_OFFSET и ORIGIN(FLASH) на 0x5000. - Современный загрузчик (STM32duino bootloader 2.0) ожидает программу именно по адресу 0x08002000 — как в примере выше. - Проверка: после прошивки через dfu-util плата автоматически перезагрузится и запустит ваш код. Светодиод PC13 должен замигать с той же частотой, что и раньше. - Размер программы: теперь у вас максимум 56 КБ Flash вместо 64 КБ. Для мигания светодиодом это не проблема, но стоит помнить при разработке крупных проектов.
Итог: прошивать Blue Pill через USB можно, но для этого сначала нужно «научить» его это делать, записав специальный bootloader через ST-Link. Это отличное решение для готовых устройств, к которым сложно подключаться программатором, но для начального освоения микроконтроллера проще и надёжнее продолжать использовать ST-Link.
[Советы для комфортной работы]
• Правила udev: чтобы прошивать без sudo, добавьте себя в группу dialout и настройте правила udev для ST-Link. Инструкции обычно есть в документации stlink-tools или openocd.
• Отладка: OpenOCD + GDB — мощный тандем. Запускаете openocd -f interface/stlink.cfg -f target/stm32f1x.cfg, затем подключаетесь gdb-multiarch и можете пошагово выполнять код, ставить точки останова и смотреть регистры.
• STM32CubeMX: хотя это GUI-инструмент, он может генерировать проекты с Makefile для Linux. Это удобный способ получить начальную инициализацию периферии, которую потом можно изучать и модифицировать.