Embedded HandbookApps
All sections › III. Peripherals
STM32F103, STM32F407 · level intermediate · 21 min read
Open in the interactive handbook

13. Timers (TIM)

The most versatile block of the microcontroller. One and the same counter is used for periodic interrupts, pulse‑width modulation, measuring pulse widths, counting encoder steps and triggering the ADC on a schedule. Once you have mastered the timer, half of the typical tasks are covered.

Terms used in the documentation

Term Meaning
prescaler (PSC) the input clock divider
auto-reload register (ARR) the value the counter counts up to
capture/compare register (CCR) the register for capture and comparison
update event the event at counter overflow that loads new settings
preload / shadow register a buffered copy loaded on the update event
counting mode: up / down / center-aligned counting up / down / up and down
output compare driving an output when the counter matches
input capture latching the counter on an input edge
duty cycle the share of the period the pulse occupies
dead time a guard interval between complementary outputs
one-pulse mode the timer produces a single pulse
master / slave mode one timer controls another
encoder interface mode counting a quadrature encoder
input filter (ICxF) digital filter on the input
detent a click — a fixed position of the knob

13.1 What kinds of timers there are

Type On the F1 On the F4 What it can do
basic TIM6, TIM7 TIM6, TIM7 counting only: triggering the ADC/DAC, pacing DMA
general-purpose TIM2…TIM5 TIM2…TIM5, TIM9…TIM14 PWM, capture, encoder
advanced-control TIM1, TIM8 TIM1, TIM8 the same + complementary outputs, dead time, break input

Advanced timers are needed for motor control and bridge circuits: they output a pair of antiphase signals with a guaranteed gap between them, so that the bridge transistors never turn on at the same time.

The table is for the top chips of the series. The F103C8 and F103CB (BluePill) have four timers: TIM1…TIM4; TIM5…TIM8 exist only on F103 parts from 256 KB of Flash. The F401 and F411 have no TIM6, TIM7, TIM8 or TIM12…TIM14. The exact set is in the feature table in the datasheet of your chip.

An important difference of the F4: its TIM2 and TIM5 are 32‑bit. On the F1 all counters are 16‑bit. This removes the main limitation — on the F1 the "frequency × duration" range sometimes has to be built up from a cascade of two timers, on the F4 one is enough.


13.2 Basic counting

Three registers define everything:

Register Meaning
PSC prescaler: by how much to slow down the input frequency
ARR how far to count before restarting
CNT the current counter value
частота событий = f_таймера / ((PSC + 1) × (ARR + 1))

The ones are added because counting starts from zero: ARR = 999 means a thousand clock cycles.

An interrupt once a millisecond at 72 MHz:

void tim2_init_1khz(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

    TIM2->PSC = 72u - 1u;              /* тик 1 мкс */
    TIM2->ARR = 1000u - 1u;            /* 1000 тиков = 1 мс */
    TIM2->EGR = TIM_EGR_UG;            /* PSC — сразу; без UG первый период 14 мкс (13.4) */
    TIM2->SR = ~TIM_SR_UIF;            /* флаг от UG — не прерывание */

    TIM2->DIER |= TIM_DIER_UIE;        /* прерывание по переполнению */
    TIM2->CR1 |= TIM_CR1_CEN;          /* пуск */

    NVIC_SetPriority(TIM2_IRQn, 6);
    NVIC_EnableIRQ(TIM2_IRQn);
}

void TIM2_IRQHandler(void)
{
    if (TIM2->SR & TIM_SR_UIF) {
        TIM2->SR = ~TIM_SR_UIF;        /* сброс флага — записью НУЛЯ */
        /* полезная работа */
    }
}

⚠️ Timer flags are cleared by writing zero, unlike EXTI->PR, which needs a one. This is not arbitrary: in the register description they are marked rc_w0 against rc_w1 for EXTI (the notation was covered in section 39). It is easy to mix them up, and the result is either a flag that does not clear and a handler that loops, or the flags of neighbouring channels being erased.

How to choose PSC and ARR. The same frequency can be obtained with different pairs, but not all are equally good: the larger ARR, the finer the adjustment step in PWM mode. The rule is simple — take the smallest prescaler needed so that ARR comes out as large as possible.


13.3 Where the timer gets its clock from

Here is the very peculiarity section 6 warned about:

