Skip to main content
Visitor II
May 27, 2025
Solved

About SAI - DMA protocol

  • May 27, 2025
  • 4 replies
  • 537 views

Hi,

I need to understand better how the "HAL_SAI_Receive_DMA(&hsai_BlockB1,ADC_Buffer,CodecBuffer_Size)" works.

I've a codec connected to the SAI 1 of a STMH723 MPU and I implemented a Ping-Pong routine to acquire and send the data to the Codec; two Callback function swap the buffers:

uint8_t ADC_Buffer[80000] __attribute__ ((aligned (4)));

 

void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai_BlockB1){

adcp31=(q31_t *)&ADC_Buffer[BUFFER_SIZE_R/2];

RxDataReady1=1;

}

 

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai_BlockB1){

adcp31=(q31_t *)&ADC_Buffer;

dacp31=(q31_t *)&DAC_Buffer;

RxDataReady=1;

}

 

If I write HAL_SAI_Receive_DMA(&hsai_BlockB1,ADC_Buffer,20000) what happens (in details) ?

 

Thank you

    This topic has been closed for replies.
    Best answer by LCE

    At half complete and a size of 20k, there will be 10k samples in the buffer, if you enabled both Codec channels, then "Left / Right" interleaved (L1R1 L2R2 L3R3 L4R4...), with 5k each.

    Don't be afraid to play with it, and check the buffers with a debugger / UART / test signal.

    Test signal: I like using another SAI as a virtual DAC, putting out a sample-sync'd counter (or simpler a prepared buffer), then connecting directly SDATA-out to SDATA-in.
    Then I should see 1,2,3,4, ... in the ADC buffer.

    4 replies

    Super User
    May 27, 2025

    HAL_SAI_RxHalfCpltCallback is called when the first half of the buffer is populated.

    HAL_SAI_RxCpltCallback is called when the second half of the buffer is populated.

    If these are in circular mode, you will need to process each half-buffer before it starts to be overwritten on the next loop.

    There is a 65535 item limit for DMA transfers.

     

    carloVAuthor
    Visitor II
    May 28, 2025

    Thank you for your fast reply,

    I need to sample at 50KHz, 32 bit stereo, with a frequency resolution of 10Hz, then I need a buffer of 5000 uint32 for the left channel and the same for the right channel.

    The SAI is connected to a stereo codec, there is a stream of L-R samples between the codec and the SAI, then I 

    set  the "uint16_t Size" at   20000  (5000 x 4  uint8); is it correct ?

    HAL_SAI_Receive_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size)

     

    uint8_t ADC_Buffer[80000] __attribute__ ((aligned (4)));                          80000 ---> 20000 x 4   (L-R ping-pong)     

    HAL_SAI_Receive_DMA(&hsai_BlockB1,ADC_Buffer,20000)   

            

    My question is: after how many DMA transfers  is the "HAL_SAI_RxHalfCpltCallback" called ? 

     

     

     

    Super User
    May 28, 2025

    > HAL_SAI_Receive_DMA(&hsai_BlockB1,ADC_Buffer,20000)   

    > My question is: after how many DMA transfers  is the "HAL_SAI_RxHalfCpltCallback" called ? 

    To answer the direct question: 10000.

     

    But I suspect you have this misconfigured if you are using byte transfers.

     

    You can cast a pointer to whatever type you want.

    uint16_t ADC_Buffer[20000];
    HAL_SAI_Receive_DMA(&hsai_BlockB1,(uint8_t*)ADC_Buffer,20000);
    Graduate II
    May 28, 2025

    My question is: after how many DMA transfers  is the "HAL_SAI_RxHalfCpltCallback" called ? 

    I admit that I don't know for byte buffers...

    Is there a specific reason that you are using an byte / uint8_t buffer?

    If you are sampling 32-bit anyway, everything gets simpler to further use 32-bit buffers and DMA.
    Then half complete is called at half of Size given to DMA.

    Here's my SAI RX DMA setup, also for H723 (and one of the few places that I'm using HAL):

    	/* Peripheral DMA init */
    		hDMA_Sai_1_A.Instance 		= DMA1_Stream0;
    		hDMA_Sai_1_A.Init.Request 	= DMA_REQUEST_SAI1_A;
    
    		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.Mode 					= DMA_CIRCULAR;
    		hDMA_Sai_1_A.Init.Priority 				= DMA_PRIORITY_VERY_HIGH;
    		hDMA_Sai_1_A.Init.FIFOMode 				= SAI2S_DMA_FIFO_MODE;
    		hDMA_Sai_1_A.Init.FIFOThreshold 		= DMA_FIFO_THRESHOLD_FULL;
    		hDMA_Sai_1_A.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_WORD;
    		hDMA_Sai_1_A.Init.MemDataAlignment 		= DMA_MDATAALIGN_WORD;
    		hDMA_Sai_1_A.Init.MemBurst 				= SAI2S_DMA_MEMORY_BURST;
    		hDMA_Sai_1_A.Init.PeriphBurst 			= SAI2S_DMA_PERIPH_BURST;
    
    		if( HAL_DMA_Init(&hDMA_Sai_1_A) != HAL_OK ) Error_Handler_FL(__FILE__, __LINE__);

     

    carloVAuthor
    Visitor II
    May 28, 2025

    Thank you for your reply,

     

    the    HAL_SAI_Receive_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size) function requires a uint8_t buffer.

    Graduate II
    May 28, 2025

    carloV:

    the    HAL_SAI_Receive_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size) function requires a uint8_t buffer.

     

    TDK:

    You can cast a pointer to whatever type you want.

     

    So that's what I do, but to the int32_t SaiAdc buffers, and all DMA stuff set to 32 bit / WORD.

    Works like a charm!

    LCEAnswer
    Graduate II
    June 2, 2025

    At half complete and a size of 20k, there will be 10k samples in the buffer, if you enabled both Codec channels, then "Left / Right" interleaved (L1R1 L2R2 L3R3 L4R4...), with 5k each.

    Don't be afraid to play with it, and check the buffers with a debugger / UART / test signal.

    Test signal: I like using another SAI as a virtual DAC, putting out a sample-sync'd counter (or simpler a prepared buffer), then connecting directly SDATA-out to SDATA-in.
    Then I should see 1,2,3,4, ... in the ADC buffer.

    carloVAuthor
    Visitor II
    June 4, 2025

    Thank you !!