2024-06-20 11:14 AM
Hi,
i'm using a STM32L031. In my code i first initialize the MCU (FLASH, CLOCK, INTERRUPTs, GPIOs), start the LPTIM (running on LSI), then slow down the MSI clock, start the timer22 for PWM and go into low-power sleep mode. When i let the program run i experience an hard-fault after exiting the ISR (count = ccr) from the LPTIM because at the moment when r7 and PC gets popped from the stack PC gets a wrong value and suddenly points to RAM instead of FLASH. The strange thing for me is also, that when i don't use the LPTIM for waking up the MCU, but use a GPIO (EXTI) ist works fine. It also works fine when i don't go into low-power sleep mode but into stop-mode and then let the LPTIM wake up the MCU. In this case of course the PWM doesn't work while in stop-mode.
So can someone help me out an tell me why the pc gets a wrong value?
Here my example code:
#######################################################
int main(void){
// Initialize
FLASH->ACR |= FLASH_ACR_PRFTEN; // Enable Pre-Fetch
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Clock für Systemconfig aktivieren
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// Clock setup
RCC->ICSCR &= ~(0b111 << 13);
RCC->ICSCR |= 0b101 << 13;
while(!(RCC->CR & RCC_CR_MSIRDY));
// GPIO setup
RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // Aktivieren des Clock-Gates für GPIOA
GPIOA->MODER &= ~(0b11); // Pin PA0 als Input - BUTTON
GPIOA->PUPDR |= 0b10; // Pin PA0 Pull-down
EXTI->IMR |= 0b1; // Interrupt Maske für PA0
EXTI->RTSR |= EXTI_RTSR_RT0; // Konfigure Interrupt für Rising Edge für PA0
NVIC_SetPriority(EXTI0_1_IRQn, 1); // Priorität von PA0 Interrupt auf 0
NVIC_EnableIRQ(EXTI0_1_IRQn); // Interrupt von PA0 aktivieren
PWR->CSR |= PWR_CSR_EWUP1; // Enable PA0 als Wakeup Pin
GPIOA->MODER &= ~(0b11 << 30); // Pin PA15 als Input - DPS INTERRUPT
EXTI->IMR |= 0b1 << 15; // Interrupt Maske für PA15
EXTI->FTSR |= EXTI_RTSR_RT15; // Konfigure Interrupt für Falling Edge für PA15
NVIC_SetPriority(EXTI4_15_IRQn, 2); // Priorität von PA15 Interrupt auf 0
NVIC_EnableIRQ(EXTI4_15_IRQn); // Interrupt von PA15 aktivieren
GPIOA->MODER &= ~(0b11 << 14); // Pin PA7 auf Alternate Function Mode
GPIOA->MODER |= 0b10 << 14;
GPIOA->AFR[0] |= 0b0101 << 28; // Pin PA7 mit TIM22 CH2 verbinden
GPIOA->MODER &= ~(0b1111 << 6); // PA4 (EN1) - PA3 (EN2) - Konfiguration Verstärker
GPIOA->MODER |= (0b0101 << 6); // PA4 + PA3 auf Output Mode
GPIOA->MODER &= ~(1 << (9 * 2)); // PA9 auf Alternate function mode
GPIOA->MODER &= ~(1 << (10 * 2)); // PA10 auf Alternate function mode
GPIOA->OTYPER |= 1 << 9; // PA9 auf open-drain
GPIOA->OTYPER |= 1 << 10; // PA10 auf open-drain
GPIOA->OSPEEDR |= 0b11 << (9 * 2); // PA9 auf High-Speed
GPIOA->OSPEEDR |= 0b11 << (10 * 2); // PA10 auf High-Speed
GPIOA->PUPDR |= 1 << (9 * 2); // PA9 auf Pull-Up
GPIOA->PUPDR |= 1 << (10 * 2); // PA10 auf Pull-Up
GPIOA->AFR[1] |= 1 << 4; // PA9 mit I2C1_SCL verbinden
GPIOA->AFR[1] |= 1 << 8; // PA10 mit I2C1_SDA verbinden
GPIOA->MODER &= ~(1 << ((6 * 2) + 1)); // Default 11 -> 01 -> PA6 auf Output Mode
GPIOA->ODR |= (1 << 6); // PA6 auf HIGH
__enable_irq();
// LPTIM setup
piepsen = 1;
RCC->CSR |= RCC_CSR_LSION; // LSI aktivieren
while(!(RCC->CSR & RCC_CSR_LSIRDY)); // Warten bis LSI Ready
RCC->CCIPR |= RCC_CCIPR_LPTIM1SEL_0; // LSI als Clock Souce für LPTIM
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN; // Aktivieren des Clock-Gates für LPTIM1
NVIC_EnableIRQ(LPTIM1_IRQn);
NVIC_SetPriority(EXTI0_1_IRQn, 3); // Priorität von PA0 Interrupt auf 0
LPTIM1->CFGR |= 0b101 << 9; // Prescaler auf 32 -> 37kHz -> 1156.25 Hz
LPTIM1->IER |= 0b11;
LPTIM1->CR |= LPTIM_CR_ENABLE; // LPTIM starten
LPTIM1->ARR = (1156.25 / 1000)*(200); // Auto Reload Register
LPTIM1->CMP = (1156.25 / 1000)*100; // Capture Compare
LPTIM1->CR |= LPTIM_CR_SNGSTRT; // Timer start in Single Shot mode
// Slow down clock
RCC->ICSCR &= ~(0b111 << 13);
RCC->ICSCR |= 0b001 << 13;
while(!(RCC->CR & RCC_CR_MSIRDY)); // Wait for MSI is ready
// Start TIMER 22
RCC->APB2ENR |= RCC_APB2ENR_TIM22EN;
TIM22->ARR = 131072/500; // Auto-reload value (period)
TIM22->CCR2 = 131072/500/2;
TIM22->CCMR1 |= 0b110 << 12; // PWM mode 1
TIM22->CCER |= TIM_CCER_CC2E; // Enable channel 2 output
TIM22->CR1 |= TIM_CR1_CEN; // Enable TIM22
// Enter LOW-POWER SLEEP MODE
while (FLASH->SR & FLASH_SR_BSY) {} // Auf Flash warten bis alles erledigt
FLASH->ACR |= FLASH_ACR_SLEEP_PD; // FLASH schlafen legen
PWR->CR |= PWR_CR_LPSDSR; // Volt Regulator in Low Power Mode --> einziger vorteil wäre sonst schnellerer wake-up
__WFI();
while(1);
}
The ISR looks like this:
void LPTIM1_IRQHandler(void) { // Überwachung Piepsdauer
if(LPTIM1->ISR & 0b1){ // Capture Compare Interrupt -> Piepsen AUS
status = 3;
LPTIM1->ICR |= 0b1;
TIM22_Stop(); // Abfrage damit nicht Gamification unterbrochen wird
}
else{ // Auto Reload Register Interrupt -> Pieps-Pause AUS
piepsen = 0;
LPTIM1->ICR |= 0b1 << 1;
LPTIM_Stop();
}
}
#####################################################################
Screenshots:
1. In screenshot "Beginning" you can see the system right after the ISR was triggered.
2. Screenshot "Before" was taken just before the hard-fault happens.
3. Screenshot "Hard-Fault" doesn't need an explanation
Thanks in advance
Greetings from Salzburg, Austria
2024-06-20 12:04 PM
Please use the </> icon / tool to paste in source code.
Not sure why the stack would get corrupt, perhaps look at other interrupts / callbacks and what they might be doing that would damage the stack frame.
2024-06-21 04:09 AM
Hi,
There are no other interrupts just the LPTIM interrupt. The sequence is like:
1. initializing
2. starting LPTIM in interrupt mode
3. starting TIM22 witb PWM
4. starting low-power sleep mode
5. waiting for interrupt. Because of the LPTIM settings (100ms and 200ms) it‘s guaranteed, that i‘m in this state when the interrupt happens
6. interrupt from LPTIM. there is no other interrupt going on and no callbacks.
7. Hard fault when exiting the ISR from LPTIM
2024-06-21 04:12 AM
int main(void){
// Initialize
FLASH->ACR |= FLASH_ACR_PRFTEN; // Enable Pre-Fetch
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Clock für Systemconfig aktivieren
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// Clock setup
RCC->ICSCR &= ~(0b111 << 13);
RCC->ICSCR |= 0b101 << 13;
while(!(RCC->CR & RCC_CR_MSIRDY));
// GPIO setup
RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // Aktivieren des Clock-Gates für GPIOA
GPIOA->MODER &= ~(0b11); // Pin PA0 als Input - BUTTON
GPIOA->PUPDR |= 0b10; // Pin PA0 Pull-down
EXTI->IMR |= 0b1; // Interrupt Maske für PA0
EXTI->RTSR |= EXTI_RTSR_RT0; // Konfigure Interrupt für Rising Edge für PA0
NVIC_SetPriority(EXTI0_1_IRQn, 1); // Priorität von PA0 Interrupt auf 0
NVIC_EnableIRQ(EXTI0_1_IRQn); // Interrupt von PA0 aktivieren
PWR->CSR |= PWR_CSR_EWUP1; // Enable PA0 als Wakeup Pin
GPIOA->MODER &= ~(0b11 << 30); // Pin PA15 als Input - DPS INTERRUPT
EXTI->IMR |= 0b1 << 15; // Interrupt Maske für PA15
EXTI->FTSR |= EXTI_RTSR_RT15; // Konfigure Interrupt für Falling Edge für PA15
NVIC_SetPriority(EXTI4_15_IRQn, 2); // Priorität von PA15 Interrupt auf 0
NVIC_EnableIRQ(EXTI4_15_IRQn); // Interrupt von PA15 aktivieren
GPIOA->MODER &= ~(0b11 << 14); // Pin PA7 auf Alternate Function Mode
GPIOA->MODER |= 0b10 << 14;
GPIOA->AFR[0] |= 0b0101 << 28; // Pin PA7 mit TIM22 CH2 verbinden
GPIOA->MODER &= ~(0b1111 << 6); // PA4 (EN1) - PA3 (EN2) - Konfiguration Verstärker
GPIOA->MODER |= (0b0101 << 6); // PA4 + PA3 auf Output Mode
GPIOA->MODER &= ~(1 << (9 * 2)); // PA9 auf Alternate function mode
GPIOA->MODER &= ~(1 << (10 * 2)); // PA10 auf Alternate function mode
GPIOA->OTYPER |= 1 << 9; // PA9 auf open-drain
GPIOA->OTYPER |= 1 << 10; // PA10 auf open-drain
GPIOA->OSPEEDR |= 0b11 << (9 * 2); // PA9 auf High-Speed
GPIOA->OSPEEDR |= 0b11 << (10 * 2); // PA10 auf High-Speed
GPIOA->PUPDR |= 1 << (9 * 2); // PA9 auf Pull-Up
GPIOA->PUPDR |= 1 << (10 * 2); // PA10 auf Pull-Up
GPIOA->AFR[1] |= 1 << 4; // PA9 mit I2C1_SCL verbinden
GPIOA->AFR[1] |= 1 << 8; // PA10 mit I2C1_SDA verbinden
GPIOA->MODER &= ~(1 << ((6 * 2) + 1)); // Default 11 -> 01 -> PA6 auf Output Mode
GPIOA->ODR |= (1 << 6); // PA6 auf HIGH
__enable_irq();
// LPTIM setup
piepsen = 1;
RCC->CSR |= RCC_CSR_LSION; // LSI aktivieren
while(!(RCC->CSR & RCC_CSR_LSIRDY)); // Warten bis LSI Ready
RCC->CCIPR |= RCC_CCIPR_LPTIM1SEL_0; // LSI als Clock Souce für LPTIM
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN; // Aktivieren des Clock-Gates für LPTIM1
NVIC_EnableIRQ(LPTIM1_IRQn);
NVIC_SetPriority(EXTI0_1_IRQn, 3); // Priorität von PA0 Interrupt auf 0
LPTIM1->CFGR |= 0b101 << 9; // Prescaler auf 32 -> 37kHz -> 1156.25 Hz
LPTIM1->IER |= 0b11;
LPTIM1->CR |= LPTIM_CR_ENABLE; // LPTIM starten
LPTIM1->ARR = (1156.25 / 1000)*(200); // Auto Reload Register
LPTIM1->CMP = (1156.25 / 1000)*100; // Capture Compare
LPTIM1->CR |= LPTIM_CR_SNGSTRT; // Timer start in Single Shot mode
// Slow down clock
RCC->ICSCR &= ~(0b111 << 13);
RCC->ICSCR |= 0b001 << 13;
while(!(RCC->CR & RCC_CR_MSIRDY)); // Wait for MSI is ready
// Start TIMER 22
RCC->APB2ENR |= RCC_APB2ENR_TIM22EN;
TIM22->ARR = 131072/500; // Auto-reload value (period)
TIM22->CCR2 = 131072/500/2;
TIM22->CCMR1 |= 0b110 << 12; // PWM mode 1
TIM22->CCER |= TIM_CCER_CC2E; // Enable channel 2 output
TIM22->CR1 |= TIM_CR1_CEN; // Enable TIM22
// Enter LOW-POWER SLEEP MODE
while (FLASH->SR & FLASH_SR_BSY) {} // Auf Flash warten bis alles erledigt
FLASH->ACR |= FLASH_ACR_SLEEP_PD; // FLASH schlafen legen
PWR->CR |= PWR_CR_LPSDSR; // Volt Regulator in Low Power Mode --> einziger vorteil wäre sonst schnellerer wake-up
__WFI();
while(1);
}
2024-06-21 04:51 AM
Look for the problem in the routines called from LPTIM ISR.
2024-06-21 08:08 AM
The functions i also use in other places in the code - they work fine. The problem also appears when i don‘t use them (comment out) in the ISR.
2024-06-21 08:32 AM
…and like i said, it works fine when i use the stop mode instead of the low power sleep mode.
2024-06-22 01:15 AM
I just noticed, that when i set a debug break point at _wfi and then continue the programm the stack frame which was pushed right before the ISR looks ok (correct PC and LR in the stack). But when i don't do that, an set a debug break point at the beginning of the ISR, the stack frame already contains a corrupt LR/PC (has a value which points at RAM instead of FLASH). Do i need some kind of waiting point like DSB, ISB oder DMB right before the _wfi (although i didn't need such before in my code)?
2024-06-22 08:53 AM
There's only an empty loop after WFI. Why would r7 content matter?
JW
2024-06-22 09:00 AM
The while(1) is only for testing purpose and r7 is not relevant for me. In my opinion it's only the program counter which has a wrong value when it is popped back from the stack. In the meanwhile i tried to put a Delay(1) right in front of the _wfi (instead of the debug break point) but i still get a hard-fault.