Sharing data between task and fast ISR

Might be interesting if you can’t use traceanalyzer to at least pull run time states to see if something else is using time.

What speed is the system tick running, could that be getting in the way?

Any lower priority interrupts? What if you make the Below the line ISR low priority to see if something is getting in the way.

Might also put a trace to watch scheduler disable calls.

Another silly question: Your main stack (usually defined in linker script) is also large enough ? Main stack is used for ISRs on Cortex-M MCUs.

All wonderful suggestions, I appreciate it. None of your questions are silly @hs2 as they probably are leading me to what is going wrong. Regarding the main stack, it is definitely big enough, as I have it relocated to live above the .text section. This causes a stack overflow to trigger a memory fault, as it tries to write to a read only section.

There is one other task that I didn’t tell people about…the one that needs to run every 3 motor control iterations (you can see my original post for the description). When I removed that task, the issue went away. Seems odd, given that its priority was lower than the motor control task so it should always be preempted…so looks like all this might just be an issue with my configuration or task initialization?

I have the following configs of interest (taken from the configuration for the demo app in my port)

#define configUSE_PREEMPTION 1
#define configUSE_TIME_SLICING 0
#define configUSE_TIMERS 1

// no define for optimized task selection in my port

// removed bitwise operations for clarity
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2
#define configKERNEL_INTERRUPT_PRIORITY 240 // (0xFO)
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 32 // (0x20)

Again, these are how NXP set them in the example app for my port. Are any of them inappropriate?

I initialize the two tasks like so:

    MODULE_mctrl_th =
        xTaskCreateStatic(motor_control_task,                           /* Function that implements the task. */
                          "mctrl_task",                                 /* Text name for the task. */
                          STACK_SIZE,                                   /* Number of indexes in the xStack array. */
                          (void*)1,                                     /* Parameter passed into the task. */
                          configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY, /* Priority at which the task is created. */
                          MODULE_mctrl_stack,                           /* Array to use as the task's stack. */
                          &MODULE_mctrl_tb); /* Variable to hold the task's data structure. */

    MODULE_dof_ctrl_th = 
        xTaskCreateStatic(dof_control_task,  /* Function that implements the task. */
                           "dof_ctrl_task",  /* Text name for the task. */
                           STACK_SIZE,       /* Number of indexes in the xStack array. */
                           (void*)1,         /* Parameter passed into the task. */
                           configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1, // one less than the motor control priority
                           MODULE_dof_ctrl_stack,  /* Array to use as the task's stack. */
                           &MODULE_dof_ctrl_tb);

The slower “every third time” task is dispatched through another direct-to-task event from within the hi_prio_motor control task like so (adding to my original pseudocode):

// initialized to a task priority of configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
MOTOR_CONTROL_TASK() {
    while (1)
    {
        // block on direct to task notification
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        // (see oscilloscope trace)
        BLUE_SCOPE_TRACE_HIGH()

        complete_nonblocking_data_acquisition();
        run_motor_control_algo()

        static int divider = 0;
        if (++divider >= 3)
        {
            divider = 0;
            // unblock 3.3 kHz control task
            BaseType_t xHigherPriorityTaskWoken = pdFALSE;
            vTaskNotifyGiveFromISR(MODULE_dof_ctrl_th, &xHigherPriorityTaskWoken);
        }

        // (see oscilloscope trace)
        BLUE_SCOPE_TRACE_LOW()
    }
}

The DOF ctrl task is also just a NOP loop, in the same manner as the motor control task.

void dof_control_task(void* p)
{
    while (1)
    {
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
       
        do_dof_control() // NOP loop

    }
}

Do you think the config values would cause this situation?

EDIT: oh to answer your question @hs2 the actual hardware IRQ priorities are set correct, because they preempt things as expected. The task priorities are shown above – hi prio motor control at configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY, and the slower “every third PWM IRQ control loop” task set to configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY+1

is a misunderstanding. Task priorities have nothing to do with interrupt priorities.
Task prios are 0 … configMAX_PRIORITIES -1.
In fact dof_control_task is/was the highest prio task due to the wrong prio.
Please see RTOS task priorities in FreeRTOS for pre-emptive and co-operative real time operation
Using vTaskNotifyGiveFromISR in task context is a typo/bug. Use normal/task level vTaskNotifyGive in tasks.
Remember that configUSE_TIMERS is only needed if you wanna use FreeRTOS software timers. The timers are managed by a usually high-prio timer task. However it doesn’t matter if you don’t have timers activated.
But the good thing is you’re on the right track and your application will finally work as expected :+1:

1 Like

That sounds like the issue! I bet that will do it. Thanks so much for your help.

One comment, you clear xHigherPriorityTaskWoken before notifying the lower task, which by definition will never set it true, so you will skip waking up the high priority task on those steps. That should happen every third instead of every second, but might be an issue.

Hmmm, I thought I was supposed to do the following from within the highest priority “below the line ISR” in order to have execution immediately fall into the hi prio task

  // unblock motor control task, since we are now below the priority threshold by which we can use FreeRTOS APIs
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    vTaskNotifyGiveFromISR(MODULE_mctrl_th, &xHigherPriorityTaskWoken);

    // we should resume execution directly in the motor control task, as it is the highest priority worken task
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

Are you saying that doing it this way does not cause the hi_prio_task (MODULE_mctrl_th task handle) to be unblocked and immediately entered on exception exit?

EDIT: because the “below the line” ISR and the hi priority task are both the same priority

@richard-damon The scope trace seems to be doing what I want now, but that could be luck :). Are you saying I should manually set that flag to true?

Wait, the code I saw as in a task, tasks shouldn’t use FromISR version of the functions. I first though it was part of the ISR.

Tasks and ISR Can’t be ‘same priority’ ISRs by definition have a higher priority than any task, as they automatically interrupt them.

Yes, sorry, I misspoke. They were the same priority erroneously before @hs2 reminded me that tasks and ISRs have different priority schemes.

He also pointed out that I shouldn’t use the FromISR function in the task. I’ve since fixed that. So the only task that wakes another task is hi_prio_task → dof_ctrl_task