top of page
  • Writer's pictureSunil Kumar Yadav

Overview Of Clock Source In ARM Cortex-M

Updated: Oct 29, 2023



Time isn't the main thing. It's the only thing! - Miles Davis

Microcontrollers rely on their clock source, which is essential for synchronizing the processor, bus, and peripherals. The clock's speed dictates the execution rate of processor instructions, making it a critical performance factor. However, the significance of the clock source and its frequency in microcontrollers can vary. The level of importance hinges on the specific tasks and interfaces of the microcontroller.


Two key factors must be considered: the clock's speed, influencing the pace of operations, and the clock's accuracy, affecting the consistency of time intervals between each clock cycle and potential fluctuations in clock speed over time. In this article, I'll try to go through various clock sources in typical ARM Cortex M devices and how to configure them using a simple C program.



Why the Clock Source Matters?

The central processor within the microcontroller can be envisioned as a synchronized sequence of logical blocks, each dedicated to a specific task. When the microcontroller's clock system runs at a slower pace, the processing time increases. Conversely, if the clock runs too quickly, there might not be sufficient time to finish the necessary tasks before the next cycle commences. This processor interacts with various component blocks, such as dynamic memory and interface pins. Any substantial deviation in clock speed will result in unpredictable outcomes for the microcontroller's internal operations.


Most clock sources for microcontrollers can be categorized into two main types: one relies on mechanical resonant components, like crystals and ceramic resonators, while the other utilizes electrical phase-shift circuits, such as RC (resistor, capacitor) oscillators.


Image 1 Clock Source (a) Piezoelectric electric oscillator (b) RC feedback oscillator

Differences Between Mechanical Resonators and RC Oscillators

Crystal and ceramic resonator-based oscillators (mechanical) typically provide very high initial accuracy and a moderately low-temperature coefficient. RC oscillators, in contrast, provide fast startup and low cost but generally suffer from poor accuracy over temperature and supply voltage, and show variations from 5% to 50% of nominal output frequency.


While the circuits illustrated in the above image, can produce clean reliable clock signals, their performance will be heavily influenced by environmental conditions, circuit component choice, and the layout of the oscillator circuit. Ceramic resonators and their associated load capacitance values must be optimized for operation with particular logic families. Crystals, with their higher Q, are not as sensitive to amplifier selection but are susceptible to frequency shifts (and even damage) when overdriven. Environmental factors like electromagnetic interference (EMI), mechanical vibration and shock, humidity, and temperature affect oscillator operation. These environmental factors can cause output frequency changes, increased jitter, and in severe cases, can cause the oscillator to stop functioning.



Clock Source in ARM Cortex M

ARM Cortex-M series controllers have three different clock sources. These clock sources are used to drive the system clock also known as SYSCLK. These clock sources are as follows:

  • HSI Oscillator Clock

  • HSE Oscillator Clock

  • Main PLL (Phase Locked Loop) Clock

ARM Cortex-M4(STM33F411) devices have the two following secondary clock sources:

  • 32 kHz low-speed internal RC (LSI RC) which drives the independent watchdog and, optionally, the RTC used for Auto-wakeup from the Stop/Standby mode.

  • 32.768 kHz low-speed external crystal (LSE crystal) which optionally drives the RTC clock (RTCCLK)

Note: LSI frequency will vary based on the exact Cortex-M chipset. Refer respective device datasheet for the exact frequency.


Each clock source can be switched on or off independently when it is not used, to optimize

power consumption. Image 2 shows a typical clock tree in the Cortex-M4 device.

Image 2: Clock Tree


HSI Clock

The HSI clock signal is generated from an internal 16 MHz RC oscillator (STM32F411) and can be used directly as a system clock, or used as PLL input. The HSI RC oscillator has the advantage of providing a clock source at low cost (no external components). It also has a faster startup time than the HSE crystal oscillator however, even with calibration the frequency is less accurate than an external crystal oscillator or ceramic resonator.


Let's use the below example to understand how to set the HSI clock for all the peripherals.

/** Program to understand how to configure the HSE clock on STM32 and
* use logic analyzer to measure the HSI clock frequency using MCOx pin
* (Microcontroller Clock Output). HSI in STM32F411 board is 16MHZ.
*/
#include <stdint.h>

