STM32 power saving: STM32F1 blue-pill manages clock and frequencies – 1

Spread the love

An essential factor of our micro-controllers is power consumption. As usual, I started to analyze that aspect without entering the detail for sleep mode but with some alternative solutions offered by the microcontroller.

STM32F1 blue pill power saving: manage clock frequencies
STM32F1 blue pill power saving: manage clock frequencies

The frequency scaling in the STM32 is more complex than in other microcontrollers, in particular, because the architecture is more complex, and STM32 chose to give the instrument to simplify that management and to masquerade the complexity.

Here my selection of STM32 STM32F103C8T6 STM32F401 STM32F411 ST-Link v2 ST-Link v2 official

Here my tested multimeters Aneng SZ18

So the first thing to do to approach that argument is to download the STM32CubeIDE because, even if you don’t develop with the native interpreter of STM32, the Arduino core wraps the function of the native core (this generates a higher space in Flash memory), and grant the possibility to use some code generator of this IDE.

STM32CubeIDE

STM32CubeIDE is an all-in-one multi-OS development tool that is part of the STM32Cube software ecosystem. STM32CubeIde is a C/C++ development platform with peripheral configuration, code generation, code compilation, and debug features for STM32 microcontrollers and microprocessors.

You can download It from this link.

Configure STM32F103 blue-pill.

This IDE allows you to program the native STM32 chip, but we don’t use the chip directly, so we need to recreate the prototyping board.

The chip-like STM32F103C8 doesn’t have an external oscillator (LSE and HSE), but the STM32F103C8 blue pill prototyping board have these peripherals integrated.

STM32F1 blue pill: 32 kHz oscillator for RTC on PC14 and PC15
STM32F1 blue pill: 32 kHz oscillator for RTC on PC14 and PC15

You can check the schema in the detailed article “STM32F103C8T6 Blue Pill: high resolution pinout and specs“.

STM32F1 blue pill: 4-to-16 MHz crystal oscillator internally connected
STM32F1 blue pill: 4-to-16 MHz crystal oscillator internally connected

To simplify your work, I share a project file with all configured as expected from STM32F103 blue pill.

Load the project by using
File --> New --> STM32 Project from Existing STM32CubeMX Configuration File (.ioc)

STM32CubeIde: import .ioc configuration files

Now you have a project completely initialized but empty. You can do double click on .ioc file, and you land on the Pinout & Configuration the tab of the configurator.

STM32CubeIde: device configuration
STM32CubeIde: device configuration

You can retrieve the clock’s complete editable block/flow diagram when you click on the clock configuration.

STM32CubeIde device configuration: Clock configuration
STM32CubeIde device configuration: Clock configuration

The diagram shows four clocks, two internal LSI and HSI, and two external HSE and LSE.

But before analyzing that diagram, we are looking at the basic architecture of an STM32.

STM32F1 Architecture

To help understand the architecture, we use a simplified diagram.

Architecture STM32
Architecture STM32

Some central parts aren’t used in this tutorial, and I describe them here.

  • DMA (Direct Memory Access, Direct memory access) allows hardware devices of different speeds to communicate; it doesn’t have to depend on the CPU’s Large amount of interrupt load.
  • SRAM(Static Random-Access Memory, Static random access memory) So-called “ static state ” means that this kind of memory, as long as it remains powered on, The data stored in it can be kept constant;
  • FSMC (Flexible Static Memory Controller, Variable static storage controller ), Ability to work with synchronous or asynchronous storage and 16 position PC Memory card connection, STM32 Of FSMC Interface support includes SRAM, NAND FLASH, NOR FLASH, and PSRAM Equal storage.
  • DRAM(Dynamic Random Access Memory, Dynamic random access memory) The data stored in it needs to be updated periodically.
    However, When the power supply stops, SRAM The stored data will still disappear (go by the name of volatile memory); it’s the same as being able to store data after a power failure ROM Or flash memory FLASH Is different.
  • ICode Bus: take M3, The instruction bus of the kernel and FLASH Command interface connection, Used for Instruction prefetching.
  • DCode Bus: take M3 Kernel data bus and FLASH Data interface connection, constant loading, and debugging.

The remaining parts are what we need, and all the peripherals are managed with two bus APBx; naturally, these buses are synchronized with clocks and are used to manage all. And now we can go in deep on clock configuration.

Clocks diagram