⚠️ Timers get twice the frequency of their APB bus if its divider is greater than one. With HCLK = 72 MHz and APB1 = 36 MHz the timers count from 72 MHz, not from 36 (these are TIM2…TIM4 on APB1); APB2 on the F103 usually has no divider, and TIM1 counts from the same 72 MHz. On the F407 at 168 MHz both buses have a divider: APB1 = 42 MHz — the timers on it count from 84 MHz, APB2 = 84 MHz — TIM1, TIM8, TIM9…TIM11 count from 168 MHz. A calculation based on the bus frequency gives a result wrong by a factor of two — and this is probably the most common mistake when working with timers.

Always check it in practice: set a period of one second and look at the LED. If it blinks twice as fast, you have been caught.

TIM2 from the APB1 bus to the interrupt: frequency doubling, prescaler, counting to ARR and an update event once a millisecond


13.4 Shadow registers

The mechanism that confuses people most.

PSC exists in two copies: the one you write to, and the one the timer actually uses. The transfer from the first to the second happens on the update event — that is, at the moment the counter overflows. ARR and CCR work the same way, but their buffer is enabled separately — by preload: ARPE for ARR, OCxPE for a channel. After reset preload is off, and a write to ARR and CCR takes effect at once.

Why: if you change the period right in the middle of a period, the waveform is distorted. Shadow registers guarantee that the new value takes effect as a whole and at the boundary of a cycle.

Two consequences follow:

Settings are not applied immediately. You wrote PSC or, with preload enabled, ARR, and the timer carries on working the old way until the next overflow. If the period was long, it looks as though "the setting did not work". To apply it forcibly, generate an update event:

TIM3->EGR = TIM_EGR_UG;    /* немедленно перенести значения в теневые регистры */

UG is a real update event: besides the transfer it clears the counter and the prescaler and sets the UIF flag. If the update interrupt is already enabled (UIE), the handler is called at once — hence the mysterious "extra" interrupt at start-up; a flag left by UG will also trigger it later, at the moment UIE is enabled. So UG is done before the interrupt is enabled and the flag is cleared straight away: TIMx->SR = ~TIM_SR_UIF. Another way is to set the URS bit in CR1 in advance: then UG transfers the values but does not set the flag, and only overflows give an interrupt.

Preload must be enabled explicitly. For ARR this is the ARPE bit, for each channel OCxPE. Without them a write goes straight through, and changing the duty in the middle of a pulse gives a glitch. And reducing ARR on the fly without preload is dangerous: if CNT is already greater than the new ARR, the counter counts up to 65 535, wraps through zero, and one period comes out as long as 65 536 ticks.


13.5 🔧 PWM

The most common use. The counter counts from zero to ARR, and the comparator compares it with CCR: while CNT < CCR the output has one level, after that the other.

частота ШИМ  = f_таймера / ((PSC + 1) × (ARR + 1))
заполнение   = CCR / (ARR + 1)
разрешение   = ARR + 1 градаций

The share of the period taken by the pulse is strictly called the duty cycle. In everyday Russian usage — and further in this handbook — it is called "skvazhnost", although strictly "skvazhnost" is the ratio of the period to the pulse width, T/τ: at a 25 % duty cycle it equals 4.

void tim3_pwm_init(uint16_t period, uint16_t duty)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    /* PA6 — выход альтернативной функции, push-pull. */
    GPIOA->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6);
    GPIOA->CRL |= GPIO_CRL_MODE6;      /* 11 -> выход 50 МГц */
    GPIOA->CRL |= GPIO_CRL_CNF6_1;     /* 10 -> альтернативная функция */

    TIM3->PSC = 72u - 1u;              /* тик 1 мкс */
    TIM3->ARR = period - 1u;
    TIM3->CCR1 = duty;

    /* Режим ШИМ 1: пока CNT < CCR — активный уровень. */
    TIM3->CCMR1 &= ~TIM_CCMR1_OC1M;
    TIM3->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;   /* 110 */
    TIM3->CCMR1 |= TIM_CCMR1_OC1PE;    /* предзагрузка CCR1 */

    TIM3->CCER |= TIM_CCER_CC1E;       /* выход канала разрешён */

    TIM3->CR1 |= TIM_CR1_ARPE;
    TIM3->EGR = TIM_EGR_UG;            /* применить настройки немедленно */
    TIM3->CR1 |= TIM_CR1_CEN;
}

After that the duty is changed with one line: TIM3->CCR1 = новое_значение;

For example, tim3_pwm_init(1000, 250) gives 1 kHz and a 25 % duty. This is how it looks on an oscilloscope; the timer does not output the counter sawtooth — it is drawn above the output for clarity:

