cancel
Showing results for 
Search instead for 
Did you mean: 

(Solved) SAI receiver and circular DMA

Thibaut Dewet
Associate

Hello,

I am working on SAI part of the NUCLEO-F756ZG board and I observe an issue with the DMA.

The configuration is:

  • SAI A as a master with master **** out and audio mode = master receive.
  • SAI B as asynchronous slave and audio mode = slave transmit. (SAI B is used to simulate a futur hardware codec)
  • Both SAI use DMA, circular for SAI A and normal for B.
  • On the board each pin (i.e. SCK, FS, SD) of both block are connected.

The code is very simple: two buffers, the transmit buffer is filled with a sequence of numbers (0, 1, 2,...), HAL_SAI_Transmit_DMA and HAL_SAI_Receive_DMA are called. When HAL_SAI_RxHalfCpltCallback is reached the receive buffer is displayed.

#define SAI_DMA_BUFFER_SIZE (320 * 8 * 2)
static uint16_t __attribute__((section(".sai_dma_buffer_section"))) __attribute__((aligned(32))) sai_receive_buffer[SAI_DMA_BUFFER_SIZE] = {0};
static uint16_t __attribute__((section(".sai_dma_buffer_section"))) __attribute__((aligned(32))) sai_transmit_buffer[320 * 8] = {0};
 
static volatile uint32_t audio_data_available = 0;
static volatile uint32_t audio_data_buffer_index = 0;
 
/**
  * @brief Rx Transfer completed callback.
  * @param  hsai pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @retval None
  */
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
  UNUSED(hsai);
  audio_data_buffer_index = 1;
  audio_data_available = 1;
  HAL_GPIO_TogglePin(AUDIO_READY_GPIO_Port, AUDIO_READY_Pin);
}
 
/**
  * @brief Rx Transfer half completed callback.
  * @param  hsai pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @retval None
  */
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
  UNUSED(hsai);
  audio_data_buffer_index = 0;
  audio_data_available = 1;
  HAL_GPIO_TogglePin(AUDIO_READY_GPIO_Port, AUDIO_READY_Pin);
}
 
 
/**
  * @brief SAI error callback.
  * @param  hsai pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @retval None
  */
void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsai);
  console_printf("error in SAI\n");
}
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();
 
  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();
 
  /* 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();
 
/* Configure the peripherals common clocks */
  PeriphCommonClock_Config();
 
  /* USER CODE BEGIN SysInit */
  mpu_configuration();
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();
  MX_LWIP_Init();
  MX_TIM2_Init();
  MX_DMA_Init();
  MX_SAI1_Init();
  /* USER CODE BEGIN 2 */
  for (size_t i = 0; i < ARRAY_LENGTH(sai_transmit_buffer); i++) {
	  sai_transmit_buffer[i] = i;
	  sai_receive_buffer[i] = 0xFFFF;
  }
 
  if (HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint8_t*)sai_transmit_buffer, 32) != HAL_OK) {
	  console_printf("HAL_SAI_Transmit_DMA failed %u\n", hsai_BlockB1.ErrorCode);
  }
 
  if (HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)sai_receive_buffer, 3200) != HAL_OK) {
	  console_printf("HAL_SAI_Receive_DMA failed\n");
  }
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if (audio_data_available) {
		  break;
	  }
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  for (size_t i = 0; i < 32; i++) {
		  console_printf("[%d] = 0x%04x\n", i, sai_receive_buffer[i]);
  }
  console_printf("end\n");
  while(1);
  /* USER CODE END 3 */
}

Unfortunatelly in the receive buffer only the first 11 slots are valid (the rest is zero).

Some investigations:

  • HAL_SAI_ErrorCallback is implemented but never called => No error
  • If the receive buffer is initialized with 0xFFFF, the content is overwritten with 0 => The DMA doesn't stop.
  • If The DMA configuration of the receiver is change from circular to normal all values are valid.

As far as I know the only difference between circular and normal DMA is the first one never stops and overwrites the oldest data. In this example the receive buffer is displayed before DMA loop so the beahvior should be the same as normal mode. Did I missed something ? What point should I verify ?

Best regards,

Thibaut

1 ACCEPTED SOLUTION

Accepted Solutions

> console_printf("[%d] = 0x%04x\n", i, sai_receive_buffer[i]);

is probably slow enough, so that by the time it prints the 12th datum, the Rx DMA has already rolled over and overwritten the Rx buffer up to that point and beyond.

JW

View solution in original post

2 REPLIES 2

> console_printf("[%d] = 0x%04x\n", i, sai_receive_buffer[i]);

is probably slow enough, so that by the time it prints the 12th datum, the Rx DMA has already rolled over and overwritten the Rx buffer up to that point and beyond.

JW

Thibaut Dewet
Associate

Hello JW,

Thanks for your answer, You're right.

HAL_SAI_RxCpltCallback is called during the print loop.

Best regards,

Thibaut