Not sure if this will help anyone else, but I have found a way to reduce the runtime power consumption of the Photon. These things could be done by modifying the bootloader, but as I am currently unable to reflash the bootloader, I wanted to find a way to reduce power consumption without touching the bootloader.
Note: This is highly experimental, and it messes with the internal clocks, and there are various flow on effects. However I have always been able to recover to DFU mode even when things have gone wrong.
I would be interested in feedback / suggestions / improvements on the following code. I have noted some of the problems below the sample code… In normal operating mode, my Photon(s) tend to use about 32mA with WiFi disabled.
Let me explain how this works… The SYSCLOCK runs at 120MHz with APB1 and APB2 prescalers set to 4 and 2 respectively. So we can reduce the SYSCLOCK to 60MHz by setting the AHB prescaler to 2. Then to retain the same speed for many of the peripherals we reduce APB1 and APB2 to 2 and 1…
void stm32f2_lowpower3(void) {
RCC->CFGR &= ~0xfcf0;
RCC->CFGR |= 0x0180;
SystemCoreClockUpdate();
SysTick_Configuration();
FLASH->ACR &= ~FLASH_ACR_PRFTEN;
}
The function SystemCoreClockUpdate() updates the value of the variable SystemCoreClock. This then comes into play with SysTick_Configuration.
The above function reduces power consumption to about 19.5mA (about 33%) - everything seems to operate correctly (although I have yet to test things like I2C or SPI). Of course with the slower CPU speed, your code will run slower.
The FLASH->ACR line is disabling the Prefetch for FLASH memory, which seems to reduce consumption by about 0.3mA. Not much, but every bit counts. Also disabling the RGB LED drops consumption by up to 2mA or so.
Next step we do a similar sort of thing… divide the CPU clock by 2 again. However we can no longer reduce APB2, but we can still reduce APB1 to 1…
void stm32f2_lowpower2(void) {
RCC->CFGR &= ~0xfcf0;
RCC->CFGR |= 0x0090;
SystemCoreClockUpdate();
SysTick_Configuration();
RGB.control(true);
RGB.color(0, 0, 0);
FLASH->ACR &= ~FLASH_ACR_PRFTEN;
}
The above code has SYSCLK running at 120MHz / 4 = 30MHz. Peripherals attached to APB1 will be running at normal speed, however those attached to APB2 will be running at half speed. Current consumption drops to about 13.8mA (about 55% reduction).
Finally, we can keep reducing the SYSCLK, however there is a diminishing return, so the final bit of code is about the slowest we can operate:
void stm32f2_lowpower1(void) {
RCC->CFGR &= ~0xfcf0;
RCC->CFGR |= 0x00c0;
SystemCoreClockUpdate();
SysTick_Configuration();
RGB.control(true);
RGB.color(0, 0, 0);
FLASH->ACR &= ~FLASH_ACR_PRFTEN;
}
The third bit of code above reduces consumption to about 8.75mA (about 33% of the original consumption).
There are a couple of problems at this point that need to be overcome:
- The RGB LED stepping / breathing behaves oddly (effectively flashing rather than breathing).
- Any timers set prior to the above code will need to be adjusted. The SparkIntervalTimer library needs to be patched to support the above code.
- With the highest power saving setting (the third code segment above) the serial output can misbehave, so I suspect other things like I2C and SPI will need some messing around.
During my experiments I have also found the timers / clocks seem a little “out”, however I have yet to followup on that (they tend to drift when compared to another independent time source). In my case I need relatively accurate timing and loosing a few seconds every couple of hours is not good.
So… rather than continuing to dig myself… anyone have any other suggestions???
Note: I know I said this earlier… but this is highly experimental code… so take care…