Embedded HandbookПриложения
Все разделы › II. Ядро и системные механизмы
STM32F103, STM32F407 · уровень базовый · 18 мин чтения
Открыть в интерактивном справочнике

6. Тактирование (RCC)

Микроконтроллер после включения работает не на паспортных 72 или 168 МГц, а на встроенном генераторе — 8 МГц у F1, 16 МГц у F4. Разогнать его до максимума — задача блока RCC, и делается это в строго определённом порядке. Нарушите порядок — получите либо зависание, либо кристалл, который работает через раз.


6.1 Четыре источника тактов

Источник Что это Частота Когда нужен
HSI внутренний RC-генератор 8 МГц (F1), 16 МГц (F4) стартовый, работает всегда
HSE внешний кварц или генератор 4–16 МГц (F1), 4–26 МГц (F4) когда нужна точность
LSI внутренний низкочастотный RC ~40 кГц (F1), 32 кГц (F4) сторожевой таймер, пробуждение
LSE часовой кварц 32 768 Гц часы реального времени

HSI против HSE. Внутренний генератор запускается мгновенно и не требует деталей на плате, но его точность около ±1 % и она плывёт с температурой. Для мигания светодиодом этого хватит, для UART на 115200 бод — уже впритык, для USB — недостаточно категорически: там нужен именно кварц.

Зачем LSI, если есть LSE. LSI неточен, зато работает всегда и не зависит от внешних деталей — именно поэтому от него питается независимый сторожевой таймер: если остановится вся остальная тактовая система, сторож продолжит считать и перезапустит камень.


6.2 Дерево тактов

Как это называется в документации

Оригинал Перевод
clock tree дерево тактов
high-speed internal / external (HSI / HSE) внутренний / внешний высокочастотный
low-speed internal / external (LSI / LSE) внутренний / внешний низкочастотный
phase-locked loop (PLL) схема фазовой автоподстройки частоты
prescaler предделитель
multiplication factor множитель
clock security system (CSS) система защиты тактирования
wait states / latency такты ожидания
prefetch buffer буфер предвыборки
peripheral clock enable разрешение тактирования периферии
ready flag флаг готовности
switch / switch status (SW / SWS) выбор источника / подтверждение выбора

Источник сам по себе ничего не даёт: частота проходит через умножитель PLL, затем через делители шин, и до каждого блока периферии доезжает своя.

Дерево тактов STM32F103 в конфигурации 72 МГц

Читается слева направо: кварц 8 МГц → PLL умножает на 9 → 72 МГц становятся системной частотой → делитель AHB (у нас 1) даёт HCLK для ядра, памяти и DMA → дальше два делителя на шины периферии.

Ограничения, которые нельзя нарушать (F1):

Шина Предел В конфигурации 72 МГц
SYSCLK, HCLK 72 МГц 72 МГц
APB2 72 МГц 72 МГц, делитель 1
APB1 36 МГц 36 МГц, делитель 2
ADC 14 МГц делитель 6 от APB2

Хитрость с таймерами. Таймеры получают удвоенную частоту своей шины APB, если её делитель больше единицы. То есть при APB1 = 36 МГц таймеры на ней считают от 72 МГц, а не от 36; APB2 у F1 работает без делителя, и удвоения там нет. Это не ошибка документации, а особенность разводки тактов — но она регулярно ломает расчёт периода у тех, кто о ней не знает (подробнее в разделе 13).


6.3 Настройка 72 МГц вручную

Порядок действий важен: сначала кварц, потом задержки Flash, потом делители, и только в конце — переключение системы на PLL.

#define HSE_TIMEOUT  0x5000u

