Наш канал в telegram

Программирование ARM-контроллеров STM32 на ядре Cortex-M3. Часть 11. Подключение и использование драйверов из пакета StdPeriph

Подключение драйверов стандартной периферии из пакета StdPeriph

В прошлый раз мы познакомились со стандартом CMSIS, а также научились подключать поставляемые вместе с пакетом Keil uVision библиотеки. Сегодня мы продолжим изучение стандарта CMSIS и рассмотрим пакет StdPeriph Drivers — драйвера стандартной периферии.

Эти драйвера представляют собой написанные в соответствии со стандартом CMSIS библиотеки высокоуровневых функций для работы с различной периферией контроллера (USART, GPIO, I2C, SPI и так далее). Фактически это фрейворк, позволяющий значительно упростить процесс программирования за счёт уже готовых функций, написанных другими программистами и решающих различные типовые задачи. Взамен, естественно (как и в случае с любыми другими фрейворками), от вас потребуется детальное изучение этих функций, — как, когда и с какими параметрами их можно вызывать, что они вернут вам в ответ и так далее.

Сразу хочу заметить, что imho изучать контроллер нужно начинать с ассемблера, потом переходить на Си, и только когда всё это уже освоено и есть чёткое понимание того, как оно работает, можно переходить к готовым фреймворкам. Мы с вами, однако, всё это уже проходили, и наше сознание от использования фрейворков сильно испортиться не должно. Так что, начнём.

  • Итак, запускаем Keil uVision и выбираем в меню пункт «Project -> New uVision Project…»
  • В открывшемся окне «Create New Project» выбираем, в какой папке будет располагаться наш новый проект, придумываем ему имя и жмём кнопку «Сохранить».
  • Далее открывается окно «Select Device for Target ‘Target1’…», в котором нужно выбрать свой чип (в моём случае выбор такой: «STMicroelectronics -> STM32F103 -> STM32F103C8») и нажать кнопку «OK».
  • После этого откроется окно «Manage Run-Time Enviroment», в котором как раз и определяется какие библиотеки будут добавлены в ваш проект.
  • В списке слева выбираем пункт «Device», щёлкаем по нему мышкой и видим в раскрывшемся списке уже знакомые нам по прошлой статье библиотеки «Startup», «GPIO» и «DMA», а также искомый пакет «StdPeriph Drivers».
  • Щёлкаем по нему и в раскрывшемся списке видим список драйверов этого пакета.
Нажми чтобы увидеть картинку

Подключение к программе для STM32 драйверов пакета StdPeriph

[свернуть]

Драйверов в этом пакете довольно много (собственно говоря, для каждого модуля контроллера есть свой драйвер). Для того чтобы подключить какой-либо из драйверов к проекту — нужно просто поставить галочку в чекбоксе рядом с именем. Если подключаемому драйверу будет не хватать для работы каких-либо компонентов — Keil честно скажет об этом в нижней части окна. Не хотите читать описание и разбираться, что ещё нужно подключить? Да пожалуйста! Просто нажмите кнопку Resolve в нижнем левом углу и все недостающие компоненты будут добавлены в проект автоматически.

Для примера я выбрал драйвера RCC и GPIO. Keil подсказал, что для их работы нужно подключить к проекту компоненты «CMSIS -> CORE», «Device -> Startup» и «Device -> StdPeriph Drivers -> Framework». Жмём «Resolve» для автоматического подключения всего, чего не хватает и далее жмём кнопку «OK».

Всё, проект создался и мы видим, что в его составе (вверху, слева) помимо уже рассмотренных нами в предыдущей части файлов (RTE_Device.h, startup_stm32f10x_md.s, system_stm32f10x.c) присутствуют 4 новых файла: 3 сишных библиотеки (misc.c, stm32f10x_gpio.c, stm32f10x_rcc.c) и один файл хидеров (stm32f10x_conf.h). Это и есть библиотеки, содержащие высокоуровневые функции для работы с выбранной нами периферией.

Нажми чтобы увидеть картинку

Состав проекта с подключенными драйверами из пакета StdPeriph

[свернуть]

Подключить-то мы эти драйвера подключили, но как теперь ими пользоваться? Открывать что ли каждую библиотеку и смотреть, какие она предоставляет функции?

Что ж, можно конечно и так, но это путь для гурманов. На самом деле рано или поздно вам этого всё равно не избежать, но прийти к такому уровню дзен нужно постепенно, поэтому мы начнём с хелпа. И тут главный вопрос — где этот хелп взять?

Давайте посмотрим ещё раз на самую первую картинку (на которой мы выбирали, какие компоненты StdPeriph нужно включить в проект). Можно увидеть, что из всех описаний драйверов пакета StdPeriph (столбец Description), только к описанию компонента «Framework» привязана ссылка. Однако, щёлкнув по этой ссылке мы откроем систему помощи, содержащую детальное описание абсолютно всех драйверов пакета StdPeriph (а не только драйвера framework, как можно было бы подумать)

Нажми чтобы увидеть картинку

вызов системы помощи по StdPeriph

[свернуть]

В этом хелповнике перечислены экспортируемые каждым драйвером функции, используемые константы и структуры, в общем, всё-всё-всё. Там есть даже примеры использования всех этих драйверов. У всего этого великолепия есть только один минус — описание существует только на английском языке. Ну, тут уж, как говорится, чем богаты.

Нажми чтобы увидеть картинку

система помощи по StdPeriph

[свернуть]

Ещё один момент, — как открыть хелповник по StdPeriph после того, как проект уже создан? Для этого нужно слева в менеджере проекта ткнуть правой кнопкой по пакету Device (или по любому из его компонентов), выбрать во всплывающем меню пункт «Options for Component Class ‘Device'», далее, в открывшемся окне, выбрать компонент «Framework», после чего в верхней части этого окна появится описание компонента «Framework» с заветной ссылкой для вызова системы помощи по библиотекам StdPeriph.

Нажми чтобы увидеть картинку

ссылка для вызова помощи по StdPeriph

[свернуть]

Пример использования драйверов для работы с RCC и GPIO.

Ну а теперь, в качестве примера, давайте перепишем нашу любимую программу переключения состояния пина PA8 в зависимости от состояния пина PA9, но уже с использованием функций, предоставляемых драйверами RCC и GPIO из пакета StdPeriph.

Выше мы уже создали в отдельной папке новый проект, включили в его состав драйвера RCC и GPIO из пакета StdPeriph, плюс сам Keil подключил нам дополнительно компоненты «CMSIS -> Core», «Device -> Startup» и «Device -> StdPeriph Drivers -> Framework».

Далее в менеджере проекта нужно добавить в созданную по умолчанию группу «Source Group 1» файл main.c, в который будем писать код нашей программы. Тем, кто забыл, напомню, что для этого нужно ткнуть правой кнопкой по группе ‘Source Group 1’, во всплывающем меню выбрать пункт «Add New Item to Group ‘Source Group 1’…», далее в открывшемся окне выбрать тип добавляемого файла «C file (.C)», ввести для нового файла имя и нажать кнопку «Add».

Текст программы

#include "stm32f10x.h" /* стандартные хидеры */
 
/* сначала всякие переменные (ну а как вы хотели с языками высокого уровня) */
volatile uint8_t Temp;			/* сюда будем читать значение пина 9 */
GPIO_InitTypeDef PORT_Structure;	/* структура (массив данных) для настройки линий порта */
 
int main(void) {
 
Port_Init:
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	/* включаем тактирование PORTA */
 
	PORT_Structure.GPIO_Pin = GPIO_Pin_8;			/* будем настраивать 8-й пин PORTA */
	PORT_Structure.GPIO_Speed = GPIO_Speed_50MHz;		/* максимальная скорость 50 МГц */
	PORT_Structure.GPIO_Mode = GPIO_Mode_Out_PP;		/* режим push-pull */
	/* выполняем настройку битов в регистре GPIOA->CRH в соответствии с заполненной структурой */
	GPIO_Init(GPIOA, &PORT_Structure);
 
	PORT_Structure.GPIO_Pin = GPIO_Pin_9;			/* будем настраивать 9-й пин PORTA */
	/* параметр GPIO_Speed для входа неважен, но что-то выбрать нужно */
	PORT_Structure.GPIO_Speed = GPIO_Speed_50MHz;		/* максимальная скорость 50 МГц */
	PORT_Structure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	/* режим input-floating */
	/* выполняем настройку битов в регистре GPIOA->CRH в соответствии с заполненной структурой */
	GPIO_Init(GPIOA, &PORT_Structure);
 
Work:
	Temp = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9);	/* Читаем значение пина 9 в переменную Temp */
	if(Temp!=0)						/* если PA9 установлен */
	{	GPIO_SetBits(GPIOA, GPIO_Pin_8);	/* устанавливаем пин PA8 */
	}
	else
	{	GPIO_ResetBits(GPIOA, GPIO_Pin_8);	/* сбрасываем пин PA8 */
	}
	goto Work;
}

[свернуть]
То же самое, но с прерываниями

#include "stm32f10x.h" /* стандартные хидеры */
 
/* сначала всякие переменные (ну а как вы хотели с языками высокого уровня) */
GPIO_InitTypeDef PORT_Structure;	/* структура для настройки линий порта */
EXTI_InitTypeDef EXTI_Structure;	/* структура для настройки внешних прерываний (EXTI) */
NVIC_InitTypeDef NVIC_Structure;	/* структура для настройки NVIC */
 
int main(void) {
 
Port_Init:
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	/* включаем тактирование PORTA */
 
	PORT_Structure.GPIO_Pin = GPIO_Pin_8;			/* будем настраивать 8-й пин PORTA */
	PORT_Structure.GPIO_Speed = GPIO_Speed_50MHz;		/* максимальная скорость 50 МГц () */
	PORT_Structure.GPIO_Mode = GPIO_Mode_Out_PP;		/* режим push-pull */
	GPIO_Init(GPIOA, &PORT_Structure);			/* выполняем настройку */
 
	PORT_Structure.GPIO_Pin = GPIO_Pin_9;			/* будем настраивать 9-й пин PORTA */
	PORT_Structure.GPIO_Speed = GPIO_Speed_50MHz;		/* максимальная скорость 50 МГц */
	PORT_Structure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	/* режим input-floating */
	GPIO_Init(GPIOA, &PORT_Structure);			/* выполняем настройку */
 
IRQ_Init:
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);	/* включаем тактирование AFIO */
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource9);	/* PA9 - источник прерываний для EXTI9 */
 
	EXTI_Structure.EXTI_Line = EXTI_Line9;		/* будем настраивать линию EXTI9 */
	EXTI_Structure.EXTI_LineCmd = ENABLE;		/* включить */
	EXTI_Structure.EXTI_Mode = EXTI_Mode_Interrupt;	/* режим генерации прерываний */
	EXTI_Structure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;	/* сработка по переднему и заднему фронту */
	EXTI_Init(&EXTI_Structure);					/* выполняем настройку */
 
	NVIC_Structure.NVIC_IRQChannel = EXTI9_5_IRQn;	/* будем настраивать прерывание от EXTI9_5 */
	NVIC_Structure.NVIC_IRQChannelCmd = ENABLE;	/* включить */
	NVIC_Structure.NVIC_IRQChannelPreemptionPriority = 0x0F;	/* низший приоритет (16) */
	NVIC_Structure.NVIC_IRQChannelSubPriority = 0x0F;		/* низший субприоритет (16) */
	NVIC_Init(&NVIC_Structure);			/* выполняем настройку */
 
	__enable_irq();					/* Разрешаем все немаскированные прерывания */
 
Work:
	goto Work;
}
 
/* код обработчика прерывания от пина PA9 */
void EXTI9_5_IRQHandler(void) {
	/* сразу сбрасываем pending bit линии 9 (EXTI9), чтобы не пропустить очередную сработку */
	EXTI_ClearITPendingBit(EXTI_Line9);
 
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) != Bit_RESET)
	{	/* переключаем выход PORTA8 в единицу */
		GPIO_SetBits(GPIOA, GPIO_Pin_8);	/* установить bit8 в регистре GPIOA_BSRR */
	}
	else
	{	/* переключаем выход PORTA8 в ноль */
		GPIO_ResetBits(GPIOA, GPIO_Pin_8);	/* установить bit8 в регистре GPIOA_BRR */
	}
}

[свернуть]

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

Кстати, на нашем сайте тоже можно найти список и небольшое описание функций библиотеки StdPeriph, — оно находится в приложении 3 (ссылка внизу). Пока там не всё, но я над этим работаю, постепенно будет всё.

  1. Часть 1. Установка MDK, создание проекта, основы Keil uVision
  2. Часть 2. Команды и директивы ассемблера, структура и синтаксис программы. Первая программа для STM32
  3. Часть 3. Карта памяти контроллеров STM32, методы работы с памятью
  4. Часть 4. Регистры, старт и режимы работы контроллеров STM32
  5. Часть 5. Как залить прошивку в контроллер
  6. Часть 6. Настройка системы тактирования
  7. Часть 7. Работа с портами ввода-вывода
  8. Часть 8. Процедуры на ассемблере для STM32
  9. Часть 9. Система прерываний
  10. Часть 10. CMSIS, использование стандартных библиотек и функций
  11. Часть 11. Подключение и использование драйверов из пакета StdPeriph
  12. Приложение 1. Набор инструкций THUMB-2 и особенности их использования
  13. Приложение 2. Таблица векторов прерываний для семейств STM32F101, STM32F102, STM32F103
  14. Приложение 3. Драйвера и функции библиотеки StdPeriph

Добавить комментарий