Hi,
I implemented simple Power Driver for STM32L1,
it supports:
- low power sleep mode with active chibios ST timer (tickless mode)
- switching between HSI in run mode and MSI in LP sleep mode
- configurable MSI clock source for LP sleep mode
- configurable VOS for LP
- automatic clock switching to MSI for LP mode, timer prescaler modification and back to HSI for run mode
- hooks for disable/enable periphs
- chibios drivers state checking - activate LP sleep mode if all drivers are in stop state
- simple calibration to compensate delays in CH_CFG_IDLE_ENTER_HOOK/CH_CFG_IDLE_LEAVE_HOOK
result: about 5uA in LP deep sleep mode @ 65kHz MSI
so I would like to discuss high level driver interface, some chibios modification (MSI values at least, and new feature: bit field for all drivers state) and share the source code.
Power Driver for stm32l1
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Power Driver for stm32l1
Hi,
Some of those features look too much L1-specific for an abstraction. You need to split them in:
- Required changes to existing infrastructure.
- An STM32L1-specific low power driver/library.
- Parts that can be abstracted.
Giovanni
Some of those features look too much L1-specific for an abstraction. You need to split them in:
- Required changes to existing infrastructure.
- An STM32L1-specific low power driver/library.
- Parts that can be abstracted.
Giovanni
Re: Power Driver for stm32l1
Hi,
1. Required changes to existing infrastructure
1.1. Always inline attribute to support gcc -O0: static inline void port_wait_for_interrupt(void) __attribute__((always_inline));
1.2. Switching clock source HSI <-> MSI and MSI <-> MSI requires MSI frequencies values to be rounded to power of 2.
Temporary adde to stm32l1 hal_lld.h:
#if !defined(STM32_MSI_NOTROUNDED) || STM32_MSI_NOTROUNDED == FALSE
#if STM32_MSIRANGE == STM32_MSIRANGE_64K
#define STM32_MSICLK 65500
#elif STM32_MSIRANGE == STM32_MSIRANGE_128K
#define STM32_MSICLK 131000
#elif STM32_MSIRANGE == STM32_MSIRANGE_256K
#define STM32_MSICLK 262000
#elif STM32_MSIRANGE == STM32_MSIRANGE_512K
#define STM32_MSICLK 524000
#elif STM32_MSIRANGE == STM32_MSIRANGE_1M
#define STM32_MSICLK 1050000
#elif STM32_MSIRANGE == STM32_MSIRANGE_2M
#define STM32_MSICLK 2100000
#elif STM32_MSIRANGE == STM32_MSIRANGE_4M
#define STM32_MSICLK 4200000
#else
#error "invalid STM32_MSIRANGE value specified"
#endif
#else
#if STM32_MSIRANGE == STM32_MSIRANGE_64K
#define STM32_MSICLK 65536
#elif STM32_MSIRANGE == STM32_MSIRANGE_128K
#define STM32_MSICLK 131072
#elif STM32_MSIRANGE == STM32_MSIRANGE_256K
#define STM32_MSICLK 262144
#elif STM32_MSIRANGE == STM32_MSIRANGE_512K
#define STM32_MSICLK 524288
#elif STM32_MSIRANGE == STM32_MSIRANGE_1M
#define STM32_MSICLK 1048576
#elif STM32_MSIRANGE == STM32_MSIRANGE_2M
#define STM32_MSICLK 2097152
#elif STM32_MSIRANGE == STM32_MSIRANGE_4M
#define STM32_MSICLK 4194304
#else
#error "invalid STM32_MSIRANGE value specified"
#endif
#endif
1.3 New feature request: drivers state in one bit field. Reason: Power Driver has to check all active drivers state and activates Low Power Sleep mode only if all drivers are not in Active state. Currently it is implemented this way (example of one driver check):
#define PWR_IS_DRIVER_ACTIVE(driverType, driver) {if (driver.state == driverType ## _ACTIVE) return;}
#if HAL_USE_SPI == TRUE
#if STM32_SPI_USE_SPI1 == TRUE
PWR_IS_DRIVER_ACTIVE(SPI, SPID1);
#endif
#if STM32_SPI_USE_SPI2 == TRUE
PWR_IS_DRIVER_ACTIVE(SPI, SPID2);
#endif
#endif
As result there are a lot of if statements.
So with Drivers State bit field there will be only one if statement with comparison of bit field and mask.
2. An STM32L1-specific low power driver/library: how to share source code of driver? as a post?
3. Parts that can be abstracted:
3.1 types:
Driver state: PWR_UNINIT/PWR_STOP/PWR_ACTIVE
Driver mode: values are lld specific
3.2 interface:
// Driver initialisation
void pwrInit(void);
// Driver activation
void pwrStart(void);
// Driver deactivation
void pwrStop(void);
// State of driver
pwrstate_t pwrGetState(void);
// Driver mode selection (e.g. for STM32L1 PWR_MODE_LOW_POWER_RUN or PWR_MODE_LOW_POWER_SLEEP)
void pwrSetMode(pwrmode_t mode);
// Current mode
pwrmode_t pwrGetMode(void);
// CH_CFG_IDLE_ENTER_HOOK and CH_CFG_IDLE_LEAVE_HOOK hooks implementation
inline void pwrEnterSleepMode(void) __attribute__((always_inline));
inline void pwrExitSleepMode(void) __attribute__((always_inline));
1. Required changes to existing infrastructure
1.1. Always inline attribute to support gcc -O0: static inline void port_wait_for_interrupt(void) __attribute__((always_inline));
1.2. Switching clock source HSI <-> MSI and MSI <-> MSI requires MSI frequencies values to be rounded to power of 2.
Temporary adde to stm32l1 hal_lld.h:
#if !defined(STM32_MSI_NOTROUNDED) || STM32_MSI_NOTROUNDED == FALSE
#if STM32_MSIRANGE == STM32_MSIRANGE_64K
#define STM32_MSICLK 65500
#elif STM32_MSIRANGE == STM32_MSIRANGE_128K
#define STM32_MSICLK 131000
#elif STM32_MSIRANGE == STM32_MSIRANGE_256K
#define STM32_MSICLK 262000
#elif STM32_MSIRANGE == STM32_MSIRANGE_512K
#define STM32_MSICLK 524000
#elif STM32_MSIRANGE == STM32_MSIRANGE_1M
#define STM32_MSICLK 1050000
#elif STM32_MSIRANGE == STM32_MSIRANGE_2M
#define STM32_MSICLK 2100000
#elif STM32_MSIRANGE == STM32_MSIRANGE_4M
#define STM32_MSICLK 4200000
#else
#error "invalid STM32_MSIRANGE value specified"
#endif
#else
#if STM32_MSIRANGE == STM32_MSIRANGE_64K
#define STM32_MSICLK 65536
#elif STM32_MSIRANGE == STM32_MSIRANGE_128K
#define STM32_MSICLK 131072
#elif STM32_MSIRANGE == STM32_MSIRANGE_256K
#define STM32_MSICLK 262144
#elif STM32_MSIRANGE == STM32_MSIRANGE_512K
#define STM32_MSICLK 524288
#elif STM32_MSIRANGE == STM32_MSIRANGE_1M
#define STM32_MSICLK 1048576
#elif STM32_MSIRANGE == STM32_MSIRANGE_2M
#define STM32_MSICLK 2097152
#elif STM32_MSIRANGE == STM32_MSIRANGE_4M
#define STM32_MSICLK 4194304
#else
#error "invalid STM32_MSIRANGE value specified"
#endif
#endif
1.3 New feature request: drivers state in one bit field. Reason: Power Driver has to check all active drivers state and activates Low Power Sleep mode only if all drivers are not in Active state. Currently it is implemented this way (example of one driver check):
#define PWR_IS_DRIVER_ACTIVE(driverType, driver) {if (driver.state == driverType ## _ACTIVE) return;}
#if HAL_USE_SPI == TRUE
#if STM32_SPI_USE_SPI1 == TRUE
PWR_IS_DRIVER_ACTIVE(SPI, SPID1);
#endif
#if STM32_SPI_USE_SPI2 == TRUE
PWR_IS_DRIVER_ACTIVE(SPI, SPID2);
#endif
#endif
As result there are a lot of if statements.
So with Drivers State bit field there will be only one if statement with comparison of bit field and mask.
2. An STM32L1-specific low power driver/library: how to share source code of driver? as a post?
3. Parts that can be abstracted:
3.1 types:
Driver state: PWR_UNINIT/PWR_STOP/PWR_ACTIVE
Driver mode: values are lld specific
3.2 interface:
// Driver initialisation
void pwrInit(void);
// Driver activation
void pwrStart(void);
// Driver deactivation
void pwrStop(void);
// State of driver
pwrstate_t pwrGetState(void);
// Driver mode selection (e.g. for STM32L1 PWR_MODE_LOW_POWER_RUN or PWR_MODE_LOW_POWER_SLEEP)
void pwrSetMode(pwrmode_t mode);
// Current mode
pwrmode_t pwrGetMode(void);
// CH_CFG_IDLE_ENTER_HOOK and CH_CFG_IDLE_LEAVE_HOOK hooks implementation
inline void pwrEnterSleepMode(void) __attribute__((always_inline));
inline void pwrExitSleepMode(void) __attribute__((always_inline));
Vitaly
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Power Driver for stm32l1
Hi,
I see most of your points, could you explain why the rounding to power of two is required?
About the power manager, you suggest to check if any driver is active, this is complex for several reasons:
1) The active state is not always XXX_ACTIVE, it could be XXX_READY, XXX_TRANSMITTING etc.
2) Checking drivers is platform-dependent and an nightmare of preprocessor #if.
I don't like that approach much, a possible alternative solution is introduce in the power manager an API that the other drivers can use to communicate their minimum compatible power state. This API could be called in xxx_lld_start() and xxx_lld_stop() in the code path where activation and deactivation occurs. This manager would also require an abstraction of run states and sleep states that the various drivers can use to declare their capability, for example:
PWR_RUN_FULL
PWR_RUN_LOW
PWR_STATE_HALT
PWR_STATE_STOP
PWR_STATE_STANDBY
etc
The manager would just keep track of the current state and the current "minimum", an attempt to switch to a state below the minimum would trigger an assertion and not be executed anyway.
This kind of mechanisms will have to wait for the next development branch (HAL 6), the code in the repository trunk (HAL 5) is frozen for release except small bug fixes.
Please everybody, post power-related ideas in this thread.
Giovanni
I see most of your points, could you explain why the rounding to power of two is required?
About the power manager, you suggest to check if any driver is active, this is complex for several reasons:
1) The active state is not always XXX_ACTIVE, it could be XXX_READY, XXX_TRANSMITTING etc.
2) Checking drivers is platform-dependent and an nightmare of preprocessor #if.
I don't like that approach much, a possible alternative solution is introduce in the power manager an API that the other drivers can use to communicate their minimum compatible power state. This API could be called in xxx_lld_start() and xxx_lld_stop() in the code path where activation and deactivation occurs. This manager would also require an abstraction of run states and sleep states that the various drivers can use to declare their capability, for example:
PWR_RUN_FULL
PWR_RUN_LOW
PWR_STATE_HALT
PWR_STATE_STOP
PWR_STATE_STANDBY
etc
The manager would just keep track of the current state and the current "minimum", an attempt to switch to a state below the minimum would trigger an assertion and not be executed anyway.
This kind of mechanisms will have to wait for the next development branch (HAL 6), the code in the repository trunk (HAL 5) is frozen for release except small bug fixes.
Please everybody, post power-related ideas in this thread.
Giovanni
Re: Power Driver for stm32l1
Hi,
1. rounding is required to have common CH_CFG_ST_FREQUENCY 1024Hz for all MSI values and HSI 16MHz system clock frequencies.
E.g. 16MHz for run mode and 65536 Hz for low power sleep mode, there is greatest common divisor 1024.
We will only 500Hz CH_CFG_ST_FREQUENCY for 65500 Hz MSI and 16MHz HSI.
2. Support drivers state interface
2.1 STM32L1/L0 power modes:
- low power run (not implemented yet)
- sleep (implemented in chibios idle thread)
- low power sleep (implemented in power manager driver)
- stop (not implemented yet)
- standby (not implemented yet)
STM32F4 modes:
- sleep
- stop
- standby
So it is lld specific and all drivers need to know it to register in Power Driver with minimum compatible state.
Will we have some high level abstraction and mapping to low level microcontroller specific states or have lld compatibility declaration for
PWR_RUN_FULL
PWR_RUN_LOW
PWR_STATE_HALT
PWR_STATE_STOP
PWR_STATE_STANDBY
2.2. New interfaces to support driver registration and driver state change notification
void pwrDriverRegistration(driver_id, supported_min_level, current_state)
void pwrDriverNotification(driver_id, current_state);
Is this correct?
1. rounding is required to have common CH_CFG_ST_FREQUENCY 1024Hz for all MSI values and HSI 16MHz system clock frequencies.
E.g. 16MHz for run mode and 65536 Hz for low power sleep mode, there is greatest common divisor 1024.
We will only 500Hz CH_CFG_ST_FREQUENCY for 65500 Hz MSI and 16MHz HSI.
2. Support drivers state interface
2.1 STM32L1/L0 power modes:
- low power run (not implemented yet)
- sleep (implemented in chibios idle thread)
- low power sleep (implemented in power manager driver)
- stop (not implemented yet)
- standby (not implemented yet)
STM32F4 modes:
- sleep
- stop
- standby
So it is lld specific and all drivers need to know it to register in Power Driver with minimum compatible state.
Will we have some high level abstraction and mapping to low level microcontroller specific states or have lld compatibility declaration for
PWR_RUN_FULL
PWR_RUN_LOW
PWR_STATE_HALT
PWR_STATE_STOP
PWR_STATE_STANDBY
2.2. New interfaces to support driver registration and driver state change notification
void pwrDriverRegistration(driver_id, supported_min_level, current_state)
void pwrDriverNotification(driver_id, current_state);
Is this correct?
Vitaly
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Power Driver for stm32l1
Hi,
It is correct overall, that is the general idea, it will require a huge rework of all drivers, F4 and others also have an option to clock/notclock a peripheral in sleep mode, currently it is not used.
Right now I want just to focus on next release, it is long overdue, this is work for the next development arc.
Giovanni
It is correct overall, that is the general idea, it will require a huge rework of all drivers, F4 and others also have an option to clock/notclock a peripheral in sleep mode, currently it is not used.
Right now I want just to focus on next release, it is long overdue, this is work for the next development arc.
Giovanni
Re: Power Driver for stm32l1
Hi,
ok, I will clean up current version to separate high level and low level and prepare new interface.
Just let me know how we will get drivers identification for registration/notification.
ok, I will clean up current version to separate high level and low level and prepare new interface.
Just let me know how we will get drivers identification for registration/notification.
Vitaly
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Power Driver for stm32l1
Hi,
We can discuss the topic but there are some unresolved issues in my mind.
My general idea is to have a counter for each "power state", each driver increases the counter of its minimal power state on start() and decreases it on stop(). The lowest counter != 0 is the power state compatible with current drivers situation.
Problem:
- Power states are dependent on the devices, we don't want to make SPI drivers for L0, L1, L4, F0 etc etc so we don't want drivers to "know" about states.
Giovanni
We can discuss the topic but there are some unresolved issues in my mind.
My general idea is to have a counter for each "power state", each driver increases the counter of its minimal power state on start() and decreases it on stop(). The lowest counter != 0 is the power state compatible with current drivers situation.
Problem:
- Power states are dependent on the devices, we don't want to make SPI drivers for L0, L1, L4, F0 etc etc so we don't want drivers to "know" about states.
Giovanni
Re: Power Driver for stm32l1
Hi,
L0, L1, L4, F0 and F4 have the same low power modes:
sleep
low power sleep (only for Lx)
stop (L4 has submodes)
standby
if counter shows active driver counts then it is enough to make decision: not to enter low power mode below sleep.
optional submodes currently implemented as macro in lld and add optional method in high level:
#if PWR_HAS_STOP_MODES == TRUE
void pwrSetStopSubmode(pwr_stop_mode_t mode);
pwr_stop_mode_t pwrGetStopSubmode(void);
#endif
optional low power sleep mode can be implemented as well.
So I think counter of active drivers will be a good solution
L0, L1, L4, F0 and F4 have the same low power modes:
sleep
low power sleep (only for Lx)
stop (L4 has submodes)
standby
if counter shows active driver counts then it is enough to make decision: not to enter low power mode below sleep.
optional submodes currently implemented as macro in lld and add optional method in high level:
#if PWR_HAS_STOP_MODES == TRUE
void pwrSetStopSubmode(pwr_stop_mode_t mode);
pwr_stop_mode_t pwrGetStopSubmode(void);
#endif
optional low power sleep mode can be implemented as well.
So I think counter of active drivers will be a good solution
Vitaly
Return to “Development and Feedback”
Who is online
Users browsing this forum: No registered users and 25 guests