2021-05-04 10:19 PM
When attempting to add an accelerometer to the LoRaWAN_End_Node program, on the SPI1 interface, I find that it works (in polled mode) until the board sleeps and is woken with an interrupt. Then the SPI1 Control Register 1 and Control Register 2 are changed (so the accelerometer can't be accessed).
I have spent ages chasing this, as the exact nature of the corruption changes as my code changes. Because the corruption showed as soon as an interrupt happens (GPIO (button press), timer, subGHz radio) I assumed the problem was related to interrupts. Then I figured the corruption might be due to activity just before the interrupt, as the chip is put in a low power state. Sure enough, changing LOW_POWER_DISABLE from 0 to 1 in sys_conf.h makes the problem vanish.
Note the corruption varies and sometimes there is no corruption: CR1 can change from 0x035f to 0x0040 or to 0x0000 and CR2 can change from 0x1700 to 0x0700. The values are the same for a given build. It is almost as though it is code-position-dependent.
Can someone please tell me why the SPI interface is broken as I describe, and what I can do about it.
I see that AN5406 includes "7.3 Low-power functions" which I assume is relevant, but I would appreciate more specific guidance.
Will this problem exist with other peripherals, as well as the SPI?
Solved! Go to Solution.
2021-06-04 08:20 AM
Hello,
you can find some valuable information in this video
https://st-onlinetraining.s3.amazonaws.com/STM32WL-System-Power-control-PWR/index.html
slide 4 and 28 are of particular interest for this topic.
So indeed, when entering stop2 mode, the SPI registers are not retained and registers content will be lost.
So the SPI (and any other not retained IP) configuration must be stored before entering stop2 and restored when waking up from stop2.
from FW perspective, this must indeed be carefully managed. The low power manager (LPM) helps to do this.
/* Disable StopMode when traces need to be printed */
void UTIL_ADV_TRACE_PreSendHook(void)
{
/* USER CODE BEGIN UTIL_ADV_TRACE_PreSendHook_1 */
/* USER CODE END UTIL_ADV_TRACE_PreSendHook_1 */
UTIL_LPM_SetStopMode((1 << CFG_LPM_UART_TX_Id), UTIL_LPM_DISABLE);
/* USER CODE BEGIN UTIL_ADV_TRACE_PreSendHook_2 */
/* USER CODE END UTIL_ADV_TRACE_PreSendHook_2 */
}
/* Re-enable StopMode when traces have been printed */
void UTIL_ADV_TRACE_PostSendHook(void)
{
/* USER CODE BEGIN UTIL_LPM_SetStopMode_1 */
/* USER CODE END UTIL_LPM_SetStopMode_1 */
UTIL_LPM_SetStopMode((1 << CFG_LPM_UART_TX_Id), UTIL_LPM_ENABLE);
/* USER CODE BEGIN UTIL_LPM_SetStopMode_2 */
/* USER CODE END UTIL_LPM_SetStopMode_2 */
}
Hope this helps.
Best regards
2021-05-05 08:07 AM
Which low power mode? Some of them conserve power by removing power from peripherals.
2021-05-05 02:13 PM
I am using the code present in the LoRaWAN_End_Node example, which uses code in stm32_lpm_if.c (documented in AN4506 - inadequately, IMHO). Looking at the code it seems that ST make available three modes, intended to be increasingly severe:
There seems to be a good deal of flexibility in stm32_lpm_if.c as to what happens in each mode (but little guidance from ST as to how to choose).
In the LoRaWAN_End_Node example "off mode" is disabled by default by a call to UTIL_LPM_SetOffMode(), so be default "stop mode" would have been used. When I changed LOW_POWER_DISABLE from 0 to 1 "stop mode" was also disabled, so "sleep mode" would be used.
So my experience seems to be: Stop 2 mode corrupts SPI register settings but sleep mode does not. Sure enough: Table 37 in RM0461 Reference Manual shows there is no "R" (indicating data retained) against the SPI peripheral in Stop 2 mode (but Stop mode 0 or 1 should be OK). I guess it would also be possible to re-initialise the SPI interface on exit from low power, or before use....
So there we have the explanation. This wasted a load of my time. It would be good if ST could provide more prominant pointers to power management, and how to make intelligent selections from what is a very complex area.
2021-06-04 08:20 AM
Hello,
you can find some valuable information in this video
https://st-onlinetraining.s3.amazonaws.com/STM32WL-System-Power-control-PWR/index.html
slide 4 and 28 are of particular interest for this topic.
So indeed, when entering stop2 mode, the SPI registers are not retained and registers content will be lost.
So the SPI (and any other not retained IP) configuration must be stored before entering stop2 and restored when waking up from stop2.
from FW perspective, this must indeed be carefully managed. The low power manager (LPM) helps to do this.
/* Disable StopMode when traces need to be printed */
void UTIL_ADV_TRACE_PreSendHook(void)
{
/* USER CODE BEGIN UTIL_ADV_TRACE_PreSendHook_1 */
/* USER CODE END UTIL_ADV_TRACE_PreSendHook_1 */
UTIL_LPM_SetStopMode((1 << CFG_LPM_UART_TX_Id), UTIL_LPM_DISABLE);
/* USER CODE BEGIN UTIL_ADV_TRACE_PreSendHook_2 */
/* USER CODE END UTIL_ADV_TRACE_PreSendHook_2 */
}
/* Re-enable StopMode when traces have been printed */
void UTIL_ADV_TRACE_PostSendHook(void)
{
/* USER CODE BEGIN UTIL_LPM_SetStopMode_1 */
/* USER CODE END UTIL_LPM_SetStopMode_1 */
UTIL_LPM_SetStopMode((1 << CFG_LPM_UART_TX_Id), UTIL_LPM_ENABLE);
/* USER CODE BEGIN UTIL_LPM_SetStopMode_2 */
/* USER CODE END UTIL_LPM_SetStopMode_2 */
}
Hope this helps.
Best regards
2021-07-26 10:23 PM
A short update ( and thanks to @YBOUV.1). I have returned to this project to look at reducing power consumption.
Previously I had set LOW_POWER_DISABLE to 1 to prevent entering STOP 2 mode and so preserve my SPI registers. I have now added code at the start of PWR_EnterStopMode() to save the values of SPI2 CR1 and CR2 registers. (There are other SPI read/write registers but I don't use them). I restore them at the end of PWR_ExitStopMode(). I can now restore LOW_POWER_DISABLE to 0, and my LIS2DW accelerometer on the SPI port works fine.
I draw 15uA from 3.3V with the accelerometer sampling at 200Hz and 2uA with the accelerometer still operational but in the 1.6Hz low power mode. This is good!
2021-08-12 11:27 AM
@charles23, can you please share with us your modified PWR_EnterStopMode() and PWR_ExitStopMode() functions? :smiling_face_with_smiling_eyes:
2021-08-13 01:57 AM
OK, here is what I have done, but use at your own risk, and this is unsupported!
First, the changes in PWR_EnterStopMode() and PWR_ExitStopMode():
void PWR_EnterStopMode(void)
{
/* USER CODE BEGIN EnterStopMode_1 */
// Save and restore SPI registers either side of STOP2 mode, since registers are not preserved
if (boardUsesSPI()) {
spiSaveState();
}
/* USER CODE END EnterStopMode_1 */
/* Suspend sysTick : work around for degugger problem in dual core (tickets 71085, 72038, 71087 ) */
HAL_SuspendTick();
/* Clear Status Flag before entering STOP/STANDBY Mode */
LL_PWR_ClearFlag_C1STOP_C1STB();
/* USER CODE BEGIN EnterStopMode_2 */
/* USER CODE END EnterStopMode_2 */
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
/* USER CODE BEGIN EnterStopMode_3 */
/* USER CODE END EnterStopMode_3 */
}
void PWR_ExitStopMode(void)
{
/* USER CODE BEGIN ExitStopMode_1 */
/* USER CODE END ExitStopMode_1 */
/* Resume sysTick : work around for degugger problem in dual core */
HAL_ResumeTick();
/*Not retained periph:
ADC interface
DAC interface USARTx, TIMx, i2Cx, SPIx
SRAM ctrls, DMAx, DMAMux, AES, RNG, HSEM */
/* Resume not retained USARTx and DMA */
vcom_Resume();
/* USER CODE BEGIN ExitStopMode_2 */
// Save and restore SPI registers either side of STOP2 mode, since registers are not preserved
if (boardUsesSPI()) {
spiRestoreState();
}
/* USER CODE END ExitStopMode_2 */
}
Then spiSaveState() and spiRestoreState() are added to my own spi.c file:
// Save and restore SPI registers either side of STOP2 mode, since registers are not preserved
void spiSaveState(void) {
HAL_SPI_Save_State(&hspi);
}
void spiRestoreState(void) {
HAL_SPI_Restore_State(&hspi);
}
And these calls HAL_SPI_Save_State() and HAL_SPI_Restore_State() are added to stm32wlxx_hal_spi.c as follows:
// Save SPI registers either side of STOP2 mode, since registers are not preserved
void HAL_SPI_Save_State(SPI_HandleTypeDef *hspi) {
hspi->saved_cr1 = READ_REG(hspi->Instance->CR1);
hspi->saved_cr2 = READ_REG(hspi->Instance->CR2);
// There are other r/w registers but not used by this app
}
// Restore SPI registers either side of STOP2 mode, since registers are not preserved
void HAL_SPI_Restore_State(SPI_HandleTypeDef *hspi) {
WRITE_REG(hspi->Instance->CR1, hspi->saved_cr1);
WRITE_REG(hspi->Instance->CR2, hspi->saved_cr2);
}
The variables that save the state (saved_cr1 and saved_cr2) are additions to the hspi structure, as follows:
typedef struct __SPI_HandleTypeDef
{
SPI_TypeDef *Instance; /*!< SPI registers base address */
<snip>
// Saved SPI register state here:
uint32_t saved_cr1; // saved value of CR1
uint32_t saved_cr2; // saved value of CR2
<snip>
} SPI_HandleTypeDef;
Obviously, I have only saved the state of registers that were causing me problems. This concept can be generalised to save whatever registers in whatever module you are using. This works well for me. Good luck.
2021-08-13 02:19 AM
Hi @charles23 ,
If questions is answered please click on the Select as Best button on my reply in order to help other members of the community find the answer more quickly.
Thank you
2021-08-13 06:17 AM
Thank you, @charles23!
I need something similar for I2C, because I have the same problem that you had before.
And yes, ofcourse, I will use it at my own risk :beaming_face_with_smiling_eyes: