Setting up the clock tree is fundamental for the Cortex M family but it isn't an often used capability. Generally these functions are called once in a piece of startup code and then forgotten. In the ST Micro family of Cortex-M processors, support for their clock trees has varied from family to family, both in name and in features. This proposal is to unify that support around some key API calls that would be available on all members of the family.
Generally there are a small number of clocks that can be used as the system clock for the ST Micro processors, these usually include a default "High Speed" internal (HSI) RC oscillator, a high speed, externally supplied, clock (HSE), and a PLL which is primed by either HSI or HSE and provides a system clock frequency that is different than the input frequency. The Lx family has added a medium speed internal clock (MSI) to this mix.
This proposal suggests that setup of all these clocks should be done in a function named: rcc_XXX_clock_setup(...)
where XXX is one of HSI, MSI, PLL, HSI16, or HSI4*. This function would be responsible for configuring the clock to the desired settings and activating it as the system clock. Additionally, this function is responsible for setting the three internal variables rcc_ahb_frequency
, rcc_apb1_frequency
, and rcc_apb2_frequency
. Those frequencies, stored as Hertz (so 8MHZ is 8000000), are used by other parts of the library (notably USART and TIMERs) in calculating clock related values. They also enable a simpler SysTick interface where one parameter can be used to specify the system tick rate as the base clock frequency is already available in the library.
The simplest clocks require no parameters, for example rcc_hsi_clock_setup(void)
, as the clock frequency is fixed. The most complex function, rcc_pll_clock_setup(pll_frequency, source_frequency)
requires both an indication of what frequency the PLL should generate, and the frequency of the source of that frequency. This data is required for the function to compute the final rcc_ahb_frequency
value and to pre-set the APB prescalers correctly.
* HSI4 is a 'psuedo' clock that occurs because the L0 offers the 16Mhz HSI clock with an optional "divide by 4" prescaler attached making it, for all intents, a second option of a 4Mhz high speed internal RC clock.
The second set of functions common to all members are rcc_set_ppre1
, rcc_set_ppre2
, and rcc_set_hpre
. These functions allow the programmer to scale the clocks to the APB1 bus and APB2 bus, as well as the main system clock. The clock setup functions above will configure the prescalers to the maximum allowable frequency (based on the datasheet) and these functions provide an option for changing that choice once the clock is set up.
Note that if you change hpre
it scales the system clock and that will change the values of both rcc_apb1_frequency
and rcc_apb2_frequency
as well.
The third set of functions involve the selection and activation of clocks, and descriptions of them. In this part the RCC subsystem defines an enum named rcc_osc
which has values HSI
, HSI16
, HSI4
, PLL
, HSE
, MSI
, etc. One for each possible clock on the chip. The function rcc_get_sysclk(void)
returns the oscillator currently being used as the system clock, the function rcc_set_sysclk(osc)
switches the system to the specified clock source. rcc_osc_on(osc)
and rcc_osc_off(osc)
turn on and off the specified oscillator. They are 'unsafe' in that they are not checked to see if they would shut down the system (turning off the current system clock). Finally there is rcc_osc_bypass
which is used to control whether an active oscillator is running or if the source is simply a clock input source.
There will continue to be family specific clock manipulation. Specifically HSI48
support for USB clocks, or LSE
and LSI
clock support for setting up real time clocks. As the details of these clocks vary markedly from chip family to chip family they will be different depending on the chip being used.