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, затем через делители шин, и до каждого блока периферии доезжает своя.
Читается слева направо: кварц 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.hFLASH_ACR_LATENCY_2равно 2 — «два такта». В заголовке Cubestm32f103xb.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
#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 считает между фронтами 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 Итоги
- После сброса камень работает от внутреннего RC-генератора: 8 МГц у F1, 16 МГц у F4. Всё остальное надо настраивать.
- Порядок настройки: кварц → задержки Flash → делители шин → PLL → переключение. Задержки Flash до повышения частоты, иначе HardFault.
- У F1 APB1 не выше 36 МГц, у F4 — не выше 42. Отсюда делители.
- Таймеры получают удвоенную частоту своей шины APB (APB1 или APB2), если её делитель больше 1.
- Ждать готовности всегда с таймаутом, подтверждение переключения читать
из
SWS, а не изSW. - У F4 PLL описывается четвёркой M, N, P, Q;
Qотвечает за 48 МГц для USB, и про него забывают чаще всего. - Периферия не работает без включённого такта в
RCC->APBxENR— и не сообщает об этом никак.
Полные исходники
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 и обработка той же кнопки без опроса в цикле.