Изучая возможности библиотеки LL и её умение работать на уровне регистров контроллера, мы начали больше понимать в аппаратной организации микроконтроллеров STM.
Теперь нам предстоит ещё более углубленное изучение данных контроллеров. В этом нам поможет другая библиотека — CMSIS.
CMSIS (Cortex Microcontroller Software Interface Standard) — библиотека стандарта программного обеспечения микроконтроллеров Cortex, являющаяся независимым от производителя уровнем аппаратной абстракции для серии процессоров Cortex®-M и определяет общие интерфейсы инструментов.
К сожалению, пользуясь данной библиотекой, мы не сможем генерировать проекты с помощью проектогенератора Cube MX, и инициализация всякого рода периферии ляжет уже на наши плечи, но, с другой стороны, это даёт больше свободы мыслям программиста.
Что же подтолкнуло меня на такой, так сказать, подвиг — на использование библиотеки CMSIS. Подтолкнуло многое. Особенно то, что я хотел создать урок по RCC на LL и причём всё у меня получилось, но только автоинициализация, которая генерируется Cube MX, сбрасывает BACKUP регистры причём даже до инициализации самой RTC, что делает не совсем удобным их использование, а именно для использования автономного литиевого источника питания. Можно, конечно, посредством оператора goto исходя из ряда условий обойти автоинициализацию, но, думаю, не только я не люблю костыли.
Вторая мотивация — это то, что очень уж хочется подобраться поближе к регистрам, к из настройкам, а затем уж к самому ядру ARM. Вообще-то, я уже давно к нему подобрался, разработан ряд проектов на ассемблере, думаю, и до него дойдём.
Сегодня мы напишем простой проект, с помощью которого мы «заставим» контроллер помигать 10-ю светодиодами, подключенными к его портам (конечно же с токоограничивающими резисторами).
Контроллер будем по-прежнему использовать самый простой и дешёвый — STM32F103, расположенный на недорогой отладочной плате. Мало того, схему мы тоже не трогаем. Оставим ту же светодиодную планку, подключенную к тем же ножкам, как и при использовании в уроке 144, в котором мы начали знакомство с режимами пониженного энергопотребления. Правда, никакой милиамперметр мы в разрыв подключать не будем и стандартно подключим наш дешёвый программатор к четырём ножкам платы

Прежде чем приступить к написанию проекта, нам нужно будет познакомиться с некоторой аппаратной организацией. Хотя мы это и так делаем постоянно, но ещё много того, что мы не знаем.
Как известно, чтобы любая периферия заработала, нужно первым делом включить ей тактирование. Данная задача решается путём включения определённых битов регистра, отвечающего за ту шину, к которой подключена та или иная периферия. Вообще тактированием занимается модуль RCC (Reset & clock control). У данного модуля существует много регистров, но нам нужны именно те, биты которых будут включать тактирование нужных нам видов периферии.
Для начала посмотрим краткую схему архитектуры контроллера, на которой очень хорошо видно, какие виды периферии к какой шине подключены

Первым делом в нашем проекте мы будем настраивать работу с интерфейсом SWD, посредством которого мы программируем и отлаживаем наш контроллер. Управление SWD происходит в регистре AFIO_MAPR, до которого мы доберёмся чуть позже, следовательно, нам надо включить тактирование AFIO (alternate-function I/O). Так как он подключен к шине APB2, то включением его тактирования занимается вот этот регистр

Название каждого бита в таких регистрах говорящее. Каждый из них включает, ту периферию, сокращённое название (аббревиатура) которой присутствует в его названии. Поэтому AFIO включит соответствующий бит данного регистра — AFIOEN

Аналогично в проекте данного урока мы задействуем тактирование портов GPIOA и GPIOB установкой соответствующих битов в данном регистре

Теперь посмотрим, собственно, регистр, с помощью определённых битов которого мы задействуем SWD — AFIO_MAPR

Этот регистр занимается переназначением ножек портов, задействованных для тех или иных видов периферии, а также конфигурирует отладочный последовательный порт. Нам нужно будет его второе назначение. За это отвечает данное битовое поле
SWJ_CFG[2:0] (Serial wire JTAG configuration): битовое поле настройки отладочного последовательного порта. Биты данного поля работают только на запись, считывать мы их не можем. Они используются для настройки SWJ и отслеживания альтернативных функций ввода / вывода. SWJ (Serial Wire JTAG) поддерживает доступ JTAG или SWD к порту отладки Cortex®. Состояние по умолчанию после сброса — SWJ ON без трассировки.
Имеют место вот такие комбинации данных настроек
000: Полный SWJ (JTAG-DP + SW-DP) с ножкой RESET
001: Полный SWJ (JTAG-DP + SW-DP), но без NJTRST
010: JTAG-DP отключен, SW-DP включен.
100: JTAG-DP отключен и SW-DP отключен.
Нас с нашим программатором будет интересовать 3 вариант с отключенным JTAG.
Так как мы будем работать сегодня с ножками портов ввода вывода, то нам интересно также будет изучить регистры контроллера, которые отвечают за настройку и управление текущим состоянием данной периферии. Хоть мы уже не раз использовали биты настройки портов, но мы никогда полностью данные регистры не изучали. Поэтому давайте восполним данный пробел.
Для каждого порта ввода-вывода, имеющегося в контроллере, существует по 7 регистров.
Первые два регистра являются частями регистровой пары настройки ножек портов. Так как ножек у портов всего 16 и для каждой ножки отведено по 4 бита для настройки, то получается, что нам требуется всего 64 бита, вот и получилось 2 регистра — младший и старший.
Первый регистр — младший, который отвечает за настройку ножек портов от 0 до 7

