Создание нового проекта в Keil. Программирование STM32F4. Создание нового проекта в Keil Конфигурирование STM32F10x SPL с помощью макроопределений

В этой публикации я попытаюсь акцентировать внимание на основных моментах для быстрого начала работы с микроконтроллерами STM32F10x на основе библиотеки стандартной периферии от компании-производителя STMicroelectronics.

В качестве среды разработки в статье будет использоваться Eclipse CDT. Поскольку основное внимание будет сосредоточено вокруг программного кода, то вы можете спокойно проделать все манипуляции в Code::Blocks.

Общая структура проекта для ARM микроконтроллеров описана в моей статье .

Здесь я коротко напомню, что для сборки проекта для ARM — микроконтроллеров (STM32F10x в частности) понадобится скрипт компоновщика и C-Startup файл.

Скрипт компоновщика представляет собой файл с инструкциями по размещению кода программы и данных в памяти микроконтроллера. Он может скомандовать загрузить код вашей программы в Flash -память программ или SRAM -память данных.

Для микроконтроллеров с различным объемом памяти программ и данных необходимы разные скрипты компоновки. Их можно достать у производителя микроконтроллеров - компании STMicroelectronics.
Распакуйте из архива ARM_Toolchain/Lib/stm32f10x_stdperiph_lib.zip библиотеку STM32F10x standard peripheral library.
В ней имеются примеры проектов для различных сред разработки (IAR EWB, Keil uVision, Atollic True Studio и т.д). Наиболее близким для нас является Atollic True Studio, поскольку представляет собой модификацию Eclipse.
Зайдите в каталог Project/StdPeriph_Template/TrueSTUDIO, там есть несколько подкаталогов, названия которых соответствуют названиям отладочных плат STM3210x-EVAL.

Узнайте, в какой из этих плат используется микроконтроллер той же линейки, что и ваш. Скопируйте файл stm32_flash.ld из соответствующего каталога в свой проект.

Возможно также создать универсальный скрипт, в котором будут изменяться только объем памяти программ и данных в соответствии с используемым микроконтроллером.

Стартовый код (C-Startup) для микроконтроллеров STM32 может быть написан на С или Assembler.
Хотя библиотеку STM32F10x Standard Peripheral Library (далее по тексту используется сокращение STM32F10x SPL) часто критикуют за наличие ошибок, все же для начала программирования под STM32 использование этой библиотеки предоставляет самый простой способ быстро приступить к работе.
Но всегда хочется, чтобы была какая-то альтернатива. На самом деле их множество, например, программировать на языке ассемблера 🙂 .

Это самый тяжелый и бессмысленный путь. Второй способ - использовать библиотеку CMSIS, которая предоставляет синтаксис обращения к структурам языка С для доступа к различной периферии микроконтроллера. Самым простым и логичным способом (на мой взгляд) является использование библиотек.

Если вы категорически настроены против STM32F10x SPL, то специально для вас имеется еще одна альтернатива - библиотека libopencm3. В ней основное количество примеров сосредоточено вокруг основной серии микроконтроллеров STM32F10x , но появление примеров для других серий (STM32F2xx/4xx) является только вопросом времени. Вы всегда можете присоединиться к проекту libopencm3 и ускорить этот процесс.

Стандарт CMSIS также является не обязательным для применения в ваших программах.
Можно обойтись и без него, потратив некоторые усилия и время для реализации HAL (Hardware Abstraction Layer) уровня на языке программирования С.

Такой способ может оказаться в некоторых случаях единственным доступным способом. Например, ваша организация использует заказные микросхемы на основе разработанных компанией ARM вычислительных ядер и специфической для отрасли периферией.

Или вам необходимо реализовать программное обеспечение на языке С для микроконтроллеров с ядром ARM9, для которого производители ориентируются на использование готовых операционных систем (Linux, QNX, Windows CE), поэтому библиотек для программирования на языке С в чистом виде или в сочетании с более легковесной RTOS производители могут не предоставлять.

К счастью производители микроконтроллеров на основе ядра Cortex-M3 предоставляют в распоряжение разработчиков большое количество библиотек кода. Это касается и микроконтроллеров STM32.
Продолжим рассмотрение библиотеки STM32F10x SPL. Рассматривать ее будем на примере .
Вы можете открыть этот пример или же создать свой проект «с нуля», чтобы лучше осознать весь процесс происходящего.

Для второго случая я перечислю список необходимых шагов:

  • Создать в Eclipse новый пустой проект
  • Скопировать в проект скрипт компоновки и стартовый файл
  • Создать новый или скопировать шаблонный Makefile
  • При использовании в качестве шаблона Makefile из моего примера необходимо создать внутри проекта каталоги src, inc, bin, obj , внутри каталогов bin и obj создать подкаталоги Debug и Release.
  • Скопировать необходимые исходные и заголовочные файлы из библиотек CMSIS и STM32F10x SPL.
  • Внести необходимые изменения в секции настроек пользователя шаблонного Makefile, если он используется.
  • Создать в окне Eclipse “make target ” новые цели “Debug”, “cleanDebug”, “Release”, “cleanRelease”, “Program”.
  • Запустить на выполнение цель «Debug» и проследить за ее выполнением в окне «Console».

Для лучшего понимания материала я разбил статью на несколько независимых параграфов, в каждом из которых описывается только какой-то один аспект работы с библиотекой STM32F10x SPL.

Конфигурирование STM32F10x SPL с помощью макроопределений

Для конфигурирования библиотеки используются предопределенные значения макросов, которые мы сейчас и рассмотрим.
Их можно задать внутри заголовочных файлов с помощью директивы препроцессора #define или же передать значения макроопределений через ключ -D компилятора GCC.
В своем примере я использую второй способ.
В Makefile переменная DEFINE содержит макросы, необходимые для компиляции библиотеки STM32F10x SPL.
Макроопределение STM32F10X_MD задает принадлежность используемого микроконтроллера к линейке Medium-density .
Сюда входят микроконтроллеры с объемом Flash-памяти от 64 до 128кБ.
В следующей таблице перечислены названия макросов для разных серий микроконтроллеров:

Наименование серии Макрос Описание
Low density Value line STM32F10X_LD_VL с объемом Flash-памяти 16 — 32кБ
Low density STM32F10X_LD
с объемом Flash-памяти 16 — 32кБ
Medium density Value line STM32F10X_MD_VL Flash — памяти
64 — 128кБ
Medium-density STM32F10X_MD микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx с объемом Flash- памяти 64 — 128кБ
High density Value line STM32F10X_HD_VL микроконтроллеры серии STM32F100xx с объемом
Flash — памяти 256 - 512кБ
High density STM32F10X_HD с объемом
Flash- памяти 256 - 512кБ
XL-density STM32F10X_XL
Flash- памяти 512 - 1024кБ
Connectivity line STM32F10X_CL

