разработка и программирование электронных устройств

Быстрый старт c микроконтроллерами STM32F10x. Использование библиотеки STM32 Standard Peripheral Library

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

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

Общая структура проекта для ARM микроконтроллеров описана в моей статье «Программирование AVR и ARM микроконтроллеров в Eclipse. Часть 2».

Здесь я коротко напомню, что для сборки проекта для 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. Рассматривать ее будем на примере stm32f10xQuickstart.
Вы можете открыть этот пример или же создать свой проект «с нуля», чтобы лучше осознать весь процесс происходящего.

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

  • Создать в 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 микроконтроллеры серии STM32F100xx с объемом Flash-памяти 16 – 32кБ
Low density STM32F10X_LD микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx
с объемом Flash-памяти 16 – 32кБ
Medium density Value line STM32F10X_MD_VL микроконтроллеры серии STM32F100xx с объемом 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 микроконтроллеры серии STM32F101xx, STM32F103xx с объемом
Flash- памяти 256 — 512кБ
XL-density STM32F10X_XL микроконтроллеры серии STM32F101xx, STM32F103xx с объемом
Flash- памяти 512 — 1024кБ
Connectivity line STM32F10X_CL микроконтроллеры серии STM32F105xx и STM32F107xx

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

#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*/

Реализованная в вашем коде функция 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 */

Тут объяснять особо нечего. Лучше рассмотрим пример использования assert_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*/


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
с объемом Flash- памяти 64 – 128кБ
Medium-density startup_stm32f10x_md.s микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx
с объемом Flash- памяти 64 – 128кБ
High density Value line startup_stm32f10x_hd_vl.s микроконтроллеры серии STM32F100xx
с объемом Flash- памяти 256 — 512кБ
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_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


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


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

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)

По названиям макросов определяем какие модули к каким шинам подключены. Также для определения принадлежности к одной из трех шин можно воспользоваться здравым смыслом. Например, модуль 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)

Однако USART1 подключен к более высокоскоростной шине APB2 :

#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)

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

PPP_Init(PPP, &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;

Рассмотрим пример из проекта 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_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;

Структура 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;

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

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

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

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

CNF Mode Описание конфигурации вывода
00 вход (00) Вход с подтяжкой к питанию
00 выход(01 — 11) Аналоговый режим
01 вход(00) Плавающий вход
01 выход(01 -11) Выход с открытым стоком
10 вход(00) Вход с подтяжкой к питанию/земле
10 выход(01 — 11) Альтернативная функция вывода с подтяжкой к питанию
11 вход(00) Зарезервировано
11 выход(01 — 11) Альтернативная функция вывода с открытым стоком

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

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);

Аналогичным образом производится настройка и работа с остальными периферийными модулями. Однако существуют некоторые отличия в связи со спецификой конкретного аппаратного модуля, поэтому я настоятельно рекомендую сначала просмотреть примеры использования выбранного модуля для библиотеки 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 :

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

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

Название группы приоритетов Количество
приоритетов
Количество
субприоритетов
NVIC_PriorityGroup_0 0 16
NVIC_PriorityGroup_1 2 8
NVIC_PriorityGroup_2 4 4
NVIC_PriorityGroup_3 8 2
NVIC_PriorityGroup_4 16 0

Рассмотрим использование контроллера прерываний в примере stm32f10xQuickstart :

	NVIC_InitTypeDef NVIC_InitStructure;
 
	NVIC_InitStructure.NVIC_IRQChannel 	= TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 	= 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority	= 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd 		= ENABLE;
	NVIC_Init(&NVIC_InitStructure);

Функция назначения группы приоритетов в коде не используется, поэтому по-умолчанию будет использована группа NVIC_PriorityGroup_0 . Приоритеты всех маскируемых векторов прерываний будут одинаковыми. Это означает , что при одновременном возникновении нескольких прерываний последовательность их обработки будет определятся субприоритетами в единственной группе. Вложенные прерывания для векторов, которым можно назначить приоритет, будут недоступны, поскольку у всех у них будет одинаковый приоритет , равный 0. В этом случае обработчик маскируемого прерывания может прерваться только одним из трех исключений с неизменным значением приоритета (-3 … -1).
Чтобы разрешить/запретить прерывания в CMSIS для компилятора GNU C используются такие функции:

__enable_irq();
__disable_irq();

Для разрешения/запрещения прерываний и исключений используются другие функции:

__enable_fault_irq(); 
__disable_fault_irq();

В библиотеке STM32F10x SPL для реализации обработчиков прерываний программы принято использовать файлы stm32f10x_it.h и stm32f10x_it.с.
Названия функций – обработчиков прерываний определены в стартовом файле (в моем примере это startup_stm32f10x_md.s) . В стартовом файле для каждого прерывания назначен обработчик по-умолчанию, который можно заменить на свой в stm32f10x_it.с:

 .weak	NMI_Handler
	.thumb_set NMI_Handler,Default_Handler
 
  .weak	HardFault_Handler
	.thumb_set HardFault_Handler,Default_Handler
 
  .weak	MemManage_Handler
	.thumb_set MemManage_Handler,Default_Handler
 
  .weak	BusFault_Handler
	.thumb_set BusFault_Handler,Default_Handler

К примеру, если в stm32f10x_it.с определить обработчик HardFault_Handler, то будет использоваться именно этот обработчик при возникновении исключения HardFault, а не объявленный в стартовом файле.

//  stm32f10x_it.с
void HardFault_Handler() 
{
	debug_printf(“HardFault_Handler”);
	for(;;)
	;
}


Анализ содержимого Makefile.


Переменная SOURCES содержит список всех компилируемых файлов с различными расширениями ( c, cpp, S, s). Поскольку все эти файлы компилируются с различными опциями , то есть необходимость их разделить на С, С++ и ассемблерные исходники.
Для этого предназначены конструкции ifneq/endif . В результате переменная OBJECTS будет содержать список всех объектных файлов, которые потом нужно скомпоновать в исполняемый образ программы.
Дальше описаны правила компиляции различных типов файлов. Компиляция ассемблерных файлов представлена дважды, поскольку исходники на ассемблере могут иметь расширение *.S или *.s .
Директива

 include $(wildcard $(OUT_DIR)/*.d)

включает в мейкфайл зависимости (все файлы с расширением .d). Образуются эти файлы при компиляции исходных текстов программ .c и .cpp при помощи опции -MMD.
Эта опция gcc записывает зависимости объектного файла от исходных и заголовочных файлов в одноименный с компилируемым файл с расширением .d.
Особенность опции -MMD в том, что в зависимости включаются только заголовочные файлы , подключаемые с помощью директивы

#include “file.h”

То есть находящиеся в каталоге inc нашего проекта.
Для заголовочных файлов, включаемых с помощью следующей директивы, зависимости не генерируются :

Следовательно при изменении этих файлов автоматическая сборка проекта не будет запущена.
Список объектных файлов OBJECTS описан выше и запускает зависимости для выполнения компиляции автоматически (make умный, он может самостоятельно определить что нужно сделать для удовлетворения нужных зависимостей ).
Созданные hex и bin файлы не используются при программировании из Makefile запуском цели «Program», но вы всегда можете переписать опции программатора , использовать встроенный загрузчик или другой адаптер, имеющийся у вас в наличии . Тогда они могут пригодиться.

1 Comment to Быстрый старт c микроконтроллерами STM32F10x. Использование библиотеки STM32 Standard Peripheral Library

  1. Volldemar's Gravatar Volldemar
    22 октября 2013 at 21:34 | Permalink

    в файле core_cm3.c пришлось заменить
    __ASM volatile («strexb %0, %2, [%1]» : «=r» (result) : «r» (addr), «r» (value) );
    на
    __ASM volatile («strexb %0, %2, [%1]» : «=&r» (result) : «r» (addr), «r» (value) );
    Этот баг рассматривается тут:
    http://lists.gnu.org/archive/html/bug-binutils/2011-12/msg00127.html

Leave a Reply

You must be logged in to post a comment.