Underclocking STM32F103 to 48MHz

petertumler
Thu Sep 24, 2015 10:58 am
Hi, I’m trying to underclock a STM32F103 (Maple Mini) to 48MHz (for energy efficiency reasons). According to the datasheet (http://www.st.com/st-web-ui/static/acti … 171190.pdf, Section 7.2) this should be possible. I found alrady some posts about overclocking (http://www.stm32duino.com/viewtopic.php … 432&p=4049) and a corresponding code snippet (https://gist.github.com/bnewbold/407140). Has somebody of you already experience with underclocking and maybe a working code snipplet? Beside setting PLL prescaler to 6 in RCC_CFGR and setting f_cpu in platform.txt/boards.txt to 48000000, which additional steps are needed? Thanks for your help. Peter

victor_pv
Thu Sep 24, 2015 1:11 pm
I underclocked successfully.
You need to do 2 things:
Set pll multiplier to 6
Set USB peripheral divider /1

That’s all for the mcu to run at 48. Then you may need some other adjustments for timing and serial, but you will get SerialUSB at 48 with just the changes above.

Find this 2 lines in boards_setup:
#define BOARD_RCC_PLLMUL RCC_PLLMUL_9
and
rcc_set_prescaler(RCC_PRESCALER_USB, RCC_USB_SYSCLK_DIV_1_5);

And change them to:
#define BOARD_RCC_PLLMUL RCC_PLLMUL_6
and
rcc_set_prescaler(RCC_PRESCALER_USB, RCC_USB_SYSCLK_DIV_1);

Last but not least. Make sure you use the very latest core downloaded from Roger’s repo, as we made several changes for that to work. I added a few things, and Roger added a ton.


stevech
Thu Sep 24, 2015 3:50 pm
an easy big power saving technique

I use the C code
WFI()


victor_pv
Fri Sep 25, 2015 12:59 am
I would also disable every peripheral that your dont need in your code.
libmaple sets the clock for most if not all of them (spis, uarts, usb, etc) on init.
If you disable their clock, they dont use any power. The datasheet shows the power consumption in the different sleep modes, speeds, and peripherals enabled or disabled.
I added this function to the core to disable clocks, to be able to change the usb multiplier after it’s enabled by the bootloader:
rcc_clk_disable(RCC_USB)

Right now is used only for disabling the usb clock before changing the multiplier and enabling it again, so I have not tested with the rest of peripherals, but it is copied from the existin rcc_clk_enable(), which is used to enable the clocks of all peripherals, so I expect it to work for disabling any peripheral clock.
If you have any trouble with it let me know.

There is another thread about low power modes, that shows some of the savings by using WFI like Stevech recommended. If you use a low quiescent voltage regulator it can do wonders.


ahull
Sun Oct 25, 2015 1:35 am
I wasn’t sure whether to post this here or in the code snippets section, but here is a condensed version of some of the work I did on low power modes over the last few days.

The sketch doesn’t do anything particularly useful, it simply monitors a PIR, or other switch and makes an LED react according to the trigger it sees from the switch. Obviously the idea can be expanded. The power saving figures are in the comments at the start of the code. I added a disableClocks() function, which appears to work, but doesn’t reduce the power significantly.

Without power saving, the sketch uses >46mA or more with power saving it drops to 12mA, so it drops to about 26% of the full power routine.
The challenge now would be to squeeze a few more mA savings without going to power down (deepsleep) mode.

I also need to work on recovering the clock back to 72MHz smoothly, without a reset, as my attempts so far have been a little hit and miss.

For what it’s worth deepsleep mode drops the power significantly, but when we wakeup, the board resets. This is not ideal for some scenarios, but would be perfectly OK for my sunset camera trigger.

// STM32_PIR_Blink - Simple Low Power demo using a PIR or other "button"

//
// Board speed is reduced to 8MHz, sketch responds to interrupt on pin BOARD_BUTTON and changes the state of the LED.
//
// At 72 MHz, the sketch consumes between 44.3mA and 46.5mA
// Slowing the clock and using WFI, the sketch consumes ~12mA - around 4mA of this will be the voltage regulator + the power LED,
// so the STM32F103R8T6 used for testing was only drawing about 8mA, while still running 32 bit ARM code at 8MHz. Not too shabby.
//
// Ensure you set the correct mode for the input, depending on your "button", one of INPUT, INPUT_PULLUP or INPUT_PULLDOWN
// Ths sketch was tested with with a button to ground (set the type to INPUT_PULLUP), and also with an HC-SR591 PIR (set the type to INPUT)
// HC-SR591 PIR => http://www.ebay.com/sch/i.html?_from=R40&_sacat=0&_sop=15&_nkw=hc-sr501%20pir%20motion%20sensor&rt=nc&LH_BIN=1&_trksid=p2045573.m1684
// These will generally operate down to 3.3V. Output *should* be 3.3 safe with VCC <20V, but check the spec. of the PIR or you may release the magic smoke.

// Defined for power and sleep functions pwr.h and scb.h
#include <libmaple/pwr.h>
#include <libmaple/scb.h>

int BOARD_LED_PIN = PB0;
int BOARD_BUTTON = PA15;
//int but = PA4;

volatile bool buttonInterruptFlag = false;

void buttonInterrupt() {
buttonInterruptFlag = true;
}

void setup() {
// Slow down to 8MHz (current drops to ~ 18mA
clockARM8();
disableClocks();
pinMode(BOARD_LED_PIN, OUTPUT);
digitalWrite(BOARD_LED_PIN, 1);
delay(1000);
pinMode(BOARD_BUTTON, INPUT);
attachInterrupt(BOARD_BUTTON, buttonInterrupt, CHANGE);
}

void loop() {
// WFI - current drops to ~12mA
asm("wfi");
if (buttonInterruptFlag)
{
// Someone pressed or released the button... Do stuff... wake up the dog, photgraph the wild life, switch on the air raid siren.. or whatever.
toggleLED();
buttonInterruptFlag = false;
}
}

void toggleLED()
{
digitalWrite(BOARD_LED_PIN, (!digitalRead(BOARD_LED_PIN)));
}

void clockARM8()
{
// Switch to the 8MHz external clock. We loose USB, but we consume 2/3 less power.
rcc_switch_sysclk(RCC_CLKSRC_HSE);
}

void disableClocks()
{
// Disable all peripheral clocks except GPIOA, GPIOB and TIMER1
rcc_clk_disable(RCC_ADC1);
rcc_clk_disable(RCC_ADC2);
rcc_clk_disable(RCC_ADC3);
rcc_clk_disable(RCC_AFIO);
rcc_clk_disable(RCC_BKP);
rcc_clk_disable(RCC_CRC);
rcc_clk_disable(RCC_DAC);
rcc_clk_disable(RCC_DMA1);
rcc_clk_disable(RCC_DMA2);
rcc_clk_disable(RCC_FLITF);
rcc_clk_disable(RCC_FSMC);
//rcc_clk_disable(RCC_GPIOA);
//rcc_clk_disable(RCC_GPIOB);
rcc_clk_disable(RCC_GPIOC);
rcc_clk_disable(RCC_GPIOD);
rcc_clk_disable(RCC_GPIOE);
rcc_clk_disable(RCC_GPIOF);
rcc_clk_disable(RCC_GPIOG);
rcc_clk_disable(RCC_I2C1);
rcc_clk_disable(RCC_I2C2);
rcc_clk_disable(RCC_PWR);
rcc_clk_disable(RCC_SDIO);
rcc_clk_disable(RCC_SPI1);
rcc_clk_disable(RCC_SPI2);
rcc_clk_disable(RCC_SPI3);
rcc_clk_disable(RCC_SRAM);
//rcc_clk_disable(RCC_TIMER1);
rcc_clk_disable(RCC_TIMER2);
rcc_clk_disable(RCC_TIMER3);
rcc_clk_disable(RCC_TIMER4);
rcc_clk_disable(RCC_TIMER5);
rcc_clk_disable(RCC_TIMER6);
rcc_clk_disable(RCC_TIMER7);
rcc_clk_disable(RCC_TIMER8);
rcc_clk_disable(RCC_TIMER9);
rcc_clk_disable(RCC_TIMER10);
rcc_clk_disable(RCC_TIMER11);
rcc_clk_disable(RCC_TIMER12);
rcc_clk_disable(RCC_TIMER13);
rcc_clk_disable(RCC_TIMER14);
rcc_clk_disable(RCC_USART1);
rcc_clk_disable(RCC_USART2);
rcc_clk_disable(RCC_USART3);
rcc_clk_disable(RCC_UART4);
rcc_clk_disable(RCC_UART5);
rcc_clk_disable(RCC_USB);
}

void systemHardReset(void) {
// Perform a system reset, see ../libmaple/nvic.c for the method
nvic_sys_reset();
// We will never get any further than this.

}


daybyter
Fri Nov 06, 2015 11:53 pm
Maybe this helps:

http://electronics.stackexchange.com/qu … -mode-stop


ahull
Sat Nov 07, 2015 1:00 am
daybyter wrote:Maybe this helps:

http://electronics.stackexchange.com/qu … -mode-stop


daybyter
Mon Nov 09, 2015 10:56 pm
Ok, I wrote some LED blink code following your example. It uses a timer interrupt to toggle the LED and let the cpu sleep the rest of the time. Without power LED is uses about 12,7 mA when the PC 13 LED is off. 14,7 while the LED is on. Long way to go.

Also need a better voltage regulator.

I’m interested in writing to an SD card and keep the current down to a minimum. My idea is to use a capacitor for the short current peaks while the actual write command is executed by the card.


mrburnette
Tue Nov 10, 2015 1:19 pm
ahull wrote:<…>better 3V3 LDo regulators

ahull
Tue Nov 10, 2015 1:22 pm
mrburnette wrote:ahull wrote:<…>better 3V3 LDo regulators

mrburnette
Tue Nov 10, 2015 1:27 pm
ahull wrote:<…>

How efficient are they with no load? Is there much leakage current?


ahull
Tue Nov 10, 2015 2:43 pm
No Load input current: 0.282mA
That’s a pretty useful figure. I think I’ll have to order a couple of those. I have a bunch of USB Power Bank (18650) devices hacked for various purposes, they are fine for +5V only projects, but something that can do a rock solid 3.3V from 5V would be very useful, particularly if it doesn’t guzzle power when the load is sleeping. It might also make a useful solar power regulator for micro-powered devices.

BTW that’s looks like the mother of all breadboards in the background of that youtube clip.


mrburnette
Tue Nov 10, 2015 4:36 pm
ahull wrote:
<…>
BTW that’s looks like the mother of all breadboards in the background of that youtube clip.

daybyter
Tue Nov 10, 2015 9:12 pm
Just checking, why my LED blinks so slowly. I used HardwareTimer.setPeriod() to set a 3s period. But since the main clock is set to 8 MHz now, and setPeriod uses a define from board.h, this won’t work.

https://github.com/rogerclarkmelbourne/ … r.cpp#L112

I think the main clock has to become a variable and we need methods like

uint32 getMainClock();
setMainClock( uint32 frequency);

to access this var? As an alternative a class would work, too. So we could add utility methods, like getMaxFreq(), getMinFreq(), getAvailableFreqs() etc.


Leave a Reply

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