Для задания тактовой частоты микроконтроллера необходимо раскомментировать в файле system_stm32f10x.c макрос с необходимым значение тактовой частоты.

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) #define SYSCLK_FREQ_24MHz 24000000 #else /* #define SYSCLK_FREQ_HSE HSE_VALUE */ /* #define SYSCLK_FREQ_24MHz 24000000 */ /* #define SYSCLK_FREQ_36MHz 36000000 */ /* #define SYSCLK_FREQ_48MHz 48000000 */ /* #define SYSCLK_FREQ_56MHz 56000000 */ #define SYSCLK_FREQ_72MHz 72000000 #endif

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)

/* #define SYSCLK_FREQ_HSE HSE_VALUE */

#define SYSCLK_FREQ_24MHz 24000000

#else

/* #define SYSCLK_FREQ_HSE HSE_VALUE */

/* #define SYSCLK_FREQ_24MHz 24000000 */

/* #define SYSCLK_FREQ_36MHz 36000000 */

/* #define SYSCLK_FREQ_48MHz 48000000 */

/* #define SYSCLK_FREQ_56MHz 56000000 */

#define SYSCLK_FREQ_72MHz 72000000

#endif

Предполагается использование кварцевого резонатора с частотой 8МГц для всех основных
серий микроконтроллеров, кроме Connectivity line, для которой необходимо установить кварцевый резонатор на 25МГц.
Если вы используете кварцевые резонаторы с другими значениями частоты, то необходимо изменить значение макроса HSE_VALUE в заголовочном файле stm32f10x.h и адаптировать соответствующим образом все зависимые функции.
Назначение макроса USE_STDPERIPH_DRIVER нетрудно догадаться - использовать библиотеку STM32F10x standard peripheral library.
USE_FULL_ASSERT – использовать макрос ASSERT для отладки программы.

Использование макроса assert_param в библиотеке

Все функции библиотеки STM32F10x SPL используют для проверки своих аргументов макрос assert_param.
Этот макрос выполняет проверку выражения с участием проверяемого аргумента функции на равенство нулю. Если значение выражения равно нулю, то вызывается функция-обработчик ошибки аргумента assert_failed, в противном случае (выражение не равно нулю) проверка аргумента проходит успешно.
В своей программе вам необходимо реализовать функцию assert_failed.
В ней выводится сообщение об ошибке, название файла и номер строки кода, вызвавшей ошибку.
Макрос debug_printf может осуществлять вывод через USART c помощью стандартной библиотеки new_lib или, например, библиотеки от мистера Чена .

#define debug_printf xprintf /* printf */ #ifdef USE_FULL_ASSERT void assert_failed(uint8_t* file, uint32_t line) { debug_printf("Wrong parameters value: file %s on line %d\r\n", file, (int)line); while (1) { } }/* assert_failed */ #endif/*USE_FULL_ASSERT*/

#define debug_printf xprintf /* printf */

#ifdef USE_FULL_ASSERT

void assert_failed (uint8_t * file , uint32_t line )

debug_printf ("Wrong parameters value: file %s on line %d\r\n" , file , (int ) line ) ;

while (1 )

} /* assert_failed */

#endif/*USE_FULL_ASSERT*/

Реализованная в вашем коде функция assert_failed используется только когда объявлен макрос USE_FULL_ASSERT. В противном случае весь отладочный код исключается из исходника. Этот функционал реализован в заголовочном файле настроек библиотеки драйверов stm32f10x_conf.h.

#ifdef USE_FULL_ASSERT #define assert_param(expr) ((expr) ? (void)0: assert_failed((uint8_t *)__FILE__, __LINE__)) void assert_failed(uint8_t* file, uint32_t line); #else #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */

#ifdef USE_FULL_ASSERT

#define assert_param(expr) ((expr) ? (void)0: assert_failed((uint8_t *)__FILE__, __LINE__))

void assert_failed (uint8_t * file , uint32_t line ) ;

#else

#define assert_param(expr) ((void)0)

#endif /* USE_FULL_ASSERT */

Тут объяснять особо нечего. Лучше рассмотрим пример использования assert_param.

void set_param(uint8_t * param, uint8_t value) { assert_param(param != NULL); *param = value; }/*set_param*/

void set_param (uint8_t * param , uint8_t value )

assert_param (param != NULL ) ;

* param = value ;

} /*set_param*/

Функция устанавливает значение параметра через указатель, передаваемый в качестве аргумента. Если макрос USE_FULL_ASSERT не объявлен, то можно считать, что строчки
assert_param(param != NULL) в коде просто нет, иначе в этом определении происходит проверка параметра.
Если указатель не определен, то значение param != NULL будет ложно и будет запущена функция assert_failed , которая выведет через USART название файла и номер строки с ошибкой, после чего зациклится, тем самым предотвратив присвоение значения по неопределенному адресу в памяти.
Вы совсем не обязаны использовать макрос assert_param в своем коде, но в коде библиотеки
STM32F10x SPL он используется везде.
Функцию set_param можно реализовать с проверкой ошибок аргумента без применения assert_param.

#define ERROR (-1) #define OK (0) int set_param(uint8_t * param, uint8_t value) { int r = ERROR; if (param == NULL) return r; *param = value; r = OK; return r; }/*set_param*/

#define ERROR (-1)

#define OK (0)

int set_param (uint8_t * param , uint8_t value )

int r = ERROR ;

if (param == NULL )

return r ;

* param = value ;

r = OK ;

return r ;

} /*set_param*/

C-Startup файл в библиотеке STM32F10x SPL

В стартовом коде выполняется первичная инициализация микроконтроллера, настраивается стек, выполняется обнуление секции BSS, происходит вызов основной функции main().
Прямого отношения к библиотеке STM32F10x SPL стартовый код не имеет. Однако в этом загрузочном коде перед вызовом основной функции main() программы вызывается функция инициализации микроконтроллера SystemInit(), что входит в состав CMSIS.
Его можно без труда отыскать в библиотеке CMSIS.
Зайдите в каталог Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/TrueSTUDIO и скопируйте нужный файл. Осталось лишь выяснить к какой линейке принадлежит используемый в вашем проекте микроконтроллер.
Для этого просмотрите следующую таблицу:

Наименование серии Название файла Описание
Low density Value line startup_stm32f10x_ld_vl.s микроконтроллеры серии STM32F100xx с объемом
Flash- памяти 16 — 32кБ
Low density startup_stm32f10x_ld.s микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx
с объемом Flash- памяти 16 — 32кБ
Medium density Value line startup_stm32f10x_md_vl.s микроконтроллеры серии STM32F100xx
Medium-density startup_stm32f10x_md.s микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx
с объемом Flash- памяти 64 — 128кБ
High density Value line startup_stm32f10x_hd_vl.s микроконтроллеры серии STM32F100xx
High density startup_stm32f10x_hd.s микроконтроллеры серии STM32F101xx, STM32F103xx
с объемом Flash- памяти 256 - 512кБ
XL-density startup_stm32f10x_xl.s микроконтроллеры серии STM32F101xx, STM32F103xx
с объемом Flash- памяти 512 - 1024кБ
Connectivity line startup_stm32f10x_cl.s микроконтроллеры серии STM32F105xx и STM32F107xx

