Перейти к содержанию

RCC и GPIO: мигаем светодиодом

У STM32 после сброса тактирование почти всей периферии выключено, а каждый вывод — вход без подтяжки. Чтобы зажечь светодиод, нужно сначала включить порт в RCC, затем перевести вывод в режим выхода и только потом менять его уровень.

Обновлено Редакция GAW

STM32 с нуляурок 3 из 830 мин

Цель урока: Включать тактирование порта в RCC, настраивать режим вывода полями CNF и MODE регистров CRL и CRH, менять уровень через BSRR и мигать светодиодом PC13.

Светодиод на Blue Pill подключён к выводу PC13 — тринадцатому биту порта C — катодом: анод через резистор идёт к 3,3 В, и светодиод горит, когда на PC13 низкий уровень (см. страницу Blue Pill). Значит, нужно настроить PC13 выходом и менять его уровень. Для этого понадобятся два блока: RCC (Reset and Clock Control), который раздаёт тактовые сигналы периферии, и порт GPIOC.

Шаг 1. Включить тактирование порта

После сброса в регистре RCC_APB2ENR (смещение 0x18 от базы RCC 0x40021000, раздел 7.3.7 RM0008, с. 112) все биты нулевые: ни порты, ни USART1, ни АЦП не тактируются. Пока порт не тактируется, запись в его регистры не действует, а чтение, как предупреждает RM0008, может возвращать 0. Порты A, B и C сидят на шине APB2 и включаются битами IOPAEN (2), IOPBEN (3) и IOPCEN (4):

RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;     /* clock for GPIOC */

Регистр RCC_AHBENR после сброса равен 0x0000 0014 (раздел 7.3.6, с. 111): включены только тактирование SRAM и интерфейса флеш-памяти. Всё остальное — выключено, и это осознанное решение: блок без тактового сигнала почти не потребляет. Подробнее — в статье глоссария «Отключение тактирования».

Шаг 2. Режим вывода: CRL и CRH

У порта STM32F1 семь регистров (раздел 9.1): два регистра конфигурации CRL и CRH, входные данные IDR, выходные ODR, установка и сброс BSRR, сброс BRR и блокировка LCKR. Режим каждого вывода задают 4 бита: CNF[1:0] и MODE[1:0]. В CRL (смещение 0x00) — выводы 0–7, в CRH (смещение 0x04) — выводы 8–15. Для вывода n поле начинается с бита 4 × (n mod 8): у PC13 это биты 23:20 регистра CRH — MODE13 в битах 21:20, CNF13 в битах 23:22.

MODEЗначение (табл. 21)
00вход (состояние после сброса)
01выход, до 10 МГц
10выход, до 2 МГц
11выход, до 50 МГц
CNFЕсли MODE = 00 (вход)Если MODE ≠ 00 (выход)
00аналоговый входвыход push-pull
01плавающий вход (после сброса)выход с открытым стоком
10вход с подтяжкой вверх или внизальтернативная функция, push-pull
11— (резерв)альтернативная функция, открытый сток

После сброса CRL и CRH равны 0x4444 4444 (раздел 9.2.1, с. 171): у каждого вывода 0100 — CNF = 01, MODE = 00, плавающий вход (раздел 9.1.1). Выводы JTAG — PA13, PA14, PA15 и PB4 — после сброса подтянуты, а PA13 и PA14 заняты SWD.

«Скорость» в поле MODE — это ограничение крутизны фронтов выходного каскада, а не тактовая частота порта. Для светодиода хватит 2 МГц, а для PC13 больше и нельзя: по примечанию 5 к таблице 5 DS5319 выводы PC13–PC15 питаются через ключ, пропускающий до 3 мА, как выходы они ограничены частотой 2 МГц при нагрузке до 30 пФ и не должны быть источником тока. Итак, для PC13: MODE = 10, CNF = 00 — выход push-pull, 2 МГц.

GPIOC->CRH = (GPIOC->CRH & ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13))
           | (0x2u << GPIO_CRH_MODE13_Pos);     /* MODE13 = 10, CNF13 = 00 */

