Введение
Привет всем.
На данный момент я активно осваиваю разработку ПО для STM32 и хотел бы поделиться моим опытом.
Как известно, для STM32 имеется много сред для разработки, однако часть из них, несмотря на удобность, имеют ограничения по использованию в случае пробной версии. Так, для IAR размер прошивки ограничивается 32 кБ, что весьма немного.
В данной публикации будет рассмотрен способ настройки окружения для полноценной разработки и отладки ПО для микроконтроллеров STM32 в среде QtCreator.
Прежде всего, определимся, что нам понадобится для проведения экспериментов.
Железо
В качестве примера я использую плату bluepill на базе STM32F103C8T6. В моем случае openocd жаловался на некорректный номер STM32. Решается 2 способами: или подменой номера в конфиге openocd или заменой МК на оригинальный. Я выбрал 2 вариант. Для загрузки и отладки программ в МК будем использовать интерфейс ST-Link.
Софт
Сами эксперименты проводились на виртуальной машине ОС Windows 10 в Virtual Box.
Для настройки окружения понадобится следующее ПО:
QtCreator. Можно скачать онлайн-установщик с сайта Qt. Но в нем при установке требуется регистрация и, кроме того, нам нужен только сам QtCreator. Поэтому я обычно его скачиваю непосредственно с GitHub. https://github.com/qt-creator/qt-creator/tags.
gcc-arm-none-eabi. https://developer.arm.com/downloads/-/gnu-rm.
Git. https://git-scm.com/downloads.
openocd. https://github.com/openocd-org/openocd/tags. Это программа для загрузки прошивки и отладки микроконтроллера STM.
ST-LINK Utility. Программа для прошивки STM32 + драйвер для самого ST-Link. https://www.st.com/en/development-tools/stsw-link004.html
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 initialization
port_init(); //Port initialization
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.
![](https://habrastorage.org/getpro/habr/upload_files/402/c9e/0a9/402c9e0a9360087a29538e625bf461ff.jpg)
Таким образом, мы рассмотрели как настроить проект для STM32, его скомпилировать и загрузить в МК. Дальше настроим отладчик для комфортной отладки STM32.
Настройка отладки
Ранее мы уже настроили удаленный отладчик через openocd, и он должен работать
Однако было весьма здорово иметь возможность видеть состояние регистров МК. Добавим такую возможность. Для этого нам понадобится .svd файл для нашего МК, т.е. STM32F103. Скачать его можно, например, из этого репозитория:
Далее перейдем в QtCreator Edit->Preferences->Devices, на вкладку Bar Metal и выберем OpenOCD. Указываем в Peripheral description file наш путь .svd файлу.
Теперь в режиме отладки перейдем в View->Views->Peripheral
Registers, чтобы отобразить состояние конфигурационных регистров. Запустив
окно отладки и, кликнув правой клавишей мыши на окне Peripheral registers,
можно выбрать группу регистров для отображения, в нашем примере View Groups->GPIOC.
В итоге мы имеем все удобства для отладки устройств на STM32 в IDE QtCreator.
Заключение
Таким образом, в рамках данной статьи рассмотрена настройка QtCreator в связке с GNU компиляторами для полноценной разработки ПО для МК STM32. Надеюсь, что статья была полезна.
Спасибо за внимание!