В стартап-файле прописаны названия обработчиков векторов прерываний и исключений, однако реализован только обработчик вектора сброса, внутри которого и производится вся начальная инициализация перед вызовом функции main().
Реализация всех остальных обработчиков исключений возложена на программиста приложения. Если в вашей программе не используется ни один обработчик, то и нет нужды их прописывать. В случае возникновения исключения будет использован обработчик по-умолчанию - зацикливание кода программы.

Состав библиотеки CMSIS

Как было уже написано ранее в этой публикации библиотека CMSIS предоставляет доступ к периферийным модулям микроконтроллера с помощью элементов структур языка C.
Реализация этой библиотеки делится на две части. Первая часть обеспечивает доступ к периферии ядра Cortex-M3 , а вторая - к периферии конкретной модели микроконтроллера.
Поскольку стандарт CMSIS единый для всех микроконтроллеров с ядром Cortex-M3, то реализация первой части будет одинакова у всех производителей, а вот вторая часть у каждого производителя будет своей.
В состав CMSIS входят несколько заголовочных и исходных файлов. К первой части относятся файлы:

  • core_cm3.h
  • core_cm3.c

Вторая часть CMSIS включает в себя С-Startup файл, а также файлы:

  • stm32f10x.h
  • system_stm32f10x.h
  • system_stm32f10x.c

Заголовочный файл stm32f10x.h содержит макроопределения для доступа к периферийным модулям микроконтроллеров stm32f10x.
В файлах system_stm32f10x.h и system_stm32f10x.c реализована начальная инициализация микроконтроллера.

Состав и конфигурация библиотеки STM32F10x SPL

Библиотека состоит из одноименных с периферийными модулями исходных и заголовочных файлов с префиксом stm32f10x_ .
Например, реализация взаимодействия с модулем USART содержится в файлах stm32f10x_usart.h и stm32f10x_usart.c .
Существуют соглашения об именах элементов библиотеки и определенные правила кодирования, которые описаны в документации.
Библиотека содержит реализацию драйверов периферийных модулей микроконтроллера.
В названиях элементов библиотеки используются такие акронимы для модулей периферии:

Акроним Периферийный модуль
ADC аналогово-цифровой преобразователь
BKP регистры резервного копирования
CAN интерфейс CAN
CEC контроллер потребления
CRC модуль расчета контрольной суммы
DAC цифро-аналоговый преобразователь
DBGMCU отладка микроконтроллера
DMA контроллер прямого доступа к памяти
EXTI контроллер внешних прерываний
FSMC контроллер внешней памяти
FLASH Flash-память программ
GPIO порты ввода/вывода общего назначения
I2C интерфейс I2C
I2S интерфейс I2S (Sound)
IWDG независимый сторожевой таймер
NVIC контроллер вложенных прерываний
PWR контроллер питания
RCC контроллер сброса и тактирования
RTC контроллер реального времени (часы)
SDIO интерфейс SDIO
SPI интерфейс SPI
SysTick системный таймер
TIM базовый таймер или таймер с расширенными возможностями
USART универсальный последовательный синхронно-асинхронный
приемо-передатчик
WWDG оконный сторожевой таймер

На основе этих акронимов формируются названия программных модулей библиотеки. Вам не обязательно использовать все модули библиотеки.
Для того, чтобы использовать в проекте только необходимые модули, библиотеку нужно сконфигурировать.
Для этих целей в каждом проекте, который использует библиотеку STM32F10x SPL, должен быть заголовочный файл stm32f10x_conf.h.

#include "stm32f10x_gpio.h" //#include "stm32f10x_i2c.h" //#include "stm32f10x_iwdg.h" //#include "stm32f10x_pwr.h" #include "stm32f10x_rcc.h"

#include "stm32f10x_gpio.h"

//#include "stm32f10x_i2c.h"

//#include "stm32f10x_iwdg.h"

//#include "stm32f10x_pwr.h"

#include "stm32f10x_rcc.h"

Для включения нужного модуля необходимо раскомментировать директиву #include с соответствующим заголовочным файлам.
Заголовочный файл stm32f10x_conf.h включен в stm32f10x.h, поэтому для использования функций библиотеки STM32F10x SPL достаточно подключить в своем исходнике только один заголовочный файл stm32f10x.h

// в файле stm32f10x.h #ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif

Повторюсь, что в проекте также должны быть определены макросы USE_STDPERIPH_DRIVER, USE_FULL_ASSERT и макрос, задающий серию используемого микроконтроллера (например STM32F10X_MD для линейки Medium density).
Если вы используете стандартное значение частоты кварцевого резонатора и контроллер будет работать на максимальной тактовой частоте 72МГц, то ничего больше изменять не придется.
В Makefile необходимо добавить список компилируемых файлов библиотеки.
Например:

SRC += stm32f10x_rcc.c SRC += stm32f10x_gpio.c

SRC += stm32f10x_rcc . c

SRC += stm32f10x_gpio . c

Использование библиотеки STM32F10x SPL. Механизмы работы

Для начала программирования с использованием библиотеки периферии проще всего смотреть в примеры, поставляемые в комплекте с библиотекой. Но все же для понимания кода этих примеров необходимо иметь базовые знания по синтаксису и использованию библиотеки.
Все перечисленные ранее периферийные модули микроконтроллера изначально являются дезактивированными, тактовый сигнал на них не подается и электроэнергии они не потребляют.
Для использования периферийного модуля в первую очередь необходимо подать на него тактовый сигнал. Подачу тактового сигнала обеспечивает модуль тактирования и сброса RCC.
Для этих целей в библиотеке существуют такие функции:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_PPPx, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_PPPx, ENABLE);

RCC_AHBPeriphClockCmd (RCC_AHBPeriph_PPPx , ENABLE ) ;

RCC_APB2PeriphClockCmd (RCC_APB2Periph_PPPx , ENABLE ) ;

RCC_APB1PeriphClockCmd (RCC_APB1Periph_PPPx , ENABLE ) ;

