2024-05-08 03:38 AM - edited 2024-05-08 04:13 AM
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();
}
}
}
2024-05-08 03:48 AM
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.
2024-05-08 04:12 AM
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.
2024-05-08 04:16 AM
callback triggers at 760Hz so nothing fancy here :)
2024-05-08 04:37 AM
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 .
2024-05-08 04:40 AM
you can do lot of stuff at 1KHz with a 250MHz MCU. It is more than 300K instructions
2024-05-08 04:41 AM
also, i dma triggers high priority interrupt so this should not be an issue.
2024-05-08 04:50 AM
maby the DMA continue to fill my buffer even after breakpoint as fired
2024-05-08 04:59 AM
Right. So there should be no problem. :)
+
just..i dont understand your setting:
hsai_BlockA1.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; hsai_BlockA1.SlotInit.SlotNumber = 4;
data = 16bit , x4 slot -> 64bit <--- but you have 2 channels ?
+
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)audioPdmBuff, 128);
So your full dma-buffer is 128 x 2 bytes ; so why you set the buffer to 4096 sampels ?
2024-05-08 04:59 AM
audio processing is most of the time on buffers of 64, 128 or 256 samples.