Введение

Привет всем.

На данный момент я активно осваиваю разработку ПО для STM32 и хотел бы поделиться моим опытом.

Как известно, для STM32 имеется много сред для разработки, однако часть из них, несмотря на удобность, имеют ограничения по использованию в случае пробной версии. Так, для IAR размер прошивки ограничивается 32 кБ, что весьма немного.

В данной публикации будет рассмотрен способ настройки окружения для полноценной разработки и отладки ПО для микроконтроллеров STM32 в среде QtCreator.

Прежде всего, определимся, что нам понадобится для проведения экспериментов.

Железо

В качестве примера я использую плату bluepill на базе STM32F103C8T6. В моем случае openocd жаловался на некорректный номер STM32. Решается 2 способами: или подменой номера в конфиге openocd или заменой МК на оригинальный. Я выбрал 2 вариант. Для загрузки и отладки программ в МК будем использовать интерфейс ST-Link.

Софт

Сами эксперименты проводились на виртуальной машине ОС Windows 10 в Virtual Box.

Для настройки окружения понадобится следующее ПО:

  1. QtCreator. Можно скачать онлайн-установщик с сайта Qt. Но в нем при установке требуется регистрация и, кроме того, нам нужен только сам QtCreator. Поэтому я обычно его скачиваю непосредственно с GitHub. https://github.com/qt-creator/qt-creator/tags.

  2. gcc-arm-none-eabi. https://developer.arm.com/downloads/-/gnu-rm.

  3. Git. https://git-scm.com/downloads.

  4. openocd. https://github.com/openocd-org/openocd/tags. Это программа для загрузки прошивки и отладки микроконтроллера STM.

  5. make. https://gnuwin32.sourceforge.net/packages/make.htm

  6. ST-LINK Utility. Программа для прошивки STM32 + драйвер для самого ST-Link. https://www.st.com/en/development-tools/stsw-link004.html

  7. Python2.7. https://www.python.org/downloads/release/python-2718/

Прежде всего, скачиваем и распаковываем QtCreator. Затем гит, компилятор, openocd, python, make и st-link.

QtCreator и openocd распаковал в C:\Tools. Все программы, кроме QtCreator, должны быть добавлены в Path.

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

Далее перейдем к настройке QtCreator.

Настройка QtCreator

Запускаем QtCreator. Для работы нужно установить плагины BareMetal и GenericProjectManager. Для этого идем в Help->About Plugins. После настройки перезапускаем QtCreator.

Переходим в Edit->Preferences. Выбираем Kits слева. Тут видим наш комплект для сборки ARM, который подтянулся из Path. Идем в раздел Devices на вкладку Bare Metal и добавляем OpenOCD server. Выбираем название, в моем случае OpenOCD-STM32F103.

В поле Executable file пишем путь к нашему openocd (по-идее, можно взять из path).

В поле Root scripts directory указываем на директорию со скриптами в openocd.

В поле Additional arguments указываем аргументы для прошивки MK. В нашем случае конфиг для STLink и target. Применяем.

Переходим на вкладку Devices. Нажимаем Add->Bare Metal Device и Start Wizard. В появившемся окне выбираем Debug server provider ранее созданный OpenOCD-STM32F103. Далее Finish.

Возвращаемся в Kits и донастраиваем наш комплект. Выбираем Device type и Device – Bar Metal Device. Применяем.

На этом QtCreator пока откладываем и переходим к настройке самого проекта.

Настройка проекта

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

Прежде всего, нужно подтянуть необходимые библиотеки в виде submodules из официальных репозиториев ARM и ST. Открываем bash-терминал, который устанавливается с git. Создадим папку led_blink_cmsis и перейдем в нее.

mkdir led_blink_cmsis && cd led_blink_cmsis

Внутри создадим папки build, lib и src.

mkdir build && mkdir src && mkdir lib

В папке build будут артефакты сборки, в src – наши исходники, а в lib – сторонние библиотеки.

Инициализируем git.

git init

Далее добавим сторонние библиотеки cmsis для билда с использованием git submodules. Первая библиотека – форк от ARM, вторая содержит настройки и определения для STM32 семейства F1.

