Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Real time embedded FreeRTOS mailing list 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT Training




Loading

Confused about interrupts in FreeRTOS

Posted by Erupter on October 21, 2011
After extensive testing and reading I'm still a bit confused.

Say I have the kernel running and I implement an ISR manually.
For example a fast ADC ISR: what happens to the context?
The ISR would surely interrupt some task(s) but since the interrupt is outside the scheduler, nothing gets saved.
Should I do something about this? Apart from carefully setting the priorities.

What are the cases in which the standard ISR return would modify the interrupted task context?
Should I worry about them?

RE: Confused about interrupts in FreeRTOS

Posted by Richard Damon on October 21, 2011
An ISR almost always needs to save context, as it can't disturb the code that it interrupted (some processors instead of saving the context, have an alternate context that the interrupt runs in).

While there are a few situations where an ISR might want to alter the context of the interrupt program, these are very specialized and very machine dependent.

If your ISR doesn't interact with FreeRTOS, then it can normally be built just like any ISR that your compiler generates. if your ISR will interact with FreeRTOS, then it needs to follow the instructions of the port, so that it saves the interrupt context in a manner compatible with the Scheduler (if needed), as well as needing to be at an interrupt priority compatible with that port.

RE: Confused about interrupts in FreeRTOS

Posted by Erupter on October 23, 2011
What do you mean exactly by "interact with FreeRTOS"?

Is calling vTaskSuspendFromISR or xQueueSendFromISR an interaction?

If not (I guess these functions exist just for being used by ISRs external to the main OS tasks), then would you mind presenting a case in which the ISR should take steps to actively save the context in an OS-compatible manner?

Also concurrence eludes my comprehension:
what happens if an ISR is started in the middle of a context switch?
Is this possible? Is this accounted for?

RE: Confused about interrupts in FreeRTOS

Posted by Richard Damon on October 23, 2011
If you call any of the ...FromISR routines, you are interacting with FreeRTOS, so that interrupt needs to meet the requirements to do so, the most important of which is having an allowed priority. These are detailed in the instructions for that port, as they vary in different ports.

If you want to be able to cause an immediate task switch due to the interrupt, there are additional rules in the port for how to invoke the task scheduler, and in some ports, this includes the preamble for the ISR to be compatible.

As to concurrence issues, the Scheduler will use a combination of Critical Sections (which disable interrupts that interact with the kernel) and internal software flags, to take into account for any concurrency issues for this, provided you follow the rules of the port (this is one of the major reason for many of the rules). You need to use similar tools if you have resources that you need to guard from concurrent access.

RE: Confused about interrupts in FreeRTOS

Posted by Erupter on October 24, 2011
Hi Richard,
This is the PIC32 official port page.

In the interrupt section there are instructions about nestable and non-nestable interrupts.
What does nestable means exactly?
That an interrupt can be interrupted by another and so the second is effectively nested inside the first?

From the UART interrupt example I understand the following:
in some cases it's best to save the context by using an OS-aware INT wrapper.
The prototype is shown in the page, and it clearly saves and then reloads the context before executing.
But when should I do this?

Then there is this:
/* If sending or receiving necessitates a context switch, then switch now. */
portEND_SWITCHING_ISR( xYieldRequired );

What does this mean?
What would happen if the function were to be called with TRUE as parameter?

The explanation says that the parameter should be non zero if a context switch is required, but the calling function (the interrupt wrapper) DOES a switch before and after calling the handler, so in theory the context switch is required.
Then why false?