#define RCC_BASE_ADDR  0x40023800UL  /* RCC base register address*/
#define RCC_CFGR_REG_OFFSET  0x8UL  /* RCC clock configuration register offset*/
#define RCC_CFGR_REG_ADDR  (RCC_BASE_ADDR + RCC_CFGR_REG_OFFSET)  /* RCC clock configuration register address*/
#define GPIOA_BASE_ADDR  0x40020000UL  /* Base address of GPIO A*/

int main(void)
{

// 1. Configure the RCC_CFGR MCO1 bit fields to select HSI as clock source
	volatile uint32_t *pRccCfgrReg = (volatile uint32_t*)RCC_CFGR_REG_ADDR;

	*pRccCfgrReg &= ~(0x3 << 21);   /* MCO1 clear 21 and 22 bit positions. 00: HSI clock selected, 01: LSE oscillator selected
10: HSE oscillator clock selected, 01: LSE oscillator selected*/

	*pRccCfgrReg  |= (1 << 25);    /* configure MCO1 prescaler: bit 25-26 = 1 --> MCO = System clock(HSI 16MHz)/4 == 4MHZ*/
	*pRccCfgrReg  |= (1 << 26);


// 2. Configure PA8 pin to AF0 mode (alternate mode) to behave as MCO1 signal. Refer datasheet for more details
// 2.1 Before accessing any peripheral/GPIO, we need to enable respective bus on which its connected. GPIO connected to AHB1bus
	volatile uint32_t *pRccAbh1Enr = (volatile uint32_t*)(RCC_BASE_ADDR + 0x30);
	*pRccAbh1Enr  |= (1 << 0);   /* enable GPIO A peripheral clock*/

// 2.2 Configure the mode of GPIOA pin 8 as alternate function mode
	volatile uint32_t *pGPIOAModeReg = (volatile uint32_t*)(GPIOA_BASE_ADDR + 0x00);
	*pGPIOAModeReg &= ~(0x3 << 16);  /*clear*/
	*pGPIOAModeReg |= (0x2 << 16);   /*set bit 16:17 GPIO A8. 10: Alternate function mode, 11: Analog mode etc*/

// 2.3 Configure the alternation function register to set the mode 0 for PA8
	volatile uint32_t *pGPIOAAltFunctionHighReg = (volatile uint32_t*)(GPIOA_BASE_ADDR + 0x24);
	*pGPIOAAltFunctionHighReg &= ~(0xf << 0);  /*0000:AF0 .. 111:AF15*/

	for(;;);
}

In the program provided above, I've established several macros to facilitate access to the RCC (Reset and Clock Control) register. To designate HSI as the clock source, I've configured the MCO1 bit field (bits 21-22) within the RCC Clock Configuration Register (RCC_CFGR). Given that my logic analyzer had a maximum sampling rate of 24MHz, I've employed a pre-scaler in the program to divide the 16MHz HSI clock by a factor of 4, which enhances the visualization of the output. To reduce the output clock frequency to 4MHz, I've configured the MCO1 PRE bit fields (bits 24-26) of the RCC_CFGR register.


The subsequent lines of the program are dedicated to enabling GPIO Port A's pin 8. This is achieved by enabling the AHB1 bus to which GPIOA is connected and configuring the alternate mode.


Upon connecting GPIO pin PA8 to channel 0 of the logic analyzer and grounding the logic analyzer's ground pin with the ground pin of the STM32F411 discovery board, we are ready to commence capturing the output. The output displayed by the logic analyzer reveals a frequency of 4MHz at GPIO pin PA8.

Image 3: Logic Analyzer Output

HSE Clock

The High Speed External clock signal can be generated from two possible clock sources:

  • HSE external crystal/ceramic resonator

  • HSE external user clock

The resonator and the load capacitors must be placed as close as possible to the

oscillator pins to minimize output distortion and startup stabilization time. The

loading capacitance values must be adjusted according to the selected oscillator. For a detailed explanation of HW design, refer to their respective device's reference manual.


Below is a simple example to understand how to set the HSE clock as the main clock source.

/** Program to understand how to configure the HSE clock on STM32 and
* use logic analyzer to measure the HSE clock frequency using MCOx pin
* (Microcontroller Clock Output). HSE in STM32F411 is 8MHZ.
*/