git submodule add https://github.com/STMicroelectronics/cmsis_core lib/cmsis_core && cd lib/cmsis_core
git checkout v5.6.0 && cd ../..
git submodule add https://github.com/STMicroelectronics/cmsis_device_f1 lib/cmsis_device_f1 && cd lib/cmsis_device_f1
git checkout v4.3.3 && cd ../..

Git checkout здесь необходим, чтобы использовать релизные версии библиотек, в данном случае 5.6.0 и 4.3.3 для cmsis_core и cmsis_device соответственно.

Далее создадим файлы main.c, init.h и init.c в папке src, в которых будем писать исходный код.

touch src/main.c src/init.h src/init.c

Наполним файлы кодом. В файле main.c находится код, которые включает и выключает светодиод, а также вызывает функции clock_init и port_init. Эти функции настраивает тактовую частоту МК и тактирование от кварца, а также работу порта C на выход в режиме пуш-пулл. Кроме того, init.c содержит реализацию функции delay_ms.

main.c
#include "init.h"

int main(void)
{
    clock_init();                                                //Clock initialisation
    port_init();                                                 //Port initialisation
    while(1)
    {
        GPIOC->BSRR = GPIO_BSRR_BR13;                            //Pin 13 of PORT C on
        delay_ms(100);
        GPIOC->BSRR = GPIO_BSRR_BS13;                            //Pin 13 of PORT C off
        delay_ms(100);
    }
    return 0;
}

init.h
#ifndef STM32F103xB
#define STM32F103xB
#endif

#include "stm32f1xx.h"

int clock_init();
void port_init();
void delay_ms(uint16_t);

init.c
#include "init.h"

volatile uint32_t SysTickDelayMsec = 0;

void SysTick_Handler(void)
{
    --SysTickDelayMsec;
}

/*
OSC - 8 MHz
PLLSRC - 8 MHz
SYSCLK - 16 MHz

PLL - 2
AHB - 1/16
*/
int clock_init()
{
    RCC->CR |= RCC_CR_HSEON;                                    //Start HSE generator
    for(volatile int StartUpCounter = 0; ; ++StartUpCounter)    //Wait for successfull start or timeout
    {
        if(RCC->CR & RCC_CR_HSERDY)                             //If started successfully, break the cycle
        {
            break;
        }
        if(StartUpCounter > 4096)                               //If not started - turn off and return an error
        {
            RCC->CR &= ~RCC_CR_HSEON;                           //Stop HSE
            return 1;
        }
    }
    RCC->CFGR = 0;                                              //PLL mult is equal 2
    RCC->CFGR |= RCC_CFGR_PLLSRC;                               //Clock PLL from HSE
    RCC->CR |= RCC_CR_PLLON;                                    //Start PLL
    for(volatile int StartUpCounter = 0; ; ++StartUpCounter)    //Wait for successfull start or timeout
    {
        if(RCC->CR & RCC_CR_PLLRDY)                             //If started successfully, break the cycle
        {
            break;
        }
        if(StartUpCounter > 4096)                               //If PLL didn't start , turn off everything and return an error
        {
            RCC->CR &= ~RCC_CR_HSEON;                           //Stop HSE
            RCC->CR &= ~RCC_CR_PLLON;                           //Stop PLL
            return 2;
        }
    }
    FLASH->ACR |= FLASH_ACR_LATENCY;                            //0 cycles for flash, core clock 16 MHz
    RCC->CFGR |= RCC_CFGR_PPRE2;                                //APB2 turned off (0 by default)
    RCC->CFGR |= RCC_CFGR_HPRE_3 |
                 RCC_CFGR_HPRE_1 |
                 RCC_CFGR_HPRE_0;                               //AHB prescaler 16
    RCC->CFGR |= RCC_CFGR_SW_1;                                 //Switch to PLL
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);      //Wait for switch to PLL
    RCC->CR &= ~RCC_CR_HSION;                                   //Turn off the internal clock for energy save
    SystemCoreClockUpdate();                                    //Apply alterations to generator
    SysTick_Config(SystemCoreClock/1000);                       //Initialisation of interrupt (1 ms)
    return 0;                                                   //Return 0 if success
}

void port_init()
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;                         //Turn on clock for GPIOC
    GPIOC->CRH = 0;
    GPIOC->CRH |= GPIO_CRH_MODE13;                              //Pin 13 of PORT C in Push-Pull mode
    GPIOC->BSRR |= GPIO_BSRR_BR13;                              //Pin 13 of PORT C reset
}

