Callback functions for posting to a queue

jmaygarden wrote on Wednesday, March 10, 2010:

I would like to setup a callback system for posting events to a task’s queue. However, I need to do so from both interrupt handlers (xQueueSendFromISR) and other tasks (xQueueSend). Is there a way to determine which method should be used at runtime? Or should I just provide a different callback function for use in an ISR versus another task?

Also, is it acceptable to use xQueueSendFromISR from another task? If so, what is different about it other than the timeout?

richard_damon wrote on Thursday, March 11, 2010:

I believe that you can call xQueueSendFromISR at task level, IF you place the call inside a critical section or disable the interrupts. The one thing that you will need to watch out for, is if the call sets the woken flag, you will need to due a yield (probably after you end your critical section).

FreeRTOS doesn’t provide a built in way to determine if you are in an interrupt or not to allow you to make the decision, and you need a bit more than that. If you are in an interrupt routine, you will need to the FromISR version, and pass it the pointer to the woken flag that the ISR is using.  It shouldn’t be too hard to adopt a standard interrupt entry style that clears the woken flag and sets a flag to let you know you are in an interrupt, and have standard exit code that clears the “in interrupt” flag (if your architecture allows nesting of interrupt that interact with FreeRTOS, if there is such a beast, you would need to account for that), and check the woken flag to do the needed task switch. Either all interrupts can share the same woken flag, or you have a pointer to what is the “current” woken flag. You could then write a generic QueueSend that tests the in interrupt flag and call the appropriate function.

rtel wrote on Thursday, March 11, 2010:

Just to add to what richard_damon said, ports that support interrupt nesting should take care of the critical sections themselves, within the FromISR() functions.  Please check this though.


jmaygarden wrote on Thursday, March 11, 2010:

Well, it does seem more straight forward to have two event posting functions (ISR & task level) and build callbacks according to usage instead of trying to track interrupt status just to reduce it to one function with a branch…

I’m using a Cortex-M3 and the context switch when the woken flag is set just sets the PENDDVSET bit in the NVIC controller. It’s safe to do this before the end of the interrupt, correct?

event_postfromisr(Event *event)
    portBASE_TYPE requestContextSwitch;
    bool ok;
    requestContextSwitch = pdFALSE;
    ok = xQueueSendFromISR(event->queue, event, &requestContextSwitch);
    if (pdFALSE == ok) {
        /* Doh! We lost an event. */
    if (requestContextSwitch)
    return ok;
event_postfromtask(Event *event)
    return xQueueSend(event->queue, event, portMAX_DELAY);

rtel wrote on Thursday, March 11, 2010:

Yes that looks fine to me - without studying it in any great detail.

On the Cortex M3 all context switches are performed in the PendSV handler, so you are correct to say that all the vPortYieldFromISR() does is set the pend bit.

You don’t actually need to do that in every ISR - whether or not it is desirable or not is really dependent of the urgency of handling the event so is application dependent.  If writing to the queue unblocks a task and that task has a higher priority than the running task then it will get selected at the next tick interrupt anyway.  It is only necessary to context switch immediately if the requirements for how quickly the event must be processed are less than the time between each tick.  Of course a really really real time system should run the task if its the highest priority anyway.  Hope I’m not confusing things too much.


jmaygarden wrote on Thursday, March 11, 2010:

No, that’s not confusing at all. It confirms my assumptions. Thanks.

I’m still getting stuck in vListInsert after running for a while when receiving from one of these event queues. I’ve ruled out stack overflow (1) and interrupt priorities (2). I don’t suspend the scheduler or use any critical sections (3). And the queue is up and working for a while before it hits this problem (4) . That seems to address items 1 through 4 in the big comment. Looks like I’m in for some heavy debugging to find out what is causing the list corruption…

anonymous wrote on Tuesday, March 23, 2010:

I had the same problem, getting stuck in vListInsert.
Problem is related to the interrupt priorities.
The prioity of the interrupt you are calling the function xQueueSendFromISR must be greater than value you choose to use in the configMAX_SYSCALL_INTERRUPT_PRIORITY configuration option.
Note that in Cortex-M3 the greater interrupt value means lower priority for that interrupt.

For example (Cortex-M3):
If you let the default value for configMAX_SYSCALL_INTERRUPT_PRIORITY (191) you need to put 192 or a gretar value to the priority of any interrupt that uses any function from FreeRTOS API.
And don’t forget to use functions …FromISR() from within interrupt handlers.

I hope this can help you in some way.

jmaygarden wrote on Tuesday, March 23, 2010:

I though that the priority could be equal to configMAX_SYSCALL_INTERRUPT_PRIORITY or lower (higher number for Cortex-M3). That worked just fine for me with an STM32, but the SAM3U is having issue with it. I set configMAX_SYSCALL_INTERRUPT_PRIORITY to configKERNEL_INTERRUPT_PRIORITY (as shown below) and it alleviated my problem. So, I know it is definitely related to interrupt priorities.

#if 0
#define configKERNEL_INTERRUPT_PRIORITY 		(0x0F << 4)	/* Priority 15, or 255 as only the top four bits are implemented.  This is the lowest priority. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	(0x05 << 4)  	/* Priority 5, or 80 as only the top four bits are implemented. */
#define configKERNEL_INTERRUPT_PRIORITY 		(255)

I’m pretty sure I even tried using configKERNEL_INTERRUPT_PRIORITY for all of my interrupts and that didn’t do the trick either.

Also, the problem is occurring with a xQueueSendFromISR from the real-time timer (RTT) alarm at 100 Hz. That module is very quirky. You have to poll the alarm status for 2 slow-clock cycles before exiting the interrupt. That makes the ISR take about 60 microseconds. I don’t like that one bit and perhaps it could mess up the kernel if its interrupts are blocked during that time?

davedoors wrote on Tuesday, March 23, 2010:

I don’t know if it is relevant in this case, but remember different M3 suppliers implement a different number of priority bits. Also different libraries expect the priority to be presented in a different way.  For example, CMSIS libraries want the priority set in the least significant bits of the byte, while LMI libraries want the priority bits shifted up to the most significant bits of the byte.

jmaygarden wrote on Tuesday, March 23, 2010:

I just looked at the NVIC_SetPriority function in the CMSIS header file. It shifts the priority value 4 bits to the left. So, the shift is happening twice when I use “FreeRTOSConfig.h” from the SAM3U example. I’m going to make the change below and give it a try. This should fix the problem.

#if 0
#define configKERNEL_INTERRUPT_PRIORITY 		(0x0F << 4) /* Priority 15, or 255 as only the top four bits are implemented.  This is the lowest priority. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	(0x05 << 4) /* Priority 5, or 80 as only the top four bits are implemented. */
#define configKERNEL_INTERRUPT_PRIORITY 		(15)

rtel wrote on Tuesday, March 23, 2010:

If I understand you correctly I don’t think that will work.  If you look at where these constants are used in port.c you will see that they expect to be correct, that is, shifted to the correct bit position within the 8 bit byte.


jmaygarden wrote on Tuesday, March 23, 2010:

You are correct. The change above breaks the FreeRTOS usage of those macros. What I actually needed to change was the value passed to NVIC_SetPriority in user code. So, here is the final disposition of the interrupt priority macros that works with Atmel’s latest CMSIS library for the SAM3U:

#define configKERNEL_INTERRUPT_PRIORITY 		(0xF << 4)
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	(0x5 << 4)

I know use CMSIS_SYSCALL_INTERRUPT_PRIORITY for all interrupts that make system calls instead of using configMAX_SYSCALL_INTERRUPT_PRIORITY directly.