Тут PPP обозначает название актонима модуля (например ADC или USART) , а x – номер периферийного модуля.
Прежде всего необходимо узнать к какой шине подключен используемый вами модуль.
Всего у микроконтроллеров с архитектурой ядра Cortex-M3 имеется в наличии три шины:
шина инструкций, шина данных и системная шина. Шина инструкций соединяет ядро с Flash — памятью программ. Шины данных и системная объединены в матрицу шин AHB (ARM Hi-Speed Bus), которая работает на частоте ядра. Однако частота шины AHB может быть понижена путем установки делителей. Через шину AHB соединяются высокоскоростные устройства, например ядро и модуль ПДП.
Устройства ввода/вывода подсоединяются к шине AHB через промежуточные шины APB1 и APB2 (ARM Peripheral Bus).
Максимальная частота работы шины APB2 составляет 72МГц, частота шины APB1
ограничена значением 36МГц.
К какой из шин подключен задействованный вами периферийный модуль можно узнать из документации или посмотреть в заголовочном файле stm32f10x_rcc.h.
Откройте этот файл и выполните последовательно поиск значений RCC_AHBPeriph, RCC_APB1Periph и RCC_APB2Periph.

#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001) #define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002) #define RCC_AHBPeriph_SRAM ((uint32_t)0x00000004) #define RCC_AHBPeriph_FLITF ((uint32_t)0x00000010) #define RCC_AHBPeriph_CRC ((uint32_t)0x00000040)

#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001)

#define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002)

#define RCC_AHBPeriph_SRAM ((uint32_t)0x00000004)

#define RCC_AHBPeriph_FLITF ((uint32_t)0x00000010)

#define RCC_AHBPeriph_CRC ((uint32_t)0x00000040)

По названиям макросов определяем какие модули к каким шинам подключены. Также для определения принадлежности к одной из трех шин можно воспользоваться здравым смыслом. Например, модуль USART является устройством ввода/вывода, значит подключен к одной из шин APB. USART является достаточно низко-скоростным интерфейсом, поэтому наверняка он подключен к шине APB1.

#define RCC_APB1Periph_USART2 ((uint32_t)0x00020000) #define RCC_APB1Periph_USART3 ((uint32_t)0x00040000) #define RCC_APB1Periph_UART4 ((uint32_t)0x00080000) #define RCC_APB1Periph_UART5 ((uint32_t)0x00100000)

После подачи тактового сигнала на периферийный модуль можно настроить его параметры путем вызова функции инициализации:

PPP_Init(PPP, &PPP_InitStructure);

PPP_Init (PPP , & amp ; PPP_InitStructure ) ;

Поскольку для инициализации периферийного модуля в функцию инициализации необходимо передать множество параметров, то в качестве аргумента используется указатель на структуру. Сама же структура с параметрами инициализации должна быть создана в памяти до вызова функции инициализации, элементам структуры должны быть присвоены необходимые значения:

PPP_InitTypeDef PPP_InitStructure = { val1, val2, …, valN};/* инициализация структуры при объявлении*/

Можно сначала создать структуру, а потом присвоить ее элементам необходимые значения:

PPP_InitTypeDef PPP_InitStructure; PPP_InitStructure.member1 = val1; PPP_InitStructure.member2 = val2; PPP_InitStructure.memberN = valN;

PPP_InitTypeDef PPP_InitStructure ;

PPP_InitStructure . member1 = val1 ;

PPP_InitStructure . member2 = val2 ;

PPP_InitStructure . memberN = valN ;

Рассмотрим пример из проекта stm32f10xQuickstart:

GPIO_InitTypeDef GPIO_InitStructure; #ifdef USE_STM32H_103 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_InitTypeDef GPIO_InitStructure ;

#ifdef USE_STM32H_103

RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC , ENABLE ) ;

GPIO_InitStructure . GPIO_Pin = GPIO_Pin_12 ;

GPIO_InitStructure . GPIO_Speed = GPIO_Speed_50MHz ;

GPIO_InitStructure . GPIO_Mode = GPIO_Mode_Out_PP ;

GPIO_Init (GPIOC , & amp ; GPIO_InitStructure ) ;

Элементам структуры GPIO_InitStructure присваивается значение номера вывода, режим и скорость работы порта.
Вызовом функции GPIO_Init производится инициализация линии 12 порта GPIOC.
Первый аргумент функции GPIO_Init представляет собой указатель на область памяти периферийного устройства GPIOC, преобразованный в указатель на структуру GPIO_TypeDef.

// stm32f10x.h #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) #define PERIPH_BASE ((uint32_t)0x40000000) typedef struct { __IO uint32_t CRL; __IO uint32_t CRH; __IO uint32_t IDR; __IO uint32_t ODR; __IO uint32_t BSRR; __IO uint32_t BRR; __IO uint32_t LCKR; } GPIO_TypeDef;

// stm32f10x.h

#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)

#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)

#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)

#define PERIPH_BASE ((uint32_t)0x40000000)

typedef struct

IO uint32_t CRL ;

IO uint32_t CRH ;

IO uint32_t IDR ;

IO uint32_t ODR ;

IO uint32_t BSRR ;

IO uint32_t BRR ;

IO uint32_t LCKR ;

} GPIO_TypeDef ;

Структура GPIO_InitStructure имеет тип GPIO_InitTypeDef, описана в заголовочном файле
stm32f10x_gpio.h:

//stm32f10x_gpio.h typedef struct { uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; }GPIO_InitTypeDef; typedef enum { GPIO_Speed_10MHz = 1, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_TypeDef; typedef enum { GPIO_Mode_AIN = 0x0, GPIO_Mode_IN_FLOATING = 0x04, GPIO_Mode_IPD = 0x28, GPIO_Mode_IPU = 0x48, GPIO_Mode_Out_OD = 0x14, GPIO_Mode_Out_PP = 0x10, GPIO_Mode_AF_OD = 0x1C, GPIO_Mode_AF_PP = 0x18 }GPIOMode_TypeDef;

//stm32f10x_gpio.h

typedef struct

uint16_t GPIO_Pin ;

GPIOSpeed_TypeDef GPIO_Speed ;

GPIOMode_TypeDef GPIO_Mode ;

} GPIO_InitTypeDef ;

typedef enum

GPIO_Speed_10MHz = 1 ,

GPIO_Speed_2MHz ,

GPIO_Speed _ 50MHz

} GPIOSpeed_TypeDef ;

typedef enum

{ GPIO_Mode_AIN = 0x0 ,

GPIO_Mode_IN_FLOATING = 0x04 ,

GPIO_Mode_IPD = 0x28 ,

GPIO_Mode_IPU = 0x48 ,

GPIO_Mode_Out_OD = 0x14 ,

GPIO_Mode_Out_PP = 0x10 ,

GPIO_Mode_AF_OD = 0x1C ,

GPIO_Mode_AF_PP = 0x18

} GPIOMode_TypeDef ;

Как видите, в качестве типов данных инициализируемой структуры могут использоваться как пользовательские типы, вроде GPIOSpeed_TypeDef, так и типы данных с определенными значениями для удобства инициализации регистров периферии, вроде GPIOMode_TypeDef.
Для конфигурирования каждого вывода GPIO выделено 4 бита.
На следующей картинке отображен формат для нулевого бита GPIO:

Mode – режим работы вывода (вход/выход). Точнее этих значений несколько больше, настроенные как выходные, порты имеют ограничение максимальной частоты выводимого сигнала.

Режим Mode Описание
00 вход
01 выход с частотой до 10МГц
10 выход с частотой до 2МГц
11 выход с частотой до 50МГц

CNF – биты конфигурации вывода. Зависят от режима работы:

Согласитесь, что при такой структуре регистра конфигурации выводов устанавливать самостоятельно все биты для конфигурации будет крайне неудобно. Гораздо проще это будет сделать при помощи библиотечной функции GPIO_Init.
После того, как вы инициализируете модуль периферии его необходимо активировать с помощью функции PPP_Cmd:

PPP_Cmd(PPP, ENABLE);

PPP_Cmd (PPP , ENABLE ) ;

Для модулей GPIO этой функции не существует, после инициализации вы можете сразу пользоваться выводами GPIO. Необходимо помнить, что библиотека предоставляет только интерфейс к аппаратуре микроконтроллера. Если у аппаратного модуля нет флага активации/деактивации, то и вызов функции PPP_Cmd(PPP, ENABLE) невозможен.
Для управления состоянием вывода GPIOx в режиме выхода и считывания значения в режиме входа или выхода в библиотеке предусмотрены следующие функции:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

void GPIO_SetBits (GPIO_TypeDef * GPIOx , uint16_t GPIO_Pin ) ;

void GPIO_ResetBits (GPIO_TypeDef * GPIOx , uint16_t GPIO_Pin ) ;

uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef * GPIOx , uint16_t GPIO_Pin ) ;

uint16_t GPIO_ReadOutputData (GPIO_TypeDef * GPIOx ) ;

uint8_t GPIO_ReadInputDataBit (GPIO_TypeDef * GPIOx , uint16_t GPIO_Pin ) ;

uint16_t GPIO_ReadInputData (GPIO_TypeDef * GPIOx ) ;

Аналогичным образом производится настройка и работа с остальными периферийными модулями. Однако существуют некоторые отличия в связи со спецификой конкретного аппаратного модуля, поэтому я настоятельно рекомендую сначала просмотреть примеры использования выбранного модуля для библиотеки STM32F10x SPL.

Обработка прерываний и исключений

В составе ядра Cortex-M3 присутствует контроллер вложенных векторизованных прерываний. Контроллер поддерживает до 240 источников, которые могут вызывать прерывания процессорного ядра. Сколько векторов из 240 возможных реализовано в конкретной модели микроконтроллера зависит от производителя. У микроконтроллеров stm32f10x этих векторов может быть до 43. Эти линии прерываний называются маскируемыми. Кроме того существует 15 векторов прерываний ядра Cortex-M3 и одно внешнее не маскируемое прерывание EXTI.
Контроллер поддерживает вложенные прерывания, когда внутри одного обработчика может возникнуть другое прерывание. В связи с этим каждый источник прерывания имеет свой приоритет. Поддерживается 16 уровней приоритетов прерываний.
Наивысшие значения приоритетов имеют векторы прерываний ядра Cortex-M3.
Три самых высоких уровня прерываний закреплены за векторами жестко и не могут быть изменены:

Номер Обработчик Приоритет Описание
1 Reset_Handler -3(наивысший) Вектор сброса
2 NMI_Handler -2 Немаскируемое прерывание
3 HardFault_Handler -1 Аварийные состояния

Всем остальным векторам прерываний могут быть назначены уровни приоритета от 0 до 15.
Высшему уровню приоритета соответствует меньшее его значение. Уровень приоритета можно назначить не только отдельному вектору, но и целой группе векторов. Такая возможность упрощает работу с большим количеством векторов прерываний.
Для установки группы приоритетов используется функция из библиотеки STM32F10x SPL.

Давно, даже очень давно, не было новых статей на нашем статье, так что пришло время наверстывать 😉 Сегодня мы положим начало изучению STM32F4. И, наверное, начнем с создания нового проекта для этих контроллеров, хотя не хотел я, честно говоря, про это писать статью, так как новый проект тут создается, в принципе, так же как и для STM32F103 (). Но все-таки бывает, что именно с STM32F4 возникают некоторые трудности, так что, все-таки, рассмотрим этот процесс в подробностях)

Так что, запускаем Keil, создаем новый проект – Project -> New uVision Project. Сохраняем новый проект в какой-нибудь папке, и затем нам предложат выбрать используемый микроконтроллер. Что ж, выбираем, пусть это будет STM32F407VG:

Готово, в появившемся диалоговом окне тыкаем «Да» и к нам в проект добавится первый файл – startup_stm32f4xx.s . Также как и раньше, мы будем использовать библиотеки CMSIS и Standard Peripheral Library , но, естественно, уже для контроллеров STM32F4xx. Так что надо их обязательно скачать и добавить нужные файлы в наш пока еще пустой проект. Кстати не раз слышал от разных людей, что попадаются какие то «не такие» библиотеки для F4, и проект даже простейший не собирается. Сам я с таким не сталкивался, тем не менее, вот проверенные библиотеки, которые я сам использую:

Итак, скачали, все готово, теперь добавляем файлы в проект. На картинке видно, какие понадобятся:

Ну вот и закончена подготовка, теперь создадим новый.c файл, в котором и будет наш код. Идем в File->New , в Keil’е открывается пустой файл, жмем File->Save as и сохраняем его под именем test.c, например. Не забываем при сохранении указать расширение файла (.c). Файл создали, отлично, но надо его еще и в проект наш добавить. Ну, собственно, в этом ничего сложного нету 😉 В этот файл запишем тестовую пустую программу:

#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" /*******************************************************************/ int main() { while (1 ) { __NOP() ; } } /*******************************************************************/

Почти все уже готово, осталось заглянуть в настройки проекта – Project->Options for target… Открывается окошко с множеством вкладок, нас тут интересуют лишь несколько. Открываем вкладку C/C++ и в поле Define прописываем:

Ну и внизу в поле надо добавить пути абсолютно ко всем файлам, включенным в проект. После выполнения этого шага можно давить на F7 (Build), и проект соберется без ошибок и предупреждений. Как видите, ничего сложного)

Но вообще я лично делаю несколько иначе. Смотрите в чем минус такого подхода. Вот мы скачали себе куда-то библиотеки CMSIS и SPL, добавили файлы из этих папок, прописали пути к файлам, все круто. НО! Проект не соберется на другом компьютере, поскольку пути все абсолютные, то есть указывают на конкретные папки на вашем компьютере. И на другой машине придется фактически заново выполнять действия по созданию нового проекта. Это огромнейший минус. Поэтому я обычно создаю отдельную папку для нового проекта, в ней создаю подпапки для CMSIS, SPL и других используемых библиотек и в эти папки запихиваю все файлы, которые мне понадобятся в каждом конкретном проекте. Вот, например, создадим папку STM32F4_Test для нашего нового проекта и в ней следующие папки:

В папки CMSIS и SPL я засунул все необходимые файлы, которые мы добавляли, создавая проект, в начале статьи. Теперь запускаем Keil, создаем новый проект и сохраняем его в нашу подпапку Project, чтобы все файлы проекта лежали в одном месте и не устраивали хаос)

Проект создан, теперь, как и раньше просто добавляем в него все файлы из папок STM32F4_CMSIS и STM32F4_SPL. В папку Source запихиваем наш тестовый.c файл с функцией main() и его тоже добавляем в проект. Осталось настроить настройки =) Все то же самое – в поле define прописываем:

USE_STDPERIPH_DRIVER,STM32F4XX



Собираем проект – ошибок нет, полет нормальный! В принципе в итоге получили то тоже самое, но теперь проект будет без проблем сразу собираться на любом другом компьютере, а это очень удобно и полезно) Абсолютно все файлы проекта теперь лежат рядом, в одной папке, а пути стали относительными и их не придется менять.
На этом то, собственно все, в ближайшее время что-нибудь поделаем для программирования STM32F4, обязательно, так что до скорого!;)

Полный проект из примера статьи –

До этого момента мы использовали стандартную библиотеку ядра - CMSIS. Для настройки какого-либо порта на нужный режим работы нам приходилось обращаться к , чтобы найти отвечающий за определенную функцию регистр, а также искать по большому документу другую связанную с этим процессом информацию. Дело примет еще большие мучительные и рутинные обороты, когда мы приступим к работе с таймером или АЦП. Количество регистров там значительно больше, чем у портов ввода-вывода. Ручная настройка отнимает немало времени и повышает шанс допустить ошибку. Поэтому многие предпочитают работать со стандартной библиотекой периферии - StdPeriph. Что же она дает? Всё просто - повышается уровень абстракции, вам не нужно лезть в документацию и думать о регистрах в большинстве своеём. В этой библиотеке все режимы работы и параметры периферии МК описаны в виде структур. Теперь для настройки периферийного устройства необходимо лишь вызвать функцию инициализации устройства с заполненной структурой.

Ниже приведена картинка со схематичным изображением уровней абстракции.

Мы работали с CMSIS (которая находится «ближе» всего к ядру), чтобы показать, как устроен микроконтроллер. Следующим шагом является стандартная библиотека, пользоваться которой мы научимся сейчас. Дальше идут драйвера устройств. Под ними понимаются *.c \ *.h -файлы, которые обеспечивают удобный программный интерфейс для управления каким-либо устройством. Так, например, в этом курсе мы предоставим вам драйверы для микросхемы max7219 и WiFi-модуля esp8266.

Стандартный проект будет включать в себя следующие файлы:


Во-первых, конечно же, это файлы CMSIS, которые позволяют стандартной библиотеке работать с ядром, о них мы уже говорили. Во-вторых, файлы стандартной библиотеки. И в-третьих, пользовательские файлы.

Файлы библиотеки можно найти на странице, посвященной целевому МК (для нас это stm32f10x4), в разделе Design Resources (в среде CooCox IDE эти файлы скачиваются из репозитория среды разработки). Каждой периферии соответствуют два файла - заголовочный (*.h) и исходного кода (*.c). Детальное описание можно найти в файле поддержки, который лежит в архиве с библиотекой на сайте.

  • stm32f10x_conf.h - файл конфигурации библиотеки. Пользователь может подключить или отключить модули.
  • stm32f10x_ppp.h - заголовочный файл периферии. Вместо ppp может быть gpio или adc.
  • stm32f10x_ppp.c - драйвер периферийного устройства, написанный на языке Си.
  • stm32f10x_it.h - заголовочный файл, включающий в себя все возможные обработчики прерываний (их прототипы).
  • stm32f10x_it.c - шаблонный файл исходного кода, содержащий сервисные рутинные прерывания (англ. interrupt service routine , ISR) для исключительных ситуаций в Cortex M3. Пользователь может добавить свои ISR для используемой периферии.

В стандартной библиотекии периферии есть соглашение в наименовании функций и обозначений.

  • PPP - акроним для периферии, например, ADC.
  • Системные, заголовочные файлы и файлы исходного кода - начинаются с stm32f10x_ .
  • Константы, используемые в одном файле, определены в этом файле. Константы, используемые в более чем одном файле, определены в заголовочных файлах. Все константы в библиотеке периферии чаще всего написаны в ВЕРХНЕМ регистре.
  • Регистры рассматриваются как константы и именуются также БОЛЬШИМИ буквами.
  • Имена функций, относящихся к периферии, содержат акроним, например, USART_SendData() .
  • Для настройки каждого периферийного устройства используется структура PPP_InitTypeDef , которая передается в функцию PPP_Init() .
  • Для деинициализации (установки значения по умолчанию) можно использовать функцию PPP_DeInit() .
  • Функция, позволяющая включить или отключить периферию, именуется PPP_Cmd() .
  • Функция включения/отключения прерывания именуется PPP_ITConfig .

С полным списком вы опять же можете ознакомиться в файле поддержки библиотеки. А теперь давайте перепишем мигание светодиода с использованием стандартной библиотеки периферии!

Перед началом работы заглянем в файл stm32f10x.h и найдем строчку:

#define USE_STDPERIPH_DRIVER

Если вы будете настраивать проект с нуля, используя файлы библиотеки из скачанного архива, то вам будет необходимо раскомментировать данную строчку. Она позволит использовать стандартную библиотеку. Данное определение (макрос) скомандует препроцессору подключить файл stm32f10x_conf.h:

#ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif

В этом файле подключаются модули. Если вам нужны только конкретные - отключите остальные, это сэкономит время при компиляции. Нам, как вы уже могли догадаться, нужны модули RTC и GPIO (однако в будущем потребуются также _bkp.h , _flash , _pwr.h , _rtc.h , _spi.h , _tim.h , _usart.h):

#include "stm32f10x_flash.h" // for init_pll() #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h"

Как и в прошлый раз, для начала нужно включить тактирование порта B. Делается это функцией, объявленной в stm32f10x_rcc.h:

Void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

Перечисление FunctionalState определено в stm32f10x.h:

Typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;

Объявим структуру для настройки нашей ножки (найти её можно в файле stm32f10x_gpio.h):

GPIO_InitTypeDef LED;

Теперь нам предстоит её заполнить. Давайте посмотрим на содержание этой структуры:

Typedef struct { uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; } GPIO_InitTypeDef;

Все необходимые перечисления и константы можно найти в этом же файле. Тогда переписанная функция init_leds() примет следующий вид:

Void led_init() { // Включаем тактирование RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Объявляем структуру и заполняем её GPIO_InitTypeDef LED; LED.GPIO_Pin = GPIO_Pin_0; LED.GPIO_Speed = GPIO_Speed_2MHz; LED.GPIO_Mode = GPIO_Mode_Out_PP; // Инициализируем порт GPIO_Init(GPIOB, &LED); }

Перепишем функцию main() :

Int main(void) { led_init(); while (1) { GPIO_SetBits(GPIOB, GPIO_Pin_0); delay(10000000); GPIO_ResetBits(GPIOB, GPIO_Pin_0); delay(10000000); } }

Главное - прочувствовать порядок инициализации: включаем тактирование периферии, объявляем структуру, заполняем структуру, вызываем метод инициализации. Другие периферические устройства обычно настраиваются по подобной схеме.

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

Ниже представлен рисунок платы STM32F3 Discovery , где: 1 — MEMS датчик. 3-осевой цифровой гироскоп L3GD20. 2 — МЕМС система-в-корпусе, содержащая 3-осевой цифровой линейный акселерометр и 3-осевой цифровой геомагнитный сенсор LSM303DLHC. 4 – LD1 (PWR) – питание 3.3V. 5 – LD2 – красный/зеленый светодиод. По умолчанию красный. Зеленый означает связь между ST-LINK/v2 (or V2-B) и ПК. У меня ST-LINK/v2-B, а также индикации пользовательского порта USB. 6. -LD3/10 (red), LD4/9 (blue), LD5/8 (orange) and LD6/7 (green). В прошлой записи мы с Вами мигали светодиодом LD4. 7. – Две кнопки: пользовательская USER и сброса RESET. 8. — USB USER with Mini-B connector.

9 — USB отладчик/программатор ST-LINK/V2. 10. — Микроконтроллер STM32F303VCT6. 11. — Внешний высокочастотный генератор 8 Мгц. 12. – Здесь должен быть низкочастотный генератор, к сожелению не запаян. 13. – SWD – интерфейс. 14. – Джемперы для выбора программирования внешних контроллеров или внутреннего, в первом случае должны быть удалены. 15 – Джемпер JP3 – перемычка, предназначена для подключения амперметра, что б измерить потребление контроллера. Понятно если она удалена, то наш камушек не запустится. 16. – STM32F103C8T6 на нем находится отладочная плата. 17. — LD3985M33R Регулятор с низким падением напряжения и уровнем шума, 150мА, 3.3В.

Теперь познакомимся поближе с архитектурой микроконтроллера STM32F303VCT6. Его техническая характеристика: корпус LQFP-100, ядро ARM Cortex-M4, максимальная частота ядра 72МГц, объём памяти программ 256 Кбайт, тип памяти программ FLASH, объём оперативной памяти SRAM 40 кбайт, RAM 8 кбайт, количество входов/выходов 87, интерфейсы (CAN, I²C, IrDA, LIN, SPI, UART/USART, USB), периферия (DMA, I2S, POR, PWM, WDT), АЦП/ЦАП 4*12 bit/2*12bit, напряжение питания 2 …3.6 В, рабочая температура –40 …...+85 С. На рисунке ниже распиновка, где видим 87 портов ввода/вывода, 45 из них Normal I/Os (TC, TTa), 42 5-volt tolerant I/Os (FT, FTf) – совместимые с 5 В. (на плате справа 5В выводы, слева 3,3В). Каждая цифровая линия ввода-вывода может выполнять функцию линии ввода-вывода общего
назначения или альтернативную функцию. По ходу продвижения проектов мы с Вами будем последовательно знакомится с периферией.

Рассмотрим блок диаграмму ниже. Сердцем является 32-битное ядро ARM Cortex-M4, работающее до 72 МГц. Имеет встроенный блок с плавающей запятой FPU и блок защиты памяти MPU, встроенные макро-ячейки трассировки — Embedded Trace Macrocell (ETM), которые могут быть использованы для наблюдения за процессом выполнения основной программы внутри микроконтроллера. Они способны непрерывно выводить данные этих наблюдений через контакты ETM до тех пор, пока устройство работает. NVIC (Nested vectored interrupt controller) – модуль контроля прерываний. TPIU (Trace Port Interface Unit). Содержит память FLASH –256 Кбайт, SRAM 40 кбайт, RAM 8 кбайт. Между ядром и памятью расположена Bus matrix (Шинная матрица), которая позволяет соединить устройства напрямую. Также здесь видим два типа шинной матрицы AHB и APB, где первая более производительная и используется для связи высокоскоростных внутренних компонентов, а последняя для периферии (устройств ввода/вывода). Контроллер имеет 4 –ри 12-разрядных ADC (АЦП) (5Мбит/с) и датчик температуры, 7 компараторов (GP Comparator1 …7), 4-ри программируеммых операционных усилителя (OpAmp1…4) (PGA (Programmable Gain Array)), 2 12 разрядных канала DAC (ЦАП), RTC (часы реального времени), два сторожевых таймера — независимый и оконный (WinWatchdog and Ind. WDG32K), 17 таймеров общего назначения и многофункциональные.

В общих чертах мы рассмотрели архитектуру контроллера. Теперь рассмотори доступные программные библиотеки. Сделав обзор можна выделить следующие: CMSIS, SPL и HAL. Рассмотрим каждую применив в простом примере мигая светодиодом.

1). CMSIS (Cortex Microcontroller Software Interface Standard) - стандартная библиотека для Cortex®-M. Обеспечивает поддержку устройств и упрощает программные интерфейсы. CMSIS предоставляет последовательные и простые интерфейсы для ядра, его периферии и операционных систем реального времени. Ее использования является профессиональным способом написания программ, т.к. предполагает прямую запись в регистры и соответственно необходимо постоянное чтение и изучение даташитов. Независим от производителя аппаратного уровня.
CMSIS включает в себя следующие компоненты:
— CMSIS-CORE: Consistent system startup and peripheral access (Постоянный запуск системы и периферийный доступ);
— CMSIS-RTOS : Deterministic Real-Time Software Execution (Детерминированное исполнение программного обеспечения реального времени);
— CMSIS-DSP: Fast implementation of digital signal processing (Быстрая реализация цифровой обработки сигналов);
— CMSIS-Driver: Generic peripheral interfaces for middleware and application code (Общие периферийные интерфейсы для промежуточного программного обеспечения и кода приложения);
— CMSIS-Pack : Easy access to reusable software components (Легкий доступ к многократно используемым программным компонентам);
— CMSIS-SVD: Consistent view to device and peripherals (Согласованное представление устройства и периферийных устройств);
— CMSIS-DAP: Connectivity to low-cost evaluation hardware (Возможность подключения к недорогому оборудованию для оценки). ПО для отладки.