I feel awfully confused about all this :(

RE: Confused about interrupts in FreeRTOS

Posted by Richard Damon on October 24, 2011
Yes, nested interrupts, when supported by the port, allow one interrupt routine to nest within another (except for with certain critical regions in the interrupt)

A call to portEND_SWITCING_ISR with a TRUE parameter changes which context the ISR will return to, i.e. which task will be running afterwards. I am not familiar with the PIC32 port, but some ports switch to a special interrupt context for processing interrupts to save user task stack space (otherwise every task need to have enough extra stack for the biggest stack that any ISR might need. Others just save the current context on the current task stack.

RE: Confused about interrupts in FreeRTOS

Posted by Jeff Andle on October 24, 2011
Richard,

I am using v.7.0.1 ported to a dsPIC33 with two serial ports running ISR's and a CAN ISR. Commands come in on the serial ports and I must enforce detecting line breaks as packet delimiters (think MODBUS, but there are also other protocols where this is needed.). I place packets on a queue (fromISR) and the tasks process that through, sending commands out the CAN to a coprocessor when needed, parsing the responses and replying as appropriate.

In order to enforce the line breaks I had each RX character GiveSemaphore() to a PortMonitorTask. If that task was unable to take a semaphore within the linebreak time, it detected a break, otherwise if it succeeded then it tried again. This seemed to work but occasionally commands were ignored.

Upon deeper examination, the system was perceiving breaks in the line because the serial ISR was being delayed. I switched to hardware timers that the RXISR would (re)start and set to zero and that were shut off when the message was posted. I still "detected" line breaks in the packets. So, I broke out an oscilloscope and there are no line breaks (the PC program feeding it has breaks but not big enough to cause the problem. The threshold was 2 ms and the longest gaps were 200 us.

So, I realized that enter/exit critical section would pause the interrupt. I added code in the port function to toggle a line and found I was never in a critical section more than 50 us. I then looked at the 1 ms tick timer:

void __attribute__((__interrupt__, auto_psv)) _T1Interrupt( void )
{
/* Clear the timer interrupt. */
IFS0bits.T1IF = 0;
//LATA |= 0x0200;

vTaskIncrementTick();

#if configUSE_PREEMPTION == 1
portYIELD();
#endif
//LATA &= 0xFDFF;
}

Usually this was under 50 us but occasionally it hit 500us and occasionally a full 1ms tick showed a high pulse.

(a) is there an event that occurs sporadically that would take 1ms to execute vTaskIncrementTick() and portYield()??

(b) I can tolerate a delayed post to queue but I cannot tolerate a false line break. If I make the RX ISR the highest priority, then I cannot call ...FromISR(). I can throw a software interrupt that calls the ...FromISR() functions and also diable the line break timer from the high priority interrupt. As long as I call no API, I should be OK, right?

(c) ... but this brings another problem. I share globals (like the RX buffer, it's length, etc.) and I need to manipulate them. If my code had it's own version of a critical section (or maybe the port already does) that prevented even the RX ISR from executing long enough to manipulate them, will this impact the RTOS (other than delaying a tick timer a few us)?

Thanks




RE: Confused about interrupts in FreeRTOS

Posted by Erupter on October 24, 2011
I would also like to know from *the other* Richard: is any part of this explained in the official book?

RE: Confused about interrupts in FreeRTOS

Posted by Jeff Andle on October 24, 2011
The PIC32 book is excellent - you should buy it - but PIC32 port allows a couple things (like this) that dsPIC port does not...

RE: Confused about interrupts in FreeRTOS

Posted by Richard Damon on October 25, 2011
Are you saying that sometimes you measure a 1 ms time in portYIELD()? that sounds very long. My first question is do you have your interrupt priorities set right. If I remember right, the dsPIC port wants all interrupts that interact with FreeRTOS to be at interrupt level 1, if you have an interrupt higher than that interact and it does a task switch, it can cause problems like this. You also may have a higher priority interrupt taking a lot of time.

If done right, you should NOT have significant delays in getting in and out of interrupts, so you shouldn't have this problem, and you should be able to keep the serial ISR interfacing with FreeRTOS.

If you really want to move the interrupt out of FreeRTOS, you could use the disable/enable interrupt function as a short term critical section. Other than the possible delays caused by this, it will not affect FreeRTOS.

One thing to warn you about your method of measuring break time, assuming a 1 ms tick rate (which is actually probably faster than you should need for most of your program) a 2ms delay for getting the semaphore is actually a 1 to 2 ms delay, as if it starts just before a time tick, that very small sliver of time counts as a tick for the timeout. What I would do for this application is have the ISR set/reset a hardware timer on each character, and if the timer expires then you have a break in transmission (which I think is what you are trying to detect). Now if you are actually trying to detect a "line break" (the serial line held in the *active* state for an extended period of time) the serial ports are capable of detecting that themselves. Having the hardware timer detect the line idle condition would allow you to drop the tick rate to something more reasonable.

RE: Confused about interrupts in FreeRTOS

Posted by Jeff Andle on October 25, 2011
I am measuring the time that LATA bit 9 is high with a digital scope. I also measured the RX ISR and so on. I have all ISR priorities at 1 (though I had earlier set RX to 2, I caught that and it changed nothing.) The RX ISR is only engaged a typical 10-20 us and closer to 60us when I post 256 bytes to the queue.

void __attribute__((__interrupt__, auto_psv)) _T1Interrupt( void )
{
/* Clear the timer interrupt. */
IFS0bits.T1IF = 0;
LATA |= 0x0200;

vTaskIncrementTick();

#if configUSE_PREEMPTION == 1
portYIELD();
#endif
LATA &= 0xFDFF;
}

I already do have a hardware timer instead of a task:

/* ----------------------- Platform includes --------------------------------*/
#include "iSAW_Base.h"
#include "MBPortTimer.h"
#include "Serial.h"

static xSemaphoreHandle rxCharRecievedSemaphore[2];

// =====================================================================
// Timer 2 is inter-character timeout for Serial 0
// Timer 3 is inter-character timeout for Serial 1
// PR3 is set to give an interrupt after the delay specified in
// the parameters, in milliseconds.
// also register the semaphores as this module will be
// keeping Serial#Monitor in line (unless it replaces it?)
//
void vInitMBTimers(unsigned short usDelay0, xSemaphoreHandle xSP0,
unsigned short usDelay1, xSemaphoreHandle xSP1)
{
rxCharRecievedSemaphore[0] = xSP0;
rxCharRecievedSemaphore[1] = xSP1;

T2CONbits.TON = 0; // Stop any 16/32-bit Timer3 operation
T2CONbits.T32 = 0; // Disable 32-bit Timer mode
T2CONbits.TCS = 0; // Select internal instruction cycle clock
T2CONbits.TGATE = 0; // Disable Gated Timer mode
T2CONbits.TCKPS = 0b01;// Select Prescaler = 8 --> 2500 pulses per millisecond
TMR2 = 0x00; // Clear 16-bit Timer
PR2 = TICKS_PER_MS * usDelay0; // Set 16-bit Timer range
T2CONbits.TON = 0; // Don't start 16-bit Timer
__TIM2_ENBIT = 1;
__TIM2_PRIOR = 1;
__TIM2_FLAG = 0;

T3CONbits.TON = 0; // Stop any 16-bit Timer3 operation
T3CONbits.TCS = 0; // Select internal instruction cycle clock
T3CONbits.TGATE = 0; // Disable Gated Timer mode
T3CONbits.TCKPS = 0b01;// Select Prescaler = 8 --> 2500 pulses per millisecond
TMR3 = 0x00; // Clear 16-bit Timer
PR3 = TICKS_PER_MS * usDelay1; // Set 16-bit Timer range
T3CONbits.TON = 0; // Don't start 16-bit Timer
__TIM3_ENBIT = 1;
__TIM3_PRIOR = 1;
__TIM3_FLAG = 0;
}


// ===================================================================================
// call this on each received character to start or reset the appropriate timer
//
void vTimerOnCharRX (unsigned short usPort)
{
switch (usPort)
{
case 0:
T2CONbits.TON = 1;
TMR2 = 0x0000;
__TIM2_FLAG = 0;
break;

case 1:
T3CONbits.TON = 1;
TMR3 = 0x0000;
__TIM3_FLAG = 0;
break;

default:
break;
}
}


void __attribute__((__interrupt__)) __attribute__((no_auto_psv)) _T2Interrupt(void)
{
portBASE_TYPE xHigherPriorityTaskWoken __attribute__ ((aligned (2))) = pdFALSE;
__TIM2_FLAG = 0;
T2CONbits.TON = 0;
//xSemaphoreGiveFromISR(rxCharRecievedSemaphore[0], &xHigherPriorityTaskWoken);

unsigned short usPort = 0;
switch (xSerState[usPort].usProtocol)
{
// In native protocol the ISR parses and this just resets
case SER_PROT_NATIVE:
//taskENTER_CRITICAL();
if (xSerState[usPort].usRxBufLen)
{
if (xDiag.ulSer1ResetRXState != 0xFFFFFFFF) xDiag.ulSer1ResetRXState++;
}
xSerState[usPort].usRxBufLen = 0;
xSerState[usPort].usCMDstate = SER_CMD_NONE; // start over on next message!
xSerState[usPort].usRXing = SER_RX_IDLE;
//taskEXIT_CRITICAL();
break;

// in MODBUS RTU the ISR collects and this dispatches
case SER_PROT_MODBUS:
//taskENTER_CRITICAL();
if (//!cSerRxBuf[usPort][0] || // need to think more about multicast
(cSerRxBuf[usPort][0] == xSerState[usPort].usAddress))
{
if (usSerProcessCRC(usPort))
{
// pop out for API call. Only post if it is ours and valid.
//taskEXIT_CRITICAL();
xQueueSendFromISR( xSerState[usPort].xRXMsgQueue, cSerRxBuf[usPort], &xHigherPriorityTaskWoken);
//taskENTER_CRITICAL();
}
}

xSerState[usPort].usRxBufLen = 0;
xSerState[usPort].usCMDstate = SER_CMD_NONE; // start over on next message!
xSerState[usPort].usRXing = SER_RX_IDLE;
//taskEXIT_CRITICAL();
break;

// case SER_PROT_STRIPPED cannot be assigned to a port.
default:
break;
}

if( xHigherPriorityTaskWoken != pdFALSE )
{
taskYIELD();
}
}

the commented critical enter and exit calls are from when the logic was in the task.

On the one hand, the Timer 1 interrupt is only long when there are commands being processed because otherwise it's idle. Tomorrow I'll contrive to make it busy without external cause (interrupts) to see if it's a task switch issue or an ISR competition issue.

To be clear, one level-1 interrupt cannot interrupt another on Microchip hardware, right?

RE: Confused about interrupts in FreeRTOS

Posted by Jeff Andle on October 25, 2011
also, "Having the hardware timer detect the line idle condition would allow you to drop the tick rate to something more reasonable." depends on what happens when taskA yields. None of my tasks do much:

Fetch MODBUS registers, strip data from coprocessor responses into registers, fetch co-processor commands from an eeprom script, etc. I bet I rarely run 1 ms... On the other hand I ended up *polling* the DMA-ECAN buffers... That needs to happen about every 5 ms and I have 6-8 tasks depending on how this fleshes out.


Not to introduce a totally different topic, but what's a "safe" excess stack high water mark? I assume 100 bytes is wasteful.

RE: Confused about interrupts in FreeRTOS

Posted by Richard Damon on October 25, 2011
Thinking back to how the dsPIC port works, your method of checking isn't really valid. On the dsPic (and PIC24) port, the call to portYield doesn't return until the task that was switched from gets restarted. In effect, the ISR ends on the call to portYield (or taskYield which is the same). The process of the scheduler saving and restore task context ends the interrupt, and when the switched out task come back again, it goes back into interrupt mode for just a moment and then back to where it was interrupted from. The instructions that the Yield must be the last thing in an ISR means it, as anything after the Yield may not me executed for awhile. If you want to scope trace to see when ISRS happen you need to clear the bit before the yield (so you will get fairly narrow pulses).

I will also add, that when I build a CAN interface routine on the dsPIC I had no trouble with using interrupts for the ECAN buffers in DMA space, I presume you have double checked the errata for the chip, I know the one I was using had a problem with ECAN buffers, and the recommended work around was to use only 1 ECAN TX Buffer.

Interrupts can only be nested by HIGHER priority interrupts, not same, on the microchip hardware.

As to how much "safe" excess stack, depends on how well you have exercised things, if you have really pushed things, you should be able to get away with only 1 byte, but I would feel safer allowing for the max ISR stack.

RE: Confused about interrupts in FreeRTOS

Posted by Richard on October 25, 2011
“ I added code in the port function to toggle a line and found I was never in a critical section more than 50 us”


You cannot measure critical section lengths, using the method, at least not on the PIC24/dsPIC. Discard any readings you have made for the following reason.

Each task maintains its own critical section nesting count. Consider the following scenario:

1) Inside the kernel code, a task enters a critical section to look at a queue. You set your toggle line high.
2) The task blocks on the queue - still inside the critical section (yes, the kernel is designed to do this on certain ports, I don't have time to go into the details as to why it is perfectly ok and does not mean interrupts remain disabled, user code must never do it itself).
3) The critical section nesting count of the task is saved as part of the task context.
4) Another task runs. Its context has a critical section nesting count of zero, so interrupts are enabled as the task context is restored - taskEXIT_CRITICAL() was never called and your toggle line is still high even though interrupts are enabled.



void __attribute__((__interrupt__, auto_psv)) _T1Interrupt( void )
{
/* Clear the timer interrupt. */
IFS0bits.T1IF = 0;
LATA |= 0x0200;

vTaskIncrementTick();

#if configUSE_PREEMPTION == 1
portYIELD();
#endif
LATA &= 0xFDFF;
}


You cannot measure the tick interrupt length in this way. Discard any readings taken using this method. The interrupt will switch to a different context when portYIELD() is called - portYIELD must be the last thing called in the interrupt.

From the documentation page for the PIC24/dsPIC port:

“Interrupt service routines that can cause a context switch must execute with priority portKERNEL_INTERRUPT_PRIORITY, and only call taskYIELD() at the very end of the service routine after the interrupt source has been cleared. See the file serial.c included in the demo application for an example.”


Regards.

RE: Confused about interrupts in FreeRTOS

Posted by Richard on October 25, 2011
“I would also like to know from *the other* Richard: is any part of this explained in the official book”


Unlike the PIC32 book, which does explain PIC32 interrupts, there is no official book for the PIC24/dsPIC. That leave the generic book, which talks about interrupts generically, that is, not in specific terms for any particular architecture.

Regards.

RE: Confused about interrupts in FreeRTOS

Posted by Jeff Andle on October 30, 2011
Precious, absolutely precious. I do all the work to use high priority serial port interrupts that throw software interrupts at kernel priority to call the API semaphore and queue functions... change delays to timers (OK, that saved me memory and made it more precise)...

Same behavior...

The Windows terminal emulator was the cause. I had tested the characters coming out of it when the embedded device was not connected. It had no idle breaks within a packet. I tested it with the embedded system connected and saw none, so I've been chasing down a "ghost in the machine".

I just used a better oscilloscope with pattern based triggering and sure enough - with characters streaming both ways, data to display, and a lot of overhead - 2-3 ms gaps in about 2-3% of the packets! This is, of course, one of the downfalls of Windows and USB-based virtual com ports. No interrupts on the PC side!

Thanks for all the help that made me look outside my system for the real problem.

RE: Confused about interrupts in FreeRTOS

Posted by Richard Damon on October 31, 2011
Actually, Windows does have interrupts for the USB device, you just don''t get them down to the application layer, and the USB protocol might insert some delay. You might want to try raising the priority for the windows application, and/or make sure it send the date for a packet as a single write operation so the device driver has the full packet to send.


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2016 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd.. See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.

Latest News:

FreeRTOS V9.0.0 is now available for download.


Free TCP/IP and file system demos for the RTOS


Sponsored Links

⇓ Now With No Code Size Limit! ⇓
⇑ Free Download Without Registering ⇑


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Renesas Electronics Gold Alliance RTOS Partner.jpg

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Atmel RTOS partner supporting ARM Cortex-M3 and AVR32 microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Xilinx Microblaze and Zynq partner

Silicon Labs low power RTOS partner

Altera RTOS partner for Nios II and Cortex-A9 SoC

Freescale Alliance RTOS Member supporting ARM and ColdFire microcontrollers

Infineon ARM Cortex-M microcontrollers

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

Cypress RTOS partner supporting ARM Cortex-M3

Fujitsu RTOS partner supporting ARM Cortex-M3 and FM3

Microsemi (previously Actel) RTOS partner supporting ARM Cortex-M3

Atollic Partner

IAR Partner

Keil ARM Partner

Embedded Artists