#include <stdint.h>

#define RCC_BASE_ADDR 0x40023800UL  /* RCC base register address*/
#define RCC_CFGR_REG_OFFSET 0x8UL  /* RCC clock configuration register offset*/
#define RCC_CFGR_REG_ADDR (RCC_BASE_ADDR + RCC_CFGR_REG_OFFSET)  /* RCC clock configuration register address*/
#define GPIOA_BASE_ADDR 0x40020000UL  /* Base address of GPIO A*/
#define RCC_CR_REG_OFFSET 0x00UL  /* RCC Clock configuration register offset*/
#define RCC_CR_REG_ADDR (RCC_BASE_ADDR + RCC_CR_REG_OFFSET )  /* RCC Clock configuration register*/

int main(void)
{
	volatile uint32_t *pRccCfgrReg = (volatile uint32_t*)RCC_CFGR_REG_ADDR;
	volatile uint32_t *pRccCrReg = (volatile uint32_t*)RCC_CR_REG_ADDR;       /* RCC Clock configuration register*/

//-----------------------------------------------------------------
// 1. Enable the HSE clock using HSEON bit RCC Clock configuration register (RCC_CR)
	*pRccCrReg |= (1 << 16);

// 2. Wait till external crystal clock (HSE) is stabilized
	while( !(*pRccCrReg & (1 << 17) ) );     /* HSERDY: HSE clock ready flag. 1: Oscillator clock ready and 0: Oscillator clock not ready*/

// 3. switch the system clock to HSE using RCC clock configuration register
    *pRccCfgrReg |= (1<<0);                 /* System Clock Switch (SW)Bit 1:0 => 01: HSE oscillator selected as system clock*/

//-----------------------------------------------------------------
// MCO1 setting for measurement via Logic analyzer
// 1. Configure the RCC_CFGR MCO1 bit fields to select HSI as clock source

	*pRccCfgrReg |= (1 << 22);  /* bit 22:21 => 00: HSI clock selected, 01: LSE oscillator selected
10: HSE oscillator clock selected, 01: LSE oscillator selected*/

	*pRccCfgrReg  |= (1 << 25);  /* configure MCO1 prescaler: bit 25-26 = 1 --> MCO = System clock(HSI 16MHz)/4 == 4MHZ*/
	*pRccCfgrReg  |= (1 << 26);


// 2. Configure PA8 pin to AF0 mode (alternate mode) to behave as MCO1 signal. Refer datasheet for more details
// 2.1 Before accessing any peripheral/GPIO, we need to enable respective bus on which its connected. GPIO connected to AHB1bus
	volatile uint32_t *pRccAbh1Enr = (volatile uint32_t*)(RCC_BASE_ADDR + 0x30);
	*pRccAbh1Enr  |= (1 << 0);  /* enable GPIO A peripheral clock*/

// 2.2 Configure the mode of GPIOA pin 8 as alternate function mode
	volatile uint32_t *pGPIOAModeReg = (volatile uint32_t*)(GPIOA_BASE_ADDR + 0x00);
	*pGPIOAModeReg &= ~(0x3 << 16);              /*clear*/
	*pGPIOAModeReg |= (0x2 << 16);               /*set bit 16:17 GPIO A8. 10: Alternate function mode, 11: Analog mode etc*/

// 2.3 Configure the alternation function register to set the mode 0 for PA8
	volatile uint32_t *pGPIOAAltFunctionHighReg = (volatile uint32_t*)(GPIOA_BASE_ADDR + 0x24);
	*pGPIOAAltFunctionHighReg &= ~(0xf << 0);  /*0000:AF0 .. 1111:AF15*/

	for(;;);
}

In the above program, I've defined additional macros from the HSI example. Since by default HSI is selected, we need to change the clock source from the RCC Clock Control (RCC_CR) register. To change the clock source from HSI to HSE, we need to enable the HSE ON bit( 16th Bit) of the RCC_CR register. Since it's an external crystal resonator, we need to monitor the HSE RDY bit(bit 17) using a while loop. The rest of the program remains the same and we can measure the clock output on the PA8 pin after dividing HSE by pre-scalar.


Image 4: Logic Analyzer Output


For more such examples, refer to my Github repository.





341 views0 comments

Recent Posts

See All
bottom of page