cancel
Showing results for 
Search instead for 
Did you mean: 

Freertos in data acquisition is not consistent sampling rate .

CLi H.1
Associate II

Hello

  i am a newbie in Freertos. i have a practice project about two threads. one thread for data acquisition from AD7606 , another thread for data transmission by uart in LabVIEW . Unfortunately, when i display data in LabVIEW which get from thread 2 , i found it will lose data or sampling rate is not consistent . How can i slove this problem?  

Up to now , i don't know whether the solution is timer interrupt in freertos or not .

Please give me the hint or some tutorial i could reference.  Please advise as necessary. Thank you very much.

void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
	 xSemaphoreTake(DeliverHandle,portMAX_DELAY); 	
	 // START read AD7607	
	 Read_AI();  	
	 float value = (float)aio_data.c2_values_in[0];
	 // END read AD7607
	 
	 // Send data in MyQueue
	  xQueueSendToBack(MyQueue, (void*)&value, (TickType_t)1);	
	  xSemaphoreGive(DeliverHandle);
	  osDelay(20);	 
	}
}	
 
	
  /* USER CODE END 5 */
 
 
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  int messagesWaiting,j;
	float value_f;
	uint32_t value_u32;
  for(;;)
  {
	 //START test queue number 	
	 messagesWaiting = uxQueueMessagesWaiting(MyQueue);
	 xSemaphoreTake(DeliverHandle, portMAX_DELAY);	
	 if ( messagesWaiting >= 8 ) {
		  for(j = 0; j < messagesWaiting; j++){
		          xQueueReceive(MyQueue, &value_f, (TickType_t)1);
			  value_u32 = Convert_float_To_U32(value_f);
			 Covert_U32_To_4U8(value_u32, Tx_data+4*j);
			}
			HAL_UART_Transmit(&huart2, Tx_data, 32, 1000);
    }
		osDelay(20);
		xSemaphoreGive(DeliverHandle);
		}
  /* USER CODE END StartTask02 */
}

1 ACCEPTED SOLUTION

Accepted Solutions
Jack Peacock_2
Senior III

The AD7606 has either an SPI or parallel output. I assume you are reading the most recent conversion at set time interval from either SPI or GPIO pins. If you need a precise sample rate set up a hardware TIM timer with your interval and use the TIM interrupt to read the GPIO pins, or start an SPI shift cycle.

First of all, your acquisition task must run at the highest priority configured in FreeRTOS if you want to guarantee the time delay. You will still have to adjust for some jitter due to task context overhead and any tasks running a higher priority than the ADC task. You don't mention what interrupts are active, or if you use the AD7606 interrupt request to sample the ADC. Interrupt latency (i.e. SYSTICK or whatever timer you use for the RTOS timebase) has to be taken into account for your soft timer. Callbacks inside interrupt service routines (a favorite for ST programmers) can be a killer for low latency.

Also keep in mind the time it takes to read your ADC samples in the daq task can impact your time interval. If you have spinlocks (i.e. continual polling of status flags) your acquisition time can be non-deterministic (i.e. unpredictable).

If you are using FreeRTOS V9 or better you might want to look at task notifications instead of regular semaphores. Notifications require less processing time as long as it is between two tasks only.

Your semaphores are configured as a mutex (mutual exclusion), but this doesn't appear to be necessary. The output queue itself can act to synchronize the two tasks. Set your MyQueue size to 8 (your host )message size?_ and the xQueueSend delay to zero. This will discard samples that arrive too quickly to be sent within your one output message of 8 samples per interval rate. You don't need any osDelay call in the output task since it is event driven by the arrival of a sample in the queue. Assuming the daq task samples at the correct rate your output message will exactly fill the output queue.

This also assumes the output message reads samples from the queue faster than the conversion rate (if it isn't faster you will have overflow conditions). If you limit MyQueue to one host message size for samples you can preempt the slower output task and convert a new sample while still processing the prior sample set. Search on the "double buffer" concept for streaming data as it is a common way to handle your requirements.

What happens if your output queue grows faster than you can send data to the host? It appears your acquisition task will eventually be blocked for a significant period beyond your sample interval. You will experience periodic dropouts at the host where data is lost.

Jack Peacock

View solution in original post

2 REPLIES 2
Jack Peacock_2
Senior III

The AD7606 has either an SPI or parallel output. I assume you are reading the most recent conversion at set time interval from either SPI or GPIO pins. If you need a precise sample rate set up a hardware TIM timer with your interval and use the TIM interrupt to read the GPIO pins, or start an SPI shift cycle.

First of all, your acquisition task must run at the highest priority configured in FreeRTOS if you want to guarantee the time delay. You will still have to adjust for some jitter due to task context overhead and any tasks running a higher priority than the ADC task. You don't mention what interrupts are active, or if you use the AD7606 interrupt request to sample the ADC. Interrupt latency (i.e. SYSTICK or whatever timer you use for the RTOS timebase) has to be taken into account for your soft timer. Callbacks inside interrupt service routines (a favorite for ST programmers) can be a killer for low latency.

Also keep in mind the time it takes to read your ADC samples in the daq task can impact your time interval. If you have spinlocks (i.e. continual polling of status flags) your acquisition time can be non-deterministic (i.e. unpredictable).

If you are using FreeRTOS V9 or better you might want to look at task notifications instead of regular semaphores. Notifications require less processing time as long as it is between two tasks only.

Your semaphores are configured as a mutex (mutual exclusion), but this doesn't appear to be necessary. The output queue itself can act to synchronize the two tasks. Set your MyQueue size to 8 (your host )message size?_ and the xQueueSend delay to zero. This will discard samples that arrive too quickly to be sent within your one output message of 8 samples per interval rate. You don't need any osDelay call in the output task since it is event driven by the arrival of a sample in the queue. Assuming the daq task samples at the correct rate your output message will exactly fill the output queue.

This also assumes the output message reads samples from the queue faster than the conversion rate (if it isn't faster you will have overflow conditions). If you limit MyQueue to one host message size for samples you can preempt the slower output task and convert a new sample while still processing the prior sample set. Search on the "double buffer" concept for streaming data as it is a common way to handle your requirements.

What happens if your output queue grows faster than you can send data to the host? It appears your acquisition task will eventually be blocked for a significant period beyond your sample interval. You will experience periodic dropouts at the host where data is lost.

Jack Peacock

CLi H.1
Associate II

Thanks for your apply. I'm sorry responded late . You gave me big hand to resolve this problem.

First , i used Xstreambuffer to replace queue . Second , I used task Notifications to replace semaphore.

NOW , it's data sampling rate could achieve 20KHZ by hardware timer interrupt and receive data correctly by LabVIEW

PS. i changed HAL_UART_Transmit to HAL_SPI_Transmit_DMA in StartTask02 Thread .