TIM3 PWM on an oscilloscope screen: the counter sawtooth up to ARR, the CCR1 level and the PA6 output

The trade-off between frequency and resolution. The product of frequency and the number of steps is limited by the clock:

PWM frequency ARR at 72 MHz without division Steps
1 kHz 71 999 (does not fit in 16 bits) a prescaler is needed
20 kHz 3599 3600 — plenty
100 kHz 719 720
1 MHz 71 72 — rather coarse

For motor control 20 kHz is used (above the audible range) and gives excellent resolution. For LED backlighting 500 Hz is enough — the eye will not notice.

The "PWM" tab in the app's calculators computes the frequency, duty and number of steps from PSC, ARR and CCR, and for a required frequency — ARR without a prescaler and CCR for the required duty.

⚠️ TIM1 and TIM8 have a main output switch. Everything is set up correctly, the counter runs, CCR changes — and the pin is silent. The reason is the MOE bit of the BDTR register, which is cleared by default:

c TIM1->BDTR |= TIM_BDTR_MOE;

TIM2…TIM5 and TIM9…TIM14 have no such register at all, so code debugged on TIM3 silently stops working when moved to TIM1. The MOE bit also exists on TIM15, TIM16 and TIM17 (F0, G0, F3, L4 and other series): their PWM is also enabled through BDTR.


13.6 Input capture

The reverse task: not generating a signal but measuring one. On an edge at the input the timer copies the current counter value into CCR — you get an exact timestamp without involving the processor.

void tim4_capture_init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;

    TIM4->PSC = 72u - 1u;              /* тик 1 мкс */
    TIM4->ARR = 0xFFFFu;               /* считаем до предела */
    TIM4->EGR = TIM_EGR_UG;            /* PSC вступает в силу сразу (13.4) */

    /* Канал 1 переключается на ВХОД. */
    TIM4->CCMR1 &= ~TIM_CCMR1_CC1S;
    TIM4->CCMR1 |= TIM_CCMR1_CC1S_0;   /* 01 -> вход TI1 */

    TIM4->CCER &= ~TIM_CCER_CC1P;      /* нарастающий фронт */
    TIM4->CCER |= TIM_CCER_CC1E;

    TIM4->DIER |= TIM_DIER_CC1IE;
    TIM4->CR1 |= TIM_CR1_CEN;

    NVIC_EnableIRQ(TIM4_IRQn);
}

The difference between two consecutive stamps gives the signal period, and hence the frequency.

Measuring the duty is done with two channels at once: one catches the rising edge, the other the falling edge, both tied to the same input. This is a separate mode — PWM input, and the timer sets it up almost automatically.

Typical uses: frequency measurement, decoding signals from distance sensors, receiving signals from remote controls and radio receivers.


13.7 Encoder

The timer can count the pulses of a rotary encoder by itself, working out the direction from two phase-shifted signals. The processor does not take part at all — it just reads CNT when needed.

It is set up with slave mode in SMCR: counting on one channel, on the other or on both at once. The last option gives four times the resolution. The encoder together with PWM in practice — in 13.10.

Encoder on both edges: signals A and B shifted by a quarter period, CNT increases by 1 on each edge — 4 counts per detent — and decreases the same way when turned back; contact B bounces near the detent, CNT goes 8, 7, 8, the level (CNT + 2) / 4 stays put, while CNT / 4 flickers

A practical detail: the counter is 16‑bit and overflows. For an encoder turned in one direction for a long time you need to catch the overflow with an interrupt and keep the high part of the count in software — or take the 32‑bit TIM2/TIM5 on the F4.


13.8 One-pulse mode

The timer produces exactly one pulse of the given width and stops. Convenient for generating a strobe, the trigger pulse of an ultrasonic sensor, a delayed signal.

It is enabled by the OPM bit in CR1: after the first update event the counter switches itself off.


13.9 Synchronising several timers

Timers can control one another: one becomes the master and, on its event, starts, stops or clocks the slave. This is configured with a pair of registers — CR2 in the master (what exactly to output) and SMCR in the slave (how to react).

What it is for:


13.10 🔧 Brightness from an encoder

You turn the knob — the LED smoothly gets brighter or dimmer. Here the encoder (13.7), PWM (13.5) and the way the eye perceives brightness (57.3) come together. Both timers work by themselves; the processor is left with one line in the loop.

