How to setup the interrupt handler in stm32? - c

void EXTI4_15_IRQHandler()
{
if(EXTI->PR & EXTI_PR_PR8)
{
EXTI->PR |= EXTI_PR_PR8;
// handle interrupt here
GPIOA->BSRR |= GPIO_BSRR_BS_10;
Delay(500);
GPIOA->BSRR |= GPIO_BSRR_BR_10;
Delay(500);
}
}
the interrupt init is initialized in the main.c and the setup is correct.
Is there anything that i am missing in the handler function?

You have posted only interrupt handler function, which is not a big help on its own. Basically, your MCU either enters it and executes it entirely or not. So if it (the interrupt handler) doesn't run, it means, the interrupt is not triggered. It means, the problem is somewhere else, so I will go over the entire logic of getting EXTI interrupt on STM32, make sure you have all of that done.
In order to get EXTI interrupts working, we need to connect 3 internal peripherals together: GPIO, EXTI and NVIC. You haven't indicated a specific microcontroller, but they all (STM32) do it in the same/similar way, so I will use STM32F746 as an example, since I have it here on my table.
First, you need to set up EXTI. If you want an interrupt on pin GPIOx8, you need to set bits 8 in the appropriate places in the EXTI registers depending on the event you want to trigger an interrupt.
Second of all, you need to connect GPIO port to EXTI. Do you want an interrupt on PA8? Or PB8? Or PC8? EXTI only understands that it's "Pin 8". It doesn't know if it's PA8 or PB8. That you do in SYSCFG peripheral. In the register EXTI->EXTICR3 you need to set port for EXTI8. This is where you decide that it's PB8 and not PA8 that triggers the interrupt, or whatever port you have.
Now, upon the GPIO behavior event defined in EXTI, the EXTI will report an EXTI9_5 event to NVIC. For now, NVIC will set pending bit if EXTI interrupt event occurs, but it will not execute the interrupt, because it's not activated. So we need to configure NVIC and activate EXTI9_5 interrupt there. At this point your interrupt should work.
To recap, the sequence of actions is the following:
Configure EXTI with the number of pin, on which you want interrupt. Number of pin, but not a GPIO port. For you, it will be pin 8.
Configure SYSCFG to select GPIO port for that EXTI pin. Configure pin 8 to be pin PB8 and not PA8 (or whatever port the interrupt is on).
Activate the corresponding NVIC interrupt.
From this also follows, that you can't have interrupts on PA8 and PB8 at the same time.
EDIT: having delays in the ISR is highly inadvisable. Also, make sure your input signal on the interrupt pin is debounced.
EDIT2: I assumed it goes without saying that every peripheral you use requires clock to be supplied to it.

Related

Clearing or preventing pending interrupts in an ISR

Summary:
An ISR necessarily causes its own trigger pin to toggle randomly multiple times. These toggles (during the ISR) should be ignored, but aren't, and result in another interrupt to be set as pending and get executed afterwards.
I have a serial bit-bang device that I read via an interrupt. Device has a data pin and a "clock" pin. Data pin is HIGH by default. When the device is ready to be read, it pulls this data pin LOW. After this falling edge, each pulse on the "clock" pin shifts one bit out to the data pin.
An interrupt triggers on the falling edge of the data pin, and ISR bangs 24 bits of data out of the same data pin. Therefore, additional random falling edges on the data pin causes another interrupt to be set as pending. Which triggers immediately after the actual ISR has returned, resulting two consecutive interrupts being run per one "real" interrupt.
I have tried multiple ways to disable interrupts and/or clear pending interrupts, none of which seem to have any effect whatsoever. I suspect that this is because manipulating interrupt related registers is not allowed/or ignored in an ISR.
The device is Atmel ATSAMD21. (ARM Cortex M0+). Code is built under Atmel Studio with optimisation level -Og.I am okay with using ASF and/or SAM libraries/definitions, ARM CMSIS or baremetal register manipulation. Whichever happens to work.
Here is what I tried so far:
void interrupt_cb ( void )
{
// Trying to disable interrupts
// Executed at the beginning of the ISR
NVIC_DisableIRQ(EIC_IRQn);
ext_irq_disable( <pin> );
__disable_irq();
// body
// < code that results in same pin >
// < that the interrupt is triggered >
// < to be toggled randomly. >
// Trying to clear pending interrupts
// Executed just before the ISR returns.
NVIC_ClearPendingIRQ( EIC_IRQn );
NVIC->ICPR[0] |= 4; //probably same as the above
EIC->INTENCLR.bit.EXTINT2 = 1;
}
Or a combination of these commands.

