4. Инструментарий
Чтобы код на экране превратился в работающую плату, нужны четыре вещи: редактор, компилятор, программатор и отладчик. Их можно взять одним пакетом или собрать по частям — этот раздел о том, что выбрать и как настроить.
Как это называется в документации
Оригинал Перевод integrated development environment (IDE) интегрированная среда разработки debug probe отладочный адаптер (ST-Link, J-Link) device family pack (DFP) пакет поддержки устройств system memory bootloader заводской загрузчик в системной памяти mantissa, exponent мантисса, порядок Intel HEX record type тип записи Intel HEX
4.1 Из чего состоит инструментарий
| Часть | Что делает | Варианты |
|---|---|---|
| IDE | редактор с навигацией по коду | Keil µVision, STM32CubeIDE, VS Code, CLion |
| Компилятор | превращает C в машинный код Cortex-M | Arm Compiler 6 (armclang), GCC (arm-none-eabi-gcc) |
| Программатор | заливает прошивку в Flash | ST-Link, J-Link, встроенный загрузчик МК |
| Отладчик | останавливает и разглядывает работающую программу | тот же ST-Link + GDB или встроенный отладчик IDE |
| Конфигуратор | генерирует код инициализации | STM32CubeMX |
Всё это может жить в одной программе, а может быть собрано из отдельных частей. Второе гибче, первое проще на старте.
4.2 Что выбрать
| Keil MDK | STM32CubeIDE | Командная строка | |
|---|---|---|---|
| цена | MDK-Lite — до 32 КБ кода; MDK-Community — без ограничения, но только для некоммерческих проектов; для STM32 на M0/M0+ — отдельная бесплатная лицензия | бесплатно без ограничений | бесплатно |
| основа | своя среда µVision | Eclipse | make / CMake + GCC |
| компилятор | Arm Compiler 6 | GCC | любой |
| платформы | µVision — только Windows; Keil Studio для VS Code — Windows, Linux, macOS | Windows, Linux, macOS | любая |
| CubeMX | отдельно | в версиях 1.x встроен, с 2.0 — отдельная программа | отдельно |
| порог входа | низкий | средний | высокий |
| в чём силён | компактный код, зрелый отладчик, удобная работа с регистрами | всё в одном, бесплатно, кроссплатформенно | воспроизводимость, CI, любые редакторы |
Практический ответ. Начинать проще с Keil: меньше настроек, отладчик показывает регистры периферии в человеческом виде, а для учёбы хватает бесплатных редакций: лимита MDK-Lite в 32 КБ надолго хватает, у MDK-Community лимита нет вовсе. Для коммерческого проекта, который перерастёт 32 КБ, разумно перейти на STM32CubeIDE — он бесплатен и умеет всё то же. Командная строка нужна, когда сборку надо повторять на сервере или проверять быстро, не открывая IDE.
Историческая справка: у ST долго не было собственной среды, и разработчики собирали связку из Eclipse и плагинов. В конце 2017 года ST купила Atollic с её TrueStudio и выпустила бесплатно под именем STM32CubeIDE — с тех пор вопрос «где взять бесплатную среду для STM32» закрыт.
4.3 Keil MDK: установка и паки
Порядок такой:
- Скачать и установить MDK-Arm с сайта Keil. Установка со всеми настройками по умолчанию.
- Открыть Pack Installer — кнопка на панели инструментов (значок с коробками)
или
Project → Manage → Pack Installer. - Найти в списке слева нужное семейство и установить пакет поддержки устройств
(Device Family Pack, DFP):
- для BluePill —
Keil::STM32F1xx_DFPверсии не ниже 2.3.0; - для Discovery F407 —Keil::STM32F4xx_DFP. - Дождаться загрузки. Без пакета в списке камней не будет строки
STMicroelectronics, и проект просто не создать.
Про бесплатную лицензию. MDK-Lite работает без оплаты с ограничением 32 КБ готового кода. Для учебных проектов этого хватает с запасом: примеры из этого справочника занимают единицы килобайт. Ограничение считается по объёму результата, а не по числу файлов, и упереться в него можно только на большом проекте — обычно вместе с подключением графических библиотек или USB-стека.
Редакция MDK-Community ограничения размера не имеет, но разрешена только для некоммерческой работы: частным лицам, учебным заведениям и совсем маленьким фирмам, и делать на ней изделия и услуги для других нельзя. Для STM32 на ядрах M0/M0+ (C0, F0, G0, L0) есть отдельная бесплатная лицензия MDK.
⚠️ Пакет устройств — не то же самое, что библиотека HAL. DFP даёт заголовки с описанием регистров (
stm32f10x.h), стартовый файл и конфигурацию отладчика. HAL ставится отдельно, и для регистрового программирования он не нужен вовсе.
4.4 Пустой проект в Keil
Пошагово, до первой строки кода:
Project → New µVision Project, выбрать папку и имя (например,Test).- В окне выбора устройства раскрыть
STMicroelectronicsи найти свой камень — для BluePill этоSTM32F103C8. - В окне Manage Run-Time Environment отметить минимум:
-
CMSIS → CORE; -Device → Startup.
Этого достаточно для работы с регистрами напрямую. Ставить Device → StdPeriph
или HAL нужно только если собираетесь ими пользоваться.
4. В дереве проекта правой кнопкой по Source Group 1 → Add New Item →
C File (.c), назвать main.c.
5. Написать каркас:
#include "stm32f10x.h"
int main(void)
{
while (1) {
}
}
- Открыть настройки проекта (
Project → Options for Target, или Alt+F7): - вкладка Target — полеXtalпрошивку не настраивает: µVision берёт его только для отладчика и программатора, а в проектах.uvprojxоно и вовсе серое. Частоту задаютsystem_stm32f10x.cи регистры RCC (раздел 6); - вкладка Output — включитьCreate HEX File, если нужен файл прошивки; - вкладка Debug — выбрать отладчик (см. ниже); - вкладка C/C++ — уровень оптимизации; для отладки ставьте-O0, иначе пошаговая отладка «прыгает» по строкам.
Проект-заготовку удобно сохранить и копировать под новые задачи, а не проходить эти шаги каждый раз.
4.5 Настройка ST-Link
В Project → Options for Target → Debug:
- В выпадающем списке справа выбрать ST-Link Debugger (не Simulator).
- Нажать
Settingsрядом. Во вкладке Debug: -Port— SW (не JTAG: у BluePill выведен только SWD); - в полеSW Deviceдолжен появиться распознанныйARM CoreSight SW-DP. Пусто — значит плата не видна. - Вкладка Flash Download:
- отметить
Reset and Run, чтобы плата стартовала сразу после прошивки; - проверить, что в спискеProgramming Algorithmесть алгоритм под вашу Flash (STM32F10x Med-density Flash), иначе добавить кнопкойAdd. - Вкладка Utilities: снять галочку
Use Debug Driver, если хотите задать отдельный драйвер для прошивки; в большинстве случаев её оставляют.
Если плата не определяется:
| Симптом | Причина |
|---|---|
SW Device пуст, «No target connected» |
не подключены SWDIO/SWCLK или нет земли между платой и программатором |
| Определяется через раз | длинные провода к SWD, помехи; снизьте частоту Max Clock до 1,8 МГц или ниже |
| Было видно, перестало | программа заняла PA13/PA14 под GPIO — отладка отключилась. Лечится подключением под сбросом (Settings → Connect: under Reset, нужен провод NRST к программатору) и стиранием Flash; без NRST — BOOT0 = 1, сброс и то же стирание по SWD |
| «No Algorithm found for: 08000000H …» | не добавлен алгоритм программирования во вкладке Flash Download |
| «Target DLL has been cancelled» | отладка не запустилась: программатор не найден или занят другой программой (например, STM32CubeProgrammer) |
4.6 STM32CubeIDE
Бесплатная среда ST на базе Eclipse: редактор, компилятор GCC, отладчик
и загрузчик прошивки. Конфигуратор CubeMX в версиях 1.x был встроен в неё,
а с версии 2.0 стал отдельной программой: файл .ioc открывают
в STM32CubeMX, и уже он генерирует проект для CubeIDE. Работает на Windows,
Linux и macOS.
Создание проекта: File → New → STM32 Project → выбрать камень или плату →
задать имя → среда сама создаст структуру, подтянет HAL и откроет графический
конфигуратор выводов. Так это устроено в версиях 1.x; в 2.0 проект
с настройкой выводов создают в STM32CubeMX.
Отличия от Keil, которые заметны сразу:
- проект сразу с HAL, регистровый вариант надо выбирать осознанно;
- конфигуратор под рукой: в версиях 1.x он встроен, и код инициализации
перегенерируется прямо из среды; с 2.0
.iocоткрывается в отдельном STM32CubeMX, а после генерации проект в CubeIDE обновляют; - сборка идёт GCC, а он даёт код чуть крупнее, чем Arm Compiler, но разница редко имеет значение;
- отладчик тот же GDB, интерфейс победнее, чем у µVision, но со всем необходимым.
4.7 STM32CubeMX
Конфигуратор: вы мышью назначаете выводы и настраиваете периферию, а он
генерирует код инициализации. Существует отдельной программой; в STM32CubeIDE
версий 1.x он был встроен, а с версии 2.0 файл .ioc открывают в STM32CubeMX.
Что действительно экономит время:
- Дерево тактирования. Вводите желаемую частоту ядра — CubeMX сам подбирает делители и множитель PLL, подсвечивая недопустимые сочетания. Вручную это считается долго и ошибочно (см. раздел 6).
- Карта выводов. Показывает конфликты: занять один вывод двумя периферийными блоками он не даст.
- Таблица альтернативных функций. Клик по выводу показывает, что на него можно назначить у этого конкретного камня.
⚠️ Свой код пишите только между маркерами. CubeMX расставляет в файлах комментарии вида
c /* USER CODE BEGIN 2 */ /* USER CODE END 2 */Всё, что вы напишете вне этих блоков, будет стёрто при следующей генерации. Это самая частая потеря работы у начинающих: настроили периферию, дописали код в
main.cне в том месте, перегенерировали — и код исчез.
4.8 Сборка без IDE
Иногда нужно просто проверить, что код компилируется, — не открывая среду. Для этого достаточно компилятора и двух путей к заголовкам.
Для STM32F103 (Cortex-M3):
armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c -DSTM32F10X_MD \
-I "<путь к DFP>/Device/Include" \
-I "<путь к CMSIS>/CMSIS/Core/Include" \
-o main.o main.c
Для STM32F407 (Cortex-M4 с плавающей точкой):
armclang --target=arm-arm-none-eabi -mcpu=cortex-m4 \
-mfpu=fpv4-sp-d16 -mfloat-abi=hard -c -DSTM32F407xx \
-I "<путь к DFP>/Drivers/CMSIS/Device/ST/STM32F4xx/Include" \
-I "<путь к CMSIS>/CMSIS/Core/Include" \
-o main.o main.c
Ключ -D обязателен: заголовки семейства сами не знают, какой у вас камень,
и без определения плотности (STM32F10X_MD) или модели (STM32F407xx)
трансляция остановится.
Зачем это нужно на практике:
- быстро проверить чужой пример перед тем, как заводить под него проект;
- убедиться, что правка компилируется, не дожидаясь полной пересборки;
- собирать проект на сервере, где IDE нет вовсе;
- поймать ошибку типов статически —
_Static_assertработает и без железа (этим приёмом проверены все числовые утверждения этого справочника).
Что получается на каждом шаге. Полезно один раз увидеть цепочку целиком —
с настоящими размерами. Ниже собран пример из раздела 4в: два файла на C и
скрипт компоновщика, arm-none-eabi-gcc -Os.
Четыре вещи, которые видно только на числах:
- ELF — не прошивка. В нём 9876 байт против 480 в
fw.bin: двадцатикратная разница уходит на символы, отладочные секции и заголовки. Отладчику они нужны, плате — нет. .bin— этоtextплюсdata, байт в байт. 476 + 4 = 480. Секцияbss(в примере 16 байт) в файле не хранится вовсе: это нули, которые стартовый код раскладывает по ОЗУ сам (раздел 4в)..hexвтрое больше.bin— 1414 против 480. Он текстовый: каждый байт записан двумя знаками, и к каждой строке добавлены адрес, длина и контрольная сумма. Зато его можно послать в программатор по последовательному каналу и прочитать глазами.- В
.binнет адресов. Это просто байты подряд, и при записи программатору указывают адрес начала —0x08000000. В.hexадреса записаны в самом файле, поэтому он годится и для образа из разнесённых областей. Если в образе есть секция по далёкому адресу,objcopy -O binaryзаполнит промежуток нулями: секции по0x08000000и0x20000000дают.binна 384 МБ.
⚠️ «Прошивка не влезает» и «ELF большой» — разные вещи. Смотрите на
textиdataизsize, а не на размер файла.elf: во флеш-память уедет только первое.
Полноценная сборка прошивки добавляет к этому линковку со стартовым файлом и скриптом компоновщика — об этом раздел 11.
4.9 Как прошивка попадает в плату
Три пути, и полезно знать все три.
1. SWD — обычный способ. Два провода данных (SWDIO, SWCLK) плюс земля
и, если плата не запитана отдельно, питание. Работает всегда, позволяет отладку.
На Nucleo и Discovery программатор уже на плате.
2. Встроенный загрузчик — без программатора. В каждом STM32 зашит заводской загрузчик в системной памяти. Чтобы попасть в него, меняют состояние выводов загрузки:
| BOOT1 | BOOT0 | Откуда стартует МК |
|---|---|---|
| × | 0 | из Flash — обычная работа |
| 0 | 1 | из системной памяти — заводской загрузчик |
| 1 | 1 | из ОЗУ — для отладочных сценариев |
На BluePill это перемычки рядом с кнопкой сброса. Переставили BOOT0 в 1,
нажали Reset — камень ждёт прошивку по USART1 (выводы PA9, PA10), для чего
и нужен переходник USB–UART. Программа со стороны компьютера — STM32CubeProgrammer.
У F4 загрузчик умеет больше: помимо UART принимает прошивку по USB DFU, то есть достаточно одного кабеля без всяких переходников.
3. Прямо из IDE. Кнопка Download (F8 в Keil) — то же SWD, просто одним
нажатием.
⚠️ Способ спасти «окирпиченную» плату. Если программа заняла
PA13/PA14или сразу уходит в глубокий сон, отладчик не успевает подключиться, и плата кажется мёртвой. Она не мертва: переведитеBOOT0в 1, сбросьте — код из Flash не запустится, стартует загрузчик, и плату можно перепрошить.
4.10 Разминка на C
Прежде чем садиться за камень, полезно размять руки на задачах, где язык встречается с железом: биты, адреса, представление чисел, форматы файлов. Семь задач решены ниже и проверены на компьютере; подробности — в части IX.
1. Биты по маске. Поставить биты — v | mask, снять — v & ~mask. Так
устроена и Flash (раздел 11): стирание ставит все единицы, запись может их
только снимать. F4 повторную запись без стирания разрешает: если в ячейку
записали 0x12345678, а потом поверх 0xFF00FF00, в ней окажется
0x12005600 — побитовое «И» обоих чисел. F1 так не умеет: нестёртую ячейку
он не пишет вовсе и поднимает флаг PGERR, а G0 и G4 — флаг PROGERR;
исключение у всех трёх — запись нуля.
2. Число в виде a · 2ᵇ. Библиотечная frexpf раскладывает float
на мантиссу и порядок: 10 = 0,625 · 2⁴, 0,1 = 0,8 · 2⁻³, −3 = −0,75 · 2².
Мантисса всегда в [0,5; 1): только при таком выборе запись единственная.
Если разрешить [0,1; 1), у 0,4 окажется четыре записи:
0,8 · 2⁻¹ = 0,4 · 2⁰ = 0,2 · 2¹ = 0,1 · 2². Так же устроен и формат
с плавающей запятой (раздел 69), только там мантиссу берут в [1; 2)
и ведущую единицу не хранят: 10 = 1,25 · 2³, порядок на единицу меньше,
чем у frexpf.
3. Битовый массив. Бит с номером index лежит в слове index / 32
на месте index % 32:
int setbit(int index, int bit, uint32_t array[])
{
uint32_t m = 1u << (index % 32);
if (bit)
array[index / 32] |= m;
else
array[index / 32] &= ~m;
return bit;
}
int getbit(int index, uint32_t array[])
{
return (int)((array[index / 32] >> (index % 32)) & 1u);
}
Биты 0, 31, 32, 63 и 70 со сброшенным потом 63-м дают слова
0x80000001, 0x00000001, 0x00000040.
4. Запись по адресу, заданному числом. Так пишут в регистры периферии
(раздел 3); volatile не даёт компилятору выбросить запись:
void set_value(uintptr_t addr, uint32_t value)
{
*(volatile uint32_t *)addr = value;
}
5. Одно слово — четыре взгляда. Объединение кладёт все поля в одни и те же четыре байта:
typedef union {
uint8_t byte[4];
uint16_t u16[2];
uint32_t u32;
float f;
} my_type;
1.0f — это слово 0x3F800000, байты 00 00 80 3F (младший идёт первым,
раздел 64) и полуслова 0x0000 и 0x3F80. Обратно: слово 0x40490FDB —
это 3,1415927.
6. Дамп в формате Intel HEX. Строка — это «:», длина, адрес, тип, данные и контрольная сумма, дополняющая сумму всех байтов строки до нуля. Старшие 16 бит адреса задаёт отдельная запись типа 04, конец файла — запись типа 01:
static char *record(char *p, uint8_t type, uint16_t addr, const uint8_t *data, int len)
{
uint8_t sum = (uint8_t)(len + (addr >> 8) + (addr & 0xFFu) + type);
*p++ = ':';
p = hex(p, (uint32_t)len, 2);
p = hex(p, addr, 4);
p = hex(p, type, 2);
for (int i = 0; i < len; i++) {
p = hex(p, data[i], 2);
sum = (uint8_t)(sum + data[i]);
}
p = hex(p, (uint8_t)(0x100u - sum), 2);
*p++ = '\n';
return p;
}
Двадцать байт по адресу 0x0800 0100 дают четыре строки:
:020000040800F2
:1001000001080F161D242B323940474E555C636A97
:0401100071787F86FD
:00000001FF
Упрощённый вариант «адрес, данные, простая сумма» Intel HEX не является: без длины и типа строку не разобрать, а простую сумму не примет ни один программатор. Разбор настоящего формата — в 48.7.
7. Кривая из картинки XPM. XPM — текстовый формат картинки: строка «ширина высота цветов символов-на-точку», строки цветов и строки точек. Функция находит символ чёрного цвета и для каждого столбца возвращает Y первой чёрной точки:
int parse_xpm(const char *const *xpm, uint16_t *result)
{
int w, h, ncol, cpp;
char black = 0;
if (sscanf(xpm[0], "%d %d %d %d", &w, &h, &ncol, &cpp) != 4 || cpp != 1)
return -1;
for (int c = 1; c <= ncol; c++)
if (strstr(xpm[c] + 1, "#000000") || strstr(xpm[c] + 1, "black"))
black = xpm[c][0];
if (!black)
return -1;
for (int x = 0; x < w; x++) {
result[x] = 0xFFFFu;
for (int y = 0; y < h; y++)
if (xpm[1 + ncol + y][x] == black) {
result[x] = (uint16_t)y;
break;
}
}
return w;
}
На картинке 16 × 6 с дугой получилось 5, 4, 3, 2, 1, 1, 0, 0, 1, 1, 2, 3, 4, 5, 5, 4.
4.11 Грабли
Установили пакет устройств, а HAL нет. Пакет даёт заголовки, стартовый файл и настройки отладчика; HAL ставится отдельно и для работы с регистрами не нужен (4.3).
После перегенерации CubeMX пропал код. Писать только между маркерами USER CODE BEGIN/END (4.7).
«Прошивка не влезает», хотя ELF маленький — или наоборот. Во Flash уходят text и data из size,
а не размер файла .elf (4.8).
Плата «умерла»: отладчик не подключается. Программа заняла PA13/PA14 или сразу уснула; BOOT0 = 1,
сброс — стартует загрузчик, плату можно перепрошить (4.9).
4.12 Итоги
- Инструментарий — это IDE, компилятор, программатор и отладчик; их можно взять комплектом или собрать по частям.
- Keil проще на старте и даёт компактный код. Бесплатно — MDK-Lite до 32 КБ или MDK-Community без ограничения, но только для некоммерческих проектов; µVision работает только под Windows. STM32CubeIDE бесплатен целиком и работает везде.
- Без пакета поддержки устройств (DFP) камень не появится в списке при создании проекта.
- Для регистрового программирования достаточно
CMSIS → COREиDevice → Startup; HAL не обязателен. - В настройках отладчика выбирайте порт SW, а не JTAG: у большинства дешёвых плат JTAG не выведен.
- Код, написанный вне маркеров
USER CODE BEGIN/END, CubeMX сотрёт при следующей генерации. - Проверить компиляцию можно одной командой
armclangбез всякой IDE — удобно и для чужих примеров, и для автоматической проверки. - Плата с занятыми выводами SWD не потеряна:
BOOT0= 1 запускает заводской загрузчик, и её можно перепрошить. - Разминка на C — биты, адреса, объединения, числа с плавающей запятой и форматы файлов — те же приёмы, что потом встретятся в регистрах, Flash и загрузчике.
Дальше: раздел 4а «Keil: проект и структура программы».