Receiving from a device without preventing other tasks to be executed

thyanger wrote on Friday, June 24, 2016:

Hi there,

I am deeply googling around to find some infos about how to organize my tasks in order to receive a data stream from a bus and then send these data to a remote device and a server. The troubles I am getting into, consist in tasks fighting each other: eg, so far there is a task in charge of receiving from the bus, another task has to initialize a 3G module (provide PIN, open network) and then login to the server, a third task is in charge to send the bus info to the remote device. Unfortunately, because of my poor design and knowledge, while the second task is trying to initialize the 3G, it is prevented by the others in transmitting/receiving to/from the module the initialization strings/acks. Each task works perfectly if alone. The fact is that I am not an expert in this field, so I am looking for good references, which I will be very thankful to whoever here around is going to suggest me. I know it is also matter on how priorities are given to the tasks, and I have also read http://www.freertos.org/tutorial/index.html. I have found a lot of info but none is giving me both the general idea and insight about the implementation

Thank you and regards.

richard_damon wrote on Friday, June 24, 2016:

The likely source of the problem is that your drivers are ‘spinning’ waiting for their device to do something and starving the other drivers of time. If the driver needs to wait for any significant time period, the driver should be enabling an interrupt to detect when the wait is over, and block on a syncronization primative until the interrupt occurs (the ISR will signal back to the driver task that the event happened).

Most sample code for running devices just does a spin loop waiting for the device, because it is simpler and often clearer. To multitask you really need to use interrupts.

If you absolutely can’t get interrupts to work, one other option that sort of works is to make all the tasks the same priority, and in the spin loops (every one of them) add a vTaskYield() to let the other tasks have a chance while you are waiting. If you can’t make them all the same priority, you can also use a vTaskDelay(1), but this may slow things down way too much.

thyanger wrote on Friday, June 24, 2016:

Yes, I was supposing that… Actually each low level IO is accomplished via interrupt (I am using transmit_it/receive_it peripheral related hals). For example, related to the bus, which actually is a CAN bus, at this stage I am doing the following (some code is better than any other explanation sometime):

void canLsReadGPIOTask(void const *pvParameters)
{
    canLsInit();
    canLsMsgElemCreate(&canLsMsg);

    while(1)
    {
        HAL_CAN_Receive_IT(&CAN_LS_Handle, CAN_FIFO0);

        while(canLsRxReady != SET)
        {}

        canLsRxReady = RESET;

       vTaskDelay(100);
    }
}

where canLsRxReady is a sync flag set within the callback related to HAL_CAN_Receive_IT. So the right next step would be using, for example, ulTaskNotifyTake + vTaskNotifyGiveFromISR (see http://www.freertos.org/ulTaskNotifyTake.html), if I got well, right?

And what about some critical jobs like pin insertion and ack reception? I don’t want to miss the ack because of a task switch (which is what happens now)! Should I use vTaskExitCritical? The point is that I have tasks which have to cycle on very small time-scales (e.g. CAN bus reception/transmission) others which cycle on very large time-scales compared with the firsts. However, during IO, the “slow tasks” must not be prevented by the faster ones.

rtel wrote on Friday, June 24, 2016:

Yes - I would agree vTaskNotifyGiveFromISR() (if the callback is called from an interrupt, which it sounds like it is) and ulTaskNotifyTake() would be the calls to use here. So replace the inner while() loop with a call to ulTaskNotifyTake() with a sensible block time - that will ensure the task only runs when there is data to process and you can remove the vTaskDelay(100). Remember to handle the condition where ulTaskNotifyTake() times out too in case the interrupt never comes.

Also, looking at your original post, you may consider simplifying the design a bit to use less tasks. If one thing must be done before another (the init must be done before data is sent for example) then they can be done from the same task - or init can be done before the scheduler starts.

thyanger wrote on Tuesday, June 28, 2016:

Yes, the callback is called from an interrupt and it works with ulTaskNotifyTake + vTaskNotifyGiveFromISR. Now, what about the following situation? There are TASK A and TASK B transmitting via the same UART peripheral with ISR. TASK A transmits a stream, TASK B transmits from time to time. I use a mutex to avoid conflicting accesses to the UART. I understand that I can also use ulTaskNotifyTake + vTaskNotifyGiveFromISR in this case since the transmission never occurs at the same time (due to mutex) and so the notification can return to the corresponding waiting task with no ambiguity. Am I getting right? Are there any caveats in doing this?

Regards.

rtel wrote on Tuesday, June 28, 2016:

As long as you manage it carefully you can still use the task
notifications. You need to store the handle of the task that needs to
be notified so the ISR knows where to send the notification. You can
see a template for this on the following page:

http://www.freertos.org/vTaskNotifyGiveFromISR.html

where the StartTransmission() function stores the handle of the task
that is performing the transmission in the xTaskToNotify variable, and
the ISR sends the notification to the handle stored in xTaskToNotify
before resetting xTaskToNotify to NULL.

thyanger wrote on Tuesday, June 28, 2016:

Good, thank you!

thyanger wrote on Tuesday, June 28, 2016:

Ok, it works perfectly!