Для примера напишем программу – мигаем светодиодом. Для этого нам понадабится документация описывающая регистры. В моем случае RM0316 Reference manual STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xE advanced ARM ® -based MCUs , а также описние конткретной ножки за что отвечает DS9118 : ARM®-based Cortex®-M4 32b MCU+FPU, up to 256KB Flash+ 48KB SRAM, 4 ADCs, 2 DAC ch., 7 comp, 4 PGA, timers, 2.0-3.6 V. Для начала в программе затактируем порт, т.к. по умолчанию все отключено чем достигается уменьшенное эрергопотребление. Открываем Reference manual и смотрим раздел Reset and clock control далее RCC register map и смотрим что за включение IOPEEN отвечает регистр

Перейдем в описание тактирования перефирии данного регистра AHB peripheral clock enable register (RCC_AHBENR) , где видим что данный порт находится под 21-м битом. Включаем его RCC->AHBENR|=(1<<21) . Далее сконфигурируем регистры GPIO. Нас интересует три: GPIOE_MODER и GPIOx_ODR . C помощью них повторим программу с предыдущей статьи, затактируем PE8. Первый отвечает за конфигурацию входа выхода, выбираем 01: General purpose output mode. GPIOE->MODER|=0×10000 . Второй за включение низкого/высокого уровня на ножке. Ниже программа:

#include "stm32f3xx.h" //Заголовочный файл микроконтроллера
unsigned int i;
void delay () {
for (i=0;i<500000;i++);
}
int main (void) {
RCC->AHBENR|=(1<<21);
GPIOE->MODER|=0×10000;
while (1){
delay ();
GPIOE->ODR|=0×100;
delay ();
GPIOE->ODR&=~(0×100);
} }

2). SPL (Standard Peripherals Library) - данная библиотека предназначена для объединения всех процессоров фирмы ST Electronics. Разработана для урощения преносимости кода и в первую очередь расчитана на начинающег разаработчика. ST работала над заменой SPL под названием «low layer», которая совместима с HAL. Драйверы Low Layer (LL) разработаны для обеспечения почти легкого экспертно-ориентированного уровня, который ближе к оборудованию, чем HAL. В дополнение к HAL также доступны LL API . Пример тойже программы на SPL.

#include
#include
#include
#define LED GPIO_Pin_8
int main() {
long i;
GPIO_InitTypeDef gpio;
// Blue LED is connected to port E, pin 8 (AHB bus)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
// Configure port E (LED)
GPIO_StructInit(&gpio); //объявляем и инициализируем переменную структуры данных
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_Pin = LED;
GPIO_Init(GPIOE, &gpio);
// Blinking LEDS
while (1) {
// On
GPIO_SetBits(GPIOE, LED);
for (i = 0; i < 500000; i++);
// All off
GPIO_ResetBits(GPIOE, LED);
for (i = 0; i < 500000; i++);
} }

Каждая функция описывается в технической документации UM1581 User manual Description of STM32F30xx/31xx Standard Peripheral Library . Здесь подключаем три заголовочных файла которые содержат необходимоые данные, структуры, функции управления сбросом и синхронизацией, а также для конфигурации портов ввода/вывода.

3). HAL - (Hardware Acess Level , Hardware Abstraction Layer) - Еще одна общая библиотека для разработки. С которой вышла и программа CubeMX для конфигурации, которую мы с Вами использовали в прошлой статье. Там же мы написали программу мигания светодиодом используя данную библиотеку. Как видим на рисунке, ниже, куб генерирует драйвера HAL and CMSIS. Что ж опишем основные используемые файлы:
— system_stm32f3x.c и system_stm32f3x.h - предоставляют минимальные наборы функций для конфигурации системы тактирования;
— core_cm4.h – предоставляет доступ к регистрам ядра и его периферии;
— stm32f3x.h - заголовочный файл микроконтроллера;
— startup_system32f3x.s — стартовый код, содержит таблица векторов прерываний и др.

#include «main.h»
#include «stm32f3xx_hal.h»
void SystemClock_Config (void); /*Объявляем функции конфигурации тактирования*/
static void MX_GPIO_Init (void); /*Инициализация ввода/вывода*/
int main (void) {
/*Reset of all peripherals, Initializes the Flash interface and the Systick.*/
HAL_Init ();
/* Configure the system clock */
SystemClock_Config ();
/* Initialize all configured peripherals */
MX_GPIO_Init ();
while (1) {
HAL_GPIO_TogglePin (GPIOE, GPIO_PIN_8);//Переключаем состояние ножки
HAL_Delay (100); }
}
void SystemClock_Config (void) {
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig (&RCC_OscInitStruct) != HAL_OK) {

}
/**Initializes the CPU, AHB and APB busses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig (&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
_Error_Handler (__FILE__, __LINE__);
}
/**Configure the Systick interrupt time*/
HAL_SYSTICK_Config (HAL_RCC_GetHCLKFreq ()/1000);
/**Configure the Systick */
HAL_SYSTICK_CLKSourceConfig (SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority (SysTick_IRQn, 0, 0);
}
/** Configure pins as Analog Input Output EVENT_OUT EXTI */
static void MX_GPIO_Init (void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE ();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin (GPIOE, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configure GPIO pins: PE8 PE9 PE10 PE11 PE12 PE13 PE14 PE15 */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init (GPIOE, &GPIO_InitStruct);
}
void _Error_Handler (char * file, int line) {
while (1) {
} }
#ifdef USE_FULL_ASSERT

Void assert_failed (uint8_t* file, uint32_t line) {
}
#endif
Здесь также как и в предыдущем примере можем просмотреть описание каждой функции в документации, например UM1786 User Manual Description of STM32F3 HAL and low-layer drivers.

Можем подвести итог, что первый выриант, используя CMSIS, является менее громоздким. Для каждой библиотеки есть документация. В последующих проектах, мы будем использовать HAL и CMSIS, используя программу для конфигурации STCube и по возможности использовать регистры напрямую, без программных оберток. На этом сегодя и остановимся. В следующей статье мы с Вами рассмотрим основные принципы построения умного дома. Всем пока.

Список статей который поможет изучить микроконтроллер STM32 даже начинающему. Подробно обо всем с примерами начиная от мигания светодиодом до управления бесколлекторным двигателем. В примерах используется стандартная библиотека SPL (Standard Peripheral Library).

Тестовая плата STM32F103, ST-Link программатор, и программное обеспечение для прошивки под Windows и Ubuntu.

VIC (Nested vectored interrupt controller) – модуль контроля прерываний. Настройка и использование прерываний. Приоритеты прерываний. Вложенные прерывания.

АЦП (аналого-цифровой преобразователь). Схема питания и примеры использования АЦП в различных режимах. Regular и Injected каналы. Использование АЦП вместе с DMA. Внутренний термометр. Аналоговый watchdog.

Таймеры общего назначения. Генерирование прерывания через равные промежутки времени. Измерение времени между двумя событиями.

Захвата сигнала таймером на примере работы с ультразвуковым датчиком HC-SR04

Использование таймера для работы с энкодером.

Генерация ШИМ. Управление яркостью светодиода. Управление сервоприводом (сервомашинками). Генерация звука.