Я новичок в электронике и программировании для микроконтроллеров, поэтому традиционно я начал с управления светодиодом, который является проектом «Hello World!» в мире микроконтроллеров. Я начал с контроллера STM32 (STM32F103), который является ARM, и я могу использовать язык C.

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

Я использую Linux, без модного графического интерфейса, извините.

Прежде чем я начал, я, конечно, спросил Google, как люди работают с STM32 в Linux. Я нашел две хорошие статьи, которые мне очень помогли:

  1. STM32 Discovery Development On Linux — эта статья о STM32F4Discovery с отладчиком/программатором ST-LINK/V2. Но у меня была только плата STM32F103, похожая, но все же немного другая.
  2. Программирование 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 г.