Hi, I'm working on M0+, but the question applies to M3 or M4 too.
I have the following situation: 2 interrupt sources inject events on a state machine that must handle each event separately (not nested execution)
The state machine is implementing a protocol and is using the UART interrupt and the SysTick for timeouts.
A UART interrupt causes the state machine execution to evolve based on related UART event.
The same routine, executed inside the SysTick interrupt service routine, handles timeout event.
It happens the 2 interrupts, SysTick and UART have different priorities.
I want to be sure that when the state machine is processing one event (pretend it is UART), the timeout event is postponed (i.e. the interrupt is served when the previous is completed)
I wrote the following code to protect state machine execution from self-nesting
extern void commStack_uart_v0_rx( void )
{
uInt8 timer_priority;
uInt8 my_priority;
my_priority = NVIC_GetPriority( UART0_IRQn );
timer_priority = NVIC_GetPriority( SysTick_IRQn );
if (timer_priority < my_priority) NVIC_SetPriority( SysTick_IRQn, my_priority );
state_machine(); // Execute the state machine to process UART event
NVIC_SetPriority( SysTick_IRQn, timer_priority );
}
Before executing state_machine, I change the priority of SysTick to match the UART priority if lower number. In other words I change the SysTick priority so that it cannot enter over the UART.
Before leaving I restore the priority.
Is it the right way to proceed? Since it is not possible to postpone the service of a SysTick interrupt with a trick like disable/enable.
The SysTick can't be temporary stopped.
I'm looking for a solution that can be used to solve problems of this category.
Thanks.
That's a really neat solution, Joseph, and a really good example of what PendSV is for!
Thank you very much Joseph for your precise answer.
I found a lot of detail I didn't know or catch in the manual and it really shed light on the topic.
It unveiled me new ways to think to solutions using fruitfully core potentiality.
In ARMv6-M architecture (Cortex-M0, M0+, M1), dynamic changing of priority level in an enabled interrupt is not supported.
You can change priority level dynamically in ARMv7-M (Cortex-M3, M4), and there is an additional special register called BASEPRI which you can use to mask interrupts/exception for certain priority or below.
In Cortex-M0/M0+, since dynamic priority level change for enabled interupt is not supported (typo correction 9-Dec-13),
you should disable it first, then change the priority level, and then reenable it.
However, for SysTick timer interrupt, it you "disable" the interrupt generation, you could lost a timer event.
Ideally you can set the two interrupts at same priority level, but assume that this is not possible, you could use the following method, by change the way your SysTick ISR work:
// Declare a global volatile variable
volatile int running_uart_task = 0;
// Set the variable to 1 when the UART task is running.
{ ...
running_uart_task = 1;
running_uart_task = 0;
...
In your SysTick handle:
void SysTick_Handler(void)
if (running_uart_task == 0) {
// Do work
} else {
// Set PendSV pending status
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
return;
Create a PendSV handler, and program it to be same level as UART or lowest priority.
void PendSV_Handler(void)
SCB->ICSR |=SCB_ICSR_PENDSTSET_Msk; // Set SysTick pending status
So when the SysTick get triggered during your UART task,
1) the SysTIck ISR will execute, finding out the running_uart_task is set, so it set PendSV pending status
2) Return to UART ISR, finishing the UART task
3) PendSV ISR execute after UART task, this set the SysTick pending status
4) SysTick handler executed.
Hope this helps.