Эта статья начинает курс по практической инициализации периферии платы разработчика STM32F401CCU6 Black Pill с использованием библиотеки CMSIS.
Прикупил я тут на Али обновленные платы разработчика STM32 – так называемая Black Pill. В приобретённой мною версии платы использован микроконтроллер STM32F401CCU6 в корпусе UQFN48. Поскольку данный микроконтроллер построен на ядре Cortex M4, то полноценной информации по его инициализации на регистрах не так много в русскоязычном интернете. Вот я и решил «заполнить» данный пробел.
Но сразу оговорюсь, что излагать материал я буду с практической точки зрения, чтобы читатель мог параллельно с чтением прошить свой микроконтроллер и запустить излагаемый в статье аппаратный модуль, либо найти решение возникших проблем при инициализации того или иного модуля.
Для программирования будем использовать DataSheet и Reference Manual, а производить инициализацию c использованием библиотеки CMSIS. Не признаю я разные HAL и SPL, а уж тем более CubeMX с его перегруженными комментариями портянками кода.
Прежде всего стоит разобраться на какой частоте происходит тактирование ядра. В этом нам поможет альтернативная функция MCO (Master Clock Output). Её основное предназначение – подавать тактовый сигнал на внешние физические устройства, используя один из выбранных источников тактирования микроконтроллера. Но мы будем использовать её в связке с осциллографом в качестве индикатора тактового сигнала.
Стоит заметить, что режимов MCO для нашего микроконтроллера бывает два (MCO1 и MCO2), но только для корпусов LQFP64, LQFP100, UFBGA100.
У нас же на платке Black Pill распаян микроконтроллер в корпусе UQFN48, поэтому будем довольствоваться одним режимом — MCO1(вывод PA8), который поддерживает вывод сигналов тактирования LSE(внешний кварц для системы RTC), HSE(внешний кварц для ядра), HSI(внутренняя RC-цепочка), PLLCLK(выход системы ФАПЧ).
Интересующий вывод GPIO мы выяснили, следует определить порядок настройки регистров GPIO для вывода PA8, для активации альтернативной функции MCO1. Открываем в Reference Manual таблицу с конфигурацией регистров GPIO для различных режимов работы.
Напишем код инициализации в соответствии с таблицей, не забыв, предварительно, включить тактирование порта A:
void vInitGPIO_MCO1(void)
{
//enable clock for GPIOA
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
//PA8 -> MCO(AF) -> PLLCLK
GPIOA->MODER |= GPIO_MODER_MODER8_1; //AF
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_8; //PP
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR8_0; //PU
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8; //High speed
...
}
Осталось настроить регистр выбора альтернативной функции. Новичку на данном этапе будет просто запутаться, поскольку регистров два (старший и младший), а обозначены они в CMSIS как элементы массива. В самом регистре имеются битовые поля по 4 бита на каждый пин, таким образом на один регистр приходится по 8 выводов. Мы настраиваем PA8, следовательно нам необходимо нулевое битовое поле старшего регистра выбора альтернативных функций – AFRH. В Reference Manual всё расписано по-человечьи, чего не скажешь о CMSIS — там битовое поле для PA8 обозначено как AFRH0.
Выясним, какой номер имеет альтернативная функция MCO. На перспективу, очень удобно пользоваться сводной таблицей по всем альтернативным функциям, которая находится в DataSheet:
Для удобства я обычно создаю define с названием номера альтернативной функции в заголовочном файле:
#define GPIO_AF_MCO 0x00UL
Прописываем номер альтернативной функции в битовом поле AFRH0:
//AF0->MCO
GPIOA->AFR[1] |= (GPIO_AF_MCO << GPIO_AFRH_AFSEL0_Pos);
Теперь выбираем источник тактирования – HSI. По умолчанию контроллер тактируется от внутреннего RC-генератора, поэтому мы выбираем именно него. Остальные источники тактирования по просту не инициализированы. За выбор источника отвечает регистр RCC_CFGR:
Назначаем источник тактирования для альтернативной функции MCO1:
RCC->CFGR &= ~RCC_CFGR_MCO1; //HSI
Для наглядности приведу окончательную функцию инициализации, которая у нас с вами получилась:
void vInitGPIO_MCO1(void)
{
//enable clock for GPIOA
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
//PA8 -> MCO(AF) -> PLLCLK
GPIOA->MODER |= GPIO_MODER_MODER8_1; //AF
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_8; //PP
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR8_0; //PU
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8; //High speed
//AF0->MCO
GPIOA->AFR[1] |= (GPIO_AF_MCO << GPIO_AFRH_AFSEL0_Pos);
RCC->CFGR &= ~RCC_CFGR_MCO1; //HSI
}
После прошивки микроконтроллера встаём щупом осциллографа на вывод PA8 и видим наш сигнал тактирования от источника HSI.
Частота у микроконтроллера без разгона (из коробки), как видно на осциллограмме составляет 15.946 МГц, что действительно соответствует внутреннему RC-генератору. В следующий раз мы подключимся к внешнему кварцу 25 МГц, распаянному на плате и выставим максимальную номинальную частоту тактирования для STM32F401CCU6 – 84 МГц, используя систему ФАПЧ.
Буду очень рад, если вы оставите комментарии к этой статье с интересующими вопросами и конструктивными советами по улучшению информативности по данной теме.