cancel
Showing results for 
Search instead for 
Did you mean: 

Data from ADC (DMA) over UART (DMA)

kcire L.
Associate III

Hi,

I am trying to perform continuous ADC conversions from a microphone using DMA, save that data to a large buffer, and then transmit that data using Uart (DMA). I have successfully setup the ADC to perform the DMA conversions along with saving them to the large buffer.

How can I modify this code so that when half of the buffer is full, that half is transmitted over UART DMA while the second half of the buffer is filling. Then continuing on with a ping pong buffer uart transmit.

Thank you for any help.

#define ADC_BUF_LEN 4096
uint16_t adc_buf[ADC_BUF_LEN];
 
uint8_t First_Half_Filled = 0;            // First half of buffer is filled with ADC values
uint8_t Second_Half_Filled = 0;      // Second half of buffer is filled with ADC values
uint8_t First_Half_Sent = 0;             // First half of buffer is send over UART DMA
uint8_t Second_Half_Sent = 0;        // Second half of buffer is sent over DMA
 
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
 
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, ADC_BUF_LEN); HAL_UART_Transmit_DMA(&huart2, (uint8_t *)adc_buf, ADC_BUF_LEN/2);
 
 while (1)
  {
	  if(First_Half_Filled == 1)
	  {
 
		  First_Half_Filled = 0;
	  }
 
	  if(Second_Half_Filled == 1)
	  {
 
		  Second_Half_Filled = 0;
	  }
	  
	  if(First_Half_Sent == 1)
	  {
		  
		  First_Half_Sent = 0;
	  }
	  
	  if(Second_Half_Sent == 1)
	  {
		  
		  Second_Half_Sent = 0;
	  }
 
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
 
 
// Callbacks
// Called when the buffer is completely filled
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	Second_Half_Filled = 1;
}
 
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef* huart2)
{
	HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
	First_Half_Sent = 1;
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart2)
{
	HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
	Second_Half_Sent = 1;
}

3 REPLIES 3
KnarfB
Principal III

If your ADC DMA is cyclic, you can start the first UART DMA from HAL_ADC_ConvHalfCpltCallback sending the first half of your buffer and start the next UART DMA from HAL_ADC_ConvCpltCallback. The UART DMAs are linear and send half of the buffer each. The UART baud rate should be such that the UART DMA terminates a little earlier before the next ADC interrupt arrivesk, triggering the next UART DMA, and so on.

If both DMAs are clocked by the same timer, you can even make the UART DMA cyclic on the same buffer and trigger it only once, when the ADC half buffer is full. Then, you don't need interrupt handlers any more and the two DMAs keep running in sync for ever.

kcire L.
Associate III

How would that be written in code? Like below? It seems like I would always be writing the first half of the buffer and never the second...? Also, how would I be able to send the 16 bit array data over 8 bit uart?

Thanks

// Called when the first half of buffer is filled
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
	HAL_UART_Transmit_DMA(&huart2, &adc_buf[0], ADC_BUF_LEN/2);
	First_Half_Filled = 1;
}
 
// Called when the buffer is completely filled
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	 HAL_UART_Transmit_DMA(&huart2, &adc_buf[ADC_BUF_LEN/2], ADC_BUF_LEN/2);
	Second_Half_Filled = 1;
}
 
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef* huart2)
{
	 
	First_Half_Sent = 1;
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart2)
{
 
	Second_Half_Sent = 1;
}

KnarfB
Principal III

>It seems like I would always be writing the first half of the buffer and never the second...?

Check (always!) the return codes of the HAL functions. Do they succeed? If not, UART baud rate might be too slow.

> Also, how would I be able to send the 16 bit array data over 8 bit uart?

The buffer is just a pointer plus its length. Its content will be sent byte for byte to the UART TDR register, see the DMA setup in HAL_UART_MspInit.

Don't think that ADC_BUF_LEN/2 is correct, thats the number of array elements (I guess), not the size in bytes.

For debugging, stimulate the mike with a sine tone. You can also slow down the ADC sampling rate considerably, lets say down to 1Hz, stimulate the ADC manually and check the output.

hth

KnarfB