CLOCKS (JSAPI_CLOCK)

An ST MCU runs with a default system clock that is relatively slow, originating from the 16 MHz HSI (internal) RC. Usually you want to run with a much faster and stable system clock generated by the MCU’s PLL (Phase Lock Loop) circuit. The ST F0 series can run up to 48 MHz, and F4 up to 96 MHz, and the F7 series can run over 100 MHz. Therefore, we will present the JSAPI_CLOCK class first, as typically one of the first lines in your main function will be a call to set up the clock so that the system runs at its optimal speed.

Clock Tree

ST MCU clocks can be notoriously difficult to set up. Indeed, a study of the system “clock-tree” is important to understand how the device generates clocks for different subsystems:image

As you can see, the clock tree is complex. The input can come from a number of sources, and is then multiplied and divided to generate the system clocks and various bus clocks. To top it off, different ST MCU series have different clock trees, and sometimes even a particular device may have a slightly different clock tree than the other devices in the same series. Furthermore, some peripherals such as USB have clock speed requirements that might limit the choices of the system clock speed.

No wonder then that other than using JSAPI, the easiest way to get the clock set up correctly for an ST MCU is to use ST’s graphical configuration tool CubeMX. With CubeMX, you can specify the input source and the desired output clock behaviors, and the tool does the rest. There are, however, issues with using CubeMX but we will not get into them here [1].

JSAPI_CLOCK Object

This table lists the API function of the JSAPI_CLOCK object. The clock system shown below is for the STM32F0xx series at the low end of the ST MCU lines, which has more limited capabilities than the other ST series, as reflected by the API functions.

In most applications, only the call to jsapi_clock.SetSystemClock is needed. This call is all that is required to set your MCU to run at a specified PLL frequency. The other API are for advanced uses. One caveat is that sometimes the requested PLL speed is not possible, so it is always a good idea to check the actual system speed by calling the GetSysClkFreq function, or even to verify the actual system clock signal using a logic analyzer and one of the SetMCO functions, as detailed in the below example.

Due to different clock tree implementations in different STM32 series, this set of JumpStart API functions, and especially the enum values, are series-dependent, more so than the other JSAPI categories.

Enum Values JSAPI_CLOCK functions

STM32F0xx

enum CLOCK_SRC {

    CLK_HSI,    

    CLK_HSE,    

    CLK_HSEBYP, 

    CLK_PLL,    

    CLK_HSI48   

};

 

enum MCO_SOURCE {

    MCO1_NONE,

    MCO1_HSI,

    MCO1_LSE,

    MCO1_HSE,

    MCO1_PLL

};

 

typedef struct {

    unsigned SYSCLK_Frequency;

    unsigned HCLK_Frequency;

    unsigned PCLK_Frequency;

    unsigned ADCCLK_Frequency;

    unsigned CECCLK_Frequency;

    unsigned I2C1CLK_Frequency;

    unsigned USART1CLK_Frequency;

    unsigned USART2CLK_Frequency;

    unsigned USART3CLK_Frequency;

    unsigned USBCLK_Frequency;  

} JSAPI_ClocksTypeDef;

STM32F3xx

STM32F4xx

STM32F7xx

enum CLOCK_SRC {

    CLK_HSI,   

    CLK_HSE,   

    CLK_HSEBYP,

    CLK_PLL,   

    CLK_PLLR   

};

STM32F3xx

enum MCO_SOURCE {

    MCO1_NONE,

    MCO1_HSI,

    MCO1_LSE,

    MCO1_HSE,

    MCO1_PLL

};

 

typedef struct {

    unsigned SYSCLK_Frequency;

    unsigned HCLK_Frequency;

    unsigned PCLK1_Frequency;

    unsigned PCLK2_Frequency;

    unsigned I2C1CLK_Frequency;

} JSAPI_ClocksTypeDef;

STM32F4xx

STM32F7xx

enum MCO1_SOURCE {

    MCO1_NONE,

    MCO1_HSI,

    MCO1_LSE,

    MCO1_HSE,

    MCO1_PLL

}; 

 

enum MCO2_SOURCE {

    MCO2_NONE,

    MCO2_SYSCLK,

    MCO2_PLL2S,

    MCO2_HSE,

    MCO2_PLL

};

 

typedef struct {

    unsigned SYSCLK_Frequency;

    unsigned HCLK_Frequency;

    unsigned PCLK1_Frequency;

    unsigned PCLK2_Frequency;

} JSAPI_ClocksTypeDef;

 

JSAPI_CLOCK API Functions

Object

extern JSAPI_CLOCK jsapi_clock;

STM32F3xx/STM32F4xx/STM32F7xx Specific

Initialize the system clock

bool SetSystemClock(

    unsigned hsi_mhz,    // HSI frequency

    unsigned hse_mhz,    // HSE frequency
    bool hse_bypass,     // is HSE bypass set?

    unsigned pll_mhz,    // desired PLL frequency

    unsigned flash_ws    // flash wait state

);

Get clock divide factors

void GetClockFactors(

    int *ahbdiv,        // AHB divider, SYSCLK->HCLK

    int *apb1div,       // APB1 divider, APB1CLK

    int *apb2div,       // APB2 divider, APB2CLK

    int *tim1mul,       // PCLK1 multiplier

    int *tim2mul        // PCLK2 multiplier

);

Set clock divide factors

int SetClockFactors(

    int ahbdiv,

    int apb1div,

    int apb2div,

    int DCKCFGR_TIMPRE

);

Set MCO1

void SetMCO1(

    enum MCO1_SOURCE source,

    unsigned div_factor

);

Set MCO2

void SetMCO2(

    enum MCO2_SOURCE source,

    unsigned div_factor

);

STM32F0xx Specific

STM32F0xx: initialize the system clock

bool SetSystemClock(

    unsigned hsi_mhz,    // HSI frequency

    bool hsi_div2,       // HSI divided by 2?

    unsigned hse_mhz,    // HSE frequency
    bool hse_bypass,     // is HSE bypass set?

    unsigned pll_mhz     // desired PLL frequency

);

Get clock divide factors

void GetClockFactors(

    int *ahbdiv,        // AHB divider, SYSCLK->HCLK

    int *apbdiv,        // APB1 divider, APBCLK

    int *timmul         // PCLK multiplier

);

Set MCO

void SetMCO(

    enum MCO_SOURCE source,

    unsigned div_factor,

    struct JSAPI_GPIO *port,

    unsigned pin_no,

    unsigned af

);

Generic Common Routines

Return the SYSCLK frequency

unsigned GetSysClkFreq(void);

Return the current clock source

enum CLOCK_SRC GetCurrentClockSource(void);

Get the clock frequencies

void RCC_GetClocksFreq(JSAPI_ClocksTypeDef *RCC_Clocks);

 

How to Use

To use the JumpStart Clock API, with jsapi_clock:

1.       Call SetSystemClock to initialize the system clock.

In most cases, that’s all you need to do. The other API function(s) you may use are the SetMCOx calls. MCO stands for Microcontroller Clock Output. All Cortex-M MCUs, regardless of their vendors, can route the clock signal to a GPIO pin. MCO is useful to check the basic operations of your system (e.g. to ensure that the MCU is running at the expected speed), and it can be used as a clock signal to drive external hardware components.

Example 1: Setting Up the System Clock

// Set up the clock

jsapi_clock.SetSystemClock(16, 0, false, 84, 5);

 

In this code fragment, HSI is used as the clock source, which is always running at 16MHz. The argument HSE_MHz is set to 0, since HSE is not being used. The target PLL frequency is 84 MHz, and the flash wait state is 5.

Example 2: Viewing the MCO

In this example, we set the system to run at a more leisurely rate of 30MHz [2], and then we divide the system clock by 5 (the maximum divide factor possible), so that the waveform can be observed using a low cost logic analyzer such as Saleae’s “Logic”.

#define DIVFACTOR   5

    jsapi_clock.SetMCO2(MCO2_SYSCLK, DIVFACTOR);

    …

 

Per recommendation in ST’s documentation, this call should be made before the call to set up the system clock.

The STM32F4xx series has two MCOs: MCO1 and MCO2. MCO1 uses PortB.8 and MCO2 uses PortC.9. On the ST-Nucleo board, PortC.9 is the first pin of ST’s Morpho connector on the right (please refer to ST’s ST-Nucleo documentation for details). In this example, MCO2 is used because the system clock (MCO2_SYSCLK) can be used as the source.

The logic analyzer display validates that the MCO2 is indeed 30MHz / 5, or 6 MHz.

image