cancel
Showing results for 
Search instead for 
Did you mean: 

STM32WB55 / NUCLEO-WB55RG PDM Clock issue?

Remyhx
Associate III

Screenshot 2024-11-15 at 17.12.02.pngI have an unexpected result, that bothers me for two days now: I use CubeMX 6.12.1 that I use with VSCode / STM extension V2.1.1. I connected a TDK3902-Evaluation PDM Microphone-board (datasheet and T3902 component datasheet), it needs between 1.0-3.3MHz clock for normal mode. (400-800khz LP mode).

I connected Vdd to 3.3V, GND to GND, select to GND, data to SAI1_D1 (PC3) and clock to SAI1_CK (PB8).

 

In CubeMX I activated SAI1, set the following options:

  • PDM
  • Mono
  • 16khz (SAI1 clock set to 40.96 MHz)
  • 16bit data/frame

Screenshot 2024-11-15 at 17.11.48.png

In clock configuration:

Screenshot 2024-11-15 at 17.12.17.png

If I probe the SAI1_CK pin (PB8) I see a 128 khz clock. Obviously this is too less and not correct, what am I overseeing here? Maybe a bug?

Screenshot 2024-11-15 at 17.11.05.png

 

The code generated by CubeMX:

 

 

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Macro to configure the PLL multiplication factor
  */
  __HAL_RCC_PLL_PLLM_CONFIG(RCC_PLLM_DIV5);

  /** Macro to configure the PLL clock source
  */
  __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSE);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48|RCC_OSCILLATORTYPE_HSI
                              |RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_10;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the SYSCLKSource, HCLK, PCLK1 and PCLK2 clocks dividers
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
                              |RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief Peripherals Common Clock Configuration
  * @retval None
  */
void PeriphCommonClock_Config(void)
{
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
  */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS;
  PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSI;
  PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE0;

  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN Smps */

  /* USER CODE END Smps */
}

/**
  * @brief SAI1 Initialization Function
  *  None
  * @retval None
  */
static 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.MckOverSampling = SAI_MCK_OVERSAMPLING_DISABLE;
  hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
  hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_16K;
  hsai_BlockA1.Init.MonoStereoMode = SAI_MONOMODE;
  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 = 16;
  hsai_BlockA1.FrameInit.ActiveFrameLength = 1;
  hsai_BlockA1.FrameInit.FSDefinition = SAI_FS_STARTFRAME;
  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 = 2;
  hsai_BlockA1.SlotInit.SlotActive = 0x00000003;
  if (HAL_SAI_Init(&hsai_BlockA1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SAI1_Init 2 */

  /* USER CODE END SAI1_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

 

 

 

The code is easy, for testing:

 

 

#define BUFFERPDMSIZE 128
uint16_t data[BUFFERPDMSIZE] = {0};

HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)data, BUFFERPDMSIZE);

 

 

 

And the callback with empty processing:

 

 

  //Callback for dma
  void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
  {
    printf("1st half of data received\n\r");
    process_audio_data(data, BUFFERPDMSIZE/2);
  }

  void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
  {
    printf("2nd half of data received\n\r");
    process_audio_data(data + BUFFERPDMSIZE/2, BUFFERPDMSIZE/2);
  }

  void process_audio_data(uint16_t *data, uint16_t size)
  {
    printf("Data received\n\r");
    
    for (int i = 0; i < size; i++)
    {
      printf("%d\n\r", data[i]);
    }
    printf("Data processed\n\r");
  }

 

 

The callbacks are activated: terminal prints the printf statements, data is dec 65535/ 0xFFFF... 

1 REPLY 1
Remyhx
Associate III

Not the most scientific way of solving... (Is it solved?)

Need a clock of 1.024 mhz for the mems mic. Audio rate 16.000 hz, oversampling x64 -> 1.024.000 hz

 

I was poking around with the MKDIV setting in register ACR1 (SAI), put it back to 1.

SAI1_Block_A->CR1 &= 0xFC0FFFFF;
SAI1_Block_A->CR1 |= 0x00100000;

//Or

SAI1_Block_A->CR1 &= ~SAI_xCR1_MCKDIV;  
SAI1_Block_A->CR1 |= SAI_xCR1_MCKDIV_0;

 

If I use these clock settings it is almost 1.024 mhz:

Screenshot 2024-11-17 at 10.56.16.png

And the oscilloscope:

Screenshot 2024-11-17 at 10.57.15.png

Especially the PLLSAI1R value is important. If I change To SAI1, it seems unpredictable.

The register:

Screenshot 2024-11-17 at 11.13.47.png

(MCKDIV is mistakenly written as MCJDIV)