Skip to main content
PZare.1
Associate II
Sep 1, 2021 2:07 PM UTC
Solved

DFSDM with DMA doesn't give expected results

Hello,

I am working on application that would use microphone array. Together with STM32H735IG board I decided to use STEVAL-MIC001V1 microphone array connected via DFSDM interface. I based my project on example available in CubeMX - DFSDM_AudioRecord project where same interface and microphones are used as an demonstration. While I use this project I don't have any problem, I am receiving echoed audio on headphones.

My problem begin here. While I copy most of this sample solution, my audio input that I read in the buffer on debug mode is totally out of expected results. I receive buffer full of 0x7fff and 0x8000 (after shifting, saturating and casting variable - see in the code).

My initialization code:

[...]
 /* MPU Configuration--------------------------------------------------------*/
 MPU_Config();
 /* 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();
 /* Configure the system clock */
 SystemClock_Config();
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_DFSDM1_Init();
[...]

As you can see ICache and DCache is enabled, as well as MPU is configured basing on sample project from CubeMX:

void MPU_Config(void)
{
 MPU_Region_InitTypeDef MPU_InitStruct = {0};
 
 /* Disables the MPU */
 HAL_MPU_Disable();
 /** Initializes and configures the Region and the memory to be protected
 */
 MPU_InitStruct.Enable = MPU_REGION_ENABLE;
 MPU_InitStruct.Number = MPU_REGION_NUMBER0;
 MPU_InitStruct.BaseAddress = 0x24000000;
 MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;
 MPU_InitStruct.SubRegionDisable = 0x0;
 MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
 MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW;
 MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
 MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
 MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
 MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
 
 HAL_MPU_ConfigRegion(&MPU_InitStruct);
 /* Enables the MPU */
 HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

For memory region configured as RAM_D1 in linker file:

MEMORY
{
 ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
 DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
 RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 320K
 RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K
 RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
}

Also I addressed the issue with CubeMX code generator, in which some of section was placed in DTCRAM and that was creating an issue because DMA has no access to that region:

/* used by the startup to initialize data */
 _sidata = LOADADDR(.data);
 
 /* Initialized data sections goes into RAM, load LMA copy after code */
 .data : 
 {
 . = ALIGN(4);
 _sdata = .; /* create a global symbol at data start */
 *(.data) /* .data sections */
 *(.data*) /* .data* sections */
 
 . = ALIGN(4);
 _edata = .; /* define a global symbol at data end */
 } >RAM_D1 AT> FLASH /* REPLACED DTCRAM TO RAM_D1 */
 
 
 /* Uninitialized data section */
 . = ALIGN(4);
 .bss :
 {
 /* This is used by the startup in order to initialize the .bss secion */
 _sbss = .; /* define a global symbol at bss start */
 __bss_start__ = _sbss;
 *(.bss)
 *(.bss*)
 *(COMMON)
 
 . = ALIGN(4);
 _ebss = .; /* define a global symbol at bss end */
 __bss_end__ = _ebss;
 } >RAM_D1 /* REPLACED DTCRAM TO RAM_D1 */
 
 /* User_heap_stack section, used to check that there is enough RAM left */
 ._user_heap_stack :
 {
 . = ALIGN(8);
 PROVIDE ( end = . );
 PROVIDE ( _end = . );
 . = . + _Min_Heap_Size;
 . = . + _Min_Stack_Size;
 . = ALIGN(8);
 } >RAM_D1 /* REPLACED DTCRAM TO RAM_D1 */

After initialization DFSDM filters are started and without any problem they are interrupting every half and full conversion completions:

void HAL_DFSDM_FilterErrorCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
{
 ItRecError = 1; // not important
}
 
void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
{
 dfsdm_filter_callback_handler_t *filter;
 // acquire filter and sets it as half-ready in it's handler
 filter = get_filter_instance(hdfsdm_filter);
 filter->half_is_ready = FRAME_READY;
}
 
void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
{
 dfsdm_filter_callback_handler_t *filter;
 // acquire filter and sets it as full-ready in it's handler
 filter = get_filter_instance(hdfsdm_filter);
 filter->is_ready = FRAME_READY; 
}
 
// in main
[...]
 if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(filter0_handler.filter, filter0_handler.audio_buffer, AUDIO_BUFFER_HALF)) {
 Error_Handler();
 }
 
 if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(filter1_handler.filter, filter1_handler.audio_buffer, AUDIO_BUFFER_HALF)) {
 Error_Handler();
 }
[...]

Then data acquiring, while loop looks like this:

