Skip to main content
Visitor II
September 1, 2021
Solved

DFSDM with DMA doesn't give expected results

  • September 1, 2021
  • 1 reply
  • 1814 views

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.
    Best answer by PZare.1

    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.1AuthorAnswer
    Visitor II
    September 14, 2021

    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)