cancel
Showing results for 
Search instead for 
Did you mean: 

SAI with blocking mode

DYann.1
Senior

Hello,

I have this code

 

 

  // Initialiser le bloc A
  fresult= HAL_SAI_Init(&hsai_BlockA1);
  if (fresult != HAL_OK)
  	{
  	return HAL_ERROR;
  	}
  // Initialiser le bloc B
  fresult= HAL_SAI_Init(&hsai_BlockB1);
  if (fresult != HAL_OK)
	{
  	return HAL_ERROR;
	}

  // Transmission
  fresult = HAL_SAI_Transmit(&hsai_BlockB1, (uint8_t *)playbuf, (sizeof(playbuf))/4, 0xFF);
  if (fresult != HAL_OK)
  	{
  	return HAL_ERROR;
  	}
  // Reception
  fresult = HAL_SAI_Receive(&hsai_BlockA1, (uint8_t *)playbuf_RX, (sizeof(playbuf_RX))/4, 0xFF);
  if (fresult != HAL_OK)
  	{
  	return HAL_ERROR;
  	}

 

 

I am not receiving some of the data, do you know where I can find the documentation to implement SAI blocking mode? thanks in advance.

DYann1_0-1704728511248.png

In my reception array I have only a part of data, and yet my TX table is well initialized like this :

DYann1_1-1704728708060.png

28 REPLIES 28
LCE
Principal

You need to understand what's going on there:

- HAL_SAI_Transmit() seems to return when data transfer to the SAI's FIFO has finished

- HAL_SAI_Receive() is started when there is only the data in the TX FIFO

Again, DMA is for most applications the best solution.

Anyway, isn't there some combined start function for TX & RX? Check the SAI HAL file...

For the 'isn't there some combined start function for TX & RX?' I'm looking for just to understand how the SAI works in blocking mode. In another post I ran the SAI in DMA mode but it only worked once I asked the question on the forum but I didn't get any feedback.

https://community.st.com/t5/stm32-mcus-products/dma-with-sai/m-p/607742#M227413

 

 

 

LCE
Principal

You try to understand the HAL stuff, not the SAI. The SAI peripheral has no "blocking mode".

For understanding the HAL functions, go line by line through the HAL source files, compare register settings with the ref manual.

That way, you will also understand the SAI peripheral a little better. 

In another post I ran the SAI in DMA mode but it only worked once I asked the question on the forum but I didn't get any feedback.

I thought that problem was solved.
You have not yet shown the DMA init for the SAI, so we couldn't comment on that.

Try first to get the SAI TX with DMA running, check the clock and data out pins with a scope if it's running continuously (hint: trigger on LRCK = FS, then you should see the always changing data output in sync).

'You try to understand the HAL stuff,....' Not really I can understand the HAL functions, the difficulty is knowing how to place the transmission and reception function in the right places and there is no example or documentation on this to implement it correctly.

> In another post I ran the SAI in DMA mode but it only worked once I asked the question on the forum but I didn't get any feedback.

Yes it works but only once. In reality I have to acquire measurements continuously. For the DMA init, I don't see too much difficulty there since it was the STM32cubeMX which generated the code for me but you can see my init below :

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMAMUX1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMAMUX1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMAMUX1_IRQn);
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);

}

In this case, I'll abandon the SAI blocking mode and work on the DMA so that operation is continuous

LCE
Principal

If you had taken a closer look at HAL_SAI_Transmit(), you would have seen that it returns only after the transfer has completely finished.

Anyway, what you show as DMA init is not what I meant, I meant the SAI / DMA init, where you set circular mode and so on. If the DMA transfer runs only once there's some bug in your code.

Here's an example for a SAI DMA as RX on a F7:

