Skip to main content
Remyhx
Associate III
November 15, 2024
Question

STM32WB55 / NUCLEO-WB55RG PDM Clock issue?

  • November 15, 2024
  • 1 reply
  • 937 views

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

Remyhx
RemyhxAuthor
Associate III
November 17, 2024

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)