void delay_ms(uint16_t msec)                                     //Delay function
{
    SysTickDelayMsec = msec;
    while (SysTickDelayMsec);
}

И, наконец, добавим в проект Makefile, в соответствии с которыми будут собираться наша прошивка. Шаблон этого файла взят из генератора кода mxcube и адаптирован под текущий проект. Файл имеет следующее содержание.

Makefile
######################################
# target
######################################
TARGET = mppt_firmware

######################################
# building variables
######################################
DEBUG = 1

OPT = -O0

#######################################
# paths
#######################################
BUILD_DIR = build

######################################
# source
######################################
C_SOURCES =  \
src/main.c \
src/init.c \
lib/cmsis_device_f1/Source/Templates/system_stm32f1xx.c

ASM_SOURCES =  \
lib/cmsis_device_f1/Source/Templates/gcc/startup_stm32f103xb.s

#######################################
# binaries
#######################################
PREFIX = arm-none-eabi-

ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
 
#######################################
# CFLAGS
#######################################
CPU = -mcpu=cortex-m3
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)

AS_DEFS = 

C_DEFS =  \
-DSTM32F103xB

AS_INCLUDES = 

C_INCLUDES =  \
-Isrc \
-Ilib/cmsis_core/Include \
-Ilib/cmsis_device_f1/Include

ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif

CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"

#######################################
# LDFLAGS
#######################################
LDSCRIPT = lib/cmsis_device_f1/Source/Templates/gcc/linker/STM32F103XB_FLASH.ld

LIBS = -lc -lm -lnosys 
LIBDIR = 
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections

#######################################
# build the application
#######################################
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin

OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))

OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@
	
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@
	
$(BUILD_DIR):
	mkdir $@

clean_build: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).bin
	find $(BUILD_DIR) ! -name '$(TARGET).bin' -type f -exec rm -f {} +

