Embedded HandbookПриложения
Все разделы › I. Основы
STM32F103, STM32F407 · уровень базовый · 16 мин чтения
Открыть в интерактивном справочнике

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: установка и паки

Порядок такой:

  1. Скачать и установить MDK-Arm с сайта Keil. Установка со всеми настройками по умолчанию.
  2. Открыть Pack Installer — кнопка на панели инструментов (значок с коробками) или Project → Manage → Pack Installer.
  3. Найти в списке слева нужное семейство и установить пакет поддержки устройств (Device Family Pack, DFP): - для BluePill — Keil::STM32F1xx_DFP версии не ниже 2.3.0; - для Discovery F407 — Keil::STM32F4xx_DFP.
  4. Дождаться загрузки. Без пакета в списке камней не будет строки STMicroelectronics, и проект просто не создать.

Про бесплатную лицензию. MDK-Lite работает без оплаты с ограничением 32 КБ готового кода. Для учебных проектов этого хватает с запасом: примеры из этого справочника занимают единицы килобайт. Ограничение считается по объёму результата, а не по числу файлов, и упереться в него можно только на большом проекте — обычно вместе с подключением графических библиотек или USB-стека.

Редакция MDK-Community ограничения размера не имеет, но разрешена только для некоммерческой работы: частным лицам, учебным заведениям и совсем маленьким фирмам, и делать на ней изделия и услуги для других нельзя. Для STM32 на ядрах M0/M0+ (C0, F0, G0, L0) есть отдельная бесплатная лицензия MDK.

⚠️ Пакет устройств — не то же самое, что библиотека HAL. DFP даёт заголовки с описанием регистров (stm32f10x.h), стартовый файл и конфигурацию отладчика. HAL ставится отдельно, и для регистрового программирования он не нужен вовсе.


4.4 Пустой проект в Keil

Пошагово, до первой строки кода:

  1. Project → New µVision Project, выбрать папку и имя (например, Test).
  2. В окне выбора устройства раскрыть STMicroelectronics и найти свой камень — для BluePill это STM32F103C8.
  3. В окне 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) {
    }
}
  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:

  1. В выпадающем списке справа выбрать ST-Link Debugger (не Simulator).
  2. Нажать Settings рядом. Во вкладке Debug: - Port — SW (не JTAG: у BluePill выведен только SWD); - в поле SW Device должен появиться распознанный ARM CoreSight SW-DP. Пусто — значит плата не видна.
  3. Вкладка Flash Download: - отметить Reset and Run, чтобы плата стартовала сразу после прошивки; - проверить, что в списке Programming Algorithm есть алгоритм под вашу Flash (STM32F10x Med-density Flash), иначе добавить кнопкой Add.
  4. Вкладка 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, которые заметны сразу:


4.7 STM32CubeMX

Конфигуратор: вы мышью назначаете выводы и настраиваете периферию, а он генерирует код инициализации. Существует отдельной программой; в STM32CubeIDE версий 1.x он был встроен, а с версии 2.0 файл .ioc открывают в STM32CubeMX.

Что действительно экономит время:

⚠️ Свой код пишите только между маркерами. 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) трансляция остановится.

Зачем это нужно на практике:

Что получается на каждом шаге. Полезно один раз увидеть цепочку целиком — с настоящими размерами. Ниже собран пример из раздела 4в: два файла на C и скрипт компоновщика, arm-none-eabi-gcc -Os.

Цепочка сборки с размерами: исходники, объектники, ELF и файлы прошивки; bin равен text плюс data

Четыре вещи, которые видно только на числах:

⚠️ «Прошивка не влезает» и «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.

Картинка XPM 16 на 6 точек и ответ разбора: строка первой чёрной точки в каждом столбце


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 Итоги

Дальше: раздел 4а «Keil: проект и структура программы».

Открыть в интерактивном справочнике