Skip to main content
Visitor II
May 8, 2024
Question

STM32H563, PDM and DMA Double buffering

  • May 8, 2024
  • 5 replies
  • 2742 views

Hello,

i would like to read output from a digital microphone using SAI, DMA and double buffering technic. 

transfert are done and callbacks are triggering properly.  The problem is got is either Half cplt or Full cmplt callback interrupt being called the full memory buffer is overwritten and not the half of it as expected.

What is wrong with my code ? How could i get only half of my buffer memory to be writen at each callback interrupts ?

Much thanks for support

i start transaction as Following 

 

__attribute__ ((aligned (32))) uint16_t audioPdmBuff[4096];

HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)audioPdmBuff, 128);

 

and catch interrupt as follow : 

 

void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai){
 flag_mic_halffull = 0;
 mic_process();

}

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai){
 flag_mic_halffull = 1;
 mic_process();
}

 

The SAI and DMA configuration is as follow : 

 

/* SAI1 init function */
void MX_SAI1_Init(void)
{

 /* USER CODE BEGIN SAI1_Init 0 */

 /* USER CODE END SAI1_Init 0 */

 /* USER CODE BEGIN SAI1_Init 1 */

 /* USER CODE END SAI1_Init 1 */

 hsai_BlockA1.Instance = SAI1_Block_A;
 hsai_BlockA1.Init.Protocol = SAI_FREE_PROTOCOL;
 hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_RX;
 hsai_BlockA1.Init.DataSize = SAI_DATASIZE_16;
 hsai_BlockA1.Init.FirstBit = SAI_FIRSTBIT_MSB;
 hsai_BlockA1.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;
 hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
 hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
 hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
 hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_HF;
 hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_22K;
 hsai_BlockA1.Init.MckOutput = SAI_MCK_OUTPUT_DISABLE;
 hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
 hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
 hsai_BlockA1.Init.PdmInit.Activation = ENABLE;
 hsai_BlockA1.Init.PdmInit.MicPairsNbr = 1;
 hsai_BlockA1.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK1_ENABLE;
 hsai_BlockA1.FrameInit.FrameLength = 64;
 hsai_BlockA1.FrameInit.ActiveFrameLength = 32;
 hsai_BlockA1.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
 hsai_BlockA1.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
 hsai_BlockA1.FrameInit.FSOffset = SAI_FS_FIRSTBIT;
 hsai_BlockA1.SlotInit.FirstBitOffset = 0;
 hsai_BlockA1.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
 hsai_BlockA1.SlotInit.SlotNumber = 4;
 hsai_BlockA1.SlotInit.SlotActive = 0x0000000F;
 if (HAL_SAI_Init(&hsai_BlockA1) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN SAI1_Init 2 */

 /* USER CODE END SAI1_Init 2 */

}
static uint32_t SAI1_client =0;

void HAL_SAI_MspInit(SAI_HandleTypeDef* saiHandle)
{

 GPIO_InitTypeDef GPIO_InitStruct;
 DMA_NodeConfTypeDef NodeConfig;
/* SAI1 */
 if(saiHandle->Instance==SAI1_Block_A)
 {
 /* SAI1 clock enable */
 if (SAI1_client == 0)
 {
 __HAL_RCC_SAI1_CLK_ENABLE();

 /* Peripheral interrupt init*/
 HAL_NVIC_SetPriority(SAI1_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(SAI1_IRQn);
 }
 SAI1_client ++;

 /**SAI1_A_Block_A GPIO Configuration
 PB2 ------> SAI1_D1
 PD11 ------> SAI1_CK1
 */
 GPIO_InitStruct.Pin = GPIO_PIN_2;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.Alternate = GPIO_AF2_SAI1;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = GPIO_PIN_11;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.Alternate = GPIO_AF2_SAI1;
 HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

 /* Peripheral DMA init*/

 NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
 NodeConfig.Init.Request = GPDMA2_REQUEST_SAI1_A;
 NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
 NodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
 NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
 NodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
 NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
 NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
 NodeConfig.Init.SrcBurstLength = 1;
 NodeConfig.Init.DestBurstLength = 1;
 NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT1;
 NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 NodeConfig.Init.Mode = DMA_NORMAL;
 NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
 NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
 NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
 if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA2_Channel0) != HAL_OK)
 {
 Error_Handler();
 }

 if (HAL_DMAEx_List_InsertNode(&List_GPDMA2_Channel0, NULL, &Node_GPDMA2_Channel0) != HAL_OK)
 {
 Error_Handler();
 }

 if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA2_Channel0) != HAL_OK)
 {
 Error_Handler();
 }
 handle_GPDMA2_Channel0.Instance = GPDMA2_Channel0;
 handle_GPDMA2_Channel0.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
 handle_GPDMA2_Channel0.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
 handle_GPDMA2_Channel0.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT1;
 handle_GPDMA2_Channel0.InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
 handle_GPDMA2_Channel0.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
 if (HAL_DMAEx_List_Init(&handle_GPDMA2_Channel0) != HAL_OK)
 {
 Error_Handler();
 }

 if (HAL_DMAEx_List_LinkQ(&handle_GPDMA2_Channel0, &List_GPDMA2_Channel0) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_LINKDMA(saiHandle, hdmarx, handle_GPDMA2_Channel0);

 if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA2_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
 {
 Error_Handler();
 }
 }
}

 

 

 

 

 

 

 

    This topic has been closed for replies.

    5 replies

    Super User
    May 8, 2024

    Hi,

    >What is wrong with my code ?

    Maybe - nothing. Its just to slow...to finish callback "in time", until circular dma overwrites again.

    How much time you have - you should know. (sample rate x number of samples /2 -> time for 1/2 buffer)

    So do, whatever you do in the callbacks, faster, to be ready until next callback comes.

    jboui.1Author
    Visitor II
    May 8, 2024

    Hi Ascha,

    in fact dont almost nothing in callbacks. Now i just break point within callback and i see full buffer is overwritten instead of half of it. 

    jboui.1Author
    Visitor II
    May 8, 2024

    callback triggers at 760Hz so nothing fancy here :)

    Super User
    May 8, 2024

    760Hz - what ? per 1/2 buffer or full buffer ? -> about 1ms, so your program has about 500us free time. 

    Not soo much , i use 16KB buffer, to get about 40ms in 1/2 buffer .

    jboui.1Author
    Visitor II
    May 8, 2024

    you can do lot of stuff at 1KHz with a 250MHz MCU. It is more than 300K instructions

    jboui.1Author
    Visitor II
    May 8, 2024

    maby the DMA continue to fill my buffer even after breakpoint as fired 

    jboui.1Author
    Visitor II
    May 8, 2024

    audio processing is most of the time on buffers of 64, 128 or 256 samples.