Сначала маска стирает оба поля, потом вписывается нужное значение — остальные 7 выводов регистра не меняются. Регистры GPIO читают и пишут только целыми словами (раздел 9.2), поэтому обращение через GPIOC->CRH к 32-битному полю структуры — правильный способ. Посчитать маску или проверить готовое число поможет калькулятор битовых масок.

Шаг 3. Уровень вывода: ODR, BSRR и BRR

Уровень выхода задаёт бит регистра ODR, но менять его лучше через BSRR (раздел 9.2.5, с. 173). Это регистр только для записи: единица в бите n (поле BSn, биты 15:0) устанавливает бит n регистра ODR, единица в бите 16 + n (поле BRn, биты 31:16) — сбрасывает, нули ничего не меняют. Если в одной записи заданы и BSn, и BRn, побеждает BSn. Регистр BRR делает только сброс, его биты 15:0 — те же BRn.

GPIOC->BSRR = GPIO_BSRR_BR13;   /* ODR13 = 0: PC13 low, LED on */
GPIOC->BSRR = GPIO_BSRR_BS13;   /* ODR13 = 1: PC13 high, LED off */

Главное свойство BSRR — атомарность: RM0008 (раздел 9.1.2) прямо говорит, что запрещать прерывания при такой записи не нужно, потому что она меняет только выбранные биты за одно обращение по шине APB2. А GPIOC->ODR |= … — это три действия: чтение, изменение, запись. Если между чтением и записью сработает прерывание и изменит другой бит порта C, при записи его изменение пропадёт. Подробнее о таких гонках — в статье глоссария «Состояние гонки».

Программа

/* main.c, lesson 3: blink the Blue Pill LED on PC13 */
#include "stm32f1xx.h"

/* Crude busy-wait: the time depends on the clock and on the compiler */
static void delay(volatile uint32_t n)
{
    while (n--) { }
}

int main(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;             /* 1. clock for GPIOC */

    GPIOC->CRH = (GPIOC->CRH & ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13))
               | (0x2u << GPIO_CRH_MODE13_Pos);     /* 2. PC13: push-pull, 2 MHz */

    for (;;) {
        GPIOC->BSRR = GPIO_BSRR_BR13;               /* 3. PC13 low: LED on */
        delay(200000u);
        GPIOC->BSRR = GPIO_BSRR_BS13;               /*    PC13 high: LED off */
        delay(200000u);
    }
}

Замените main.c из урока 1 этим файлом и выполните make flash — Makefile менять не нужно, пути к заголовкам CMSIS в нём уже есть. Светодиод замигает: после сброса STM32F103 работает от внутреннего RC-генератора HSI на 8 МГц, и пустой цикл на 200 000 итераций длится доли секунды. Точное время зависит от того, во что компилятор превратит цикл; параметр объявлен volatile, чтобы оптимизатор не выбросил цикл, который «ничего не делает». Задержку с точностью до миллисекунды сделаем на таймере SysTick в уроке 4.

Что прячется за RCC и GPIOC

RCC и GPIOC в stm32f103xb.h — это указатели на структуры, поля которых повторяют порядок регистров блока. Например, GPIOC — это ((GPIO_TypeDef *)GPIOC_BASE), где база порта C — 0x40011000, а поле CRH стоит вторым, со смещением 0x04. Запись GPIOC->CRH = x компилируется в одну команду записи слова по адресу 0x40011004. Поля объявлены volatile (через макрос __IO), поэтому компилятор не выбрасывает и не переставляет обращения к ним. Адреса и биты всех регистров GPIO и RCC серии с их значениями после сброса собраны на страницах атласа регистров GPIO STM32F1 и регистров RCC STM32F1.

На STM32F4

У STM32F411 на Black Pill порты сидят на шине AHB1, а режимы выводов разнесены по четырём регистрам. Ниже они по RM0368 для STM32F401 (разд. 8.4.1–8.4.4, с. 158–160); значения сброса MODER в RM0383 для F411 те же:

РегистрБиты на выводЗначения
MODER200 — вход, 01 — выход, 10 — альтернативная функция, 11 — аналог
OTYPER10 — push-pull, 1 — открытый сток
OSPEEDR200 low, 01 medium, 10 high, 11 very high
PUPDR200 — без подтяжки, 01 — вверх, 10 — вниз
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
GPIOC->MODER = (GPIOC->MODER & ~GPIO_MODER_MODER13) | GPIO_MODER_MODER13_0;  /* 01: output */
GPIOC->BSRR = GPIO_BSRR_BR13;                                                 /* LED on */

BSRR у F4 устроен так же, как у F1. Значение сброса MODER у порта A — 0xA800 0000: выводы PA13, PA14 и PA15 сразу в режиме альтернативной функции для отладки, поэтому при записи в GPIOA->MODER целым числом легко отключить себе SWD. На Black Pill светодиод тоже на PC13 и тоже горит при нуле — по схеме WeAct MiniF4x1Cx V3.1 он подключён через резистор 1,5 кОм к 3,3 В (подробно — на странице Black Pill).

Задание

  1. Что станет с выводами PC8–PC15, если вместо строки с маской написать GPIOC->CRH = 0x00200000;?
  2. Подключите кнопку между PB12 и GND. Настройте PB12 входом с подтяжкой вверх и сделайте так, чтобы светодиод горел, пока кнопка нажата.
Ответ и решение
  1. В поле PC13 запишется 0010 — нужный режим, но в остальные семь полей — 0000, то есть CNF = 00, MODE = 00: аналоговый вход. До записи они были плавающими входами (0100). На Blue Pill PC14 и PC15 подключены к часовому кварцу, а остальных выводов порта C на контактах платы нет, так что вреда не будет. Но на порту A или B такая запись сделала бы аналоговыми, например, вход приёмника USART: в этом режиме триггер Шмитта выключен, и IDR читается как 0 (RM0008, разд. 9.1.10). Привычка «стереть маской, потом вписать» защищает от этой ошибки.

  2. Подтяжка вверх — CNF = 10, MODE = 00, а направление подтяжки задаёт бит ODR: 1 — вверх, 0 — вниз (табл. 20 RM0008):

RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
GPIOB->CRH = (GPIOB->CRH & ~(GPIO_CRH_MODE12 | GPIO_CRH_CNF12))
           | (0x2u << GPIO_CRH_CNF12_Pos);          /* PB12: input with pull */
GPIOB->BSRR = GPIO_BSRR_BS12;                       /* ODR12 = 1: pull-up */
GPIOC->CRH = (GPIOC->CRH & ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13))
           | (0x2u << GPIO_CRH_MODE13_Pos);

for (;;) {
    if (GPIOB->IDR & GPIO_IDR_IDR12)
        GPIOC->BSRR = GPIO_BSRR_BS13;               /* released: LED off */
    else
        GPIOC->BSRR = GPIO_BSRR_BR13;               /* pressed: LED on */
}

Что дальше

В уроке 4 переведём STM32F103 с 8 МГц HSI на 72 МГц от кварца через PLL и заведём системный таймер SysTick для точных задержек. Все режимы выводов, токи и выводы, допускающие 5 В, разобраны в статье «GPIO STM32», а схемы выходных каскадов — в статьях глоссария push-pull и «Открытый сток». Готовый код инициализации выводов в стиле регистров, LL или HAL сгенерирует генератор инициализации STM32.

Частые вопросы

Почему вывод не переключается, хотя регистры GPIO записаны?

Скорее всего, не включено тактирование порта в RCC_APB2ENR. Пока бит IOPxEN равен нулю, порт не тактируется, запись в его регистры не действует, а чтение по RM0008 может возвращать 0. Включайте тактирование порта первой строкой, до любой записи в его CRL, CRH или BSRR.

Чем BSRR лучше записи в ODR?

Строка GPIOC->ODR |= (1 << 13) — это чтение, изменение и запись: если между чтением и записью прерывание изменит другой бит того же порта, его изменение потеряется. Запись в BSRR меняет только биты, где стоят единицы, одной операцией, поэтому её не нужно защищать от прерываний (RM0008, разд. 9.1.2).