As already described, we can examine four clocks’ sources.

  • High-speed external clock (HSE): The external crystal oscillator is used as the clock source, and the crystal oscillator frequency is in the range of 4 to 16 MHz, and the crystal oscillator of 8 MHz is generally used.
  • High-Speed ​​Internal Clock (HSI): Generated by the internal RC oscillator with a frequency of 8 MHz, but not stable, and the accuracy is not high.
  • Low-speed external clock (LSE): The external crystal oscillator is used as the clock source, which is mainly provided to the real-time clock module, so 32.768 kHz is generally used.
  • Low-speed internal clock (LSI): Generated by the internal RC oscillator, also mainly supplied to the real-time clock module, with a frequency of approximately 40 kHz, provides a low-power clock.

Now we can add the block diagram of STM32CubeIde with the default STM32F103 blue-pill configuration again.

STM32F1 manage frequencies: clock diagram
STM32F1 manage frequencies: clock diagram

With this diagram, we can follow the flow of the clock starting from PLL Source Mux.

PLL clock source

There are two PLL clock sources: one is HSE, the other is HSI/2, and one is HSI which is the internal high-speed clock signal with a frequency of 8MHz. The frequency will drift according to temperature and environmental conditions. Generally, it is not used as the clock source of PLL, so HSE is selected as the clock source of PLL.

PLL clock PLLCLK

By setting the frequency multiplication factor of the PLL, the clock source of the PLL can be multiplied. The frequency multiplication factor can be 2 to 16. The frequency multiplication is set here by 9. Because the clock source of the PLL is set to HSE=8MHz in the previous step, the PLL clock after PLL frequency multiplication is: PLLCLK=8M×9=72MHz. 72MHz is the stable operating clock officially recommended by ST. If you want to overclock, you can increase the multiplier factor up to 128MHz. Set the PLL clock here: PLLCLK=8M×9=72MHz.

Clock Security System (CSS)

If the HSE clock fails, the HSE oscillator is automatically shut down, and the clock failure event will be sent to the brake input of the advanced timers (TIM1 and TIM8), and a clock safety interrupts CSSI will be generated, allowing the software to complete the rescue operation. This CSSI interrupt is connected to the NMI interrupt (non-maskable interrupt) of Cortex™-M3.

USB Clock

There is a full-speed USB module in STM32, and its serial interface engine requires a clock source with a frequency of 48MHz. The clock source can only be obtained from the output of the PLL (the only one). It can be divided by 1.5 or divided by 1. When the USB module is required, the PLL must be enabled and the clock frequency is configured to 48MHz or 72MHz.

System Clock Mux SYSCLK

The system clock source can be HSI, PLLCLK, and HSE. Set the system clock here: SYSCLK=PLLCLK=72MHz.

AHB bus clock HCLK

The clock obtained after the system clock SYSCLK is divided by the AHB Prescaler is called the APB bus clock, that is, HCLK, and this is delivered to AHB bus, core, memory, flash, and DMA. The frequency division factor can be [1, 2, 4, 8, 16, 64, 128, 256, 512]. The clocks of most of the peripherals on the chip are obtained by HCLK frequency division. As for the clock setting of the peripherals on the AHB bus, it must be set when the peripherals are used. Here, you only need to roughly set the APB clocks it is set to divide by 1, that is, HCLK=SYSCLK/1_AHB=72MHz.

STM32F1 blue pill: power saving with frequencies management
STM32F1 blue pill: power saving with frequencies management

APB2 bus clock PCLK2

The APB2 (High-speed peripherals) bus clock PCLK2 (Peripheral clock) is obtained by HCLK through the high-speed APB2 Prescaler, and the frequency division factor can be [1, 2, 4, 8, 16]. HCLK2 is a high-speed bus clock, and on-chip high-speed peripherals are mounted on this bus, such as all GPIO, USART1, SPI1, etc. As for the clock setting of the peripheral on the APB2 bus, it must be set when the peripheral is used. Here, it is only necessary to set the clock of APB2 roughly and set it to 1 frequency division, that is, PCLK2=HCLK=72MHz.

ADC clock

The ADC clock is obtained by PCLK2 through the ADC Prescaler, and the frequency division factor can be [2, 4, 6, 8]. Weirdly, there is no divide by 1. The ADC clock can only be up to 14MHz. If the sampling period is set to the shortest 1.5 cycles, the ADC conversion time can reach the shortest 1μs. If you really want to achieve the shortest conversion time of 1μs, then the clock of the ADC must be 14MHz, and the clock of the reverse push PCLK2 can only be 28MHz, 56MHz, 84MHz, 112MHz. Since PCLK2 is up to 72MHz, it can only take 28MHz and 56MHz.

