I am trying to implement the following pseudocode on a cortex-m3 controller, (STM32L151 in particular)
void SysTick_Handler() {
do_high_priority_periodic_tasks(); // not to be interrupted
lower_interrupt_priority();
do_low_priority_periodic_tasks(); // these may be interrupted
}
In other words, run the first part with priority level 0, then somehow lower the current interrupt priority to 15, so that the rest could be preempted by other hardware interrupts.
One idea is to move do_low_priority_periodic_tasks(); into a separate interrupt handler, and invoke this handler through NVIC_SetPendingIRQ() which sets a pending bit in the NVIC->ISPR[] register. This way, the other interrupt would immediately follow SysTick, unless there is anything with priority between 0 and 14 pending.
#define LOWPRIO_IRQn 55
void IRQ55_Handler() {
do_low_priority_periodic_tasks(); // these may be interrupted
}
void SysTick_Handler() {
do_high_priority_periodic_tasks(); // not to be interrupted
NVIC_SetPendingIRQ(LOWPRIO_IRQ);
}
void main() {
HAL_Init();
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
HAL_NVIC_SetPriority(LOWPRIO_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(LOWPRIO_IRQn);
while(1) {
/* main loop */
}
}
I've picked IRQ 55 because it's not occupied on my controller, it would be the AES interrupt handler on a STM32L162, but I'm a bit worried there. Should I pick a different IRQ instead, perhaps an unused DMA channel interrupt? Is it safe to use interrupts 57-67, which are defined in the Cortex-M3 core, but not on the STM32L series? Is there any better way to do it?
Is it safe to use interrupts 57-67, which are defined in the Cortex-M3 core, but not on the STM32L series?
No. Your NVIC may not actually implement them.
But the PendSV is exactly made for this task:
void SysTick_Handler() {
do_high_priority_periodic_tasks(); // not to be interrupted
// Set the PENDSVSET to trigger a PendSV exception
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
void PendSV_Handler() {
do_low_priority_periodic_tasks(); // these may be interrupted
}
See also this answer about PendSV.
Related
I am using an LPC2378 (ARM7TDMI-S) uC for a project. Timer0 on the uC is being used to measure frequency of a signal on one of the inputs of the uC. It is setup to interrupt on the rising edge of the signal (input capture) as well as interrupt on match register. The match register interrupt is set such that an interrupt occurs every 50mS. In my main code I have a while loop, which will only exit if a variable is set which only happens whenever the match interrupt of timer0 occurs. My ISR looks like this
static __irq __arm void TIMER_handler0(void) {
DWORD T0int;
T0int = T0IR;
if (T0int & IIR_CR0) { // interrupt due to rising edge
T0IR_bit.CR0INT = 1; // acknowledge interrupt
//Do stuff to figure out frequency of input signal
}
if ((T0int & IIR_MR0) || (T0int & IIR_MR1)) { // interrupt due to match
T0IR_bit.MR0INT = 1; // acknowledge interrupt
T0CCR_bit.CAP0I = 0; // turn off both interrupts (capture & match)
T0MCR_bit.MR0I = 0;
T0TCR_bit.CE = 0; // turn off the counter and reset the counter register
T0TC = 0;
timer0busy = 0;
}
VICADDRESS = 0x0;
}
While loop in my main code checks "timer0busy" flag and looks like this
while(timer0busy) {
//Do stuff
}
"timer0busy" flag is set to 1 when the timer0 is enable which is done before this while loop
On some occasions I have noticed that my uC would stop sending output through the UART. On further invertigation I found that it was stuck in the above while loop because "timer0busy" flag never became 0. I did some more testing and figured that this usually happens when the frequency of the input signal (which is measured by timer0) is increased. Now my understanding is that as the frequency of the input signal is increased the interrupts due to rising edge will also increase, but what I cannot figure out is that why does at some point the interrupt due to match register stops happening. The match register is setup to interrupt every 50mS regardless of the frequency of the input signal, and when this interrupt happens, interrupts are disabled and flag is set to 0. For both capture and match register interrupts the ISR is the same. Is the timer overshooting the match value without getting caught?. I don' know how that could happen. What would happen if the rising edge and match happens at the same time?. Could this be the result when that happens
At what frequency approximately does interrupt no longer work?
From what I understand this part of interrupt runs at every rising edge of the signal:
if (T0int & IIR_CR0) { // interrupt due to rising edge
T0IR_bit.CR0INT = 1; // acknowledge interrupt
//Do stuff to figure out frequency of input signal
}
Are you sure it has enough time to run before interrupt is triggered again? I would count clock cycles if you're unsure.
LPC2378 Does not have a Nester VIC, just a regular VIC. What that means is if interrupt A has occurred, and while it is mid-way through running, interrupt B is triggered, regular VIC cannot pause process called by A, run process B, then reenter A. It runs process A to completion, then runs process B.
My guess is that when frequency of your signal is high enough, your system calls interrupt upon interrupt without time to execute them to completion, causing a rapidly growing build up of work until VIC panics.
Try limiting the frequency of interrupts due to rising edge (like every second edge) when frequency gets too high.
RTC alarm interrupt (IRQ number 41) is being called on my development board with STM32F411CEU MCU continuously without even initializing RTC. I have this default interrupt handler (I commented out the Default_Handler in startup_stm32F411ceux.s):
void Default_Handler(void)
{
uint32_t irq_number = __get_IPSR() & IPSR_ISR_Msk;
for(int i=0; i<nf::HAL_IRQ_map::n_interrupts;i++)
{
if(nf::HAL_IRQ_map::map[i].irq == irq_number)
{
nf::HAL_IRQ_map::map[i].irq_handler(nf::HAL_IRQ_map::map[i].params);
break;
}
}
HAL_NVIC_ClearPendingIRQ((IRQn_Type) irq_number);
}
On the first line I get the IRQ number and it's always 41 which means RTC alarm interrupt. On the last line I try to clear the IRQ, but the handler gets called continuously. My stack looks like this:
Default_Handler() at hal_irq_map.cpp:15 0x801d3e0
<signal handler called>() at 0xfffffff9
HAL_TIM_Base_Start_IT() at stm32f4xx_hal_tim.c:447 0x8016eec
HAL_InitTick() at stm32f4xx_hal_timebase_tim.c:83 0x80131ea
HAL_Init() at stm32f4xx_hal.c:176 0x8013448
main() at main.c:95 0x8012696
The interrupt gets called over and over again and the stack always looks like this (the program doesn't progress). HAL_Init() is on the first line of main(). I have Stm32CubeIDE and ST-LINK V2.
The questions:
Am I doing something wrong when trying to clear the interrupt?
Why is the RTC alarm interrupt being called without initializing?
EDIT:
I tried this:
void Default_Handler(void)
{
//Check if we handle the interrupt
uint32_t irq_number = __get_IPSR() & IPSR_ISR_Msk;
bool handled = false;
for(int i=0; i<nf::HAL_IRQ_map::n_interrupts;i++)
{
if(nf::HAL_IRQ_map::map[i].irq == irq_number)
{
nf::HAL_IRQ_map::map[i].irq_handler(nf::HAL_IRQ_map::map[i].params);
handled = true;
break;
}
}
if(!handled)
HAL_NVIC_DisableIRQ((IRQn_Type) irq_number);
HAL_NVIC_ClearPendingIRQ((IRQn_Type) irq_number);
}
Now I'm disabling the interrupt explicitly, but the same interrupt keeps still firing.
The problem is "half" solved:
__get_IPSR() & IPSR_ISR_Msk doesn't seem to return the correct IRQ number. I'm now getting the IRQ number through HAL_NVIC_GetActive. I'm still new to ARM programming and didn't read the documentation well enough. "Active" IRQ means that it's being handled i.e. its' handler is currently being executed. "Pending" IRQ means that its' handler is to be called as soons as possible. So when you are in the default handler and you want to know which interrupt caused this call, you look for the "active" IRQ.
However, I still don't know why RTC alarm interrupt is pending and why it can't be disabled or cleared. I don't think it's ever even enabled.
Now my Default_Handler looks like this:
void Default_Handler(void)
{
uint32_t irq_number;
for(int i=0; i<50; i++) // loop is for debugging only - comment out when not used
{
if(HAL_NVIC_GetActive((IRQn_Type) i))
{
irq_number = i;
break;
}
}
bool handled = false;
for(int i=0; i<nf::HAL_IRQ_map::n_interrupts;i++)
{
if(HAL_NVIC_GetActive(nf::HAL_IRQ_map::map[i].irq))
{
nf::HAL_IRQ_map::map[i].irq_handler(nf::HAL_IRQ_map::map[i].params);
handled = true;
HAL_NVIC_ClearPendingIRQ(nf::HAL_IRQ_map::map[i].irq);
break;
}
}
if(!handled)
for(;;); //sit here forever
}
When I looped through the IRQ numbers I found out that the actual active IRQ number was TIM1_UP_TIM10_IRQn. But why is Default_Handler being called instead of (the correct handler) TIM1_UP_TIM10_IRQHandler?
I thought maybe it's because I have defined Default_Handler myself. There's two lines in startup_stm32f411ceux.s:
.weak TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
TIM1_UP_TIM10_IRQHandler is defined .weak which means that if you define the handler yourself, it's strong definition and will be used instead. The meaning of the second line is a bit fuzzy to me, but I think it means that Default_Handler will be used instead as a handler if TIM1_UP_TIM10_IRQHandler is not defined. In my code there are strong definitions for both TIM1_UP_TIM10_IRQHandler and Default_Handler. For a reason that is unclear to me, the linker chooses Default_Handler as the handler for timer 1 update interrupt.
Trying to define my own Default_Handler weak with either pragma weak or __attribute__((weak)) didn't help. So I simply commented out .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler in startup_stm32f411ceux.s and now TIM1_UP_TIM10_IRQHandler was called correctly.
So, I am doing a very simple time triggered pattern based on ARM Cortex M3.
The idea is:when SysTick is serviced, the task array index is incremented at systick, and so is a function pointer to the task. PendSV handler is called, and calls the task. I am using a Atmel ICE JTAG to debug it.
What happens is that it stucks at first task, and does not even increment the counter. It does not go anywhere.
Code pattern:
#include <asf.h> // atmel software framework. cmsis and board support package.
#define NTASKS 3
typedef void (*TaskFunction)(void);
void task1(void);
void task2(void);
void task3(void);
TaskFunction run = NULL;
uint32_t count1 = 0; //counter for task1
uint32_t count2 = 0; // 2
uint32_t count3 = 0; // 3
TaskFunction tasks[NTASKS] = {task1, task2, task3};
volatile uint8_t tasknum = 0;
void task1(void)
{
while(1)
{
count1++;
}
}
void task2(void)
{
while(1)
{
count2++;
}
}
void task3(void)
{
while(1)
{
count3++;
}
}
void SysTick_Handler(void)
{
tasknum = (tasknum == NTASKS-1) ? 0 : tasknum+1;
run = tasks[tasknum];
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
void PendSV_Handler(void)
{
run();
}
int main(void)
{
sysclk_init();
board_init();
pmc_enable_all_periph_clk();
SysTick_Config(1000);
while(1);
}
This design pattern is fundamentally flawed, I'm afraid.
At the first SysTick event, task1() will be called from within the PendSV handler, which will consequently not return. Further SysTick events will interrupt the PendSV handler and set the PendSV bit again, but unless the running task ends and the PendSV handler is allowed to return it can't possibly be invoked again.
The good news is that a proper context switch on the M3 is only a small amount of assembly language - perhaps 10 lines. You need to do some setup too, to get user mode code to use the process stack pointer and so on, and you need to set up a stack per task, but it's not really all that involved.
If you really want to cancel the running task when a SysTick arrives and launch another, they could all share the same stack; but it would be much easier if this was the process stack so that its stack pointer can be reset from within PendSV without affecting the return from handler mode. You'd also need to do some stack poking to convince PendSV to 'return' to the start of the next task you wanted to run.
I'm new to Embedded programming and multi-threading and I'm trying to understand how Interrupt handlers work in different contexts/scenarios. For the current question, I just want to know how a interrupt handler would work in the following scenario.
We have a data stream coming from a RS232 interface that is processed by some microcontroller. An interrupt handler(of void type) has a read() function which reads the incoming data bytes. If a character is detected then the interrupt handler invokes a function called detectString() which returns TRUE if the string matches the reference string which is "ON". If detectString() returns boolean TRUE it invokes a function called LED_ON() which should turn on an LED for 1 minute. If it returns false it should turn off the LED. Lets say the microcontroller has a clock frequency of 20MHz and an addition operation taken 5 clock cycles.
My questions are as follows
How do we approach this problem with an FSM?
The RS232 interface keeps transmitting data even after the LED is turned on. So am I correct in assuming that the interrupt handler should work with a one thread and the functions that it invokes should work from a different threads?
How would a skeletal program implementing this FSM look like? (a C pseudocode might really help to understand the backbone of the design)
If you are doing this in an interrupt handler, why would you need different threads? It shouldn't matter what else you're doing, as long as interrupts are enabled.
As for FSM, I wouldn't call a "detect_string". RS232 is going to give you one character at a time. It's possible your UART interrupts you only when you've received more than one, but there's usually a time component as well so it would be unwise to count on that. Make your FSM take one input character at a time. Your states would be something like:
=> new state = [Init] (turn LED off if on)
Init: (Get 'O') => new state = [GotO]
Init: (Get anything else) => new state = [Init]
Init: (Timer expires) => who cares? new state = [Init]
GotO: (Get 'N') => new state = [GotON] (turn on LED, set timer)
GotO: (Get anything else) => new state = [Init]
GotO: (Timer expires) => who cares? new state = [GotO]
GotON: (Get anything) => who cares? new state = [GotON]
GotON: (Timer expires) => turn LED off, new state = [Init]
Obviously lots of tinkering you could do with details, but that's the general idea.
A preemptive kernel will usually provide the ability for an interrupt to set an event that a higher priority thread is pending on.
As for the interrupts, one way of implementing something like a state machine is to use nested pointers to function, similar to an asynchronous callback, but with optional nesting: For example:
typedef void (*PFUN)(void);
/* ... */
PFUN pFunInt = UnexpectedInt; /* ptr to function for interrupt */
PFUN pFunIntSeqDone;
/* ... */
void DoSeq(void)
{
pFunIntSeqDone = IntSeqDone;
pFunInt = IntStep0;
/* enable interrupt, start I/O */
}
void IntStep0(void)
{
pFunInt = IntStep1;
/* handle interrupt */
}
void IntStep1(void)
{
pFunInt = IntStep2;
/* handle interrupt */
}
void IntStep2(void)
{
/* done with sequence, disable interrupt */
pFunInt = UnexpectedInt;
pFunIntSeqDone(); /* call end action handler */
}
void IntSeqDone(void)
{
/* interrupt sequence done handling code */
/* set event for pending thread */
}
void UnexpectedInt(void)
{
/* ... error handling code */
}
/*kernel calls attach the interrupt function handler to the hardware interrupt specified by intr(i.e irq) */
// InterruptAttach() : Attach an interrupt handler to an interrupt source
// interrupt source is handler1 for this example
void ConfigureISR(void) //void *ISR (void *arg)
{
/* the software must tell the OS that it wishes to associate the ISR with a particular source of //interrupts. On x86 platforms, there are generally 16 hardware Interrupt Request lines (IRQs) */
StartInterruptTime = GetTimeStamp(); //startTime of the interrupt
volatile int irq = 7; //0 : A clock that runs at the resolution set by ClockPeriod()
ThreadCtl (_NTO_TCTL_IO, NULL); // enables the hardware interrupt
id1 = InterruptAttach(irq, &ISR, NULL, 0, 0); // ISR is the interrupt service routine
//sleep(20);
}
int main ()
{
ConfigureISR();
return 1;
}
I created a interrupt handler on the server side to handle the interrupt from the client side. but the above code is breaking at the interrupt attach function call. could someone tell me why is it breaking ?? Is it the right way to handle interrupts in user application.