Wrong PC (progam counter) when exiting ISR
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
