I've been learning to program in bare-metal and I'm trying to learn how EXTIs work. I wrote a simple program that would turn on an onboard LED on PB7 when triggered by a button on PC13. Code as follows:
Initialization:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
GPIOB->MODER |= GPIO_MODER_MODER0_0 | GPIO_MODER_MODER7_0 | GPIO_MODER_MODER14_0;
GPIOC->PUPDR |= GPIO_PUPDR_PUPD13_1;
SYSCFG->EXTICR[4] |= SYSCFG_EXTICR4_EXTI13_PC;
EXTI->IMR |= EXTI_IMR_IM13;
EXTI->RTSR |= EXTI_RTSR_TR13;
EXTI->FTSR &= ~EXTI_FTSR_TR13;
NVIC_EnableIRQ(EXTI15_10_IRQn);
Interrupt Handler:
void EXTI15_10_IRQHandler(void)
{
if (EXTI->PR & EXTI_PR_PR13)
{
GPIOB->BSRR |= GPIO_BSRR_BS7;
HAL_Delay(1000);
GPIOB->BSRR |= GPIO_BSRR_BR7;
}
EXTI->PR |= EXTI_PR_PR13;
}
When running this program, the LED turns on and stays on. When checking the SYSCFG_EXTICR4 register, bit 5 which corresponds to Port C seems to be set to zero even though I enabled it in the code (Although I think it could have something to do with them displaying the bits).
But so far, I've followed the exact instructions in the datasheet and can't figure out where I'm going wrong. Also, I'm using a Nucleo F413ZH 144 pin board and STM32CubeIDE. Thanks!
Update: SYSCFG_EXTICR4_EXTI13_PC
is mapped to SYSCFG->EXTICR[3]
. Also, EXTI->PR = EXTI_PR_PR13
;
1 Answer 1
This line looks suspect: EXTI->PR |= EXTI_PR_PR13;
The user manual tells you to write a '1' bit to clear the interrupt. That line does not quite do what you expect. Why you might ask? The register you read is not the same register you write. This is why doing a read/modify/write is not correct in this situation.
the required code is: EXTI->PR = EXTI_PR_PR13;
or if you want to clear all flags in this register: EXTI->PR |= EXTI_PR;
You might also want to check that EXTI_PR_PR13 is actually a bit mask and not a bit number. eg: a bit mask might be (1 << 13) and the bit number is 13.
There might be other issues with your code, but the above is what jumped out at me as it is a common mistake.
Other observations:
it is generally bad juju to put a delay in an isr. In this instance it is test code, but tying up a processor with a delay is not good practice. Use a timer to generate a tick (or use systick).
using pushbutton/switches external signals with external interrupts. Ok if you want to wake the cpu from sleep, but beyond that disable the external interrupt and use a timer tick to poll the input and filter/debounce it. Switches work in the realm of 10's of milliseconds. Interrupts on a STM F4 series are sub-microsecond. Use interrupts where you need the fast response.
-
\$\begingroup\$ This doesn’t work. Also, I can confirm that EXTI_PR_PR13 is a bit mask and not bit number. As for the delay, I haven’t learnt how to create delays with timers yet, and thats definitely next in my list of things to learn. \$\endgroup\$Joshua John– Joshua John2021年05月01日 12:33:11 +00:00Commented May 1, 2021 at 12:33
-
\$\begingroup\$ How do you know it doesn’t work? It has probably resolved one problem, but theres more. \$\endgroup\$Kartman– Kartman2021年05月01日 13:14:18 +00:00Commented May 1, 2021 at 13:14
-
\$\begingroup\$ I figured it out. HAL_Delay doesn't work within an ISR, so I replaced the delay and GPIO_BSRR with
GPIOB->ODR|= GPIO_ODR_OD7
. Also,SYSCFG_EXTICR4_EXTI13_PC
is mapped toSYSCFG->EXTICR[3]
which is confusing but the program works now. \$\endgroup\$Joshua John– Joshua John2021年05月01日 14:45:45 +00:00Commented May 1, 2021 at 14:45
Explore related questions
See similar questions with these tags.
HAL_Delay()
inside an ISR.HAL_Delay()
relies on the SysTick interrupt firing and its ISR incrementing a counter, but that will never happen because your code is already inside your EXTI ISR. Nesting ISRs to make this work is possible, although not what I'd recommend to solve this problem and not at a beginner level anyway. In addition, Kartman's answer is correct aboutEXTI->PR
. The way you're doing it could be unintentionally clearing other bits in addition toEXTI_PR_PR13
. \$\endgroup\$