F103 (BluePill) F407 (Discovery)
encoder TIM2: PA0, PA1 TIM3: PB4, PB5 (AF2)
PWM TIM3, channel 1: PA6 TIM4, channel 1: PD12 (AF2) — the board's green LED
PWM tick 1 µs PSC = 71 at 72 MHz PSC = 83 at 84 MHz

The BluePill LED on PC13 is not connected to a timer, and PWM on it is only possible in software — you need an external LED with a resistor on PA6. The encoder contacts short the pins to ground, so the inputs have pull-ups.

void enc_pwm_init(void)
{
    pins();

    /* энкодер: оба канала — входы с самым длинным фильтром, счёт по обоим фронтам */
    ENC->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0 | TIM_CCMR1_IC1F | TIM_CCMR1_IC2F;
    ENC->SMCR  = TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0;             /* 011: энкодер, ×4 */
    ENC->ARR   = 0xFFFFu;
    ENC->CR1   = TIM_CR1_CKD_1 | TIM_CR1_CEN;                 /* f_DTS = f / 4 — для фильтра */

    /* ШИМ: 1 кГц, 1000 градаций */
    PWM->PSC   = PWM_PSC;
    PWM->ARR   = 999u;
    PWM->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;   /* ШИМ 1, предзагрузка */
    PWM->CCER  = TIM_CCER_CC1E;
    PWM->CR1   = TIM_CR1_ARPE;
    PWM->EGR   = TIM_EGR_UG;
    PWM->CR1  |= TIM_CR1_CEN;

    brightness_reset((uint16_t)ENC->CNT);
}

int main(void)
{
    enc_pwm_init();
    for (;;)
        PWM->CCR1 = brightness_step((uint16_t)ENC->CNT);
}

Counting. In encoder mode on both channels the timer catches both edges of both signals — four counts per full cycle. In common mechanical encoders a full cycle is one detent, so a detent gives four counts; on some models a detent is half a cycle, and then two. The timer determines the direction itself: CNT grows when turned one way and decreases the other way.

Bounce. The longest input filter is 8 samples at f_DTS / 32; at 72 MHz and CKD = ÷4 this is 8 / (18 MHz / 32) ≈ 14 µs. Contact bounce lasts milliseconds, and the filter will not remove it, but bounce does not hurt a quadrature count: while one contact bounces, the other stays still, and the count goes +1 −1 in place. The brightness level changes midway between detents — (отсчёты + 2) / 4. A knob released on a detent sits two counts away from a level boundary, and a contact bouncing at the edge does not make the brightness jump.

Passing through zero. Turn the knob left from the start — CNT becomes 65535, 65534… If CNT is taken as the position, the brightness jumps to maximum. The right way is to take the signed difference of readings: (int16_t)(uint16_t)(cnt - s_last) is correct across the wrap too, as long as there are fewer than 32 768 counts between reads. The accumulated value is clamped on both sides: a knob that has hit the end responds to turning back at once, without winding off extra turns.

#define STEPS 32                    /* уровней яркости */
#define PER   4                     /* отсчётов на щелчок: оба фронта обоих каналов */

/* round(1000 · (i / 31)^2,2); при ARR = 999 значение 1000 — горит постоянно */
static const uint16_t gamma22[STEPS] = {
       0,    1,    2,    6,   11,   18,   27,   38,   51,   66,   83,
     102,  124,  148,  174,  202,  233,  267,  302,  341,  381,  425,
     470,  519,  569,  623,  679,  738,  799,  864,  930, 1000,
};

static uint16_t s_last;             /* прошлое показание CNT */
static int32_t  s_acc;              /* отсчёты от нижнего края: 0 … PER·(STEPS−1) */

uint16_t brightness_step(uint16_t cnt)
{
    int32_t acc = s_acc + (int16_t)(uint16_t)(cnt - s_last);   /* верно и через 0 ↔ 65535 */
    s_last = cnt;
    if (acc < 0)
        acc = 0;                                /* упёрлись — обратный поворот */
    if (acc > PER * (STEPS - 1))                /* откликается сразу */
        acc = PER * (STEPS - 1);
    s_acc = acc;
    return gamma22[(acc + PER / 2) / PER];      /* граница уровня — между щелчками */
}

The scale. 32 levels along a gamma of 2.2 (why not linear — in 57.3). The top value 1000 = ARR + 1: the LED is on continuously (13.12). The first steps are 1, 2 and 6 out of 1000, which the eye sees as 4.3 %, 5.9 % and 9.8 % brightness. From zero to full brightness takes 31 detents: with 20 detents per turn that is one and a half turns.

What the model showed. The encoder was modelled on a computer: a 16-bit count on both edges, three bounces on every contact transition, the brightness recomputed after every edge.

What we did CNT Level
40 detents right 160 = 40 × 4 31 — at the end stop
5 detents back 140 26 — responds at once
2 right, the contact at the detent bounces 10 times 148 28, not a single change
from zero 3 detents left 65 524 0, not maximum
and 1 right 65 528 1

If the level were computed by dropping the remainder (отсчёты / 4), the same bounce at the detent would give 20 brightness changes.

Blinking with no program at all. The same PWM with a period of one second blinks by itself: a tick of 0.1 ms (PSC = 7199 on the F103, 8399 on the F407), ARR = 9999 — a 1 s period, CCR1 = 2500 — on for 250 ms. For comparison: toggling a pin in an interrupt every 0.5 s gives blinking at 1 Hz, not 2 — a full period consists of two toggles.


13.11 F0 and G0: the same timers, other clock names

Timers are the part of the STM32 that hardly changes from series to series: CR1, PSC, ARR at 0x2C, CCR1 at 0x34, capture and compare modes, shadow registers — all as on the F1. The PWM recipe from 13.5 carries over to the F0 and G0 with two changes: the name of the clock enable register and the prescaler for their frequency.

F1 F0 G0
TIM3 clock APB1ENR APB1ENR APBENR1
TIM1 clock APB2ENR APB2ENR APBENR2
TIM3_CH1 on PA6 AF mode in CRL AF1 AF1
timer clock at maximum 72 MHz 48 MHz 64 MHz
PSC for a 1 MHz count 71 47 63

F1/F0 code with RCC->APB1ENR does not build on the G0: no member named 'APB1ENR' in 'RCC_TypeDef'. 1 kHz PWM on the G0:

    RCC->APBENR1 |= RCC_APBENR1_TIM3EN;                                  /* у G0 — APBENR1 */
    GPIOA->AFR[0] = (GPIOA->AFR[0] & ~(0xFu << 24)) | (1u << 24);       /* PA6 -> AF1 = TIM3_CH1 */
    GPIOA->MODER = (GPIOA->MODER & ~(3u << 12)) | (2u << 12);
    TIM3->PSC = PSC_VAL;
    TIM3->ARR = ARR_VAL;
    TIM3->CCR1 = 250u;
    TIM3->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
    TIM3->CCER = TIM_CCER_CC1E;
    TIM3->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;

The set of timers depends on the chip. On the F072 and G071, on which the listings were checked, these are the advanced TIM1 (with BDTR and the MOE bit, without which the outputs are silent — 13.5), TIM2, TIM3, the basic TIM6 and TIM7 and the small TIM14–TIM17; TIM15–TIM17 also have BDTR with MOE, and without this bit their outputs are just as silent. TIM2 in both series is 32-bit: the counter and ARR in the chip description are two 16-bit fields. On the F1, for 1 kHz at the full clock, ARR "did not fit in 16 bits" (13.5), while here it fits in any timer without a prescaler: ARR = 47 999 on the F0 and 63 999 on the G0 — less than 65 535. The 32 bits of TIM2 are needed for longer periods: a 16-bit counter without a prescaler lasts only 1.37 ms at 48 MHz and 1.02 ms at 64 MHz, while TIM2 without one can count a whole second. But the F030, F070, G030 and G070 have no TIM2 at all, and the F030x6, F070x6 and G030 also lack TIM6 and TIM15: there a long period can only be obtained with the prescaler. The exact set is in the feature table of the chip's datasheet.


13.12 Pitfalls

The frequency is twice what was expected. The doubling of the timer frequency on an APB bus with a divider greater than 1 was not taken into account.

Settings do not take effect. No update event (UG) was generated: PSC, and with preload enabled also ARR and CCR, wait for it.

An interrupt came right after start. It was triggered by the UIF flag set by writing UG: clear the flag after UG or set URS in CR1 in advance (13.4).

One period is suddenly tens of times longer. ARR was reduced on the fly without preload, and CNT ended up above the new value: the counter went up to 65 535 and wrapped through zero. Enable ARPE.

TIM1 is silent. MOE in BDTR is not set. TIM8 and TIM15…TIM17 are silent in the same way.

The flag is cleared incorrectly. For a timer you write zero, not one. And TIM->SR = 0 also erases the flags of all channels — the right way is TIM->SR = ~TIM_SR_UIF.

The pin is not configured for the alternate function. The timer runs, CCR changes, but the pin stays an ordinary GPIO and outputs nothing.

Overflow during capture. If the signal period is longer than a full counter cycle, the difference of stamps is meaningless. Count overflows or increase the prescaler.

Zero duty. CCR = 0 gives a constant inactive level, CCR > ARR a constant active one. This is not a fault but normal behaviour, but when calculating percentages it is easy to get CCR equal to ARR + 1 and be surprised that the PWM has disappeared.

The timer interrupt does not come. HAL_TIM_Base_Start only starts counting; the update interrupt is enabled by HAL_TIM_Base_Start_IT — it sets the UIE bit in DIER.

It blinks twice as slowly as calculated. Toggling every 0.5 s is a 1 s period, that is 1 Hz (13.10).

The brightness jumps to maximum when turning left. CNT was taken as the position and wrapped through zero. You need the signed difference of readings (13.10).

The BluePill LED does not respond to PWM. Pin PC13 is not connected to a timer.

HAL_GetTick for short intervals. It counts milliseconds — a shorter interval cannot be measured. For microseconds take a free timer with a 1 µs tick or DWT->CYCCNT (section 73).


13.13 Summary

Full sources

tim_f0.c — 1 kHz PWM on TIM3 of the STM32F072.

/* Раздел 13.11: ШИМ 1 кГц на TIM3 канал 1 (PA6, AF1) у STM32F072 — таймер как у F1. */
#include <stddef.h>
#include "stm32f0xx.h"

_Static_assert(offsetof(TIM_TypeDef, ARR) == 0x2Cu && offsetof(TIM_TypeDef, CCR1) == 0x34u, "регистры как у F1");
#define TIM_HZ 48000000u                        /* такт таймера = PCLK 48 МГц (делитель APB 1) */
#define PSC_VAL 47u
#define ARR_VAL 999u
_Static_assert(TIM_HZ / (PSC_VAL + 1u) == 1000000u && TIM_HZ / (PSC_VAL + 1u) / (ARR_VAL + 1u) == 1000u, "1 МГц счёт, 1 кГц ШИМ");

void pwm_init_f0(void)
{
    RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    GPIOA->AFR[0] = (GPIOA->AFR[0] & ~(0xFu << 24)) | (1u << 24);       /* PA6 -> AF1 = TIM3_CH1 */
    GPIOA->MODER = (GPIOA->MODER & ~(3u << 12)) | (2u << 12);
    TIM3->PSC = PSC_VAL;
    TIM3->ARR = ARR_VAL;
    TIM3->CCR1 = 250u;                          /* 25 % */
    TIM3->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;   /* PWM1, с буфером */
    TIM3->CCER = TIM_CCER_CC1E;
    TIM3->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;
}

tim_g0.c — the same PWM on the STM32G071.

/* Раздел 13.11: тот же ШИМ на STM32G071 — TIM3 канал 1 (PA6, AF1), такт с APBENR1, 64 МГц. */
#include <stddef.h>
#include "stm32g0xx.h"

_Static_assert(offsetof(TIM_TypeDef, ARR) == 0x2Cu && offsetof(TIM_TypeDef, CCR1) == 0x34u, "регистры как у F0 и F1");
#define TIM_HZ 64000000u
#define PSC_VAL 63u
#define ARR_VAL 999u
_Static_assert(TIM_HZ / (PSC_VAL + 1u) == 1000000u && TIM_HZ / (PSC_VAL + 1u) / (ARR_VAL + 1u) == 1000u, "1 МГц счёт, 1 кГц ШИМ");

void pwm_init_g0(void)
{
    RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
    RCC->APBENR1 |= RCC_APBENR1_TIM3EN;                                  /* у G0 — APBENR1 */
    GPIOA->AFR[0] = (GPIOA->AFR[0] & ~(0xFu << 24)) | (1u << 24);       /* PA6 -> AF1 = TIM3_CH1 */
    GPIOA->MODER = (GPIOA->MODER & ~(3u << 12)) | (2u << 12);
    TIM3->PSC = PSC_VAL;
    TIM3->ARR = ARR_VAL;
    TIM3->CCR1 = 250u;
    TIM3->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
    TIM3->CCER = TIM_CCER_CC1E;
    TIM3->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;
}

Next: section 13а "Servo: 50 Hz PWM, 1–2 ms pulse, angle, smooth motion and power".

Open in the interactive handbook