Management and priority of interrupt

giu14 wrote on Sunday, November 25, 2018:

Hi,
I have developed a project with three task. Task1 and Task 2 have the same priority “1”, and Task 3 a higher priority “2”. I need to pass values from Task1 and Task2 to Task3, which sends them to external device with an usart, and wait the answer coming from from the same usart.
In particular, Task3 waits 25ms for the response, if this doesn’t arrive, the message is retransmitted.

I have implement the code in this way:
Task1 and Task2 send data to Task3 on a shared queue, Task3 writes data to usart and
waits to recive date from it. I have enabled the usart interrupt, so when the data is received the code referring to interrupt is executed. These are sent to the Task3 via another queue with xQueueSendFromISR.
Now, here’s how I would like it to work and I cannot make it work and what my doubts are.
To read date from usart I use usart_read(), I don’t know the number of bytes I receive but I have a special character at the last byte telling me that the message is finished.
I would like to capture all the message coming from usart before returning to task3. From what I understood to do this is set up in xQueueSendFromISR a pointer &xHigherPriorityTaskWoken, but I did not understand how it is used. Is it an input or is it ouput of xQueueSendFromISR? If it is an input I initialize it to pdFALSE until I receive the whole message and then I set it to pdTrue to do a switch context and return to task3. Is it exact?
Now, I done several attemps, in some of this the code worked, but not always, i.e I runned the same code and only 1 time on three it works.
Here is my other doubt:
The interrupt always has a higher priority on the task execution, but I read that there are different types of priority, suppose I have 255 levels, some of them, those from 0 to a number that I can specify “x”, they can do API calls (i.e. xQueueSendFromISR), but they will be delayed by anything the kernel is doing; from “x” to 255 will never be delayed by anything the kernel is doing, but cannot use any FreeRTOS API functions.
So, if I want to use xQueueSendFromISR do I have to use the priority from 0 to “x”? but in this case can there be delays that can cause the code not work?
I use cortex-M and I know that priority 0 is higher than 255, in the case of this cortex, is the same valid? Or is everything reversed?
I.e. from 0 to “x” cannot do API calls and “x” to 255 can do but they will be delayed.
I hope that my question and my doubts are clear enough.
Thanks for your help
Best regards.
Giuseppe

richarddamon wrote on Sunday, November 25, 2018:

On interrupt priorities, Yes, on the Cortex, 0 is the highest and 255 is the lowest, although for many of the parts/drivers, the top number is lower, often 15 or 31 as there are fewer usable bits and the drivers shift the value up.

The priorities below (higher in value) than the threshold can indeed be delayed by critical sections in the kernal (or user code), but the kernal keeps these very short, so unless you need very fast response times (like microseconds) it shouldn’t be an issue, Just make sure YOU don’t create a too long critical section in your own code.

If you don’t want to wake Task3 until the full message is received, then don’t use a queue of characters, but assemble the message in a buffer that is accessable by both the ISR and the task, and send a signal (semaphore or direct-to-task-notification) that a full message is available, or send a message of a pointer to that buffer to the task.

You could in the ISR just not invoke the scheduler unless the end of message character was received, but Task3 might still get woken early if a timer tick comes along, so that isn’t reliable.

giu14 wrote on Monday, November 26, 2018:

Hi,
Thanks for your answer.
to not wake up task3 I do:

void USART_SERIAL_ISR_HANDLER(void)
{
BaseType_t xHigherPriorityTaskWoken;
uint32_t dw_status = usart_get_status(USART_SERIAL);
uint32_t received_byte;
int i=0;
if (dw_status & US_CSR_RXRDY) {
i=0;
xHigherPriorityTaskWoken = pdFALSE;
do
{
while (usart_read(USART_SERIAL, &received_byte));
buffer[i]=received_byte;
i++;
} while (received_byte<0x100);
i=0;
xSemaphoreGiveFromISR( xBinarySemaphore,&xHigherPriorityTaskWoken);}
if( xHigherPriorityTaskWoken == pdTRUE )
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}

where USART_SERIAL_ISR_HANDLER is the function call when interrupt arrives, buffer is the buffer shared with Task3. Is it right?

Sorry, I don’t understand:
You could in the ISR just not invoke the scheduler unless the end of message character was received, but Task3 might still get woken early if a timer tick comes along, so that isn’t reliable.

The scheduler is invoke with portYIELD_FROM_ISR, is it right? So untill I don’t call it the Task3 doesnt’ woken. What do you mean with “timer tick comes along”.
Sorry, but I’m not pratical di freeRTOS.
Thanks.
Best regards.
Giuseppe

richarddamon wrote on Monday, November 26, 2018:

Yes, not calling portYIELD_FROM_ISR (testing some other flag as well as xHigherPriorityTaskWoken) would keep THAT interrupt from waking Task3, but if any other interrupt occurs that decides to active the schedule, like the system timer tick, then Task3 will get started when you don’t want it to. That may or may not be a problem Your new ISR won’t attempt to wake Task3 unti you do the xSemaphoreGiveFromISR. FreeRTOS uses an interrupt from some system timer to provide a time base for operation, and this interrupt can cause task switching to occur if needed.

A couple comments on the code I see. One is that this ISR is NOT checking that it received data has included your ‘special character’ before waking Task3, and your ISR is resetting the address to store data on each new interrupt, so unless the UART itself knows enough not to generate the interrupt until that character arrives (in which case you never had an issue) you will be overwriting the received data.

giu14 wrote on Monday, November 26, 2018:

Hi,
maybe I don’t understand well, the special charcter is a value >= 0x100, and I check in
while (received_byte<0x100);
Doesn’t the code execute while cycle until is 0x100 received? It overwritten the received_byte but the value before that it is overwritten is stored in the buffer. Or at any interrupt does the code restart from
if (dw_status & US_CSR_RXRDY) {
?
Thanks.
Best regards
Giuseppe

richard_damon wrote on Monday, November 26, 2018:

Uarts are generally 8 bit devices, but I suppose that if you are configured in some wider mode it might be able to work.

Looking closer at your code, I see that it does wait in the ISR for the full message to come in, which while that might make it ‘work’, is awful. You do realize that means your whole system stops doing ANY tasks between the first character of the message and the last. ISRs should get the ready data and then leave and wait for the next interrupt before processing more. Waiting in an ISR basically means you have no need for a Real-Time system as you are totally defeating it.

giu14 wrote on Tuesday, November 27, 2018:

Hi,
I configure the usart to work with 9 bit data. I use Freertos because I need to use Freertos because the same uart is shared amoung several independent task in RX/TX. So, when one task send date on the uart it wait for 5ms to arrive the first byte, if it arrive acquire all receive date until the special character. The others task doesn’t send data on the uart untill the 5ms have passed and/or the date on the uart are received.
But, I don’t understand why the code still does not work.
In some cases seemed to me that insert breakpoint the code have different behaviors.
What can this mean?
Thanks.
Best regards.
Giuseppe