APB1 bus clock PCLK1

The APB1 (Low-speed peripherals) bus clock PCLK1 (Peripheral clock) is obtained by HCLK through the low-speed APB Prescaler, and the frequency division factor can be [1, 2, 4, 8, 16]. HCLK1 is a low-speed bus clock, up to 36MHz; on-chip low-speed peripherals are mounted on this bus, such as USART 2/3/4/5, SPI 2/3, I2C 1/2, etc. As for the clock setting of the peripheral on the APB1 bus, it has to wait until the peripheral is used. Here, you only need to set the clock of APB1 roughly and set it to divide by 2, that is, PCLK1=HCLK/2=36MHz.

Other

Cortex system clock

The Cortex system clock is obtained by dividing HCLK by 8, which is equal to 9MHz. The Cortex system clock is used to drive the system timer SysTick of the kernel. SysTick is generally used for the clock beat of the operating system and can also be used for ordinary timing.

RTC clock, independent watchdog clock

The RTC clock can be obtained by dividing the frequency of HSE/128, or the low-speed external clock signal LSE can provide it with a frequency of 32.768kHz, or it can be provided by the low-speed internal clock signal HSI. The clock of the independent watchdog is provided by LSI and can only be provided by LSI. LSI is a low-speed internal clock signal with a frequency of 30-60kHz, generally 40kHz.

Pinout STM32 STM32F1 STM32F103 STM32F103C8 low resolution
Pinout STM32 STM32F1 STM32F103 STM32F103C8 low resolution

MCO clock output

MCO is the abbreviation of Microcontroller Clock Output. It is the clock output pin of the microcontroller. PA8 multiplexes it in the STM32 F1 series. Its primary function is to provide a clock to the outside world, which is equivalent to an active crystal oscillator. In addition to giving a clock to the outside world, we can also monitor the clock output of the MCO pin with an oscilloscope to verify that the system clock configuration is correct. The clock source of the MCO can be PLLCLK/2, HSI, HSE, and SYSCLK.

Change clock configuration

Now we know how STM32 is composed, and we can try to change that values according to the previous schema to save power.

To change that parameter STM32 core gives us the possibility to override the default value in practice; the declaration of the function in the core is marked WAKE, and so you override It with the use of extern.

WEAK void SystemClock_Config(void)
{
[...]
}

To do a full test for many situations, I use ST-LINK and external FDTI for debugging; I’d like to use Serial2 because USART2 works on APB2 and uses the low-speed buffer.

STM32: programming via ST-Link, Serial2 debug via FTDI, and Amperage check on breadboard
STM32: programming via ST-Link, Serial2 debug via FTDI, and Amperage check on a breadboard

But I think you can use the standard without a problem Serial.

STM32: programming via ST-Link, Serial debug via FTDI, and Amperage check on breadboard
STM32: programming via ST-Link, Serial debug via FTDI, and Amperage check on a breadboard

But pay attention, you can’t use USB Serial debug because we will put the USB out of order with the frequency management.

Get current frequences

Now we are going to launch a simple sketch, and we will use a series of prints to show current frequences.

/**
 * Test SysClock configuration on STM32
 * Disable USB Serial2
 * and use an external 
 * 
 * by Renzo Mischianti <www.mischianti.org>
 */

// If you use generic STM32F103C8 
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2);   // PA3  (RX)  PA2  (TX)

void setup() {
  Serial2.begin(115200);
  while (!Serial2){ delay(100); }
  Serial2.println("START!");
}

void loop() {
  Serial2.println("------------------------------------");
  Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);

  Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
  Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
  Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
  Serial2.println("------------------------------------");
  Serial2.println();
  delay(5000);
}

As expected, the result.

------------------------------------
SYCLK= 72MHz
HCLK = 72MHz
APB1 = 36MHz
APB2 = 72MHz
------------------------------------

We can see in the multimeter a power consumption of 17,20mAh.

Now we try to create a configuration that replicates standard settings, I add here the whole sketch, and now we are going to do the verification.

/**
 * Test SysClock configuration on STM32
 * Disable USB Serial2
 * and use an external 
 * 
 * by Renzo Mischianti <www.mischianti.org>
 */

// If you use generic STM32F103C8 
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2);   // PA3  (RX)  PA2  (TX)