uint8_t SystemClock_72MHz(void)
{
    uint32_t timeout = HSE_TIMEOUT;

    /* 1. Запускаем кварц и ждём готовности — обязательно с таймаутом. */
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR & RCC_CR_HSERDY)) {
        if (--timeout == 0u) {
            return 1u;                 /* кварц не завёлся */
        }
    }

    /* 2. Flash не успевает за ядром: предвыборка и два такта ожидания. */
    FLASH->ACR |= FLASH_ACR_PRFTBE;
    FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | 2u;   /* LATENCY = 010: два такта */

    /* 3. Делители шин: AHB = 72, APB2 = 72, APB1 = 36. */
    RCC->CFGR &= ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2);
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;

    /* 4. PLL: источник HSE, множитель 9. */
    RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
    RCC->CFGR |= RCC_CFGR_PLLSRC_HSE;
    RCC->CFGR |= RCC_CFGR_PLLMULL9;

    /* 5. Запускаем PLL и ждём захвата. */
    RCC->CR |= RCC_CR_PLLON;
    timeout = HSE_TIMEOUT;
    while (!(RCC->CR & RCC_CR_PLLRDY)) {
        if (--timeout == 0u) {
            return 1u;
        }
    }

    /* 6. Переключаемся и убеждаемся, что переключение состоялось. */
    RCC->CFGR &= ~RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) {
    }

    return 0u;
}

Три вещи, которые стоит заметить.

Таймаут в ожидании кварца. В большинстве примеров пишут просто while (!(RCC->CR & RCC_CR_HSERDY)) {}. Если кварц не запаян, повреждён или плата с браком — программа зависнет здесь навсегда, без всяких признаков жизни. Возврат кода ошибки позволяет откатиться на HSI и хотя бы моргнуть светодиодом.

SW и SWS — разные поля. В SW вы записываете желаемый источник, а SWS железо заполняет само, подтверждая фактический. Проверять надо именно SWS: между записью и переключением проходит несколько тактов, и если PLL не захватил частоту, система останется на прежнем источнике.

Множитель кодируется со сдвигом. RCC_CFGR_PLLMULL9 — это значение 7 в поле PLLMUL, потому что кодировка идёт как «множитель минус два». Поэтому не пытайтесь писать туда девятку.

⚠️ Одно имя — разные числа. В заголовке пака Keil stm32f10x.h FLASH_ACR_LATENCY_2 равно 2 — «два такта». В заголовке Cube stm32f103xb.h (его подключает stm32f1xx.h в проектах CubeIDE и HAL) то же имя — это бит 2, то есть 4: поле получит 100, а такого значения в описании регистра нет. Два такта там — FLASH_ACR_LATENCY_1, в HAL — FLASH_LATENCY_2. Поэтому в листинге поле записано числом: строка верна с обоими заголовками.


6.4 Задержки Flash — почему обязательно

Flash-память медленнее ядра. На 72 МГц одно чтение команды не успевает за такт, и контроллер Flash должен вставлять такты ожидания.

Частота ядра (F1) Тактов ожидания
до 24 МГц 0
24–48 МГц 1
48–72 МГц 2

⚠️ Порядок критичен. Задержки выставляются до повышения частоты. Если сначала переключиться на 72 МГц, а потом настраивать FLASH->ACR, процессор начнёт читать команды быстрее, чем Flash их отдаёт, и получит искажённые слова. Дальше что угодно: неверные числа, прыжок не туда, HardFault. Бывает и так, что при комнатной температуре всё будто бы работает, а при нагреве сбоит. Обратный порядок (сначала уменьшить частоту, потом снизить задержки) тоже важен при понижении частоты.

Предвыборка (PRFTBE) читает Flash с опережением и частично компенсирует задержки — включать её стоит всегда.


6.5 Тактирование периферии

Отдельная и самая частая ошибка новичка: настроить периферию, но забыть подать на неё такт. Регистр не запишется, а компилятор ничего не скажет.

RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;    /* порт C */
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;    /* АЦП 1  */
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;    /* таймер 2 */

Что на какой шине у F1:

Шина Что на ней
AHB DMA, SRAM, Flash, CRC
APB2 (72 МГц) GPIO, AFIO, ADC1/2/3, USART1, SPI1, TIM1, TIM8
APB1 (36 МГц) USART2/3, SPI2/3, I2C1/2, TIM2…TIM7, CAN, USB, PWR, DAC

Отсюда практическое следствие: USART1 тактируется вдвое быстрее, чем USART2, и при одинаковых настройках делителя даст другую скорость. Регистр BRR считается от частоты своей шины.