Второй регистр — старший, который отвечает за настройку ножек портов от 8 до 15

Теперь рассмотрим назначение битов битовых полей данных регистров для отдельно взятой ножки
CNFy[1:0] (Port x configuration bits): битовое поле настройки ножки y порта x.
В случае использования ножки на вход:
00: Аналоговый режим
01: Плавающий вход (состояние после сброса) без подтяжки
10: Вход с подтягиванием резистора или к плюсу или к общей шине (завит от состояния бита в регистре ODR)
11: не используется
В случае использования ножки на выход:
00: Обычный двухтактный выход
01: Выход с открытым коллектором
10: Альтернативная функция, двухтактный выход
11: Альтернативная функция, выход с открытым коллектором.
MODEy[1:0] (Port x mode bits): битовое поле настройки режима работы ножки y порта x.
00: Режим входа (состояние после сброса)
01: Режим выхода, максимальная скорость 10 MHz.
10: Режим выхода, максимальная скорость 2 MHz.
11: Режим выхода, максимальная скорость 50 MHz.
Следующий регистр — регистр входных данных

Каждый бит данного регистра содержит состояние соответствующей ножки порта. Доступен только для чтения.
Следующий регистр — регистр выходных данных

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

Данный регистр интересен тем, что мы можем сразу устанавливать или сбрасывать отдельные ножки порта в одну операцию. С помощью регистра ODR мы сначала его полностью читаем, если нам нужно не тронуть уровень других ножек, затем устанавливаем бит в считанном слове, а затем результат обратно записываем в регистр.
Старшая половина регистра отвечает за сброс ножек, младшая — за установку. Ноль в записываемом значении регистра не имеет эффекта, поэтому уровни ножек, биты которых установлены в ноль не тронутся, а значения ножек, биты которых установлены в 1 либо сбросятся либо установятся в зависимости от того в каком полуслове они будут находиться.
Другими словами, битовое поле BRy, где y — номер ножки порта, работает следующим образом
0 — нет эффекта
1 — сброс ножки порта в низкий уровень.
А битовое поле BSy, где также y — номер ножки порта, работает следующим образом
0 — нет эффекта
1 — установка на ножке порта высокого уровня.
Следующий регистр — регистр сброса ножек порта

Данный регистр работает аналогично предыдущему, но здесь только биты сброса ножек.
Следующий регистр — регистр защиты конфигурации порта

Биты данного регистра предназначены для защиты ножек портов от изменения. Если мы хотим защитить какие-то ножки от записи, то мы записываем в соответствующие им биты данного регистра, находящиеся в младшем его полуслове, единицы, остальные биты оставляем в нулях. Затем при помощи специальной комбинации над битом 16 — LCKK мы производим применение блокировки. Данная последовательность следующая: записываем в данный бит 1, затем 0, затем опять 1, затем данный бит считываем дважды, первый раз должен считаться 1, второй — 0. Если всё так, то значит мы защитили нужные ножки.
Также стоит сказать, что у микроконтроллеров высших моделей (F4, F7 и т.д.) регистры для настройки портов другие. Мы помним наши первые уроки по STM32, так как в них мы работали с CMSIS, но использовали мы контроллер STM32F407VG, установленный на плате Discovery 4, там регистры были другие. Об этом надо помнить, то есть переносимость кода на CMSIS на другие контроллеры не всегда имеет место. Также мы перенастраивали порты, когда работали с датчиком DS18B20 уже на контроллере F103.
Пока всё по аппаратной части.
Можно приступить к проекту, который мы, конечно же, не будем преобразовывать из первых уроков, а создадим с нуля.
Cube MX мы никакой не запускаем, запускаем сразу Keil и создаём в нём новый проект. Как это делать, мы знаем, но в видеоверсии я это всё равно покажу.
Проект создадим с именем BLINK01_CMSIS, также назовём и папку, в котором его создадим, выберем соответствующий контроллер

Затем выберем следующие пункты

Переименуем группу в дереве проекта. Хотя это и необязательно, но, как по мне, так лучше смотрится

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

Прежде чем начнём писать код, давайте немного настроим наш проект.
Зайдём в его настройки и во вкладке Target изменим частоту тактирования

Установим галку вот здесь в следующей вкладке, может пригодится HEX-файл

Также можно настроить и формирование чистого бинарника — файла BIN.
Для этого во вкладке User вот здесь напишем вот такую строку «fromelf —bin —output .ObjectsBLINK01_CMSIS.bin .ObjectsBLINK01_CMSIS.axf» (кавычки не нужны)

В следующей вкладке напишем вот это «STM32F103xB,STM32F10X_MD» (без кавычек), убедившись также в том, что уровень оптимизации — 0

Сохраним настройки, нажав кнопку OK, затем откроем их снова, т.к. дальнейшие настройки зависят от уже проделанных.
Во вкладке Debug выберем программатор

Нажмём кнопку Setting рядом со строкой выбора отладчика и, попав в его настройки в закладке и в следующем пункте изменим значение, также убедимся, что остальные настройки такие же как в скриншоте

Перейдём в третью вкладку и настроим программатор на автоперезагрузку контроллера после прошивки

Вот сколько настроек, и будут потом ещё. Думаю, теперь не каждому придёт в голову начинать каждый раз проект с нуля.
Библиотека CMSIS включает в себя следующие компоненты:
- CMSIS-CORE: API для ядра Cortex-M и периферии. Стандартизированный интерфейс доступен для Cortex-M0, Cortex-M3, Cortex-M4, SC000, и SC300. Включает дополнительные SIMD-инструкции для Cortex-M4.
- CMSIS-Driver: определяет основные драйверы интерфейсов периферии. Содержит API для операционных систем реального времени (ОСРВ, или англ. Real-Time operating systems — RTOS) и соединяет микроконтроллер с промежуточным ПО (стек коммуникации, файловая система или графический интерфейс).
- CMSIS-DSP: коллекция из более чем 60 функций для различных типов данных (относятся к обработке сигналов): с фиксированной точкой и с плавающей точкой (одинарной точности, 32 бита). Библиотека доступна для Cortex-M0, Cortex-M3, и Cortex-M4. Реализация библиотеки для Cortex-M4 оптимизирована c использованием SIMD-инструкций.
- CMSIS-RTOS API: общий API для систем реального времени. Используя функции данного интерфейса вы можете отойти от конкретной реализации операционной системы.
- CMSIS-DAP (Debug Access Port): стандартизованное программное обеспечение для отладчика (Debug Unit).
Рассмотрим только CMSIS-CORE.
Библиотека состоит из стандартной (предоставляется ARM) и вендор-зависимой (предоставляется в нашем случае ST) частей.
Стандартная часть
Заголовочный файл core_
.h предоставляет интерфейс к ядру. Для stm32f103c8 это core_cm3.h , так как он работает на Cortex-M3. Для Cortex-M0+ это будет файл core_cm0plus.h .
Под интерфейсом понимается удобный доступ к его регистрам. Например, в состав ядра входят еще две сущности: системный таймер и контроллер прерываний NVIC. Поэтому в этом файле содержатся вспомогательные функции для их быстрой настройки. Включить прерывание можно вызовом функции:
Библиотека CMSIS включает в себя следующие компоненты:
- CMSIS-CORE: API для ядра Cortex-M и периферии. Стандартизированный интерфейс доступен для Cortex-M0, Cortex-M3, Cortex-M4, SC000, и SC300. Включает дополнительные SIMD-инструкции для Cortex-M4.
- CMSIS-Driver: определяет основные драйверы интерфейсов периферии. Содержит API для операционных систем реального времени (ОСРВ, или англ. Real-Time operating systems — RTOS) и соединяет микроконтроллер с промежуточным ПО (стек коммуникации, файловая система или графический интерфейс).
- CMSIS-DSP: коллекция из более чем 60 функций для различных типов данных (относятся к обработке сигналов): с фиксированной точкой и с плавающей точкой (одинарной точности, 32 бита). Библиотека доступна для Cortex-M0, Cortex-M3, и Cortex-M4. Реализация библиотеки для Cortex-M4 оптимизирована c использованием SIMD-инструкций.
- CMSIS-RTOS API: общий API для систем реального времени. Используя функции данного интерфейса вы можете отойти от конкретной реализации операционной системы.
- CMSIS-DAP (Debug Access Port): стандартизованное программное обеспечение для отладчика (Debug Unit).
Рассмотрим только CMSIS-CORE.
Библиотека состоит из стандартной (предоставляется ARM) и вендор-зависимой (предоставляется в нашем случае ST) частей.
Стандартная часть
Заголовочный файл core_
.h предоставляет интерфейс к ядру. Для stm32f103c8 это core_cm3.h , так как он работает на Cortex-M3. Для Cortex-M0+ это будет файл core_cm0plus.h .
Под интерфейсом понимается удобный доступ к его регистрам. Например, в состав ядра входят еще две сущности: системный таймер и контроллер прерываний NVIC. Поэтому в этом файле содержатся вспомогательные функции для их быстрой настройки. Включить прерывание можно вызовом функции:





