STM32F103 RCC Clock Control Reference Chapter
Clock source selection, SYSCLK distribution, prescalers, and enable registers
Contents
Front matter: Document scope.
- RCC overview
- Clock tree overview
- Derived clocks and important exceptions
- RCC register map
- Clock control register (RCC_CR)
- Clock configuration register (RCC_CFGR)
- AHB/APB enable registers
- Programming sequences
- Student register guide and code examples
Document scope
This chapter follows the official STM32F103 document split: the datasheet provides the big clock-tree view, while RM0008 explains the control registers, prescalers, and peripheral enable sequence. Trainer-executable examples are kept separate in Chapter 9.
1 RCC overview
On STM32F103, RCC performs three jobs. First, it decides which oscillator or PLL output becomes SYSCLK. Second, it divides that system clock into the AHB and APB bus clocks. Third, it enables or disables the bus clock that reaches each peripheral register block.
- HSI, HSE, and PLLCLK are SYSCLK candidates.
- SYSCLK feeds the AHB prescaler to produce HCLK.
- HCLK feeds the APB1 and APB2 prescalers to produce PCLK1 and PCLK2.
- Peripheral registers become readable and writable only after the relevant RCC enable bit is active on real hardware.
2 Clock tree overview
Figure 1. Reconstructed STM32F103 RCC clock-tree slice
Source reconstruction: STM32F103 datasheet (DS5319 Rev 20, Figure 2. Clock tree) and RM0008 Rev 21, Figure 8. Clock tree
The official documents place clock distribution before register details. That order matters because RCC_CR and RCC_CFGR only make sense after the reader can see where SYSCLK, HCLK, PCLK1, PCLK2, and ADCCLK come from.
2.1 Register callouts in Figure 1
| Callout | Clock-tree position | Register | What the student should connect it to |
|---|---|---|---|
| 1 | SW, AHB, APB1, APB2, and ADC prescaler path | RCC_CFGR | Clock-source selection and divider fields. The trainer provides these base settings for the current lab. |
| 2 | PCLK2 peripheral-enable path | RCC_APB2ENR | Open APB2 register blocks. IOPAEN enables GPIOA for PA2/PA3/PA4, and IOPBEN enables GPIOB for PB0/PB1. |
| 3 | PCLK1 peripheral-enable path | RCC_APB1ENR | Open APB1 register blocks such as TIM2. In the current trainer, this is the timer-side clock gate for the PWM route. |
PA2, PA3, and PA4 belong to GPIOA; PB0 and PB1 belong to GPIOB. Both GPIO port clocks are opened from RCC_APB2ENR on the PCLK2 side, not from PCLK1. PCLK1 is the APB1 side used here for TIM2.
- After reset, the internal 8 MHz RC oscillator (HSI) is the default CPU clock.
- AHB and APB2 can run up to 72 MHz.
- APB1 must stay at or below 36 MHz.
- Timer clocks follow the hardware x2 rule whenever the APB prescaler is not 1.
3 Derived clocks and important exceptions
| Clock | Generated from | Main consumers | Important limit or rule |
|---|---|---|---|
| SYSCLK | HSI, HSE, or PLLCLK | Core clock root | Selected through RCC_CFGR.SW |
| HCLK | SYSCLK through HPRE | Core, memory, DMA, AHB bus | Maximum 72 MHz |
| PCLK1 | HCLK through PPRE1 | APB1 peripherals such as TIM2, USART2, I2C | Maximum 36 MHz |
| PCLK2 | HCLK through PPRE2 | APB2 peripherals such as GPIO, ADC1, USART1, SPI1, TIM1 | Maximum 72 MHz |
| ADCCLK | PCLK2 through ADCPRE | ADC interface clock | PCLK2 divided by 2, 4, 6, or 8 |
| TIMxCLK | Derived from APB domain clock | TIM1, TIM2, TIM3, TIM4 and similar timers | APB prescaler = 1 then x1, otherwise x2 |
4 RCC register map
| Offset | Register | Purpose | Trainer status |
|---|---|---|---|
| 0x00 | RCC_CR | Clock source enable and ready flags | Reference-only in current trainer |
| 0x04 | RCC_CFGR | SYSCLK switch, PLL source, PLL multiplier, AHB/APB prescalers, ADC prescaler | Reference-only in current trainer |
| 0x14 | RCC_AHBENR | AHB peripheral clock enable register | Reference-only in current trainer |
| 0x18 | RCC_APB2ENR | APB2 peripheral clock enable register | Exposed in trainer header and runtime |
| 0x1C | RCC_APB1ENR | APB1 peripheral clock enable register | Exposed in trainer header and runtime |
Official base address: RCC = 0x40021000. Trainer pointer base: RCC = 0x1800. The trainer pointer is intentionally compact; do not infer official RCC_CR or RCC_CFGR pointer layout from it.
Figure 2. Representative RCC enable-register split across AHB, APB2, and APB1
Source reconstruction: RM0008 Rev 21, Sections 7.3.6 through 7.3.8. Trainer-highlighted bits show the currently exposed subset.
5 Clock control register (RCC_CR)
Address offset: 0x00
Reset value: 0x0000 XX83
| Bit | Name | Meaning | Clock-control role |
|---|---|---|---|
| 0 | HSION | Internal 8 MHz RC enable | Turns on the reset-default clock source |
| 1 | HSIRDY | Internal 8 MHz RC ready flag | Wait until the internal oscillator is stable |
| 16 | HSEON | External high-speed clock enable | Starts crystal mode or bypass mode |
| 17 | HSERDY | External high-speed clock ready flag | Wait for stable external clock before switching |
| 18 | HSEBYP | External clock bypass | Use only when feeding an already-generated external clock into OSC_IN |
| 24 | PLLON | PLL enable | Enable PLL only after the multiplier and source are configured |
| 25 | PLLRDY | PLL lock flag | Wait here before selecting PLL as SYSCLK |
RCC_CR is the "turn it on and wait until ready" register. Each oscillator or PLL enable bit has a matching ready status bit, so software can confirm that the source is stable before routing it into SYSCLK.
6 Clock configuration register (RCC_CFGR)
Address offset: 0x04
Reset value: 0x00000000
| Field | Meaning | Clock path role |
|---|---|---|
| SW | System clock switch | Selects which source becomes SYSCLK |
| SWS | System clock switch status | Confirms which source is actually driving SYSCLK |
| HPRE | AHB prescaler | Divides SYSCLK to produce HCLK |
| PPRE1 | APB1 prescaler | Must keep APB1 at or below 36 MHz |
| PPRE2 | APB2 prescaler | Divides HCLK to produce PCLK2 |
| ADCPRE | ADC prescaler | Selects PCLK2 / 2, /4, /6, or /8 |
| PLLSRC | PLL input source | Chooses HSI/2 or HSE |
| PLLXTPRE | HSE divider before PLL | Optionally divides HSE by 2 before the PLL stage |
| PLLMUL | PLL multiplication factor | Must be programmed while PLL is disabled and must not generate more than 72 MHz |
RCC_CFGR is the "which clock do I select and how do I divide it" register. The official RM0008 wording makes a clean distinction: RCC_CR enables sources, while RCC_CFGR routes and scales them.
6.1 Clock-tree bit and field summary
| Register bit or field | Clock-tree position | Effect on clock path |
|---|---|---|
| RCC_CR.HSION | HSI 8 MHz oscillator block | Enables the internal 8 MHz oscillator. |
| RCC_CR.HSIRDY | HSI ready state | Reports that the internal oscillator is stable. |
| RCC_CR.HSEON | HSE oscillator block | Enables the external crystal or bypass-clock source. |
| RCC_CR.HSERDY | HSE ready state | Reports that the external high-speed clock is stable. |
| RCC_CR.PLLON | PLL block | Enables the PLL multiplication path. |
| RCC_CR.PLLRDY | PLL ready state | Reports that the PLL is locked. |
| RCC_CFGR.SW | SYSCLK selection mux | Selects HSI, HSE, or PLL as the requested SYSCLK source. |
| RCC_CFGR.SWS | SYSCLK status path | Reports which source is actually driving SYSCLK. |
| RCC_CFGR.HPRE | SYSCLK -> HCLK divider | Divides SYSCLK to produce the AHB clock, HCLK. |
| RCC_CFGR.PPRE1 | HCLK -> PCLK1 divider | Divides HCLK to produce the APB1 clock, PCLK1. |
| RCC_CFGR.PPRE2 | HCLK -> PCLK2 divider | Divides HCLK to produce the APB2 clock, PCLK2. |
| RCC_CFGR.ADCPRE | PCLK2 -> ADCCLK divider | Derives the ADC conversion clock from PCLK2. |
| RCC_APB2ENR.IOPAEN | PCLK2 -> GPIOA gate | Supplies the bus clock to the GPIOA register block. |
| RCC_APB2ENR.ADC1EN | PCLK2 -> ADC1 interface gate | Supplies the bus clock to the ADC1 register interface. |
| RCC_APB1ENR.TIM2EN | PCLK1 -> TIM2 gate | Supplies the bus clock to the TIM2 register and timer block. |
7 AHB/APB enable registers
7.1 AHB peripheral clock enable register (RCC_AHBENR)
| Bit | Name | Domain example |
|---|---|---|
| 0 | DMA1EN | DMA1 clock enable |
| 1 | DMA2EN | DMA2 clock enable |
| 2 | SRAMEN | SRAM interface clock enable |
| 4 | FLITFEN | Flash interface clock enable |
7.2 Enable-register domain split
- RCC_AHBENR controls AHB-side interfaces such as DMA, SRAM, and Flash interface clocks.
- RCC_APB2ENR controls high-speed peripheral register blocks such as AFIO, GPIO ports, ADC1, and USART1.
- RCC_APB1ENR controls lower-speed peripheral register blocks such as TIM2.
7.3 APB2 peripheral clock enable register (RCC_APB2ENR)
Address offset: 0x18
Reset value: 0x00000000
| Name | Bit | Mask | Write example | Unlocks |
|---|---|---|---|---|
| AFIOEN | 0 | 0x00000001 | RCC->APB2ENR |= (1u << 0); | Alternate-function routing helpers |
| IOPAEN | 2 | 0x00000004 | RCC->APB2ENR |= (1u << 2); | GPIOA register block |
| IOPBEN | 3 | 0x00000008 | RCC->APB2ENR |= (1u << 3); | GPIOB register block |
| ADC1EN | 9 | 0x00000200 | RCC->APB2ENR |= (1u << 9); | ADC1 register block |
| Enabled bits | Register value | Use case |
|---|---|---|
| AFIOEN | IOPAEN | 0x00000005 | AFIO + GPIOA |
| AFIOEN | IOPAEN | IOPBEN | 0x0000000D | AFIO + GPIOA + GPIOB |
| AFIOEN | IOPAEN | ADC1EN | 0x00000205 | AFIO + GPIOA + ADC1 |
| AFIOEN | IOPAEN | IOPBEN | ADC1EN | 0x0000020D | AFIO + GPIOA + GPIOB + ADC1 |
Example whole-register write: RCC->APB2ENR = 0x0000020D
| Bit | Name | Description |
|---|---|---|
| 0 | AFIOEN | 0x00000001; enables alternate-function routing helpers used with EXTI and timer/GPIO interaction |
| 2 | IOPAEN | 0x00000004; opens the GPIOA register path |
| 3 | IOPBEN | 0x00000008; opens the GPIOB register path |
| 9 | ADC1EN | 0x00000200; opens the ADC1 register path, while conversion clock still depends on RCC_CFGR.ADCPRE |
7.4 APB1 peripheral clock enable register (RCC_APB1ENR)
Address offset: 0x1C
Reset value: 0x00000000
| Name | Bit | Mask | Write example | Unlocks |
|---|---|---|---|---|
| TIM2EN | 0 | 0x00000001 | RCC->APB1ENR |= (1u << 0); | TIM2 register block |
| TIM3EN | 1 | 0x00000002 | RCC->APB1ENR |= (1u << 1); | TIM3 register block |
| USART2EN | 17 | 0x00020000 | RCC->APB1ENR |= (1u << 17); | USART2 register block |
| I2C1EN | 21 | 0x00200000 | RCC->APB1ENR |= (1u << 21); | I2C1 register block |
| Enabled bits | Register value | Use case |
|---|---|---|
| TIM2EN | 0x00000001 | Enable TIM2 only |
| TIM2EN | TIM3EN | 0x00000003 | Enable TIM2 and TIM3 |
Example whole-register write: RCC->APB1ENR = 0x00000001
| Bit | Name | Description |
|---|---|---|
| 0 | TIM2EN | 0x00000001; opens the TIM2 register path used by the trainer PWM route |
| 1 | TIM3EN | 0x00000002; opens the TIM3 register path |
| 17 | USART2EN | 0x00020000; opens the USART2 register path |
| 21 | I2C1EN | 0x00200000; opens the I2C1 register path |
8 Programming sequences
Official STM32F103 clock-source switching uses RCC_CR, RCC_CFGR, and FLASH_ACR. The current trainer code path keeps execution narrower and uses the APB enable registers directly.
9 Student register guide and code examples
This final section separates the current trainer code path from the full STM32F103 register path. The main chapter above remains the RCC reference section; this section is the student-oriented bridge into runnable examples.
9.1 Which RCC registers the student should look at
| Register | Current trainer status | Register role | Where it appears in the figures |
|---|---|---|---|
| RCC_CR | Full STM32F103 example | Enables HSI, HSE, and PLL sources and reports their matching ready status bits. | Figure 1 source blocks: HSI / HSE / PLL |
| RCC_CFGR | Full STM32F103 example | Defines the clock source selection, status, prescalers, PLL source, and PLL multiplication factor. | Figure 1 SW mux and AHB/APB/ADC Prescaler path |
| RCC_AHBENR | Reference table in this chapter | Enables AHB-side interfaces such as DMA, SRAM, and the Flash interface. | Figure 1 HCLK to AHB domain path |
| RCC_APB2ENR | Used directly in current trainer examples | Enables APB2 peripheral register blocks such as AFIO, GPIOA, GPIOB, ADC1, and USART1. | Figure 1 PCLK2 peripheral-enable path |
| RCC_APB1ENR | Used directly in current trainer examples | Enables APB1 peripheral register blocks such as TIM2. | Figure 1 PCLK1 peripheral-enable path |
| FLASH_ACR | Full STM32F103 example | Sets Flash wait states before raising SYSCLK. | Preparation step before the 72 MHz clock-tree example |
9.2 Current trainer route and enable examples
| Peripheral / route | Required RCC enable | Then configure | Related chapter |
|---|---|---|---|
| GPIOA output or input | GPIOA sits on APB2, so enable RCC->APB2ENR.IOPAEN. | Program GPIOA_CRL / GPIOA_CRH, then use IDR, ODR, BSRR, or BRR. | GPIOA chapter |
| ADC1 reading on PA1 / CURRENT_SENSE | GPIOA and ADC1 both sit on APB2, so enable IOPAEN and ADC1EN. Official STM32 flow also derives ADCCLK from PCLK2 through RCC_CFGR.ADCPRE. | Place PA1 in analog mode, then configure ADC channel selection, sample time, and conversion start logic. | ADC1 chapter |
| TIM2 PWM output on the trainer route | TIM2 sits on APB1, so enable RCC->APB1ENR.TIM2EN. The routed pad still needs its GPIO port clock. | Configure the GPIO pin for alternate-function output and then program timer output enable and duty registers. | TIM2 + GPIOA chapters |
| EXTI6-related routing for PA6 / HALL_IN | AFIO and the GPIO port depend on APB2 clock enables. | Configure the pad mode and then program the interrupt registers. | EXTI and GPIOA chapters |
9.3 Current trainer code example
/* Current trainer example: enable only the APB gates that the lab needs */
RCC->APB2ENR |= (1u << 0); /* AFIOEN: PCLK2 alternate-function helper gate */
RCC->APB2ENR |= (1u << 2); /* IOPAEN: PCLK2 -> GPIOA gate */
RCC->APB2ENR |= (1u << 3); /* IOPBEN: PCLK2 -> GPIOB gate */
RCC->APB2ENR |= (1u << 9); /* ADC1EN: PCLK2 -> ADC1 interface gate */
RCC->APB1ENR |= (1u << 0); /* TIM2EN: PCLK1 -> TIM2 gate */
This is the compact RCC code path students use most often in the current trainer. It enables the APB2 and APB1 peripheral register blocks needed by the visible GPIO, ADC, EXTI helper, and TIM2 routes.
9.4 Full STM32F103 example when every register is available
/* Full-register STM32F103 example: HSE 8 MHz -> PLL x9 -> SYSCLK 72 MHz */
FLASH_ACR |= 0x2u; /* FLASH latency = 2 before 72 MHz */
RCC->CR |= (1u << 16); /* HSEON: HSE source ON */
while ((RCC->CR & (1u << 17)) == 0u) { } /* HSERDY: HSE stable */
{
uint32_t cfgr = RCC->CFGR;
cfgr &= ~((1u << 16) | (1u << 17) | (0xFu << 18) | (0xFu << 4) | (0x7u << 8) | (0x7u << 11) | (0x3u << 14));
cfgr |=
(1u << 16) | /* PLLSRC = HSE: HSE -> PLL path */
(0u << 17) | /* PLLXTPRE = /1 */
(7u << 18) | /* PLLMUL = x9 */
(0u << 4) | /* HPRE = /1 */
(4u << 8) | /* PPRE1 = /2 */
(0u << 11) | /* PPRE2 = /1 */
(2u << 14); /* ADCPRE = /6 */
RCC->CFGR = cfgr;
}
RCC->CR |= (1u << 24); /* PLLON: PLL source ON */
while ((RCC->CR & (1u << 25)) == 0u) { } /* PLLRDY: PLL stable */
RCC->CFGR = (RCC->CFGR & ~0x3u) | 0x2u; /* SW = PLL */
while (((RCC->CFGR >> 2) & 0x3u) != 0x2u) { } /* SWS = PLL */
This example shows the full-device clock-source path using RCC_CR, RCC_CFGR, and FLASH_ACR. Read it along the Figure 1 path: HSE -> PLLXTPRE -> PLL -> SW -> SYSCLK -> AHB/APB/ADC Prescaler. For the HSE 8 MHz -> PLL x9 -> 72 MHz route, PLLSRC = 1 selects HSE as the PLL input.