⚠️ После включения такта периферии сделайте хотя бы одну «холостую» операцию, прежде чем писать в её регистры. На высоких частотах между записью в APBxENR и готовностью периферии проходит несколько тактов; библиотеки ST для этого вставляют чтение регистра обратно. На практике проблема возникает редко, но диагностируется мучительно.


6.6 То же на F4: 168 МГц

У F4 дерево сложнее: PLL получил три делителя вместо одного множителя.

SYSCLK = HSE / M × N / P        частота USB = HSE / M × N / Q

Для 168 МГц от кварца 8 МГц: M = 8, N = 336, P = 2, Q = 7.

8 МГц / 8   = 1 МГц      ← вход VCO, допустимо 1…2 МГц
1 МГц × 336 = 336 МГц    ← выход VCO, допустимо 100…432 МГц
336 / 2     = 168 МГц    ← системная частота
336 / 7     = 48 МГц     ← ровно то, что требует USB

Дерево тактов STM32F407 на 168 МГц: кварц 8 МГц, делитель M 8, умножитель N 336, делитель P 2 — системная частота, делитель Q 7 — ровно 48 МГц для USB, SDIO и RNG; APB1 через 4 — 42 МГц, таймеры на ней 84, APB2 через 2 — 84 МГц, таймеры 168

#define PLL_M   8u
#define PLL_N   336u
#define PLL_P   2u
#define PLL_Q   7u

/* Кварц: запускаем и ждём готовности (с таймаутом, как в 6.3). */
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)) { }

/* Питание ядра: для 168 МГц нужен режим регулятора Scale 1. */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;

/* Делители шин: APB1 не выше 42 МГц, APB2 не выше 84 МГц. */
RCC->CFGR &= ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2);
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;

/* PLLCFGR собирается целиком: поле P кодируется как (P/2 − 1). */
RCC->PLLCFGR = PLL_M
             | (PLL_N << RCC_PLLCFGR_PLLN_Pos)
             | (((PLL_P >> 1u) - 1u) << RCC_PLLCFGR_PLLP_Pos)
             | (PLL_Q << RCC_PLLCFGR_PLLQ_Pos)
             | RCC_PLLCFGR_PLLSRC_HSE;

/* Flash: 5 тактов ожидания на 168 МГц при 3,3 В, плюс кэши команд и данных. */
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN
           | FLASH_ACR_LATENCY_5WS;

/* PLL: запускаем, ждём захвата, переключаемся и проверяем SWS. */
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) { }
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) { }

Чем отличается от F1:

F1 F4
PLL один множитель ×2…×16 делители M, N, P, Q
пределы шин APB1 36, APB2 72 APB1 42, APB2 84
задержка Flash на максимуме 2 такта 5 тактов
кэш Flash только предвыборка кэши команд и данных
режим питания нет нужен Scale 1 через PWR->CR
частота HSI 8 МГц 16 МГц

⚠️ Про Q забывают чаще всего. Он влияет только на USB, SDIO и генератор случайных чисел. Собрали дерево «на глаз», получили красивые 168 МГц — и потом неделю ищете, почему USB-устройство не определяется компьютером. USB требует ровно 48 МГц: при N = 336 подходит Q = 7, при других N может не существовать целого делителя вовсе.

⚠️ Вход VCO держите ближе к 2 МГц. Формально допустимо 1…2 МГц, но чем ниже частота на входе, тем выше собственный шум PLL. ST рекомендует 2 МГц, то есть для кварца 8 МГц лучше M = 4, N = 168, P = 2, Q = 7: на входе VCO 2 МГц, на выходе те же 336 МГц, а значит, те же 168 и 48 МГц.


6.7 F0 и G0: тактирование

У младших серий дерево проще, чем у F4, и обе умеют работать на полной частоте от внутреннего генератора — кварц нужен только ради точности:

F1 F0 G0
внутренний HSI 8 МГц 8 МГц 16 МГц
предел частоты 72 МГц 48 МГц 64 МГц
PLL множитель множитель ×2…×16 делители M, N, R, как у G4 и L4
шины APB две одна одна
тактов ожидания Flash на максимуме 2 1 2