/**
 *  Standard configuration
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 72000000
  *            HCLK(Hz)                       = 72000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 2
  *            APB2 Prescaler                 = 1
  *            PLL_Source                     = HSE
  *            PLL_Mul                        = 9
  *            Flash Latency(WS)              = 2
  *            ADC Prescaler                  = 6
  *            USB Prescaler                  = 1.5
  * @param  None
  * @retval None
  */
extern "C" void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }

}

void setup() {
  Serial2.begin(115200);
  while (!Serial2){ delay(100); }
  Serial2.println("START!");
}

void loop() {
  Serial2.println("------------------------------------");
  Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);

  Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
  Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
  Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
  Serial2.println("------------------------------------");
  Serial2.println();
  delay(5000);
}

And the output result is the same:

------------------------------------
SYCLK= 72MHz
HCLK = 72MHz
APB1 = 36MHz
APB2 = 72MHz
------------------------------------

Some minor change on power consumption (probably we change divider respect to the original) now is 17.40mAh.

First frequencies reduction test

Now we are going to make a little reduction of the frequencies, and we are going to check the result.

STM32F1 manage frequencies: power saving example
STM32F1 manage frequencies: power-saving example

As you can see in the block diagram, we make a minor reduction of the SYSCLK with the consequent decrease in the peripheral. Still, we practically maintain excellent performance of all parts.

/**
 * Test SysClock configuration on STM32
 * Disable USB Serial2
 * and use an external 
 * 
 * by Renzo Mischianti <www.mischianti.org>
 */

// If you use generic STM32F103C8 
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2);   // PA3  (RX)  PA2  (TX)

 /**
  * @brief  System Clock Configuration Reduced with good main performance
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 72000000 --> 48000000
  *            HCLK(Hz)                       = 72000000 --> 48000000
  *            AHB Prescaler                  = 1        
  *            APB1 Prescaler                 = 2        
  *            APB2 Prescaler                 = 1
  *            PLL_Source                     = HSE     --> HSE Pre DIV2
  *            PLL_Mul                        = 9       --> 12
  *            Flash Latency(WS)              = 2       --> 1
  *            ADC Prescaler                  = 6       --> 2
  *            USB Prescaler                  = 1.5     --> 1
  * @param  None
  * @retval None
  */
extern "C" void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

void setup() {
  Serial2.begin(115200);
  while (!Serial2){ delay(100); }
  Serial2.println("START!");
}

void loop() {
  Serial2.println("------------------------------------");
  Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);

  Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
  Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
  Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
  Serial2.println("------------------------------------");
  Serial2.println();
  delay(5000);
}

The console output now is

------------------------------------
SYCLK= 48MHz
HCLK = 48MHz
APB1 = 24MHz
APB2 = 48MHz
------------------------------------

But we don’t gain a lot of mAh, and the multimeter result is 14.77mAh.

Minimal reduction of frequencies

Now we will reduce some frequencies without putting out of order any peripheral to maintain the exact behavior of a sketch.

STM32F1 manage frequencies: max power saving with considerable peripheral speed reduction
STM32F1 manage frequencies: max power saving with considerable peripheral speed reduction

In this case, we are going to touch AHB Prescaler that manages HCLK speed, which, as explained, is the main bus that manages core, memory, flash, and DMA.

/**
 * Test SysClock configuration on STM32
 * Disable USB Serial2
 * and use an external 
 * 
 * by Renzo Mischianti <www.mischianti.org>
 */

// If you use generic STM32F103C8 
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2);   // PA3  (RX)  PA2  (TX)

 /**
  * @brief  System Clock Configuration Reduced with big peripheral reduction
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 72000000 --> 48000000
  *            HCLK(Hz)                       = 72000000 --> 12000000
  *            AHB Prescaler                  = 1        --> /4
  *            APB1 Prescaler                 = 2        --> /1
  *            APB2 Prescaler                 = 1
  *            PLL_Source                     = HSE     --> HSE Pre DIV2
  *            PLL_Mul                        = 9       --> 12
  *            Flash Latency(WS)              = 2       --> 1
  *            ADC Prescaler                  = 6       --> 2
  *            USB Prescaler                  = 1.5     --> 1
  * @param  None
  * @retval None
  */
extern "C" void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV4;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

void setup() {
  Serial2.begin(115200);
  while (!Serial2){ delay(100); }
  Serial2.println("START!");
}

void loop() {
  Serial2.println("------------------------------------");
  Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);

  Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
  Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
  Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
  Serial2.println("------------------------------------");
  Serial2.println();
  delay(5000);
}

And here is the console output result

------------------------------------
SYCLK= 48MHz
HCLK = 12MHz
APB1 = 12MHz
APB2 = 12MHz
------------------------------------

But naturally, as expected, we have an outstanding reduction of the power consumption, but remember, even if we maintain a good performance of the central unit, we sacrify the memory with a loss of main performance.

In this configuration, we have 8.8mAh.

Significant reduction with potential peripheral problems

Now we try to make the best reduction. To do this, we must sacrify some peripherals, but if you don’t use that (like USB or high-speed SPI), you don’t have any issue.

STM32F1 manage frequencies: max power saving
STM32F1 manage frequencies: max power saving

You can see some red rectangles, the IDE informs you that this configuration can generate issues on some peripheral, but we are going ahead and checking the result.

/**
 * Test SysClock configuration on STM32
 * Disable USB Serial2
 * and use an external 
 * 
 * by Renzo Mischianti <www.mischianti.org>
 */

// If you use generic STM32F103C8 
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2);   // PA3  (RX)  PA2  (TX)

/**
* Max reduction
* 
*  Reduced frequences, but some peripheral of APB1 can't work
* and USB MHz reduced to 8 but for full speed USB you need 48
* @brief  System Clock Configuration Reduced
*         The system Clock is configured as follow :
*            System Clock source            = PLL (HSE)
*            SYSCLK(Hz)                     = 72000000 --> 8000000
*            HCLK(Hz)                       = 72000000 --> 8000000
*            AHB Prescaler                  = 1
*            APB1 Prescaler                 = 2
*            APB2 Prescaler                 = 1
*            PLL_Source                     = HSE     --> HSE Pre DIV2
*            PLL_Mul                        = 9       --> 2
*            Flash Latency(WS)              = 0
*            ADC Prescaler                  = 6       --> 2
*            USB Prescaler                  = 1.5     --> 1
* @param  None
* @retval None
*/
extern "C" void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

void setup() {
  Serial2.begin(115200);
  while (!Serial2){ delay(100); }
  Serial2.println("START!");
}

void loop() {
  Serial2.println("------------------------------------");
  Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);

  Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
  Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
  Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
  Serial2.println("------------------------------------");
  Serial2.println();
  delay(5000);
}

The Serial output becomes

------------------------------------
SYCLK= 8MHz
HCLK = 8MHz
APB1 = 8MHz
APB2 = 4MHz
------------------------------------

And the power consumption now is 5.6mAh.

Conclusion

We noticed that the possibility of STM32 is a lot, and we must know the architecture very well if we want to get better results. We can consider to lost some peripheral if not needed and adding more power to some specified functional unit.

Of course, we immediately understand that the STM32s are qualitatively and architecturally much superior to the espressif products, but this implies more substantial knowledge of architecture.

Thanks

  1. STM32F1 Blue-Pill: pinout, specs, and Arduino IDE configuration (STM32duino and STMicroelectronics)
  2. STM32: program (STM32F1) via USB with STM32duino bootloader
  3. STM32: programming (STM32F1 STM32F4) via USB with HID boot-loader
  4. STM32F4 Black-Pill: pinout, specs, and Arduino IDE configuration
  5. STM32: ethernet w5500 with plain HTTP and SSL (HTTPS)
  6. STM32: ethernet enc28j60 with plain HTTP and SSL (HTTPS)
  7. STM32: WiFiNINA with ESP32 WiFi Co-Processor
    1. STM32F1 Blue-pill: WiFi shield (WiFiNINA)
    2. STM32F4 Black-pill: WiFi shield (WiFiNINA)
  8. How to use SD card with stm32 and SdFat library
  9. \STM32: SPI flash memory FAT FS
  10. STM32: internal RTC, clock, and battery backup (VBAT)
  11. STM32 LoRa
    1. Unleashing IoT Potential: Integrating STM32F1 Blue-Pill with EByte LoRa E32, E22, and E220 Shields
    2. Unleashing IoT Potential: Integrating STM32F4 Black-Pill with EByte LoRa E32, E22, and E220 Shields
  1. STM32 Power saving
    1. STM32F1 Blue-Pill clock and frequency management
    2. STM32F4 Black-Pill clock and frequency management
    3. Intro and Arduino vs STM framework
    4. Library LowPower, wiring, and Idle (STM Sleep) mode
    5. Sleep, deep sleep, shutdown, and power consumption
    6. Wake up from RTC alarm and Serial
    7. Wake up from the external source
    8. Backup domain intro and variable preservation across reset
    9. RTC backup register and SRAM preservation


Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *