Last active
December 4, 2023 10:53
-
-
Save tomtor/3dff8dd2f2514912aacbab1b1652be76 to your computer and use it in GitHub Desktop.
stm32 low power sleep code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <libmaple/pwr.h> | |
#include <libmaple/scb.h> | |
#include <RTClock.h> | |
// Define the Base address of the RTC registers (battery backed up CMOS Ram), so we can use them for config of touch screen or whatever. | |
// See http://stm32duino.com/viewtopic.php?f=15&t=132&hilit=rtc&start=40 for a more details about the RTC NVRam | |
// 10x 16 bit registers are available on the STM32F103CXXX more on the higher density device. | |
#define BKP_REG_BASE ((uint32_t *)(0x40006C00 + 0x04)) | |
RTClock rt(RTCSEL_LSI, 39); // 1 milli second alarm | |
void storeBR(int i, uint32_t v) { | |
BKP_REG_BASE[2*i]= (v << 16); | |
BKP_REG_BASE[2*i+1]= (v & 0xFFFF); | |
} | |
uint32_t readBR(int i) { | |
return ((BKP_REG_BASE[2*i] & 0xFFFF) >> 16) | (BKP_REG_BASE[2*i+1] & 0xFFFF); | |
} | |
void sleepMode(bool deepSleepFlag) | |
{ | |
// Clear PDDS and LPDS bits | |
PWR_BASE->CR &= ~(PWR_CR_LPDS | PWR_CR_PDDS | PWR_CR_CWUF); // Thanks @dalbert2 for spotting the missing ~ | |
// Set PDDS and LPDS bits for standby mode, and set Clear WUF flag (required per datasheet): | |
PWR_BASE->CR |= PWR_CR_CWUF; | |
// Enable wakeup pin bit. | |
PWR_BASE->CR |= PWR_CSR_EWUP; | |
SCB_BASE->SCR |= SCB_SCR_SLEEPDEEP; | |
// System Control Register Bits. See... | |
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/Cihhjgdh.html | |
if (deepSleepFlag) { | |
// Set Power down deepsleep bit. | |
PWR_BASE->CR |= PWR_CR_PDDS; | |
// Unset Low-power deepsleep. | |
PWR_BASE->CR &= ~PWR_CR_LPDS; | |
} else { | |
adc_disable(ADC1); | |
adc_disable(ADC2); | |
#if STM32_HAVE_DAC | |
dac_disable_channel(DAC, 1); | |
dac_disable_channel(DAC, 2); | |
#endif | |
// Unset Power down deepsleep bit. | |
PWR_BASE->CR &= ~PWR_CR_PDDS; | |
// set Low-power deepsleep. | |
PWR_BASE->CR |= PWR_CR_LPDS; | |
} | |
// Now go into stop mode, wake up on interrupt | |
asm(" wfi"); | |
// Clear SLEEPDEEP bit so we can use SLEEP mode | |
SCB_BASE->SCR &= ~SCB_SCR_SLEEPDEEP; | |
} | |
uint32 sleepTime; | |
void AlarmFunction () { | |
// We always wake up with the 8Mhz HSI clock! | |
// So adjust the clock if needed... | |
#if F_CPU == 8000000UL | |
// nothing to do, using about 12 mA | |
#elif F_CPU == 16000000UL | |
rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_2); | |
#elif F_CPU == 48000000UL | |
rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_6); | |
#elif F_CPU == 72000000UL | |
rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_9); // 72MHz => 48 mA -- datasheet value => between 40 and 41mA | |
#else | |
#error "Unknown F_CPU!?" | |
#endif | |
extern volatile uint32 systick_uptime_millis; | |
systick_uptime_millis+= sleepTime; | |
} | |
void mdelay(int n, bool mode= false) | |
{ | |
sleepTime= n; | |
time_t nextAlarm = (rt.getTime() + n); // Calculate from time now. | |
rt.createAlarm(&AlarmFunction, nextAlarm); | |
sleepMode(mode); | |
} | |
#define RCC_CFGR_HPRE_DIV1 0x00000000U | |
#define RCC_CFGR_HPRE_DIV2 0x00000080U | |
#define RCC_CFGR_HPRE_DIV4 0x00000090U | |
#define RCC_CFGR_HPRE_DIV8 0x000000A0U | |
#define RCC_CFGR_HPRE_DIV16 0x000000B0U | |
#define RCC_CFGR_HPRE_DIV64 0x000000C0U | |
#define RCC_CFGR_HPRE_DIV128 0x000000D0U | |
#define RCC_CFGR_HPRE_DIV256 0x000000E0U | |
#define RCC_CFGR_HPRE_DIV512 0x000000F0U | |
void msleep(uint32_t ms) | |
{ | |
uint32_t start= rt.getTime(); | |
while (rt.getTime() - start < ms) { | |
asm(" wfi"); | |
} | |
} | |
void setup() { | |
//rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_2); | |
pinMode(PA0, INPUT_ANALOG); | |
pinMode(PA1, INPUT_ANALOG); | |
pinMode(PA2, INPUT_ANALOG); | |
pinMode(PA3, INPUT_ANALOG); | |
pinMode(PA4, INPUT_ANALOG); | |
pinMode(PA5, INPUT_ANALOG); | |
pinMode(PA6, INPUT_ANALOG); | |
pinMode(PA7, INPUT_ANALOG); | |
pinMode(PA8, INPUT_ANALOG); | |
pinMode(PA9, INPUT_ANALOG); | |
pinMode(PA10, INPUT_ANALOG); | |
pinMode(PA11, INPUT_ANALOG); | |
pinMode(PA12, INPUT_ANALOG); | |
pinMode(PA13, INPUT_ANALOG); | |
pinMode(PA14, INPUT_ANALOG); | |
pinMode(PA15, INPUT_ANALOG); | |
pinMode(PB0, INPUT_ANALOG); | |
pinMode(PB1, INPUT_ANALOG); | |
pinMode(PB2, INPUT_ANALOG); | |
pinMode(PB3, INPUT_ANALOG); | |
pinMode(PB4, INPUT_ANALOG); | |
pinMode(PB5, INPUT_ANALOG); | |
pinMode(PB6, INPUT_ANALOG); | |
pinMode(PB7, INPUT_ANALOG); | |
pinMode(PB8, INPUT_ANALOG); | |
pinMode(PB9, INPUT_ANALOG); | |
pinMode(PB10, INPUT_ANALOG); | |
pinMode(PB11, INPUT_ANALOG); | |
pinMode(PB12, INPUT_ANALOG); | |
pinMode(PB13, INPUT_ANALOG); | |
pinMode(PB14, INPUT_ANALOG); | |
pinMode(PB15, INPUT_ANALOG); | |
// We have just started or woken up from sleep! System clock is set to 72MHz HSE. | |
Serial.begin(115200); | |
#if 0 | |
delay(1000); | |
//#if 0 | |
char str[100]; | |
sprintf(str, "%x", *(uint32*)0xE0042004); | |
Serial.println(str); | |
pinMode(PA12, INPUT_ANALOG); | |
randomSeed(rt.getTime()); | |
Serial.println(""); | |
long m1= micros(); | |
delayMicroseconds(2000); | |
long m2= micros(); | |
Serial.println(m2-m1); | |
storeBR(0, readBR(0)+1); | |
Serial.print(readBR(0)); | |
Serial.print(": "); | |
Serial.print(rt.getTime()/100/3600%24); Serial.print(':'); | |
Serial.print(rt.getTime()/100/60%60); Serial.print(':'); | |
Serial.println(rt.getTime()/100%60); | |
//delay(1500); | |
// for (int d= 0, e= random(10000); d < e; d++) | |
// millis(); | |
// | |
// long t1= systick_get_count(), m1= millis(), mi1= micros(); | |
// long sum= 0; | |
// for (int i= 0; i < 99; i++) | |
// sum+= i; | |
// long t2= systick_get_count(), m2= millis(), mi2= micros(); | |
// Serial.println(sum); | |
// Serial.println(t1); | |
// Serial.println(t2); | |
// Serial.println(t1-t2); | |
// Serial.println(mi2-mi1); | |
#endif | |
} | |
void blinkN(int n, int d= 400, int t= 800) | |
{ | |
pinMode(LED_BUILTIN, OUTPUT); | |
for (int i= 0; i < n; i++) { | |
digitalWrite(LED_BUILTIN, 0); | |
mdelay(5); | |
digitalWrite(LED_BUILTIN, 1); | |
mdelay(d); | |
} | |
pinMode(LED_BUILTIN, INPUT_ANALOG); | |
mdelay(t); | |
} | |
void blinkTemp(int n, int d= 500, int t= 800) | |
{ | |
const int tempBlinkPin= PB7; | |
pinMode(tempBlinkPin, OUTPUT); | |
for (int i= 0; i < n; i++) { | |
digitalWrite(tempBlinkPin, 0); | |
mdelay(5); | |
digitalWrite(tempBlinkPin, 1); | |
mdelay(d); | |
} | |
pinMode(tempBlinkPin, INPUT_ANALOG); | |
mdelay(t); | |
} | |
#define tempPin PA0 | |
#define powerPin PA2 | |
void loop() { | |
adc_enable(ADC1); | |
adc_reg_map *regs = ADC1->regs; | |
regs->CR2 |= ADC_CR2_TSVREFE; // enable VREFINT and temp sensor | |
regs->SMPR1 = (ADC_SMPR1_SMP17 /* | ADC_SMPR1_SMP16 */); // sample rate for VREFINT ADC channel | |
//int vref= 3300; | |
int vref = 1200 * 4096 / adc_read(ADC1, 17); // ADC sample to millivolts | |
regs->CR2 &= ~ADC_CR2_TSVREFE; // disable VREFINT and temp sensor | |
//float tempr; | |
// following 1.43 and 0.0043 parameters come from F103 datasheet - ch. 5.9.13 | |
// and need to be calibrated for every chip (large fab parameters variance) | |
//tempr = (1.43 - (vref * adc_read(ADC1, 16) / 4096.0 / 1024)) / 0.0043 + 25.0; | |
pinMode(powerPin, OUTPUT); | |
digitalWrite(powerPin, 1); | |
int v= analogRead(tempPin); | |
pinMode(powerPin, INPUT_ANALOG); | |
double steinhart= v; | |
steinhart = 4095 / steinhart - 1; | |
steinhart = 10000 * steinhart; | |
steinhart = steinhart / 10000; // (R/Ro) | |
steinhart = log(steinhart); // ln(R/Ro) | |
steinhart /= 4050; // 1/B * ln(R/Ro) | |
steinhart += 1.0 / (25 + 273.15); // + (1/To) | |
steinhart = 1.0 / steinhart; // Invert | |
steinhart -= 273.15; // convert to C | |
double Temp= steinhart; | |
// double Temp = log(10000.0*((4096.0/v-1))); | |
// // =log(10000.0/(4096.0/v-1)) // for pull-up configuration | |
// Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp ); | |
// Temp = Temp - 273.15; // Convert Kelvin to Celcius | |
//Serial.println(vref); delay(2); | |
vref+= 5; | |
if (vref < 2000 || vref >= 3000) | |
blinkN(vref / 1000); | |
blinkN(vref % 1000 / 100); | |
blinkN(vref % 100 / 10); | |
Temp+= 0.5; // round | |
blinkTemp(int(Temp) / 10); | |
blinkTemp(int(Temp) % 10); | |
//blinkTemp(int(Temp * 10) % 10); | |
//int power= (2 * (vref * v / 4095) / 10); | |
//Serial.println(power); | |
// delay(3000); | |
// rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_CFGR_HPRE_DIV8); | |
// delay(3000 / 8); | |
// rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_CFGR_HPRE_DIV1); | |
msleep(3000); | |
//mdelay(5000); | |
} |
Line 25 looks like it has an error (you need to invert those bits to clear them):
PWR_BASE->CR &= ~(PWR_CR_LPDS | PWR_CR_PDDS | PWR_CR_CWUF);
They ARE inverted, the ~ is before (...)
The ~( ) was my suggestion....they aren't there at the moment; scroll up and take a look at line 25.
The ~( ) was my suggestion....they aren't there at the moment; scroll up and take a look at line 25.
Ah, I see.
I probably copied those specific lines from some piece of example code, because the comments are not my style.
I will change it, thanks for your feedback!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 25 looks like it has an error (you need to invert those bits to clear them):
PWR_BASE->CR &= ~(PWR_CR_LPDS | PWR_CR_PDDS | PWR_CR_CWUF);