Частоты HSI — из заголовков LL: HSI_VALUE = 8 МГц у F0 и 16 МГц у G0.

F0: 48 МГц без кварца. HSI делится на 2 и умножается на 12. Множитель кодируется со сдвигом на два, как у F1: RCC_CFGR_PLLMUL12 — это 10 в поле PLLMUL. Поле задержки Flash — один бит: 0 или 1 такт, и один нужен уже выше 24 МГц:

FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;   /* выше 24 МГц — один такт ожидания */
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL | RCC_CFGR_PPRE))
          | RCC_CFGR_PLLSRC_HSI_DIV2 | RCC_CFGR_PLLMUL12;
RCC->CR |= RCC_CR_PLLON;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL;

У F072 есть ещё HSI48 — отдельный генератор на 48 МГц для USB, его включает бит HSI48ON в RCC->CR2. Сам по себе он для USB неточен: заводская калибровка держит −2,8…+2,9 % при 25 °C и −4,9…+4,7 % во всём диапазоне температур, а USB Full Speed требует ±0,25 % (раздел 20). Точность даёт блок CRS: он подстраивает HSI48 по пакетам SOF, которые хост шлёт раз в миллисекунду. После сброса источник синхронизации у CRS уже USB SOF, а тактом USB уже выбран HSI48 (бит USBSW в RCC->CFGR3 равен 0), так что остаётся включить такт блока (RCC_APB1ENR_CRSEN) и биты CRS_CR_AUTOTRIMEN и CRS_CR_CEN. Тогда USB работает без кварца.

G0: 64 МГц от HSI16. PLL здесь — делитель M, множитель N и делитель R на выходе системной частоты. Пределы берём из библиотеки LL (stm32g0xx_ll_utils.c): вход VCO 4…8 МГц, сам VCO 64…344 МГц, система не выше 64 МГц. HSI16 / 2 = 8 МГц, × 16 = 128 МГц, / 2 = 64 МГц:

RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC_HSI
             | ((PLL_M - 1u) << RCC_PLLCFGR_PLLM_Pos)    /* M и R — «минус один» */
             | (PLL_N << RCC_PLLCFGR_PLLN_Pos)
             | ((PLL_R - 1u) << RCC_PLLCFGR_PLLR_Pos)
             | RCC_PLLCFGR_PLLREN;                     /* выход R разрешается отдельно */

В регистр уходит 0x30001012 — это видно в объектном файле. Две ловушки: M и R кодируются «минус один», а N — как есть; выход R без бита PLLREN молчит, и переключиться на PLL не получится. Задержку Flash — два такта при 64 МГц — выставляют первой и ждут, пока она вступит в силу:

FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | FLASH_ACR_LATENCY_1 | FLASH_ACR_PRFTEN;
while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_1) {
}

Источник системной частоты у G0 — трёхбитное поле: PLL — это 010 (RCC_CFGR_SW_1), а подтверждение читается в SWS, как у F1 и F4.


6.8 Подстройка HSI

Внутренний генератор — это RC-цепочка на кристалле, и её частота зависит от температуры и от партии. Отсюда и заявленные проценты: для мигания светодиодом всё равно, для UART на 115200 бод уже заметно (раздел 16: расхождение больше 2–3 % ломает приём). Но у HSI есть подстройка, и ею мало кто пользуется.

В RCC->CR под него отведены два поля:

Поле Биты Кто пишет
HSICAL 15:8 только чтение: заводская калибровка, её кладут при сбросе
HSITRIM 7:3 ваши пять бит, они складываются с заводскими

Заводское значение попадает в HSICAL само, а HSITRIM после сброса всегда равен середине диапазона — 16 из 31. Сдвигая его, частоту двигают вверх или вниз относительно заводской.

С чем сравнивать. Внутри камня есть один источник, которому можно верить, — часовой кварц LSE на 32 768 Гц. На вход захвата таймера его заводят внутри камня: у F4 это вход TIM5_CH4, поле TI4_RMP в TIM5->OR = 10 (01 — LSI, 11 — пробуждение RTC); у L4 — TIM15 и TIM16 через TIMx->OR1, у G0 — TIM16 через TIMx->TISEL. У F1 такого пути нет: бит CCO в BKP->RTCCR выводит такт RTC, делённый на 64, на вывод TAMPER (PC13), и его соединяют проводом со входом захвата. Таймер, тактованный от HSI, считает между двумя фронтами LSE; сколько насчитал — столько тактов HSI укладывается в период часового кварца. При 16 МГц это 488,28 такта. Неопределённость счёта — один такт, то есть 0,205 % за один период LSE: хуже, чем сама ошибка HSI, мерить так бессмысленно. Считать надо на длинном отрезке: за 16 периодов LSE набирается 7812,5 такта, и тот же единственный такт неопределённости даёт уже 0,013 %.

Таймер от HSI считает такты между фронтами часового кварца: 488 тактов за период, 7812 за шестнадцать

/* измерение: таймер от HSI считает между фронтами LSE, счёт за 16 периодов */
uint32_t ticks = capture_over(16);              /* захват 16 периодов LSE */
int32_t  want  = 16 * 16000000 / 32768;         /* сколько должно быть: 7812 */
int32_t  err   = (int32_t)ticks - want;         /* знак ошибки: быстрее или медленнее */

Дальше HSITRIM двигают на шаг, повторяют измерение и смотрят, в какую сторону и на сколько поехала частота: цена шага у разных камней своя, и её узнают тем же измерением, а не из головы.

⚠️ HSITRIM живёт до сброса. После перезапуска поле снова равно 16, и если найденное значение нигде не сохранено, подстройка пропала. Хранят его в резервных регистрах или во флеш-памяти и восстанавливают при старте — вместе с проверкой, что кварц LSE вообще запустился.

⚠️ Подстройка не превращает RC-цепочку в кварц. Она убирает постоянный сдвиг «эта партия быстрее», но не уход от нагрева: измерение верно для той температуры, при которой его сделали. Для USB и точных интерфейсов по-прежнему нужен HSE — либо, у F072, HSI48 с подстройкой блоком CRS (6.7).


6.9 Грабли

Кварц не той частоты. На плате распаян кварц 8 МГц, а в проекте прописано 25 МГц (значение по умолчанию у F4) — все расчёты уезжают в три раза. Признак: HAL_Delay(1000) отмеряет не секунду. Лечится определением HSE_VALUE в настройках проекта (см. раздел 2).

Бесконечное ожидание готовности. Без таймаута программа зависает на первом же цикле while (!(RCC->CR & RCC_CR_HSERDY)). Плата выглядит мёртвой, хотя дело всего лишь в кварце.

Забытый такт периферии. Самая частая причина «код правильный, а не работает». Записи в регистры уходят в никуда молча.

Проверка SW вместо SWS. Код выглядит рабочим, но фактически система может остаться на HSI, и всё будет идти в девять раз медленнее ожидаемого.

Система защиты тактов (CSS). У RCC есть механизм, который при пропадании HSE автоматически переключает систему на HSI и вызывает немаскируемое прерывание. Для изделий, где остановка недопустима, его включают битом RCC_CR_CSSON — но тогда нужно написать обработчик NMI_Handler, иначе камень уйдёт в бесконечный цикл по умолчанию. Обработчик сбрасывает флаг записью RCC->CIR |= RCC_CIR_CSSC (иначе NMI будет вызываться снова и снова) и заново настраивает такты: после срабатывания HSE и PLL выключены, система идёт от HSI.

Вывод такта наружу (MCO). На PA8 можно вывести любую внутреннюю частоту — удобно проверить осциллографом, что PLL действительно настроен как задумано, вместо гадания по миганию светодиода.


6.10 Итоги

Полные исходники

rcc_f0.c — 48 МГц на STM32F072 от HSI без кварца.

/* Раздел 6.7: 48 МГц на STM32F072 от внутреннего HSI — без кварца.
 * HSI 8 МГц делится на 2 и умножается PLL на 12. Шина APB у F0 одна. */
#include "stm32f0xx.h"

#define HSI_HZ 8000000u                                /* HSI_VALUE в stm32f0xx_ll_rcc.h */
#define PLL_MUL 12u
#define SYSCLK_HZ (HSI_HZ / 2u * PLL_MUL)