How do I know which pin is triggering the interrupt if multiple pins share the same interrupt?

I am writing code with the STM323 ide and using the STM32f303re nucleo board. I configured pin PA7 as an interrupt, however it does not have its own interrupt handler like previous development boards i've worked with.
As you can see, this one interrupt handler handles interrupts from pins 9-5, thus if any of these pins are triggered they will call the same interrupt. I need to be able to perform different functions depending on which pin is triggered. Is there a way to know which specific pin was causing the interrupt?
You can use EXTI_GetITStatus, to check which line causing interrupt.
/* Handle PA7 interrupt */
void EXTI9_5_IRQHandler(void) {
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
/* Do your stuff when PA7 is changed */
/* Clear interrupt flag */
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
Do not forget to clear flag after.
For the cases when multiple pins share an interrupt, when the interrupt fires, you need to check what pin specifically caused an interrupt. Unlike pin 1 interrupt, where the interrupt itself means it was pin 1 and you can process it right away, in this case an interrupt means "it's either pin 5, or 6, or 7, or 8, or 9", so in the ISR you need to check "was it pin 5? or 6?..."
I think it's a good opportunity to look directly into the registers of EXTI peripheral.
If you open the reference manual of your MCU on page 299, section 14.3.6, you can see this EXTI_PR1 register holds pending bits for lines 0..31. Those bits are marked as rc_w1, which from the start of the document means (reference manual, page 46):
read/clear (rc_w1)
Software can read as well as clear this bit by writing 1. Writing ‘0’
has no effect on the bit value.
So the logic is the following: if an interrupt of lines 5..9 occurred, you need to check what bit specifically in that register is set to 1, and then you write 1 there to reset it. This will clear the flag.
void EXTI9_5_IRQHandler(void)
{
if(EXTI_PR1 & (1U<<5U)) //check if it's line 5, returns 0 if PR5 is 0, otherwise returns non-zero, which is true
{
EXTI_PR1 |= (1U<<5U); //write 1 to that bit to clear it so interrupt doesn't fire again once ISR is finished
do_stuff_if_it's_pin5();
}
}
Alternatively, instead of 1U<<5U you should be able to use EXTI_PR1_PR5, so the code would look a little easier to read, like this:
void EXTI9_5_IRQHandler(void)
{
if(EXTI_PR1 & EXTI_PR1_PR5) //check if it's line 5, returns 0 if PR5 is 0, otherwise returns non-zero, which is true
{
EXTI_PR1 |= EXTI_PR1_PR5; //write 1 to that bit to clear it so interrupt doesn't fire again once ISR is finished
do_stuff_if_it's_pin5();
}
}
This is what the functions provided by #Lime7 do behind the scenes, I suspect (can't check, but it makes logical sense).
I don't have that microcontroller to test it, but it should work.
Other answers already covered the interrupt flags. I'm going to add some info regarding CubeMX and HAL, since it looks like you are using them.
When you enable a GPIO interrupt in CubeMX, the code generator adds a line in the appropriate IRQ handler function. When multiple interrupts are enabled, the generated code looks like so (comments removed):
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
}
The HAL_GPIO_EXTI_IRQHandler() function checks the interrupt status for the given pin and calls HAL_GPIO_EXTI_Callback() function if the flag is set.
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
In the HAL flow, you don't typically write code directly in the IRQ handler, but implement the HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) function. The pin that triggered the interrupt is provided as a parameter for your convenience.

Atmel SAM3X / SAM3A, handle interrupts from Parallel Input/Output Controller (PIO)

I want to enable the PA8 pin in a processor (Atmel SAM3X / SAM3A). I have set PIOA_MDERp[7]=0 and PIOA_MDDR[7]=0 and PIO_OER[7]=0 and PIO_ODR[7]=1, so that the tri-state buffer will no longer send data out of the pin, and the pin is enabled to receive data from the environment and to send interrupts if needed. I also want to set my interrupt to rising edge, so I also did PIOA_RHLSR[7]=1 and PIOA_FELLSR[7]=0 and also PIO_ESR[7]=1 and PIOA_LSR[7]=0.
My problem now is how to modify NVIC registers so that this interrupt is enabled. I mean, on a rising edge on this pin, the state of this pin is pending, and then the changes I will apply to NVIC causes this pending state to active state.
My interrupt group priority and subgroup priority are both 3.
I know how to modify Interrupt set-enable register, interrupt clear-enable register, application interrupt and reset control register, and interrupt priority register. Still, my problem is that now I don't have any number for interrupts from this specific pin (PIOA8), so it is different from the time I had my interrupt number (from 1 to 240), and so I could know which register in the NVIC I should modify.
Thanks for your help.

Only two IO port interrupts for Xmega (INT0 and INT1)

In avr Xmega device, there are only two IO port interrupt vectors INT0 and INT1.
I have three switches that should generate three interrupts.
I have written an interrupt code for the first switch using INT0. Now, I can write the second interrupt using INT1 which will exhaust my vectors.
Below is my code for INT0 ISR:
ISR (PORTD_INT0_vect){
PORTD.INTFLAGS = 0x01; // clear INT0IF flag.
PORTD_OUT = PORTD_OUT | (1<<4); // led on.
}
Can I redefine this ISR to enable interrupt for second switch?
The registers that I set in main function are as follows:
PORTD.INT0MASK = 0x04; // PD2 is the source of interrupt for INT0.
PORTD.INTCTRL = 0x03; // Disable INT1, enable INT0 and place it a high-priority level.
PORTD.PIN2CTRL = 0x03; // configure PD2 pin to interrupt on the low level of signal.
PMIC.CTRL = 0x07; // enable high,medium, and low level interrupts.
sei(); // enable interrupt globally.
The idea of the external interrupts is that you get to know that something happened, but not what happened. In your ISR you have to read out the state of the inputs the switches are connected to and determine based on the readout, what to do. To detect, if the state did alter, keep a copy of the ports input status and do an XOR with the newly read out value (do this for a whole port, not just for single pins).
I'm not expert of XMEGA, but I can suggest, for example using PD1 as interrupt pin:
PORTD.INT0MASK = 0x04; // PD2 is the source of interrupt for INT0.
PORTD.INT1MASK = 0x02; // PD1 is the source of interrupt for INT1.
PORTD.INTCTRL = 0x0F; // Enable INT1, enable INT0 and place them a high-priority level.
PORTD.PIN2CTRL = 0x03; // configure PD2 pin to interrupt on the low level of signal.PORTD.PIN1CTRL = 0x03; // configure PD1 pin to interrupt on the low level of signal.
Every port has 2 interrupts. You can use more ports if you need more interrupts than 2. You can connect the third switch to a second port and use the INT0 there.
How about polling input(s) from your code, in a loop or from timer interrupt routine(s)? This way you can handle as many input signals as you have inputs capable of sensing the signal change.
The limiting factor, though is the frequency of polling and the delay the polling software introduces.
Of course I assume you want to observe high/low levels of the signal. For more elaborate signal sensing (rising, fallsing, frequency change) the use of ADC is unavoidable.
I hope this helped.

LPC17XX SPI: Implementing pulse-sensitive(edge-triggered) interrupts

I would like to implement a pulse-sensitive, aka edge-triggered, interrupt on an LPC1759 microcontroller. In the UM10360.pdf datasheet and ARM Cortex-M3 user guide, it says that interrupts can be triggered based on level- or pulse-sensitive behavior, but I am unable to find how to set this to be pulse-sensitive. Can someone please tell me where to set this?
For my particular application(interfacing the LPC1759 with an AD7794 ADC), I would like to trigger an interrupt based on the falling edge of the MISO pin. Although it is not explicitly stated that the interrupt is trigger on the MISO value, I am assuming this based on the fact that, of the four SPI signals, the MISO is the only input to the microcontroller. Please let me know if this is not correct.
See UM10360.pdf, chapter 9.5.6: "GPIO interrupt registers". You can enable rising and falling edge interrupts only on port 0 and 2 pins. The interrupt vector is the same as external interrupt 3.

Resources