Можно ли подключить светодиод к PC13 анодом, чтобы он горел от единицы?

Нет. По примечанию 5 к таблице 5 DS5319 выводы PC13–PC15 питаются через ключ на 3 мА и не должны работать источником тока — например, зажигать светодиод. Поэтому на Blue Pill светодиод подключён к PC13 катодом и горит при нуле. Для своих светодиодов берите другие выводы, например PA0–PA7, PB0, PB1 или PB5–PB15. PB3 и PB4 после сброса заняты JTAG и освобождаются только записью SWJ_CFG в AFIO_MAPR (RM0008, разд. 9.3.5), а PB2 на Blue Pill служит выводом BOOT1 и подключён к перемычке.

Источники

  1. STMicroelectronics. RM0008 Rev 21. Reference manual STM32F101xx–STM32F107xx — разд. 7.3.6 (с. 111): RCC_AHBENR, смещение 0x14, сброс 0x0000 0014; разд. 7.3.7 (с. 112): RCC_APB2ENR, смещение 0x18, сброс 0x0000 0000, при выключенном тактировании регистры периферии могут читаться как 0; разд. 9.1 (с. 161): у порта CRL, CRH, IDR, ODR, BSRR, BRR и LCKR; разд. 9.1.1: после сброса выводы — плавающие входы (CNF = 01, MODE = 00), JTAG-выводы с подтяжками; разд. 9.1.2: запись в BSRR и BRR меняет биты ODR одной атомарной записью; табл. 20 и 21 (с. 161): режимы CNF/MODE, подтяжка вверх или вниз по биту ODR, скорость 10, 2 и 50 МГц; разд. 9.2 (с. 171): регистры GPIO — только словами; разд. 9.2.1: CRL, сброс 0x4444 4444; разд. 9.1.10: в аналоговом режиме триггер Шмитта выключен, IDR читается как 0; разд. 9.2.5 (с. 173): BSRR, BRy в битах 31:16, BSy в битах 15:0, при обоих единицах приоритет у BSx
  2. STMicroelectronics. DS5319 Rev 20. STM32F103x8, STM32F103xB datasheet — табл. 5, примечание 5: PC13–PC15 питаются через ключ, который пропускает до 3 мА; как выходы — не выше 2 МГц при нагрузке до 30 пФ, источником тока (например, для светодиода) быть не должны; разд. 5.3.13, I/O port characteristics, Output driving current: остальные выводы — до ±8 мА, с ослабленными VOL/VOH — до ±20 мА, PC13–PC15 — до ±3 мА
  3. STMicroelectronics. cmsis-device-f1 v4.3.5: Include/stm32f103xb.h — RCC_APB2ENR_IOPCEN, GPIO_CRH_MODE13 и GPIO_CRH_CNF13 (позиции 20 и 22), GPIO_BSRR_BS13, GPIO_BSRR_BR13; GPIOC по адресу 0x40011000, RCC — 0x40021000
  4. STMicroelectronics. RM0368 Rev 6 (STM32F401) и RM0383 Rev 4 (STM32F411); cmsis-device-f4 v2.6.11, stm32f411xe.h — RM0368, разд. 8.4.1–8.4.4 (с. 158–160): MODER (00 вход, 01 выход, 10 альтернативная функция, 11 аналог; сброс 0xA800 0000 у порта A, 0x0000 0280 у порта B), OTYPER, OSPEEDR, PUPDR; RM0383, с. 158 — те же значения сброса MODER; в заголовке F411 — RCC_AHB1ENR_GPIOCEN, GPIO_MODER_MODER13_0
  5. Исходная схема Blue Pill (vcc-gnd.com, 26.01.2016), опубликована STM32-base — пользовательский светодиод подключён к PC13 катодом и горит при низком уровне
  6. WeAct Studio. WeActStudio.MiniSTM32F4x1 (коммит 05380f5): схема MiniF4x1Cx_V31 — на Black Pill V3.1 синий светодиод — на PC13 через резистор 1,5 кОм от 3,3 В, горит при низком уровне

Нашли ошибку?