Hi,
I'm using Keil MDK-ARM 4.22a RTX OS on a STM3220G Eval board.
I added RTX and FS to the "Memory" example and everything is working fine. I have tasks that read a file while another one is blinking leds.
I tried to add and external interrupt on the KEY button (PG15 pin) and to do so I integrated the ST StdPeriph library (which is used mainly for setting up the interruption).
But if the button is working, I can't clear the Interrupt Pending bit in the IRQ handler. So when I send a semaphore from it, the OS raises quickly an overflow error.
Here is the code for the interruption setup :
void EXTI_User_Button_Config(void) { EXTI_InitTypeDef EXTI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* Enable GPIOG clock */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); /* Enable SYSCFG clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* Configure PG15 pin as input floating */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_Init(GPIOG, &GPIO_InitStructure); /* Connect EXTI Line0 to PG15 pin */ SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource15); /* Configure EXTI Line0 */ EXTI_InitStructure.EXTI_Line = EXTI_Line15; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_ClearITPendingBit(EXTI_Line15); EXTI_Init(&EXTI_InitStructure); /* Enable and set EXTI Line15 Interrupt to the lowest priority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
And the IRQ handler :
void EXTI15_10_IRQHandler (void) __irq { if(EXTI_GetITStatus(EXTI_Line15) != RESET) { /* Clear the EXTI line 15 pending bit */ EXTI_ClearITPendingBit(EXTI_Line15); /* Toggle LED1 */ STM_EVAL_LEDToggle(LED1); /*Send Semaphore */ isr_sem_send (semaphore1); cpt++; } }
The 'cpt' variable is only for debugging purpose, as it's decremented by the task that handles semaphore1. When it's reaching 32 (the ISR FIFO size I defined), the OS raises the overflow error.
The code from the Std library to reset the pending bit :
void EXTI_ClearITPendingBit(uint32_t EXTI_Line) { /* Check the parameters */ assert_param(IS_EXTI_LINE(EXTI_Line)); EXTI->PR = EXTI_Line; }
The interrupt setup is done before launching the OS.
Am I doing something wrong ? Is there another way to clear the interrupt bit ?
Is it possibly because you are getting many interrupts on a single button press?
Try deboucing the button.
Basically disable the interrupt in the IRQ and re-enable it in the task that is waiting on the semaphore after processing.
Hi Marc,
With a oscilloscope, I clearly see that pushing on the button generate one single pulse.
And as I run the task in unprivileged mode, I can't re-enable the interrupt in the task that handles the semaphore (or there is another way than the NVIC functions to do this ?).
Also, I tried in debug, and when I put a breakpoint in the interrupt handler, the bit seems to be cleared. But as soon as I remove it, the overflow occur again.
You can either use a privileged mode task setup or use SVC calls to re-enable the interrupt.
Also, even though it may look like a single pulse on the scope I wouldn't be surprised if the micro received multiple interrupts.
For testing just set all your tasks to run privileged and try disabling/re-enabling the interrupt.
Maybe see this:
http://www.keil.com/forum/18951/
Normally the Cortex-M3 wait until the previous write is completed before the interrupt return. However, some microcontrollers has a write buffer in the peripheral bridge. The write buffer accelerates single write transfers to peripherals so that the processor don't have to wait until the transfer is done (peripheral bus might be running at slower clock speed).
However, for clearing an interrupt request, the write buffer reply to the processor that transfer is done, while the actual write transfer actually haven't been completed in the peripheral. So the processor executed interrupt return, and found that the interrupt request from the peripheral is still high and think that it is a new interrupt request.
By Joseph Yiu
Marc : We looked with the scope with more zoom, and clearly the pulse produced by the button is not perfect. But it won't explain the 32 (or more, this is the size of the FIFO) interrupts generated.
I did what you suggested, disabling the interrupts in the handler, and the button is now working fine, almost all the time. I see that the interrupt is still triggered twice, when I press on the button too long. Disabling the interrupts might be a workaround. But what are the advantages/disadvantages to run all tasks in privileged mode ? (I'm new to the Cortex and RTX-OS, I was working on ST10 and XC166 before)
John : Thanks for the link, it would explain why it works in debug.
Trigged twice if you keep the button pressed too long, you say.
Is it only trigged twice, or can you get many interrupts if you keep the button pressed for 10 seconds?
If you get interrupts while the button is pressed, then you have either failed to set the controller to be edge-trigged. Or you have a problem with the signal level, so that noise makes the signal ripple above/below the trig level. Or you have failed to acknowledge the interrupt controller, so it reenters.
If you "only" get an extra interrupt when you release the button again, then you either have ripple/bounce or have failed to configure the input to only trig on one flank - a number of processors supports external interrupts to trig on high-to-low, low-to-high, both or level.
If you combine the interrupt handling with a timer (used for reactivating the input), then the timer ISR should check the static state of the pin and wait more timer periods before reactivating the interrupt, if the pin hasn't returned to the idle state.
I modified the IRQ handler like suggested on the other topic :
void EXTI15_10_IRQHandler (void) __irq { if(EXTI_GetITStatus(EXTI_Line15) != RESET) { /* Clear the EXTI line 15 pending bit */ EXTI_ClearITPendingBit(EXTI_Line15); /*Send Semaphore */ isr_sem_send (semaphore1); /* Re-clears the interrupt NVIC_ClearPendingIRQ(EXTI15_10_IRQn); } }
and there is no more overflow, the semaphore is only set once.
Hi Per,
When I keep the button pressed, I get no interrupts. But sometimes they occur when I release it. This is not a big issue, because we probably put a task polling for buttons (and use very different ones on our final hardware), as we want to implement some debounce on them. This was just for testing the external interrupts.
Any mechanical button in my experience will cause several edge interrupts on both edges on a press and release. That is the whole point of debouncing.
A very rough method I use is to use task events.
If I get an interrupt on say the rising edge the ISR disables the interrupt and raises an event.
A task is waiting for the event and when it receives the event it checks the state of the pin for X amount of time to make sure it is a press and not a bounce.
If it's a press handle the press otherwise re-enable the interrupt and wait for the event again.