Я новичок в электронике и программировании для микроконтроллеров, поэтому традиционно я начал с управления светодиодом, который является проектом «Hello World!» в мире микроконтроллеров. Я начал с контроллера STM32 (STM32F103), который является ARM, и я могу использовать язык C.
Когда мой светодиод, наконец, начал мигать, я подумал, что это может быть полезно для меня (и надеюсь, что я могу быть полезен для кого-то еще), если я создам простой проект шаблона многократного использования. В результате теперь на GitHub появился еще один репозиторий.
Я использую Linux, без модного графического интерфейса, извините.
Прежде чем я начал, я, конечно, спросил Google, как люди работают с STM32 в Linux. Я нашел две хорошие статьи, которые мне очень помогли:
- STM32 Discovery Development On Linux — эта статья о STM32F4Discovery с отладчиком/программатором ST-LINK/V2. Но у меня была только плата STM32F103, похожая, но все же немного другая.
- Программирование ARM Cortex (STM32) под GNU/Linux — это тоже про STM32F4.
Наверняка в интернете есть статьи про STM32F103, но я бросил гуглить.
STM32, что еще?
Давайте поговорим об оборудовании.
Честно говоря, гораздо проще использовать какую-нибудь плату STM32 Discovery. Но он такой большой… Некоторые из этих плат даже имеют ЖК-дисплей, что круто, но… Есть ли что-нибудь поменьше?
К счастью, есть проект Maple Mini, основанный на микроконтроллере STM32. Но нашел клон у парней в Китае… Стоил всего 1,90$. Я был так счастлив, поэтому я заказал два. А, бесплатной доставки нет… ладно, всего 4,24 доллара. Думаю, не слишком много.
Эта плата основана на STM32F103C8T6, но не имеет встроенного отладчика/программатора, такого как STM32F4Discovery. Тогда мне нужен был программатор. Меня снова спасли ребята из Китая. Я нашел клон программатора ST-LINK/V2. Это было всего 3,45 доллара. И бесплатная доставка, ура!
Мне также понадобился светодиод и резистор. Резистор может быть 200 или 330 Ом. Я написал отдельный пост о подключении светодиода к микроконтроллеру и выборе токоограничивающего резистора.
Кстати, все пришло за пару недель, что для Алиэкпресс очень быстро. Но может мне просто повезло. Светодиод должен быть подключен к контакту PA1
.
Нам нужен компилятор C для ARM
sudo aptitude install gcc-arm-none-eabi
В качестве проверки работоспособности вы можете просто распечатать версию arm-none-eabi-gcc:
arm-none-eabi-gcc --version arm-none-eabi-gcc (15:4.9.3+svn227297-1) 4.9.3 20150529 (prerelease) Copyright (C) 2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
У нас есть оборудование, компилятор и все?
Мне нужно было программное обеспечение STLINK для загрузки кода в микроконтроллер. Исходники доступны на GitHub. Я просто клонировал репо и построил его. Для установки требуется пара инструментов и библиотек. В Ubuntu 16.04 я сделал следующее:
sudo apt-get install automake autoconf pkg-config libusb-1.0 git
Когда это будет сделано, STLINK может быть собран с помощью следующих команд:
git clone https://github.com/texane/stlink.git stlink cd stlink ./autogen.sh ./configure make
Можем ли мы уже начать управлять светодиодом?
STMicroelectronics предоставляет стандартную периферийную библиотеку STM32, которая содержит API для работы с микроконтроллерами STM32. Я использовал версию 3.5.0. Библиотеку можно скачать здесь. Другой вариант — использовать STM32Cube, который, похоже, рекомендован STMicroelectronics.
Теперь мы наконец можем начать писать код. Как я уже упоминал ранее, я залил код на GitHub:
git clone https://github.com/artem-smotrakov/stm32f103-template
Я хотел, чтобы это было просто, чтобы в нем не было много вещей.
main.c
— основная программа. Во-первых, функция main() инициализирует вывод PA1
. Затем он запускает бесконечный цикл, который включает/выключает светодиод с задержкой.
#include "main.h" void delay(int millis) { while (millis-- > 0) { volatile int x = 5971; while (x-- > 0) { __asm("nop"); } } } int main(void) { // GPIO structure for port initialization GPIO_InitTypeDef GPIO_InitStructure; // enable clock on APB2 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // configure port A1 for driving an LED GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // output push-pull mode GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // highest speed GPIO_Init(GPIOA, &GPIO_InitStructure) ; // initialize port // main loop while(1) { GPIO_SetBits(GPIOA, GPIO_Pin_1); // turn the LED on delay(DELAY); GPIO_ResetBits(GPIOA, GPIO_Pin_1); // turn the LED off delay(DELAY); } }
main.h
— это заголовочный файл, который включает в себя пару других заголовочных файлов из стандартной периферийной библиотеки STM32.
#ifndef __MAIN_H #define __MAIN_H #include <stm32f10x.h> #include <stm32f10x_gpio.h> #include <stm32f10x_rcc.h> #define DELAY 1500 // in millis #endif
stm32f10x_conf.h
— это заголовочный файл, который используется стандартной периферийной библиотекой STM32. Он содержит только assert_param()
функцию, которая ничего не делает.
#ifndef __STM32F10x_CONF_H #define __STM32F10x_CONF_H // no asserts #define assert_param(expr) ((void)0) #endif
Без этой функции компиляция завершается с ошибкой, подобной следующей:
/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c: In function 'RCC_HSEConfig': /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:273:3: warning: implicit declaration of function 'assert_param' [-Wimplicit-function-declaration] assert_param(IS_RCC_HSE(RCC_HSE)); ^ /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c: In function 'GPIO_DeInit': /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c:111:3: warning: implicit declaration of function 'assert_param' [-Wimplicit-function-declaration] assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); ^ /tmp/ccvazqC6.o: In function `RCC_HSEConfig': /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:273: undefined reference to `assert_param' /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:273: undefined reference to `assert_param' /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:273: undefined reference to `assert_param' /tmp/ccvazqC6.o: In function `RCC_AdjustHSICalibrationValue': /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:338: undefined reference to `assert_param' /tmp/ccvazqC6.o: In function `RCC_HSICmd': /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:357: undefined reference to `assert_param' /tmp/ccvazqC6.o:/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c:383: more undefined references to `assert_param' follow collect2: error: ld returned 1 exit status Makefile:39: recipe for target 'led.elf' failed make: *** [led.elf] Error 1
Makefile
— это make-файл. Ничего удивительного.
# path to STM32F103 standard peripheral library STD_PERIPH_LIBS ?= ./STM32F10x_StdPeriph_Lib_V3.5.0/ # list of source files SOURCES = main.c SOURCES += $(STD_PERIPH_LIBS)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c SOURCES += $(STD_PERIPH_LIBS)/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c SOURCES += $(STD_PERIPH_LIBS)/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c SOURCES += $(STD_PERIPH_LIBS)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/TrueSTUDIO/startup_stm32f10x_md.s # name for output binary files PROJECT ?= led # compiler, objcopy (should be in PATH) CC = arm-none-eabi-gcc OBJCOPY = arm-none-eabi-objcopy # path to st-flash (or should be specified in PATH) ST_FLASH ?= st-flash # specify compiler flags CFLAGS = -g -O2 -Wall CFLAGS += -T$(STD_PERIPH_LIBS)/Project/STM32F10x_StdPeriph_Template/TrueSTUDIO/STM3210B-EVAL/stm32_flash.ld CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 CFLAGS += -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER CFLAGS += -Wl,--gc-sections CFLAGS += -I. CFLAGS += -I$(STD_PERIPH_LIBS)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/ CFLAGS += -I$(STD_PERIPH_LIBS)/Libraries/CMSIS/CM3/CoreSupport CFLAGS += -I$(STD_PERIPH_LIBS)/Libraries/STM32F10x_StdPeriph_Driver/inc OBJS = $(SOURCES:.c=.o) all: $(PROJECT).elf # compile $(PROJECT).elf: $(SOURCES) $(CC) $(CFLAGS) $^ -o $@ $(OBJCOPY) -O ihex $(PROJECT).elf $(PROJECT).hex $(OBJCOPY) -O binary $(PROJECT).elf $(PROJECT).bin # remove binary files clean: rm -f *.o *.elf *.hex *.bin # flash burn: sudo $(ST_FLASH) write $(PROJECT).bin 0x8000000
Он имеет три цели:
- «все» цели компилируют исходники
- «очистить» удалить двоичные файлы, которые были созданы «всеми» целями
- «burn» запускает утилиту st-flash, которая загружает бинарные файлы в микроконтроллер. Требуются права суперпользователя.
Примечание: флаги кажутся немного излишними, см. также Комментарий Ивана ниже о флагах.
Переменная SOURCES
содержит список исходных файлов для компиляции. Помимо main.c
, он содержит пару файлов из стандартной периферийной библиотеки STM32.
Наконец, код можно собрать и загрузить на устройство с помощью таких команд:
STD_PERIPH_LIBS=/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0/ make all ST_FLASH=/home/artem/tools/stlink/st-flash make burn
Переменная STD_PERIPH_LIBS
содержит путь к стандартной периферийной библиотеке STM32, которая была загружена ранее. Переменная ST_FLASH
содержит путь к утилите st-flash
, которая была собрана ранее.
Вы можете увидеть примерно следующее, если все прошло гладко:
arm-none-eabi-gcc -g -O2 -Wall -T/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Project/STM32F10x_StdPeriph_Template/TrueSTUDIO/STM3210B-EVAL/stm32_flash.ld -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER -Wl,--gc-sections -I. -I/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/ -I/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/CMSIS/CM3/CoreSupport -I/home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/inc main.c /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c /home/artem/projects/stm32/src/STM32F10x_StdPeriph_Lib_V3.5.0//Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/TrueSTUDIO/startup_stm32f10x_md.s -o led.elf arm-none-eabi-objcopy -O ihex led.elf led.hex arm-none-eabi-objcopy -O binary led.elf led.bin sudo /home/artem/tools/stlink/st-flash write led.bin 0x8000000 2016-07-04T23:11:34 INFO src/common.c: Loading device parameters.... 2016-07-04T23:11:34 INFO src/common.c: Device connected is: F1 Medium-density device, id 0x20036410 2016-07-04T23:11:34 INFO src/common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x10000 bytes (64 KiB) in pages of 1024 bytes 2016-07-04T23:11:34 INFO src/common.c: Attempting to write 4040 (0xfc8) bytes to stm32 address: 134217728 (0x8000000) Flash page at addr: 0x08000c00 erased 2016-07-04T23:11:35 INFO src/common.c: Finished erasing 4 pages of 1024 (0x400) bytes 2016-07-04T23:11:35 INFO src/common.c: Starting Flash write for VL/F0/F3 core id 2016-07-04T23:11:35 INFO src/flash_loader.c: Successfully loaded flash loader in sram 3/3 pages written 2016-07-04T23:11:35 INFO src/common.c: Starting verification of write complete 2016-07-04T23:11:35 INFO src/common.c: Flash written and verified! jolly good!
Вот и все. Удачного дня!
Первоначально опубликовано на https://blog.gypsyengineer.com 12 июля 2016 г.