_Static_assert(SYSCLK_HZ == 48000000u, "48 МГц — предел F0");
_Static_assert(RCC_CFGR_PLLMUL12 == ((PLL_MUL - 2u) << RCC_CFGR_PLLMUL_Pos), "множитель со сдвигом на два, как у F1");
_Static_assert(FLASH_ACR_LATENCY == 1u, "у F0 поле задержки — один бит: 0 или 1 такт");

int clock_48mhz_f0(void)
{
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;   /* выше 24 МГц — один такт ожидания */
    RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL | RCC_CFGR_PPRE))
              | RCC_CFGR_PLLSRC_HSI_DIV2 | RCC_CFGR_PLLMUL12;
    RCC->CR |= RCC_CR_PLLON;
    for (uint32_t t = 0x5000u; !(RCC->CR & RCC_CR_PLLRDY); t--) {
        if (t == 0u)
            return -1;
    }
    RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) {
    }
    return 0;
}

/* F072: HSI48 — отдельный генератор на 48 МГц для USB, кварц не нужен. */
void usb_clock_hsi48_f0(void)
{
    RCC->CR2 |= RCC_CR2_HSI48ON;
    while (!(RCC->CR2 & RCC_CR2_HSI48RDY)) {
    }
}

rcc_g0.c — 64 МГц на STM32G071 от HSI16.

/* Раздел 6.7: 64 МГц на STM32G071 от внутреннего HSI16.
 * PLL устроен как у G4 и L4: делитель M, множитель N, делитель R. */
#include "stm32g0xx.h"

#define HSI_HZ 16000000u                               /* HSI_VALUE в stm32g0xx_ll_rcc.h */
#define PLL_M 2u
#define PLL_N 16u
#define PLL_R 2u
#define VCO_IN_HZ (HSI_HZ / PLL_M)
#define VCO_HZ (VCO_IN_HZ * PLL_N)
#define SYSCLK_HZ (VCO_HZ / PLL_R)

_Static_assert(VCO_IN_HZ >= 4000000u && VCO_IN_HZ <= 8000000u, "вход VCO 4…8 МГц — пределы из stm32g0xx_ll_utils.c");
_Static_assert(VCO_HZ >= 64000000u && VCO_HZ <= 344000000u, "VCO 64…344 МГц");
_Static_assert(SYSCLK_HZ == 64000000u, "64 МГц — предел G0");
_Static_assert(RCC_CFGR_SW_1 == 2u && RCC_CFGR_SWS_1 == 0x10u, "SW = 010 — PLLRCLK");

int clock_64mhz_g0(void)
{
    /* при 64 МГц — два такта ожидания; дождаться, пока задержка вступит в силу */
    FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | FLASH_ACR_LATENCY_1 | FLASH_ACR_PRFTEN;
    while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_1) {
    }
    RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC_HSI
                 | ((PLL_M - 1u) << RCC_PLLCFGR_PLLM_Pos)    /* M и R — «минус один» */
                 | (PLL_N << RCC_PLLCFGR_PLLN_Pos)
                 | ((PLL_R - 1u) << RCC_PLLCFGR_PLLR_Pos)
                 | RCC_PLLCFGR_PLLREN;                     /* выход R разрешается отдельно */
    RCC->CR |= RCC_CR_PLLON;
    for (uint32_t t = 0x5000u; !(RCC->CR & RCC_CR_PLLRDY); t--) {
        if (t == 0u)
            return -1;
    }
    RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_1;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {
    }
    return 0;
}

uint32_t pllcfgr_64mhz(void)
{
    return RCC_PLLCFGR_PLLSRC_HSI | ((PLL_M - 1u) << RCC_PLLCFGR_PLLM_Pos) | (PLL_N << RCC_PLLCFGR_PLLN_Pos)
         | ((PLL_R - 1u) << RCC_PLLCFGR_PLLR_Pos) | RCC_PLLCFGR_PLLREN;
}

Дальше: раздел 7 «Прерывания и исключения» — NVIC, приоритеты, EXTI и обработка той же кнопки без опроса в цикле.

Открыть в интерактивном справочнике