#######################################
# clean up
#######################################
clean:
	rm -fR $(BUILD_DIR)/*

  • В C_INCLUDES указываем пути к нашим .h файлам.

  • В C_SOURCES указываем пути к нашим .c/.cpp файлам.

  • DEBUG указывает на добавление дебаг-символов в прошивку. Для релиза выставляем в 0.

  • OPT указывает на оптимизацию. Для релиза включаем оптимизацию, например, Os.

Конечно, в качестве билд-системы можно использовать любую другую, например, CMake с тулчейном для STM32.

Добавим папку build в .gitignore, чтобы гит ее не отслеживал.

echo build/* > .gitignore

На этом настройка проекта завершена и его можно импортировать в QtCreator.

Импорт и настройка проекта в QtCreator

QtCreator не очень дружит с Makefiles. Если попытаться использовать Autotools плагин, то у меня добавлялись только .c и .cpp файлы. Поэтому импортируем проект через специальный интерфейс QtCreator. Для этого понадобится плагин GenericProjectManager.

Переходим в File->New Project. Слева в Import Existing Project.

Далее указываем название проекта led_blink_cmsis и путь C:\led_blink_cmsis. Next.

Выбираем какие директории и файлы импортировать. Выбираем пока только нашу папку src. Next.

Затем add to version control – none и нажимаем Finish. Файлы теперь импортированы.

Далее очень важная деталь – надо добавить в led_blink_cmsis.config определение нашего контроллера, #define STM32F103xB. Без этого дефайна ничего не будет работать, т.к. в библиотеке cmsis требуется указать тип контроллера для включения кода для работы с ним.

На данном этапе ide не распознает зависимости. Настроим это.

Добавим наши .c файлы в led_blink_cmsis.files

src/init.c
src/main.c

А в led_blink_cmsis.inludes пути к заголовочным файлам.

src/
lib/cmsis_core/Include
lib/cmsis_device_f1/Include

Теперь все должно отображаться корректно.

Попробуем собрать проект, нажав кнопку build. Проект должен собраться, а в конце будет таблица с размером нашей прошивки.

Теперь добавим автозагрузку нашей прошивки на МК по нажатию на кнопку Run. Переходим в Projects слева, выбираем наш комплект arm-baremetal-generic-elf-32bit, затем снизу Run. Справа, где deployment, Add Deploy Step->Make. Затем повторяем, выбрав Custom Process Step и добавляем туда команду для прошивки через ST_link CLI.

C:\Program Files (x86)\STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility\ST-LINK_CLI.exe
-P ".\build\led_blink_cmsis.bin" 0x08000000 –Rst

Перед прошивкой подключаем наш ST-Link c МК. Мы прошиваем МК, начиная с адреса 0x08000000, и перезагружаем МК после прошивки.

Таким образом, подключив МК через ST-Link и нажав кнопку Run, все должно скомпилиться, загрузиться, а светодиод начать моргать. Это можно видеть по показаниям осциллографа, который подключен к PC13.

Таким образом, мы рассмотрели как настроить проект для STM32, его скомпилировать и загрузить в МК. Дальше настроим отладчик для комфортной отладки STM32.

Настройка отладки

Ранее мы уже настроили удаленный отладчик через openocd, и он должен работать

Однако было весьма здорово иметь возможность видеть состояние регистров МК. Добавим такую возможность. Для этого нам понадобится .svd файл для нашего МК, т.е. STM32F103. Скачать его можно, например, из этого репозитория:

https://github.com/fduignan/stm32f103c8t6

Далее перейдем в QtCreator Edit->Preferences->Devices, на вкладку Bar Metal и выберем OpenOCD. Указываем в Peripherial description file наш путь .svd файлу.

Теперь в режиме отладки перейдем в View->Views->Peripheral
Registers
, чтобы отобразить состояние конфигурационных регистров. Запустив
окно отладки и, кликнув правой клавишей мыши на окне Peripheral registers,
можно выбрать группу регистров для отображения, в нашем примере View Groups->GPIOC.

В итоге мы имеем все удобства для отладки устройств на STM32 в IDE QtCreator.

Заключение

Таким образом, в рамках данной статьи рассмотрена настройка QtCreator в связке с GNU компиляторами для полноценной разработки ПО для МК STM32. Надеюсь, что статья была полезна.

Спасибо за внимание!

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


  1. GeorgeIV
    13.12.2022 13:20

    mbed - не имеет ограничений и бесплатна.


    1. mctMaks
      13.12.2022 15:30

      для тех же STM и "кубик" то бесплатен со средой.

      хотя в целом, что такого дает Qt, без чего трудно будет в микроконтроллере? Code Lite тоже open source, тоже можно подцепить внешний компилятор. Только более легкая по сравнению с тем Qt, особенно если последний используется только как редактор кода.


    1. aax
      13.12.2022 17:24

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


      1. buldo
        15.12.2022 19:53

        Вы не правы. Уже очень давно есть mbed cli. Год назад уже тосно была mbed ide на основе электрона. Плюс можно использовать все библиотеки через PlatformIO. ЕМНИП, кажется начали деприкейтить Web ide


  1. juramehanik
    13.12.2022 15:44

    Спасибо за статью, но
    Возвращаемся в Kits и донастраиваем наш комплект. Выбираем Device type и Device – Bar Metal Device. Применяем.

    QtCreator не очень дружит с Makefiles. 


    Ушел много лет назад от креатора на vscode так как очень неудобно реализован процесс переносимости. В vscode достаточно плагинов cortex-debug и settings-sync для полноценной работы с cmake makefile проектами STM32 на другой машине. Все настройки для отладки и прошивки как есть хранятся в директории проекта.
    В креаторе же надо было ручками заводить таргеты или копировать файлы из системной директории, я так понял в этом плане не поменялось ничего?
    Ну хоть поддержка svd появилась, это хорошо!

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


  1. mgupi
    13.12.2022 20:53

    Столько возни ради блинка светодиода.. На avr и ассемблере программа блинк занимала несколько десятков байт.


    1. ZhksB Автор
      13.12.2022 20:55

      Блинк светодиода - это всего лишь пример. Вместо блинка тут может быть любой другой код, как asm, так и C и С++.


  1. NR_electronics
    13.12.2022 20:56
    +1

    Может быть Вы еще запилите подобный мануал для подключения Qt DesignStudio к STM32 ?


    1. mctMaks
      14.12.2022 15:44

      А зачем, если есть официальное решение от qt? https://www.qt.io/product/develop-software-microcontrollers-mcu

      на youtube можно даже найти ролики, где показывают производительность системы.

      те же st предлагают touch gfx, который и дизайнер, и код на с++, и заточек по stm32 более чем. и зачем тогда дизайнер?


  1. MW_dem1305
    13.12.2022 20:56
    -1

    Скачай IAR for ARM с торрента и прекрати ненужные извращения. Удивительно стремление людей усложнять себе жизнь.Твой метод-для изощрённых извращенцев.


    1. ZhksB Автор
      13.12.2022 20:57
      +1

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


      1. MW_dem1305
        13.12.2022 21:23
        -2

        относись.


    1. ZhksB Автор
      13.12.2022 20:59

      И цель данной статьи была именно в настройке QtCreator - IDE, к которой привык и постоянно пользуюсь.


  1. kuzulis
    13.12.2022 22:26

    Все бы вроде хорошо, но использование мейкфайлов это конечно, дичь.

    Для программирования МК хорошо подходит сборочная система Qbs. Хотя, на крайний случай, можно извратиться и с CMake.

    Еще в QtC есть поддержка кейловского отладчика, т.е можно использовать KEIL и для сборки и для отладки в самом QtC.

    Я хотел в QtC добавить и поддержку отладчика CSpy из IAREW, но это желание как то пропало после их реакции на известные события.

    Кроме того, GDB отладка в QtC, требует наличия питона v2.x, а также условия чтобы сам GDB, был собран с поддержкой питона, что не во всех ARM тулчейнах сделано. Это минус у QtC, я считаю, что нельзя без питона.

    Там был еще казус, в том, что они хотели дропнуть питон 2x и заменить на 3x. Я им говорил, что тогда половина bare-metal тулчейнов сразу отвалится. Те не поверили, дропнули, но у них провалился их плагин McuSupport и они откатили взад это как было. Смех и грех в общем.

    А сейчас тоже, вот, присматриаюсь к VSCode, т.к. там завезли плагин отладки для IAR, для отладчика CSpy. Т.е. можно взять любимую систему сборки Qbs, или CMake и все делать из VSCode.

    GDB, плагин для VSCode, был уже сто лет как. Возможно для полного комплекта подумаю, а не добавить ли туда плагин и для отладки KEIL овским отладчиком. ))

    В общем, для меня VSCode, выглядит более заманчиво.


  1. OldFashionedEngineer
    15.12.2022 11:42

    Интересно, чем автора не устраивает CubeIDE? Среда построена на эклипс, тот же GCC в основе. Бесплатно качаешь и используешь, ни каких лицензионных ограничений. И в добавок ко всему CubeIDE поддерживается производителем МК. Я могу понять желание работать с периферией на CMSIS, но это тоже можно в кубе делать.

    Ладно бы для Arduino такое сделать, там реально среда неудобная в работе.


    1. mctMaks
      15.12.2022 15:20

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


      1. OldFashionedEngineer
        15.12.2022 15:29

        В каком плане "медленная"? Он вроде уже в нескольких потоках компилирует.

        Графическая настройка тактирования и прочие фичи - это функции CubeMX. Его не обязательно использовать совместно с IDE. Можно отдельно скачать и использовать для генерации проектов под другие IDE. Ну, и по сути, CubeMX в CubeIDE встроен как плагин. Он даже обновляется отдельно. (Капитан очевидность в деле...)


        1. mctMaks
          15.12.2022 18:26

          В каком плане "медленная"?

          запуск самой среды, переключение между отладка/написание кода (забыл как в экплисе это называется), по сравнению с Segger Embedded Studio тормозит.

           Он вроде уже в нескольких потоках компилирует.

          Сам этап компиляции может и быстро происходит, не сравнивал настолько детально.

          Графическая настройка тактирования и прочие фичи - это функции CubeMX

          Это нравится. Cube IDE нет.

          Опять же повторюсь, на мой вкус этот фломастер не вкусный). Я не говорю что оно плохое, я говорю что оно не всем нравится. Поэтому я смотрю на альтернативы Cube IDE, тем более что она только под ST, что не есть удобно при работе с другими МК.


          1. OldFashionedEngineer
            15.12.2022 20:40

            Так можно щаморочиться, и заставить cubeide шить другие контроллеры)))

            Я раньше на работе вынужден был использовать кейл. После этого куб как манна небесная!