Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

UART Interrupts in FreeRTOS

Status
Not open for further replies.

May Powitzer

Newbie
Newbie level 3
Joined
Nov 29, 2020
Messages
3
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
33
Hi everyone,
I am a newbie to FreeRTOS and I am trying to implement communication using UART on my zcu104 board.
I implemented an infinite loop in my main thread to listen for a flag raised by an interrupt handler. It works fine.
When I placed the loop after the schedular, it stopped working.
I found that inside the schedular function it has a function called “portDISABLE_INTERRUPTS()”, above it the following comment:

“Interrupts are turned off here, to ensure a tick does not occur
before or during the call to xPortStartScheduler(). The stacks of
the created tasks contain a status word with interrupts switched on
so interrupts will automatically get re-enabled when the first task
starts to run.”
I created a task as suggested in the comment and it still not working.
I tried to find out if there are any priority issues and it seems that the interrupts have a higher priority than my task as it should.

What am I missing?

Thank you.
 

Hi,

I didn't use RTOS before...but I think an infinite loop is not what one wants in an RTOS.
I'd use a task instead an endless loop.

Usually the (UART) interrupt stores received data in a software buffer (FIFO), maybe sets a FLAG when data is received.
The buffer need to be big enough to store at least the count data being received in the time between the main (parse) tasks.
Add some headroom.

Let's say the baudrate is 115200 and the UART_PARSE task runs every 100ms, then the buffer needs to handle 1152 bytes.
But often there is a "request - response communication" with a max expectable packet size, then it just needs to handle the count of a packet size. ... plus headroom.

I recommend to draw a timing diagram according your UART protocol. Then a flow chart. Not only for us, mainly for yourself.

Klaus
 

Thank you @KlausST for your fast response, I also tried something else like you said, I opened a task and stored the loop inside it. The task listens for a flag that changes by the handler when an interrupt occurs, as you said. But unfortunately, it doesn't work because it is after the scheduler starts.
The baud rate I use is 460800, I'm supposed to get a message every 1 millisecond and the buffer needs to handle not more than 30 bytes per message.
 

What I've done in the past is to have the ISR set a semaphore or push something to a queue (depending on the situation) - there are specific versions of the appropriate routines that operate from within an ISR. (They usually have something like "...FromISR" at the end of the function name.)
Then in your task you simply wait for the semaphore/queue and then process the data. That can be done in a loop because the wait triggers the task switch etc so nothing else is held up.
Susan
 

Thank you for the replays, I’ve tried to enable the UART interrupt from a task after the scheduler has started, and it seems to work better. The problem now is that the other task (my_main_task) runs only once and then no more. I didn't understand how to implement this code without a loop inside the task, I need the task to run immediately after I received a message by the UART interrupt. I tried to use suspend instead of delay, I hope it is as good as the delay @KlausST suggested or the semaphore as @Aussie Susan sujjested.

the code:
C:
/* the UART task - it enables the UART interrupt and then suspend the task until a message has arrived with UART */
void uart_task()
{
    int Status;

    //UART 1 init
    Status = Init_uart_1(&InterruptController, &UartPs, UART_DEVICE_ID_WEAP,

                                                                            UART_INT_IRQ_ID_WEAP);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    while(1)
    {
        vTaskSuspend(NULL); // suspend itself
        printf("received uart message\r\n");
    }
}


/* the UART interrupt handler - it counts the bytes received by the UART and raises a flag when a message with 10 bytes long has received */
void Handler_uart_1(void *CallBackRef, u32 Event, unsigned int EventData)
{
    XUartPs_Recv(&UartPs, &RecvByte_uart_1, 1); // receive data from uart into RecvByte_uart_1
    // push the received byte into cyclic buffer
    CB_Push_byte(CycleBuffer, CYCLE_BUFFER_SIZE, &CB_count, &CB_tail, RecvByte_uart_1);
    // Later on, I will use the cyclic buffer in the uart_task to inspect the received message.

    if (RecvByte_uart_1 == 0x5dU) // check if it is the beginning of a message
    {
        hadler_msg_counter = 0;
    }
    hadler_msg_counter++;
    if (hadler_msg_counter == 10) // if finished reading a message
    {
        BaseType_t checkIfYieldRequired;
        checkIfYieldRequired = xTaskResumeFromISR(myIntTaskHandle);
        portYIELD_FROM_ISR(checkIfYieldRequired);
    }
}


/* the main task*/
void my_main_task(void * p)
{
    int count = 0;

    while(1)
    {
        printf("hello world! : %d\r\n", count++);
        vTaskDelay(1000 * configTICK_RATE_HZ / 1000);
    }
}


int main( void )
{
    xTaskCreate(my_main_task, "main_task", 200, (void*) 0, tskIDLE_PRIORITY, &myTaskHandle);
    xTaskCreate(uart_task, "UartListener", 200, (void*) 0, tskIDLE_PRIORITY, &myIntTaskHandle);

    vTaskStartScheduler();
    while(1);
    return 0;
}

the output:

hello world! : 0
received uart message
received uart message
received uart message
received uart message
received uart message
received uart message
received uart message
. . .
the “hello world !” message occurs only once even if I don’t send any data through UART.

I am not sure what is wrong here.
 

Hi,

inside your main there is a "while(1)". This makes you stop further processing.

Klaus
 

@KLAUS - the 'vTaskStartScheduler()' before the 'while(1)' you mention should never return: in effect it is the overall 'main loop'. It only returns if the scheduler can't start.
@May - can I suggest tat you use the more conventional (at least it is the only one I use and it has never failed):
vTaskDelay(1000/portTICK_PERIOD_MS);
if you want a 1 second delay. I'm not sure exactly what effect your code will have but I'm guessing that it may be causing a very long delay which is why you don't see it run a 2nd time.
Also 200 is rather a small task stack - I typically use 1024 as a minimum and often much more.
If what you really want it is your 'my_main_task' to restart after each received message then I would use something like:
Code:
SemaphoreHandle_t uartSemaphore;

void Handler_uart_1(...)
{
    signed BaseType_t higherTaskReady = pdFALSE;

    // Process the interrupt and when you want to process the whole line...
    xSemaphoreGiveFromISR(uartSemaphore, &higherTaskReady);
   portYIELD_FROM_ISR( higherTaskReady);
}

void my_main_task(void* parameters)
{
    uartSemaphoreCreateBinary();     // Even put this in your 'real' main() function
    xSemaphoreTake( uartSemaphore, portMAX_DELAY);   // Hold the semaphore - the ISR will release it
    while(1)
    {
        // Wait until the ISR has released the semaphore
        xSemaphoreTake(uartSemaphore, portMAX_DELAY);  // Should really check for a timeout
        // process the received line
        // loop again still holding the semaphore which the next ISR will release
    }
}
(Sorry - I'm not use to this forum and the above seems to have lost my indenting) Formatting tags fixed by moderator
By the way, the above requires that the lines can be processed faster than the next line can be received. If that is not the case then use a queue in much the same way - your ISR will push the line into the queue and the main loop will pause until at least 1 line is available in the queue. Just be wary of exceeding the size of the queue.
In this way you only need one task and the ISR.
Susan
 
Last edited by a moderator:

the 'vTaskStartScheduler()' before the 'while(1)' you mention should never return: in effect it is the overall 'main loop'. It only returns if the scheduler can't start.
I understand. It makes sense.

Klaus
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top