2 years ago.

STM32 Clock Sources

Hi

I'm a little confused about how the clock souce is determined in mbed for the Nucleo devices. I know by default, the ST-Link on the same board as the Nucleo's will provide a 8MHz MCO output which is used as the HSE clock.

Say you break off the ST-Link part of the Nucleo board. I'm suspecting the clock source will revert to the internal oscillator (HSI)? If you solder a 8Mhz crystal in X3 with the appropriate caps and solder bridges bridged, will the STM automatically switch to using the crystal as the source?

I tried reading through https://os.mbed.com/teams/ST/wiki/Automatic-clock-configuration

In the explanation it says the controller will look in this order:

Quote:

If several values are set in the mask, STM32 SDK tries to configure the clock in this order:

1. USE_PLL_HSE_EXTC:

2. USE_PLL_HSE_XTAL

3. USE_PLL_HSI

4. USE_PLL_MSI

But then it states:

Quote:

Note that : HSE_BYPASS and HSE_ON (USE_PLL_HSE_EXTC and USE_PLL_HSE_XTAL) are exclusive: you should not select both value in the mask.

The default clock source in the targets.json for all the STM32-family

targets.json

"clock_source": {
                "help": "Mask value : USE_PLL_HSE_EXTC (SYSCLK=72 MHz) | USE_PLL_HSE_XTAL (need HW patch) | USE_PLL_HSI (SYSCLK=64 MHz)",
                "value": "USE_PLL_HSE_EXTC|USE_PLL_HSI",
                "macro_name": "CLOCK_SOURCE"
            },

From the value for clock_source in targets.json it doesn't seem like it would fall back to the crystal? What am I missing?

2 Answers

2 years ago.

Taking a look at: https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F411xE/TARGET_NUCLEO_F411RE/system_clock.c#L96

SetSysClock() first attempts to use HSE via external clock source (Provided USE_PLL_HSE_EXTC is set). If that fails, it then tries to use HSE via external oscillator (provided USE_PLL_HSE_XTAL is set). Finally, if both fail, it reverts to setting up the HSI.

The reason USE_PLL_HSE_EXTC and USE_PLL_HSE_XTAL are exclusive is that you _cannot_ have a hardware configuration that supports both. Since the OSC_IN clock pins are shared (PH0/PH1) - you either have an external clock source or an external crystal hooked up to those pins, not both.

Note: Technically the code does not ASSERT that you only have one set - you could set them both and use that code on 2 hardware builds - one with an xtal and one with clock source. The one with the xtal should fail to set up as external clock then fallback to using xtal

posted by Curt Black 08 Jul 2018

I'm struggling with this part here in the source file you linked:

void SetSysClock()

#if ((CLOCK_SOURCE) & USE_PLL_HSE_EXTC)
    /* 1- Try to start with HSE and external clock */
    if (SetSysClock_PLL_HSE(1) == 0)
#endif
    {
#if ((CLOCK_SOURCE) & USE_PLL_HSE_XTAL)
        /* 2- If fail try to start with HSE and external xtal */
        if (SetSysClock_PLL_HSE(0) == 0)
#endif

The default targets.json sets CLOCK_SOURCE = "USE_PLL_HSE_EXTC|USE_PLL_HSI" Doesn't that mean that the second #ifdef in the above code snippet (/* 2- If fail try to start with HSE and external xtal */) won't be executed?

posted by Riaan Cillie 08 Jul 2018

Yes, you are correct

posted by Curt Black 08 Jul 2018
2 years ago.

Hello Riaan,

USE_PLL_HSE_EXTC and USE_PLL_HSE_XTAL are defined in the system_clock.c as below:

system_clock.c

//...

#define USE_PLL_HSE_EXTC     0x8  // Use external clock (ST Link MCO)
#define USE_PLL_HSE_XTAL     0x4  // Use external xtal (X3 on board - not provided by default)
#define USE_PLL_HSI          0x2  // Use HSI internal clock

//...

If we examine them in binary format then we can see that they are designed to represent bit-positions.

USE_PLL_HSE_EXTC = 0b00001000
USE_PLL_HSE_XTAL = 0b00000100
USE_PLL_HSI      = 0b00000010

Hence

CLOCK_SOURCE = USE_PLL_HSE_EXTC | USE_PLL_HSI = 0b00001100  // notice that "|" is a bitwise OR (not a logical OR)

Since & in the snippet below is a bitwise AND (not a logical AND) implies that the second #ifdef still can be executed.

void SetSysClock()

#if ((CLOCK_SOURCE) & USE_PLL_HSE_EXTC)
    /* 1- Try to start with HSE and external clock */
    if (SetSysClock_PLL_HSE(1) == 0)
#endif
    {
#if ((CLOCK_SOURCE) & USE_PLL_HSE_XTAL)
        /* 2- If fail try to start with HSE and external xtal */
        if (SetSysClock_PLL_HSE(0) == 0)
#endif