while (1) {
 if (filter0_handler.half_is_ready || filter1_handler.half_is_ready) {
 SCB_InvalidateDCache_by_Addr((uint32_t *)&LeftRecBuff[0], AUDIO_BUFFER_SIZE);
 SCB_InvalidateDCache_by_Addr((uint32_t *)&RightRecBuff[0], AUDIO_BUFFER_SIZE);
 for (int i = 0; i < AUDIO_BUFFER_SIZE/4; i ++) {
 AudioBuffer[i*2] = (uint16_t)(SaturaLH((LeftRecBuff[i] >> 8), -32768, 32767));
 AudioBuffer[i*2+1] = (uint16_t)(SaturaLH((RightRecBuff[i] >> 8), -32768, 32767));
 }
 SCB_CleanDCache_by_Addr((uint32_t*)&AudioBuffer[0], AUDIO_BUFFER_SIZE);
 memcpy(temp_array, AudioBuffer, AUDIO_BUFFER_HALF*4);
 filter0_handler.half_is_ready = FRAME_NOT_READY;
 filter1_handler.half_is_ready = FRAME_NOT_READY;
 }
 if (filter0_handler.is_ready || filter1_handler.is_ready) {
 SCB_InvalidateDCache_by_Addr((uint32_t *)&LeftRecBuff[AUDIO_BUFFER_SIZE/4], AUDIO_BUFFER_SIZE);
 SCB_InvalidateDCache_by_Addr((uint32_t *)&RightRecBuff[AUDIO_BUFFER_SIZE/4], AUDIO_BUFFER_SIZE);
 for (int i = AUDIO_BUFFER_SIZE/4; i < AUDIO_BUFFER_HALF; i ++) {
 AudioBuffer[i*2] = (uint16_t)(SaturaLH((LeftRecBuff[i] >> 8), -32768, 32767));
 AudioBuffer[i*2+1] = (uint16_t)(SaturaLH((RightRecBuff[i] >> 8), -32768, 32767));
 }
 SCB_CleanDCache_by_Addr((uint32_t*)&AudioBuffer[AUDIO_BUFFER_SIZE/2], AUDIO_BUFFER_SIZE/2);
 memcpy(temp_array[AUDIO_BUFFER_HALF], AudioBuffer[AUDIO_BUFFER_HALF], AUDIO_BUFFER_HALF*4);
 filter0_handler.is_ready = FRAME_NOT_READY;
 filter1_handler.is_ready = FRAME_NOT_READY;
 }
}

So program logic works without a flaw, interrupts fire correctly, data goes through some processing and everything repeats forever, but the data, as I mentioned on beginning, is just 0x8000 and 0x7fff (~32767) with occasional different values.

My DFSDM configuration should results in 8 kHz sampling frequency (using audio clock with 12 MHz, divided by 4 (results with 3 MHz) and 375 oversampling parameter):

// for both filters
hdfsdm1_filter0.Instance = DFSDM1_Filter0;
hdfsdm1_filter0.Init.RegularParam.Trigger = DFSDM_FILTER_SW_TRIGGER;
hdfsdm1_filter0.Init.RegularParam.FastMode = DISABLE;
hdfsdm1_filter0.Init.RegularParam.DmaMode = ENABLE;
hdfsdm1_filter0.Init.FilterParam.SincOrder = DFSDM_FILTER_SINC3_ORDER;
hdfsdm1_filter0.Init.FilterParam.Oversampling = 375; // 12 MHz / ( 4 * 8 kHz)
hdfsdm1_filter0.Init.FilterParam.IntOversampling = 1;
if (HAL_DFSDM_FilterInit(&hdfsdm1_filter0) != HAL_OK)
{
 Error_Handler();
}
 
// simillar for both channels
hdfsdm1_channel1.Instance = DFSDM1_Channel1;
hdfsdm1_channel1.Init.OutputClock.Activation = ENABLE;
hdfsdm1_channel1.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO;
hdfsdm1_channel1.Init.OutputClock.Divider = 4;
hdfsdm1_channel1.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS;
hdfsdm1_channel1.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
hdfsdm1_channel1.Init.Input.Pins = DFSDM_CHANNEL_FOLLOWING_CHANNEL_PINS;
hdfsdm1_channel1.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_RISING;
hdfsdm1_channel1.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
hdfsdm1_channel1.Init.Awd.FilterOrder = DFSDM_CHANNEL_SINC1_ORDER;
hdfsdm1_channel1.Init.Awd.Oversampling = 1;
hdfsdm1_channel1.Init.Offset = 0;
hdfsdm1_channel1.Init.RightBitShift = 0x00;
if (HAL_DFSDM_ChannelInit(&hdfsdm1_channel1) != HAL_OK)
{
 Error_Handler();
}

What is interesting when oversampling is set to 68 (44,1 kHz sampling) values are much different, but still they are unexpected (most of them are based around ~64000 + few of them around 10-300).

PS. I checked clock signals with oscyloscope and they are correct - clocking with 3 MHz, data is changing on data pin.

I hope I gave out all of important information for you, and you will be able to help me in any way, I will be happy to give more information if needed.

Pawel

This topic has been closed for replies.
PZare.1
PZare.1Best answer
Associate II
Sep 14, 2021 7:33 AM UTC

Hello,

after some work with this application and some support from ST employees I managed to get some pretty good outcome, as you can se on Picture 1. On output I can see sinusoidal wave that I am giving on input of microphones, but I get a lot of offset and noise as well.0693W00000DmmobQAB.pngChanges that was applied:

  • changed speed of clocks (12 MHz -> 4.8 MHz audio clock, divider 4 -> 3, Oversampling 375 -> 200)
  • switched up L/R signal on two microphones
  • changed casting to int16_t because old one was causing wrapping
AudioBuffer[i*2] = (int16_t)(SaturaLH((LeftRecBuff[i] >> 8), -32768, 32767)); // changed from (uint16_t)

1 reply

PZare.1
PZare.1AuthorBest answer
Associate II
Sep 14, 2021 7:33 AM UTC

Hello,

after some work with this application and some support from ST employees I managed to get some pretty good outcome, as you can se on Picture 1. On output I can see sinusoidal wave that I am giving on input of microphones, but I get a lot of offset and noise as well.0693W00000DmmobQAB.pngChanges that was applied:

  • changed speed of clocks (12 MHz -> 4.8 MHz audio clock, divider 4 -> 3, Oversampling 375 -> 200)
  • switched up L/R signal on two microphones
  • changed casting to int16_t because old one was causing wrapping
AudioBuffer[i*2] = (int16_t)(SaturaLH((LeftRecBuff[i] >> 8), -32768, 32767)); // changed from (uint16_t)