In my application, I am receiving bunch of data via UART port. I am using ARM A5 core. I am facing data loss issue while using vTaskDelay() inside the task. I provided 100ms as a delay period. Suppose if I comment this API or increase the delay period from 100 to 10000, I could see all data in my rxbuffer. I registered a callback after reception of each character.
Welcome to the FreeRTOS forum.
There is not enough information in your post to analyze the problem you’re having. ARM Cortex-A5 tells us the CPU architecture (ARMv7-A), and the fact that you’re posting the question to the FreeRTOS community (plus the API function name you included) tell us that it’s a FreeRTOS application, but without information about the target hardware, the drivers that are being used, etc.
Please include a bit more information, and perhaps somebody here can help you diagnose and fix the problem you’re having.
Also - the Community Media is not the best place for this question. Try the kernel forum.
Hi Daniel,
Apologies for not giving target details in the earlier mail. I am using SC594 Evaluation kit as a target. It contains Cortex-A5 and Sharc core. I have configured UART0 using driver APIs. I have configured descriptor in a chained manner. If I send a single character at a time via tera term, I could see that data in Rxbuffer. But if I copy set of character and send via teraterm using ALT+v option, I could see one or two characters only. If I comment out vTaskDelay() API inside task handler or increase the delay period from 100 to 10000, I could receive all the data in the Rxbuffer. With vTaskDelay() API, I could receive one or two data in the rxbuffer with the delay period of 100ms. But same application works fine in Sharc core as well as ARM55 core in SC598 eval kit. I suspect that the problem is related to port specific files. Could you please share your comments.
It is not clear from your description where are you calling vTaskDelay. Please share relevant code snippets.
Hi,
Please find the code snippet here.
void Task_1(void *param)
{
Uart_Init();
int i=0;
while (true)
{
for (volatile size_t _cycle_cnt = 1000000UL; _cycle_cnt; _cycle_cnt--) __asm volatile("NOP");
vTaskDelay((TickType_t)100/ portTICK_PERIOD_MS); /* dispatch */
}
}
int main()
{
adi_initComponents();
xReturned_1 = xTaskCreate(Task_1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+3, &xHandle_Task_1);
if( xReturned_1 != pdPASS )
abort();
/* Start the scheduler. */
vTaskStartScheduler();
}
Another observation is, we just commented vTaskDelay() API and placing portYIELD_WITHIN_API(); in the task handler function also creating the same issue. So we suspect that the problem is due to this port specific macro implementation
#define portYIELD() __asm volatile ( "SWI 0" ::: "memory" ); in ARM cortex A5 core.
void Task_1(void *param)
{
Uart_Init();
int i=0;
while (true)
{
// for (volatile size_t _cycle_cnt = 1000000UL; _cycle_cnt; _cycle_cnt--) __asm volatile("NOP");
// vTaskDelay((TickType_t)100/ portTICK_PERIOD_MS); /* dispatch */
portYIELD_WITHIN_API();
}
}
Please share your comments.
well, the busy loop will hog the CPU. If your eco system implicitly creates worker tasks at lower priority than your task to process the input, that worker thread may get starved.
What if you only do a small computation in your task?
Hi,
We are doing only a simple operation inside the task handler. UART0 has been configured to receive the data. The requirement is whenever we are sending a bunch of data it has to be received by the UART. UART callback will be triggered for every character. But we could see one or two bytes in the Rx buffer while using vTaskDelay() call inside the handler function. UART Rx Interrupt ID is 171 for ARM core. We suspect that the ISR is not getting serviced for all data if the vTaskDelay() API is used or portYIELD_WITHIN_API() is used inside the Task
void Task_1(void *param)
{
Uart_Init();
while (true)
{
vTaskDelay((TickType_t)100/ portTICK_PERIOD_MS);
//portYIELD_WITHIN_API();
}
}
Are you using an interrupt for the UART? How large is the memory buffer for the UART (not the internal Rx FIFO)?
I’m not allowed to post code snippets (not the website, my employer) so I cannot provide an example of something that I know works for me with FreeRTOS on ARM Cortex-M in STM32 microcontrollers and Microchip SmartFusion SoC FPGAs, and ARM Cortex-A53 in the PS of a Xilinx (AMD) MPSoC FPGA.
I also don’t know the SoC, and importantly, the vendor provided drivers. Interrupts should happen asynchronously to your loops unless you’re using blocking read calls. On the STM32, I typically use DMA with a circular buffer and register callbacks with the driver for buffer half-full and I/O complete. Depending on whether you have a dedicated task waiting for the next characters (blocking read with timeout) or the task polls the input buffer status periodically also affects things.
I have used FreeRTOS in a number of embedded applications, one of which had 3 UARTs connected to different types of navigation receivers accepting and parsing NEMA data along a 4th (BLE) for remote control, and a USB 2.0 High Speed connection to a host device. 8 foreground tasks in total (not counting the FreeRTOS timer task and the Idle task), no character loss.
you need to understand the control flow in your vendor provided device driver. In what context is your callback executed?
What is the point of having this task? Did you share the trimmed down version? Can you share the code which receives UART data and processes it?
Hi,
Please find the trimmed version of code snippet here. As like your application, I am also using DMA with a circular buffer and registered callbacks.
static void callback_uart_rx(void *param, uint32_t event, void *arg)
{
switch (event)
{
case ADI_UART_EVENT_RX_PROCESSED:
*RecvBuf_Uart_Rx_Curr++ = *(char *)arg;
if (RecvBuf_Uart_Rx_Curr >= &RecvBuf_Uart_Rx[1024]) RecvBuf_Uart_Rx_Curr = RecvBuf_Uart_Rx;
break;
default:
break;
}
}
void Uart_Init(void)
{
/* Initialize SPU */
if (adi_spu_Init(0u, Spu_memory, NULL, NULL, &Handle_Spu))
abort();
if (adi_spu_EnableMasterSecure(Handle_Spu, 29, true)) /* UART0 */
abort();
if (adi_spu_EnableMasterSecure(Handle_Spu, 79, true)) /* UART0 Rx DMA */
abort();
/* Set Descriptor List */
DescList_Uart_Rx[0].pNxtDscp = &DescList_Uart_Rx[1];
DescList_Uart_Rx[0].pStartAddr = &Dma_RecvBuf_Uart_Rx[0];
DescList_Uart_Rx[0].Config = ENUM_DMA_CFG_XCNT_INT;
DescList_Uart_Rx[0].XCount = 1;
DescList_Uart_Rx[0].XModify = 1;
DescList_Uart_Rx[0].YCount = 0;
DescList_Uart_Rx[0].YModify = 0;
DescList_Uart_Rx[1].pNxtDscp = &DescList_Uart_Rx[0];
DescList_Uart_Rx[1].pStartAddr = &Dma_RecvBuf_Uart_Rx[1];
DescList_Uart_Rx[1].Config = ENUM_DMA_CFG_XCNT_INT;
DescList_Uart_Rx[1].XCount = 1;
DescList_Uart_Rx[1].XModify = 1;
DescList_Uart_Rx[1].YCount = 0;
DescList_Uart_Rx[1].YModify = 0;
/* Initialize UART */
if (adi_uart_Open(ADI_UART_0, ADI_UART_DIR_BIDIRECTION, Uart_memory, ADI_UART_BIDIR_MEMORY_SIZE, &Handle_Uart))
abort();
if (adi_uart_ConfigBaudRate(Handle_Uart, 1, 135))
abort();
if (adi_uart_SetWordLen(Handle_Uart, ADI_UART_WORDLEN_8BITS))
abort();
if (adi_uart_EnableParity(Handle_Uart, false))
abort();
if (adi_uart_RegisterCallback(Handle_Uart, callback_uart_rx, NULL))
abort();
if (adi_uart_DMARead(Handle_Uart, DescList_Uart_Rx, 2, ADI_PDMA_DESCRIPTOR_LIST))
abort();
}
void Task_1(void *param)
{
Uart_Init();
while (true)
{
for (volatile size_t _cycle_cnt = 1000000UL; _cycle_cnt; _cycle_cnt--) __asm volatile("NOP");
vTaskDelay((TickType_t)100/ portTICK_PERIOD_MS); /* dispatch */
}
}
int main()
{
/**
* Initialize managed drivers and/or services that have been added to
* the project.
* @return zero on success
*/
adi_initComponents();
xReturned_1 = xTaskCreate(Task_1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+3, &xHandle_Task_1);
if( xReturned_1 != pdPASS )
abort();
/* Start the scheduler. */
vTaskStartScheduler();
}
What is the purpose of this task?
Is this callback called from an ISR?
Assuming that you want to receive 1024 characters in ISR and then process them in the task, you need to use some FreeRTOS synchronization primitive to notify the task from ISR when the buffer is full and ready to be processed. Below is an example using direct to task notifications:
static void callback_uart_rx(void *param, uint32_t event, void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
switch (event)
{
case ADI_UART_EVENT_RX_PROCESSED:
*RecvBuf_Uart_Rx_Curr++ = *(char *)arg;
if (RecvBuf_Uart_Rx_Curr >= &RecvBuf_Uart_Rx[1024])
{
/* Notify Task_1 to process the data. */
vTaskNotifyGiveFromISR( xHandle_Task_1, &( xHigherPriorityTaskWoken ) );
RecvBuf_Uart_Rx_Curr = RecvBuf_Uart_Rx;
}
break;
default:
break;
}
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
void Task_1(void *param)
{
Uart_Init();
while (true)
{
/* Wait for a notification from UART ISR. */
ulTaskNotifyTake( pdTRUE,
portMAX_DELAY );
/* Process the data. */
}
}
Hi Gaurav,
Sorry for the delayed reply. Please find the details here.
Is this callback called from an ISR?
Is this callback called from an ISR?
Yes this is called from ISR only. After each data received, the interrupt needs to get trigged and need to call this callback function.
This application will receive random number of data via teraterm port. That is the challenge here. If I send a single character at a time, I could see it in the rxbuffer. But while sending group of characters only not working.
Assuming that you want to receive 1024 characters in ISR and then process them in the task, you need to use some FreeRTOS synchronization primitive to notify the task from ISR when the buffer is full and ready to be processed. Below is an example using direct to task notifications:
Thank you for sharing the snippet. I tried your method. But it didn’t help. I suspect that the problem in vTaskDelay()API. Looks like in A5 core, this call, disabled UART interrupts or global interrupt which might be the cause of the ISR missing.
Best Regards,
Santha
This is almost certainly a problem in the driver, so you may want to reach out to your eco system vendor. An easy test to verify this is to add a counter to your Rx callback and match that against the expected number of received characters.
FreeRTOS itself will certainly not disable any interrupts during vTaskDelay(), otherwise nothing would work. There may be something specific in your port that does that, but again, this does not look like anything in the FreeRTOS kernel.
Do you mean that when you type a string and press enter, it is not working? Is this callback supposed to be called for each character individually or a group of characters?
I do not think that is the case. We can verify that by commenting out the task and verifying if the ISR callback is getting invoked for all the characters.