Оглавление
Стандартная библиотека периферии
Тип переменных. Соответствуют подобной конструкции.
Что точно пригодится при работе.
Первым делом нужно скачать сам CMSIS архив STM32F4xx_DSP_StdPeriph_Lib с сайта st.com
Разбираемся на примере МК STM32F4хх, первоисточник — stm32f4xx_dsp_stdperiph_lib_um.chm, в составе набора
После того как разыщем и откроем stm32f4xx_dsp_stdperiph_lib_um.chm, ищем примеры идущие в составе библиотеки
Путь к примерам может быть STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Project\STM32F4xx_StdPeriph_Examples\
Совсем простые вещи вроде акронимов ADC и подобных или «сложные» блок схемы я не включил — смотреть в исходнике.
Своими словами.
CMSIS (Cortex Microcontroller Software Interface Standard) это стандарт по которому созданы
Удобные структуры для обращения к регистрам контроллера. Эти структуры уже «приклеены» к правильным адресам МК — т. е. полностью готовы к работе.
Функции позволяющие настраивать и управлять модулями через обращение к тем же структурам (а значит регистрам).
Функции общего назначения которые редко используют: математика и прочее.
В итоге охватывается и ядро и периферия через один общий подход настройки и использования, унификация это всегда хорошо ибо есть избавление от лишних сущностей. Зная один МК - мы знаем все.
Более детально перечисленные блоки смотреть в главе «Архитектура»
Как в руководстве.
DSP and SPL это завершенный пакет, содержащий драйверы устройств для всех стандартных периферийных узлов STM. Эта либа является пакетом который содержит набор процедур, структур данных и макросов покрывающих тему периферии. Включают описания и примеры по каждой периферии. Позволяет менее глубоко изучать описания железа, но применять их при этом. Эта либа так же содержит CMSIS DSP Software Library, содержащая общие функции Cortex-M камней. Вся либа написана исключительно на СИ.
DSP and SPL состоит из трех слоев:
Описание регистров.
Регистры с адресами и битами в них
Функции и структуры
Покрываю все функции обращений с периферийными регистрами
Примеры исходников.
Примеры сильно ускоряют разработку и продлевают жизнь.
PPP |
Пусть это будет обозначать что речь идет об узле периферии, например такой как ADC или любой другой |
stm32f4xx_ |
Все системные файлы исходников/заголовков начинаются с такой приставки |
Константы используемые в одном файле там же и определены. В нескольких — определяются в хедерах. Все константы пишутся верхними буквами (дефайны). Регистры в том же стиле. Тоже заглавные. Как правило совпадают с именами регистров в руководстве на МК. Шаблон для именования функций следующий[периферийный модуль аббревиатура]_[название функции]. |
|
PPP_SendData |
Функция работы с периферией. |
PPP_Init |
Инициализация периферийного устройства PPP в соответствии с указанными параметрами. |
PPP_DeInit |
Функции, используемые для сброса периферийных регистров PPP к их значениям по умолчанию. |
PPP_StructInit |
Используется для сброса значений модуля PPP. |
PPP_Cmd |
Включает/отключает периферию PPP. |
PPP_ITConfig |
Функции, используемые для настройки периферийной функции, всегда заканчиваются строкой ‘Config’, например PPP_DMAConfig, GPIO_PinRemapConfig |
PPP_GetFlagStatus |
Функции используемые для проверки состояния флагов регистров. |
PPP_ClearFlag |
Функции используемые для сброса состояния флагов регистров. |
PPP_GetITStatus |
Функции используемые для проверки состояния произошло ли прерывание. |
PPP_ClearITPendingBit |
Функции используемые для сброса бита прерывания который требуется чистить через ПО. |
Конкретные типы переменных уже определены с фиксированным типом и размером. Эти типы определены в файле stm32f4xx.h
Например:
typedef enum {
ERROR = 0,
SUCCESS = !ERROR
}
ErrorStatus;
Те самые пресловутые «регистры контроллера» - основные объекты с которыми программист имеет дело большую часть времени, когда работает с CMSIS.
Доступ к регистрам обеспечивается через указатель на структуру. Под каждый модуль (типа АЦП, USART и подобный) выделяется одна структура. Все такие структуры объявлены в одном файле — stm32f4xx.h.
Пример как это может выглядеть:
typedef struct
{
__IO uint16_t SR; //регистр статуса
uint16_t RESERVED0; /*!< Reserved, 0x02 */
__IO uint16_t DR; //регистр данных
uint16_t RESERVED1; /*!< Reserved, 0x06 */
__IO uint16_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
uint16_t RESERVED2; /*!< Reserved, 0x0A */
__IO uint16_t CR1; /*!< USART Control register 1, Address offset: 0x0C */
uint16_t RESERVED3; /*!< Reserved, 0x0E */
__IO uint16_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
uint16_t RESERVED4; /*!< Reserved, 0x12 */
__IO uint16_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
uint16_t RESERVED5; /*!< Reserved, 0x16 */
__IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
uint16_t RESERVED6; /*!< Reserved, 0x1A */
} USART_TypeDef;
В реальности это более развесистые «деревья» и очень хорошо что нам не пришлось их писать, а мы пришли «на готовенькое».
На этапе объявления этой структуры место в памяти под нее еще не выделено, а это значит что если мы сделаем объект типа USART_TypeDef и попробуем по-управлять юсартом, настроить передать принять данные — ничего из этого у нас не выйдет.
Реальная память МК с точки зрения периферии, разбита не на байты а на поля битов: по три, по четыре, по одному, и так в любых необходимых для модулей сочетаниях. Выше описанные структуры позволяют повторить «огибание этого рельефа» больших и малых областей битов и правильно работать с реальными регистрами которые есть по сути адреса и биты, использую удобные для человека псевдонимы.
Ниже приведен пример декларации позволяющий понять как структура обретает жизнеспособность.
#define PERIPH_BASE ((uint32_t)0x40000000) //Задаем адрес с которого начинается вся периферия
#define APB1PERIPH_BASE PERIPH_BASE //периферия это шина APB1 и связанные с ней устройства
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000) //но периферия это еще и шина APB2
...
/* SPI1 Base Address definition*/
#define USART1_BASE (APB2PERIPH_BASE + 0x3000) //а теперь конкретный модуль — его местоположение нам известно
...
/* SPI1 Peripheral declaration */
#define USART1 ((SPI_TypeDef *) USART1_BASE) //тут происходит оживление декларированной ранее структуры. Теперь воздействие на регистры будет иметь смысл
с этим можно работать, и снова это все сделано за нас — это часть CMSIS.
Пример применение:
USART1->CR1 = 0x0001;
Все биты периферийных регистров содержатся в в stm32f4xx.h и они определены как константы (дефайны). Формат названий таких констант следующий
PPP_[register_name]_[bit_name]
Для примера:
#define SPI_CR2_RXDMAEN ((uint8_t)0x01) /*!<Rx Buffer DMA Enable */
#define SPI_CR2_TXDMAEN ((uint8_t)0x02) /*!<Tx Buffer DMA Enable */
#define SPI_CR2_SSOE ((uint8_t)0x04) /*!<SS Output Enable */
#define SPI_CR2_ERRIE ((uint8_t)0x20) /*!<Error Interrupt Enable */
#define SPI_CR2_RXNEIE ((uint8_t)0x40) /*!<RX buffer Not Empty Interrupt Enable */
#define SPI_CR2_TXEIE ((uint8_t)0x80) /*!<Tx buffer Empty Interrupt Enable */
Как же происходит привязка?
Все периферийные регистры отображаются на некий регион памяти. Для использования регистров и их конкретных бит нужно использовать такую формулу.
bit_word_offset = (byte_offset x 32) + (bit_number × 4)
byte_offset - число байт до целевого регистра
bit_number - номер целевого бита
bit_word_offset - номер бита в регионе
bit_word_addr = bit_band_base + bit_word_offset
bit_word_addr - адрес регистра
bit_band_base - стартовый адрес региона содержащего регистр
Бит PLLON[24] в регистре RCC_CR
..
/*!< Peripheral base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000) //адрес — точка отсчета в области псевдонимов
...
/*!< Peripheral base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000) //адрес - точка отсчета в области битового диапазона
...
/* ------------ RCC registers bit address in the alias region ----------- */
#define RCC_OFFSET (RCC_BASE — PERIPH_BASE) //регистры модуля RCC в области псевдонимов
...
/* --- CR Register ---*/
/* Alias word address of PLLON bit */
#define CR_OFFSET (RCC_OFFSET + 0x00) //байтов до нужного нам регистров ноль — т. е. Он в самом начале
#define PLLON_BitNumber 0x18 //номер бита
#define CR_PLLON_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLLON_BitNumber * 4)) //итоговый адрес до байт:бит которым можно пользоваться
Заметим, что, опять же все это сделано за нас и это очень хорошо.
Здесь показан пример как поменять значение в подготовленном CR_PLLON_BB, что приведет к изменения поведения МК.
void RCC_PLLCmd(FunctionalState NewState)
{
*(__IO uint32_t *) CR_PLLON_BB = (uint32_t)NewState;
}
Тут мы видим пример применения константы бита регистра RCC_CR_PLLON для выставления/сброса одного из регистров. RCC_CR_PLLON это число в котором только нужный(ные) бит выставлены и когда мы делаем ИЛИ в итоговом регистре в этот бит записывается единичка.
void RCC_PLLCmd(FunctionalState NewState)
{
if (NewState != DISABLE)
{ /* Enable PLL */
RCC->CR |= RCC_CR_PLLON; //ИЛИ = выставляем единицу
}
else
{ /* Disable PLL */
RCC->CR &= ~RCC_CR_PLLON; /И = сбрасываем единицу — пишем ноль
}
}
Хорошая глава для тех кто окончательно хочет понять что есть что среди этих абстрактных или до конца не понятных акронимов SPL, SMSIS, DSP.
Библиотека построена на модульной модели программирования, обеспечивающей независимость между несколькими компонентами, создающими основное приложение, и позволяющей легко переносить на большой ассортимент продуктов, оценочные платы и даже использовать некоторые интегрированные компоненты прошивки для других приложений с минимальными изменениями в коде общих частей.
Application layer |
Приложение пользователя: примеры или пользовательский код. Прикладной уровень состоит из набора примеров, охватывающих все доступные периферийные устройства, с шаблонными проектами для наиболее распространенных инструментов разработки. При наличии соответствующей оценочной платы аппаратного обеспечения это позволяет начать работу с совершенно новым микропроцессором в течение нескольких часов |
||
BSP |
Обертки для работы с оценочными платами — боковая вспомогательная ветка библиотеки, в «боевом» применении не используется. BSP (Board Specific Package) - это пакет для конкретной платы (BSP), который реализует уровень абстракции для взаимодействия с ресурсами «человеческого» интерфейса; кнопки, светодиоды, ЖК-дисплей и COM-порты (USARTs), доступные на оценочных платах STMicroelectronics. Для управления этими различными ресурсами предоставляется общий API, который может быть легко адаптирован для поддержки любой другой платы разработки, просто адаптировав процедуру инициализации. |
||
HAL |
SPL Standard Peripherals driver |
Драйверы для узлов МК — работают через CMSIS Стандартный драйвер периферийных устройств STM32F4xx, который предоставляет драйверы и заголовочные файлы для всех периферийных устройств. Он использует уровень CMSIS для доступа к регистрам STM32F4xx. |
|
CMSIS |
CPAL Core Peripheral Access Layer. |
Регистры ядра: адрес — название — функции доступа. Базовый уровень периферийного доступа: содержит определения имен, определения адресов и вспомогательные функции для доступа к основным регистрам и периферийным устройствам. Он также определяет независимый от устройства интерфейс для ядер RTOS, который включает определения каналов отладки. |
|
CMSIS DSP DSP Driver Software Library |
Функции общего спектра. Библиотека программного обеспечения CMSIS DSP: содержит набор общих функций обработки сигналов для использования на устройствах на базе процессора Cortex-M. Библиотека полностью написана на C и полностью совместима с CMSIS. |
||
DPAL Device Peripheral Access Layer. |
Регистры периферии: адрес — название — функции доступа. Уровень доступа к периферийному устройству STM32F4xx: предоставляет определения для всех определений периферийного регистра, определения битов и сопоставления памяти для STM32YXXX камней. |
||
MCU |
Каждый периферийный узел имеет свой код в stm32f4xx_ppp.c и .h файлах, другими словами в stm32f4xx_ppp.c содержаться все функции требуемые для работы узла PPP.
Есть файл для отображения памяти, stm32f4xx.h, там вся периферия отображена. Он содержит все объявления регистров и определения битов. Это единственный файл, который необходимо включить в пользовательское приложение для взаимодействия с библиотекой.
stm32f4xx_conf.h используется для определения набора параметров интерфейса с библиотекой драйверов до запуска любого приложения.
Название файла |
Описание |
stm32f4xx_conf.h |
Конфигурационный файл периферийных драйверов. Пользователь может включит или отключить тут модули МК. |
stm32f4xx_ppp.h |
Заглавный файл драйвера модуля PPP. |
stm32f4xx_ppp.c |
Драйвер — все функции для работы PPP. |
stm32f4xx_it.h |
Тут все прототипы обработчиков прерываний для PPP. |
stm32f4xx_it.c |
Шаблоны функций обработчиков прерываний. Пользователь может добавить дополнительные обработчики. Для того чтобы узнать какие можно использовать нужно посмотреть в startup_stm32f4xx.s |
Название файла |
Описание |
stm32f4xx.h |
Файл является уникальным включаемым файлом, который программист приложения использует в исходном коде, обычно в main.c. Этот файл содержит:
|
system_stm32f4xx.h |
Системный заголовочный файл уровня периферийного доступа устройств CMSIS Cortex-M4F STM32F4xx. |
system_stm32f4xx.c |
Системный исходный файл уровня периферийного доступа устройств CMSIS Cortex-M4F STM32F4xx. |
startup_stm32f4xx.s |
Стартап файл.. |
Создать проект и настроить все start-up файлы набора (или использовать шаблонный проект где это уже есть)
Выбрать соответствующий startup файл зависящий от набора инструментов и MCU.
Например:- startup_stm32f40_41xxx.s, - startup_stm32f427_437xx.s, - startup_stm32f429_439xx.s
Стартап файлы доступны Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates
Точкой входа(крепления) библиотеки является файл stm32f4xx.h (under Libraries\CMSIS\Device\ST\STM32F4xx\Include), у пользователя есть возможность её подключения в приложении через main и её настройки таким образом:
Выбрать семейство MCU которое нужно использовать и раскоментить один из подобных дефайнов /* #define STM32F40_41xxx */
Затем нужно определить использовать или нет драйверы периферии.
Вариант 1. Код приложения основан на API стандартного драйвера периферии (исходники в Libraries\STM32F4xx_StdPeriph_Driver)
Раскоментить #define USE_PERIPH_LIBRARY
В stm32f4xx_conf.h выбрать периферию для включения.
Используя API драйверов периферии для создания приложения.(следующая глава)
В дополнение к периферийному драйверу вы можете использовать богатый набор настроек доступный в библиотеке для ускорения разработки.
Вариант 2. Код приложения основан на прямом доступе к регистрам периферии.
Закоментить #define USE_PERIPH_LIBRARY
Использовать структуры периферийных регистров и определения битов внутри stm32f4xx.h для создания приложения.
Добавить system_stm32f4xx.c (в Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates) в свое приложение, он обеспечит поддержку настроек MCU: настройка PLL, системных часов и инициализацию интерфейса работы с флешем. Этот файл позволяет настроить разные частоты тактирования, например как следуюем блоке:
#define PLL_M (HSE_VALUE / 1000000) /* Possible value 0 and 63 */
#define PLL_N 336 /* Possible value 192 and 432 */
/* SYSCLK = PLLVCO / PLL_P !!!! DO NOT EXCEED 168 MHz */
#define PLL_P 2 /* Possible value 2, 4, 6, or 8 */
/* OTGFS, SDIO and RNG Clock = PLLVCO / PLLQ */
#define PLL_Q 7 /* Possible value between 4 and 15 */
/* ex. to have SYSCLK @ 168 MHz
SYSCLK = PLLVCO / PLL_P
= ((HSE_VALUE / PLL_M) * PLL_N) / PLL_P
= ((HSE_VALUE / PLL_M) * PLL_N) / PLL_P
= ((25 MHz / 25) * 336 ) / 2 = 168 MHz
*/
Здесь описан пошаговый скрипт инициализации и настройки использования периферийных драйверов. Модуль периферии будет указан как PPP.
В main.c задать структуру PPP_InitTypeDef. Например PPP_InitTypeDef PPP_InitStructure. Рабочей переменной будет PPP_InitStructure, через нее нам будет доступна иниция PPP.
Заполнить PPP_InitStructure нужными значениями. Это можно сделать двумя способами:
Вариант 1. Заполнить всю структуру
PPP_InitStructure.member1 = val1;
PPP_InitStructure.member2 = val2;
PPP_InitStructure.memberN = valN; /* where N is the number of the structure members */
заполнять кстати можно и так PPP_InitTypeDef PPP_InitStructure = { val1, val2,.., valN}
Вариант 2. Заполнить часть структуры.
В этом случае предварительно нужно вызвать PPP_StructInit() которая заполнит ВСЕ поля значениями по умолчанию (правда в руководстве говорится «почти все»), а уже потом поправить под себя что нужно.
PPP_StructInit(&PPP_InitStructure);
PP_InitStructure.memberX = valX;
PPP_InitStructure.memberY = valY; /*where X and Y are the members the user wants to configure*/
Проинициализировать периферию PPP вызовом PPP_Init()
PPP_Init(PPP, &PPP_InitStructure);
На этом шаге периферия настроена и можно её включить вызовом PPP_Cmd(..)
PPP_Cmd(PPP, ENABLE);
Отметим:
До конфигурирования периферии, тактирование относящееся к ней должно быть включено подобными функциями:
RCC_AHBxPeriphClockCmd(RCC_AHBxPeriph_PPPx, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_PPPx, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PPPx, ENABLE);
PPP_DeInit(..) может быть использована для сброса всех регистров модуля PPP к значениям по умолчанию
PPP_DeInit(PPP);
Для изменения настроек после конфигурирования периферии, можно еще повторно изменить настойки таким образом:
PPP_InitStucture.memberX = valX;
PPP_InitStructure.memberY = valY; /* where X and Y are the only members that user wants to modify*/
PPP_Init(PPP, &PPP_InitStructure);
Скопировать файлы
Файлы из проекта
main.h (если есть)
main.c
stm32f4xx_it.h
stm32f4xx_it.c
system_stm32f4xx.c
platform_config.h (если есть)
stm32f4xx_conf.h
и любые другие исходные файлы из папок урока содержащихся в папку созданного тулчейна Project\STM32F4xx_StdPeriph_Templates. В роекте уже содержаться папки набора инсртументов для создания проекта для stm.
Открыть предпочитаемый тулчейн.
Если нужно, добавить в в проект дополнительные требуемые для запуска примера.
Собрать проект, загрузить проект, запустить проект.
В папке STM32F4xx_StdPeriph_Templates есть пример проекта под Keil и Atollic.
v5.