/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* SAI 1 A - RX ADC 1&2 */
	if( SAI1_Block_A == hsai->Instance )
	{
		SAI1_client++;

	/* SAI1_A_Block_A GPIO Configuration */
	/* NO MCLK output!
	 * 	MCLK is done by I2S2
	 *	because STM32 SAI needs fixed oversampling ratio of 256
	 */
		GPIO_InitStruct.Pin 		= SAI1_SCLK_Pin | SAI1_LRCK_Pin | SAI1_DAT_A_Pin | SAI1_DAT_B_Pin;
		GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull 		= GPIO_NOPULL;
		GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_HIGH;
		GPIO_InitStruct.Alternate 	= GPIO_AF6_SAI1;
		HAL_GPIO_Init(SAI1_CLKDAT_GPIO_Port, &GPIO_InitStruct);

	/* Peripheral DMA init */
		hDMA_Sai_1_A.Instance = DMA2_Stream1;

		hDMA_Sai_1_A.Init.Channel 				= DMA_CHANNEL_0;
		hDMA_Sai_1_A.Init.Direction 			= DMA_PERIPH_TO_MEMORY;
		hDMA_Sai_1_A.Init.PeriphInc 			= DMA_PINC_DISABLE;
		hDMA_Sai_1_A.Init.MemInc 				= DMA_MINC_ENABLE;
		hDMA_Sai_1_A.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_WORD;
		hDMA_Sai_1_A.Init.MemDataAlignment 		= DMA_MDATAALIGN_WORD;
		hDMA_Sai_1_A.Init.Mode 					= DMA_CIRCULAR;
		hDMA_Sai_1_A.Init.Priority 				= DMA_PRIORITY_MEDIUM;	//	DMA_PRIORITY_HIGH; DMA_PRIORITY_VERY_HIGH;
		hDMA_Sai_1_A.Init.FIFOMode 				= DMA_FIFOMODE_ENABLE;
		hDMA_Sai_1_A.Init.FIFOThreshold 		= DMA_FIFO_THRESHOLD_FULL;
		hDMA_Sai_1_A.Init.MemBurst 				= DMA_MBURST_SINGLE;
		hDMA_Sai_1_A.Init.PeriphBurst 			= DMA_PBURST_SINGLE;

		if( HAL_DMA_Init(&hDMA_Sai_1_A) != HAL_OK ) Error_Handler_FL(__FILE__, __LINE__);

		__HAL_LINKDMA(hsai, hdmarx, hDMA_Sai_1_A);
		__HAL_LINKDMA(hsai, hdmatx, hDMA_Sai_1_A);

	/* DBM - double buffer mode activation
		- AFTER LINK to DMA!
		- BEFORE DMA enable!
	 */
		u32RegTemp = hsai->hdmarx->Instance->CR;
		hsai->hdmarx->Instance->CR &= ~DMA_SxCR_EN;
		hsai->hdmarx->Instance->CR |= DMA_SxCR_DBM;
		u32RegTemp &= DMA_SxCR_EN;
		hsai->hdmarx->Instance->CR |= u32RegTemp;
		/* CT - current buffer reset */
		hsai->hdmarx->Instance->CR &= ~DMA_SxCR_CT;
	}

 

Next thing needed is the DMA ISR, quite simple, the important work is done within the HAL_DMA_IRQHandler(), like resetting interrupt flags. Maybe that was missing in your code?

/* SAI DMA interrupt handlers */
/* the global HAL_DMA_IRQHandler():
 *	- resets all flags
 *	- calls the half- / complete callback functions
 */

/* SAI 1 A as ADC */
void DMA2_Stream1_IRQHandler(void)
{
	HAL_DMA_IRQHandler(&hDMA_Sai_1_A);
}

And the callbacks for half / complete, which override the __weak callbacks in the HAL source, which you can use for your custom stuff. 
Maybe toggle an LED, set / clear flags, ...

 

 

 

 

Thank you for your helps ! Give me time to read and understand. Now I can see what am I missing in my code. IRQ and the circular mechanism to retrieve data. I see that in your code the DMA directly accesses ADC which is probably not the case for me (I'm not sure).

LCE
Principal

For now, forget the double buffer mode (DBM) init at the end of the DMA init.

Just one think, I used this configuration in my previous post :

DYann1_0-1704873891770.png

Can I use this same method : Connecting TX with the RX (so for the moment the CODEC is of no use to me). Can I code (circulaire mechanism, IRQ...) with this configuration before actually plugging in the CODEC ?

LCE
Principal

Sure, why not?

IMPORTANT: do not only rely on software tools, monitor the SD TX pin with a scope. And as I said before, trigger scope on FS = LRCK.

And focus on getting the TX part running continuously via DMA. Only then start the RX part.
It seems you try to do too much different stuff at once, it seems. 😉