cancel
Showing results for 
Search instead for 
Did you mean: 

DFSDM with DMA doesn't give expected results

PZare.1
Associate II

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

1 ACCEPTED SOLUTION

Accepted Solutions
PZare.1
Associate II

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)

View solution in original post

1 REPLY 1
PZare.1
Associate II

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)