With an ARM Cortex-M3, such as an NXP LPC1788, why would someone use the Interrupt Set-Pending Register(s) or Interrupt Clear-Pending Registers?
Can someone provide a simple, canonical example of using these registers?
The only use case I can think of is the triggering of a low-priority software excaption form a high priority IRQHandler - like the GPIO interrupt handler.
Normally you would use PendSV for that, but when you have more than one task or priority level you can use any unused peripherial exception vector. Could be useful in programs that use the Sleep-on-Exit feature - where the µC will only run in exception handlers.
// Example for LPC17xx
void ETHERNET_Handler (void)
{
// toggle LED on P0.4
LPC_GPIO0->FIODIR0 ^= (1<<4);
}
void main(void)
{
// set Ethernet IRQ to loewst Priority
NVIC_SetPriority(ENET_IRQn,31);
NVIC_EnableIRQ(ENET_IRQn);
NVIC_SetPendingIRQ(ENET_IRQn); // trigger Ethernet IRQ Handler
// ...
while (1);
}
Related
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.
I have a problem with my STM32F103C8T6 microcontroller. I am using (as an exercise) external interrupts to toggle on/off a led, by pressing an external switch wich in turn is connected to PC13. I am using StdPeriph Library.
When the chip has programmed, nothing happens. On the contrary, when I am using the debugger (debug in Coocox), the chip is working fine. I can not figure out where is the problem.
Can you help me please?
Here is my code.
#include<stm32f10x.h>
#include<stm32f10x_rcc.h>
#include<stm32f10x_gpio.h>
#include<stm32f10x_exti.h>
#include<misc.h>
typedef enum{
on,
off
}state;
state led=on;
int main(void){
// enable clocks
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// uncomment to disable/remap JTAG pins
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
// configure PC13 as input
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
// configure PB8 as led output
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
// connect PC13 to EXTI controller
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource13);
// enable and configure EXTI controller
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line13;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
// enable IRQ
NVIC_EnableIRQ(EXTI15_10_IRQn);
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
// Configure NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructure);
// switch on led
GPIO_WriteBit(GPIOB,GPIO_Pin_8,Bit_SET);
while(1);
return 0;
}
void EXTI15_10_IRQHandler(void){
// clear pending bit
if(EXTI_GetITStatus(EXTI_Line13)!=RESET){
EXTI_ClearITPendingBit(EXTI_Line13);
}
if(led==off){
GPIO_WriteBit(GPIOB,GPIO_Pin_8,Bit_SET);
led=on;
}else{
GPIO_WriteBit(GPIOB,GPIO_Pin_8,Bit_RESET);
led=off;
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t * file,uint32_t line){
/* Infinite loop */
while (1);
}
#endif
I had this issue as well. I'm using a STM32F030.
The problem for me was not having the SYSCFG clock enabled, which is bit 0 of RCC APB2ENR register. I'm guessing this setting is enabled in debug, so that the software can debug? Otherwise the clock is disabled!
I finally found this by looking into the STM32F1 reference manual, which is slightly more comprehensive.
It is generally a very bad idea to use external interrupts for the buttons and keys. You should use the timer interrupt instead.
You can see simple implementation of the key in the timer interrupt (click, double click, long click events supported) here : https://www.diymat.co.uk/arm-three-function-click-double-and-long-click-button-library-timer-interrupt-driven/
I don't have knowledge about the mentioned controller, but I worked on STM32L4 series controllers. In STM32L4 push button is connected to the PC13 pin. I observed gpio debouncing when pressing the push button. In your EXTI15_10_IRQHandler() implement debouncing logic. Make sure the interrupt is reaching this function for only once per button press. May be the debugger is slowing down the processor(cpu running in lower frequency compared to free run) and you are getting the interrupts properly.
Do you use different build config for debug? If so, try making the led variable volatile and see if it helps.
If not: add some delay loop, i.e. voliatile unsigned i; for (i=0; i < 50000u; ++i); after clearing the EXTI bit in the ISR. This is generally a bad pratcice to use blocking delays in the interrupt service (if it's ever a good practice...) but might be helpful to see if it is related to debuncing not done properly. If it helps, then debouncing the switch is the most likely problem.
EDIT: consider using bit-banding (if possible) to access the output port, then you could do sth like bind_band_led_port ^= 1;, but that's just a side note.
I'd like to write an open-sourced core driver for controlling stepper motors in Linux. In this case, especially for 3D-Printers.
The basic idea is that the driver reserves pins on one IO port, and then manipulates those pins at once. It receives a buffer full of "toggle this, toggle that" values, and then emits those to the port, using a hardware timer.
Now the question is: Is there any way to handle a specific hardware interrupt as fast as possible?
The chip in question is an Allwinner H3, and I am using the TMR1 resource of said chip (IRQ 51). I can use it just fine, and it works as an interrupt as well:
static irqreturn_t stepCore_timer_interrupt(int irq, void *dev_id)
{
writel(TMR1_IRQ_PEND, TMR_IRQ_ST_VREG);
icnt++;
porta_state = readl(PA_VDAT);
porta_state &= porta_mask;
if(icnt & 0x00000001)
{
porta_state |= 0x00000001;
}
writel(porta_state, PA_VDAT);
return IRQ_HANDLED;
}
static struct irqaction stepCore_timer_irq = {
.name = "stepCore_timer",
.flags = IRQF_DISABLED | IRQF_NOBALANCING , IRQF_PERCPU,
.handler = stepCore_timer_interrupt,
.dev_id = NULL,
};
static void stepCore_timer_interrupt_setup(void)
{
int ret;
u32 val;
writel( 24000000, TMR1_INTV_VALUE_VREG );
writel( ( TMR1_MODE_CONTINUOUS | TMR1_CLK_PRES_1 | TMR1_CLK_SRC_OSC24M ), TMR1_CTRL_VREG );
ret = setup_irq(SUNXI_IRQ_TIMER1, &stepCore_timer_irq);
if (ret)
printk("%s: ERROR: failed to install irq %d\n", __func__, SUNXI_IRQ_TIMER1);
else
printk("%s: irq %d installed\n", __func__, SUNXI_IRQ_TIMER1);
ret = irq_set_affinity_hint(SUNXI_IRQ_TIMER1, cpumask_of(3));
if (ret)
printk("%s: ERROR: failed to set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
else
printk("%s: set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
/* Enable timer0 interrupt */
val = readl(TMR_IRQ_EN_VREG);
writel(val | TMR1_IRQ_EN, TMR_IRQ_EN_VREG);
}
TMR1 is otherwise unused (in fact, I had to add it myself) and so far works. However, there is quite some latency in handling the rather simple IRQ routine. Since I want to produce some code that is usable for a 3D printer, I very much like a more "stable" timer interrupt.
So, my question is: Is there any way to have a very short IRQ routine in Linux that has the highest possible priority? Or even doesn't care about the Linux scheduler at all, and just "does it's thing"? Basically a raw IRQ handler, ignoring what Linux thinks it should be?
The core it runs on is dedicated to just that task, anyways. The handler will be as short as possible: fetch an u32 from an array, write that to the port, done.
Preferably I would like to have something that just ignores the remainder of Linux all together. Yes, I know that that isn't the way to do it. But this is meant for a rather special case, so I have no qualms with adapting the regular kernel sources to suit those needs.
Oh, that reminds me, the kernel is 3.4.112 with the suitable preempt-rt patches.
Any help is greatly appreciated.
Greetings,
Chris
Here is a general solution to this issue. You can write a kernel module which will overwrite the existing interrupt handling routine and will be replaced by your own routine, where you can handle your irq of interest and redirect all the irq to the existing kernel interrupt handling routine. It's possible for x86 arch where you will get low level CPU instructions to get the existing address of interrupt description routine (lidt). I believe it should be possible for ARM too. Now, Linux has technique of CPU isolation isolcpus by utilizing this technique you can take a CPU out of scheduler domain i.e. no task will be scheduled on that particular CPU, until you specify a task to be run on that particular CPU (using taskset). After you take a CPU out of scheduler domain you can take help of the technique of affine a interrupt to that isolated CPU, you can do it via /proc/irq/IRQ_NUMBER/smp_affinity. Now all of your interrupt will be handled by this isolated CPU and 100% dedicated to that interrupt. And with your own IRQ routine you have full control over the interrupt handling.
Hopefully this will help!
Have you thought about using FIQ for that. We have a blog post about it:
http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/
I am trying to write a simple interrupt handler for a GPIO in the linux kernel. I use request_threaded_irq to get an interrupt context handler, and a threaded handler.
My problem is that the work done by the threaded handler has a big impact on the timing of the calls to the interrupt handler.
The code to setup the interrupt is:
gpio_request(93, "test")
gpio_direction_input(93);
gpio_request(33, "mirror");
gpio_direction_output(33, 1);
request_threaded_irq(gpio_to_irq(93),
interrupt_handler,
threaded_interrupt_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_TIMER,
"test", NULL);
Here, I am simply requesting gpio 93 to fire an interrupt on rising and falling edges. I also request gpio 33 to use as a mirror, see below.
(In my setup, I put a clock source on gpio 93).
The code of the interrupt handler is this:
static irqreturn_t interrupt_handler(int irq, void *data)
{
int state = gpio_get_value(93);
gpio_set_value(33, state);
return IRQ_WAKE_THREAD;
}
Here, the interrupt handler is simply mirroring gpio 93 input value as an output value for gpio 33. This allows me to monitor the effective rate of the calls to interrupt_handler.
Lastly, the threaded handler:
static irqreturn_t threaded_interrupt_handler(int irq, void *data)
{
/* doing msleep here is apparently problematic... */
msleep(1);
return IRQ_HANDLED;
}
In the threaded interrupt handler, calling msleep (or actually performing work) is problematic: by looking with a scope at the gpio output 33, I can see that the interrupt_handler callback rate changes drastically when the threaded_interrupt_handler sleeps or perform too much work.
How can I setup/use request_threaded_irq() so that the interrupt handler is always called "on-time" even if the threaded handler as some big work to do?
I eventually understand what was happening. According to this answer on SO, the interrupt is masked while processing the interrupt and the threaded interrupt handler.
So it seems I misunderstood the point of request_threaded_irq: it should really be used so that the interrupt handling is in a task scheduled by the scheduler.
For my needs, what I really wanted was simply a wait_queue. I remove the threaded interrupt handler for my code and changed the interrupt handler to something like:
static wait_queue_head_t wq;
static irqreturn_t interrupt_handler(int irq, void *data)
{
int state = gpio_get_value(93);
gpio_set_value(33, state);
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
Now, the interrupt handler is called with a correct timing!
I'm developing software for an NXP LPC1788 microcontroller, and I'm using the embOS RTOS. Whenever a message is received over USB, I want to use the OS_PutMailCond() function to store the USB message in a mailbox which a handler function is waiting on. In other words, I want to make message handling interrupt-driven.
The embOS user manual can be found here. Page 145 describes the OS_PutMailCond() function.
Whenever a USB message is received, it triggers the USB interrupt service routine on the LPC, but to let embOS know that it's an ISR I have to place OS_EnterInterrupt() and OS_LeaveInterrupt() at the start and end of the ISR respectively. This is necessary if I want to call embOS functions within it, including OS_PutMailCond().
The problem is that if I put OS_EnterInterrupt()/OS_LeaveInterrupt() anywhere within the USB ISR, the USB stops functioning properly and Windows informs me that the device has malfunctioned.
I have no idea why this is the case. We've tried something similar for handling messages over CAN, as shown below, and it works fine.
void CAN_IRQHandler(void)
{
OS_EnterInterrupt();
...
if (MBfieldCANframeInitialised)
OS_PutMailCond (&MBfieldCANframe, &recMessage);
OS_LeaveInterrupt();
}
OS_EnterInterrupt() and OS_LeaveInterrupt() are described on pages 252 and 253 of the linked manual. From the additional information section of the former:
If OS_EnterInterrupt() is used, it should be the first function to be
called in the interrupt handler. It must be used with
OS_LeaveInterrupt() as the last function called. The use of this
function has the following effects, it:
disables task switches
keeps interrupts in internal routines disabled
EDIT
I've investigated further and found out that using OS_EnterInterrupt() and OS_LeaveInterrupt() within the USB ISR (and other ISR's like the one for the GPIO when a rising or falling edge is detected on a pin) causes an OS error. The error value is 166, which means "OS-function called from ISR with high priority".
I'll update if I find out anything else.
Problem solved. It turns out the guy that made this work for the CAN ISR changed the code of one of the embOS source files to set the CAN ISR priority level from 0 to 29 (higher level = lower priority). I did the same thing for the USB ISR:
void OS_InitHW(void) {
OS_IncDI();
//
// We assume, the PLL and core clock was already set by the SystemInit() function
// which was called from the startup code
// Therefore, we don't have to initailize any hardware here,
// we just ensure that the system clock variable is updated and then
// set the periodic system timer tick for embOS.
//
SystemCoreClockUpdate(); // Update the system clock variable (might not have been set before)
if (SysTick_Config (OS_PCLK_TIMER / OS_TICK_FREQ)) { // Setup SysTick Timer for 1 msec interrupts
while (1); // Handle Error
}
//
// Initialize NVIC vector base address. Might be necessary for RAM targets or application not running from 0
//
NVIC_VTOR = (OS_U32)&__Vectors;
//
// Set the interrupt priority for the system timer to 2nd lowest level to ensure the timer can preempt PendSV handler
//
NVIC_SetPriority(SysTick_IRQn, (1u << __NVIC_PRIO_BITS) - 2u);
NVIC_SetPriority(CANActivity_IRQn, (1u << __NVIC_PRIO_BITS) - 3u);
NVIC_SetPriority(CAN_IRQn, (1u << __NVIC_PRIO_BITS) - 3u);
NVIC_SetPriority(USB_IRQn, (1u << __NVIC_PRIO_BITS) - 3u);
OS_COM_INIT();
OS_DecRI();
}
I found this in the embOS documentation:
Why can a high priority ISR not use the OS API ?
embOS disables low priority interrupts when embOS data structures are modified. During this time high priority ISR are enabled. If they would call an embOS function, which also modifies embOS data, the embOS data structures would be corrupted.