Библиотека CMSIS

Библиотека CMSIS включает в себя следующие компоненты:

Рассмотрим только CMSIS-CORE.

Библиотека состоит из стандартной (предоставляется ARM) и вендор-зависимой (предоставляется в нашем случае ST) частей.

Стандартная часть

Заголовочный файл core_<processor_unit>.h предоставляет интерфейс к ядру. Для stm32f103c8 это core_cm3.h, так как он работает на Cortex-M3. Для Cortex-M0+ это будет файл core_cm0plus.h.

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

Вам не нужно работать с регистрами ядра напрямую.

Другие файлы нам не столь интересны, но справедливости ради упомянем их. Например файл core_cmInstr.h содержит обертки инструкций, а core_cmFunc.h — обертки некоторых важных системных функций.

Если вы не разрабатываете приложение на самом низком уровне, то заглядывать в эти файлы незачем. Тем не менее, подробное описание работы ядра можно найти в документе ARM — Cortex-M3 Devices Generic User Guide, и мы им даже воспользуемся при настройке системного таймера.

Вендор-зависимая часть

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

В ассемблеровском файле startup_<device>.s (в нашем случае это startup_stm32f10x_md.s) реализуется функция обработчика сброса Reset_Handler. Он задает поведение МК при запуске, т.е. выполняет некоторые задачи до входа в функцию main(), в частности, вызывает функцию SystemInit() из файла system_<device>.c (system_stm32f10x.c). Также в нем задается таблица векторов прерываний (англ. interrupt vector table) с их названиями:

Заголовочный файл system_<device>.h (system_stm32f10x.h) предоставляет интерфейс двум функциям и глобальной переменной и отвечает за систему тактирования.

Обратите внимание

Меняя это число, вы не меняете тактовую частоту! Переменную SystemCoreClock стоит использовать только как индикатор. Более того, никто не гарантирует, что число, записанное в этой переменной, будет отображать реальную частоту: во-первых, оно может не обновиться после изменения регистров; во-вторых, оно никак не учитывает погрешность хода генератора; и в-третьих, стандартная частота (определенная как макрос HSE_VALUE в библиотеке) внешнего кварцевого генератора — 8 МГц, но никто не мешает разработчику поставить, скажем, кварц на 12 МГц.

И последний файл, самый важный для программиста, это драйвер микроконтроллера <device>.h (stm32f10x.h). Вся карта памяти микроконтроллера (о ней еще поговорим) записана там в виде макросов. Например, адрес начала регистров периферии, флеш и оперативной памяти:

Регистры модулей, таких как порты ввода-вывода, обернуты в структуры.

Вместо того, чтобы обращаться к ячейке по нужному адресу, это можно сделать через структуру.

Так как элементы в структуре расположены линейно, друг за другом, а длина регистра фиксирована (uint32_t, 4 байта), то регистр CRL хранится по адресу GPIOA_BASE, а следующий за ним CRH через четыре байта, по адресу GPIOA_BASE + 4. Ниже приведен пример настройки одной из ножек порта на выход. Вам этот код пока что ничего не скажет, но суть сейчас в другом — вам нужно увидеть пример использования библиотеки.

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

Мы рассмотрели, как эти операции работают, в разделе «Микроконтроллер под микроскопом». Т.е. код выше можно переписать так (и он будет более читаем):

Для всех стандартных типов (определенных в <stdint.h>) вводятся сокращенные синонимы, например:

Слово __IO не входит в стандарт языка, а переопределено в core_cm3.h:

Последнее, о чём нужно упомянуть, это перечисление IRQn_Type.

Оно устанавливает номер исключительной ситуации в соответствие с его названием. Когда вы вызываете функции NVIC_Enable() или NVIC_Disable(), в качестве параметра нужно использовать одно из имен в этом перечислении.


Назад | Оглавление | Дальше