cancel
Showing results for 
Search instead for 
Did you mean: 

Trying to source a 12.288MHz clock for STM32L433

freeflyer
Senior III

 

I am trying to source a 12.288MHz clock for an STM32L433 but having difficulties finding something.

The clock is required for I2S playback with a bluetooth receiver (BT401).

I have tried using the internal MSI RC to generate the 12.288MHz clock for SAI,  but the BT401 is misbehaving so it might be because the MSI RC clock has too much jitter.

I was wondering if a ASFL1-12.288MHZ-EC-T would be suitable for the clock source for the SAI external clock input....

https://uk.rs-online.com/web/p/crystal-oscillators/2403583

Below is the setup... 

 

freeflyer_0-1766340884039.png

 

The I2S amplifier (MAX98357A) seems to work fine with the 12.288MHz clock generated by the internal MSI RC and PLL.

 

But the BT401 either requires multiple attempts to start playing and sometimes plays just a screeching sound for a few seconds then stops.  So I need to rule out whether the issue is caused by clock jitter by trying a more accurate clock source.

 

I am currently using the STM32L433 Nucleo board which has a footprint for an external HSE crystal, but I cannot find a crystal for that footpring with a 12.288MHz frequency.

 

The ASFL1-12.288MHZ-EC-T seems to be an all in one device, you just provide power and it generates the output clock.  So its not like a crystal which needs a special drive circuit ?

 

I have read the application note AN2867 Recommended resonators for STM32 MCUs/MPUs, but its complicated and just confused me.

55 REPLIES 55

Yes its the BT401 (there are many suppliers).

I ordered the evalution board from RS but it is sold under DFRobot...

https://www.dfrobot.com/product-2178.html 

The supplier in China has some more information...

https://www.aliexpress.com/item/1005008896447977.html

The buffers are the same size (4K total, 2K each half)...

 

// Stereo DMA buffer (int16 elements)
#define AUDIO_STEREO_INT16_FULL   4096
#define AUDIO_STEREO_INT16_HALF   AUDIO_STEREO_INT16_FULL / 2

__attribute__((aligned(4))) static int16_t a2dpTxBuffer[AUDIO_STEREO_INT16_FULL]; // tx dma buffer for I2S left and right channels (stereo)
__attribute__((aligned(4))) static int16_t a2dpRxBuffer[AUDIO_STEREO_INT16_FULL]; // rx dma buffer for I2S left and right channels (stereo)

 

Buffers are copied like you say (easy)...

 

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
	if (hsai != &hsai_BlockA1)
		return;
		memcpy(&a2dpTxBuffer[0],&a2dpRxBuffer[0],AUDIO_STEREO_INT16_HALF * sizeof(int16_t));
}


void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
	if (hsai != &hsai_BlockA1)
		return;
memcpy(&a2dpTxBuffer[AUDIO_STEREO_INT16_HALF],&a2dpRxBuffer[AUDIO_STEREO_INT16_HALF],AUDIO_STEREO_INT16_HALF * sizeof(int16_t));
}

 

It plays the audio from the BT401 and for ~20 seconds its fine and then there must be clock drift (MSI) because for the next ~20 seconds it sound like static/stutter.  This cycle repeats

 

Its such a shame I can''t get the oscillator working with the SAI EXT CLOCK input.

Even if this did work, my only concern with using an oscillator vs MSI is whether the oscillator will draw more current than the MSI ?

1.

>my only concern with using an oscillator vs MSI is whether the oscillator will draw more current

see ds:

AScha3_0-1766835836980.png

...about 8mA for the ASFL1-12.288MHZ-EC-T .

2. 

Try using the ASFL1-12.288MHZ-EC-T , to check, how long its playing fine and /stuttered (this indicates how much different the "48k" are ) .  

20 secs for this "fine and /stuttered" sound indicates : one of the clocks is "bad" , probably the clock from MSI ;

20 x 48k = ~ 1 Mio samples , for 2k samples "drift" , so 2/1000 = 0.2 %  one clock is wrong.

This is to be expected for the MSI...

AScha3_1-1766836495484.png

3. ...so MSI too bad for audio, i think. If want low power HSE , use a 12.288 crystal , as HSE .

It will need only 0.5 mA about:

AScha3_2-1766836656722.png

But for now : use the ASFL1-12.288MH , to check, what "drift time" we get with correct 12.288 source.

 

If you feel a post has answered your question, please click "Accept as Solution".

I found a clue why the SAI EXT CLOCK is not working.

I stepped through the intitilisation (SAI B is currently configured as a Tx master) and when it reaches the function RCCEx_GetSAIxPeriphCLKFreq the value for srcclk = 0 (see line 13 in the code snippet below) but I would expect it to be RCC_SAI1CLKSOURCE_PIN....

 

static uint32_t RCCEx_GetSAIxPeriphCLKFreq(uint32_t PeriphClk, uint32_t InputFrequency)
{
  uint32_t frequency = 0U;
  uint32_t srcclk = 0U;
  uint32_t pllvco, plln;    /* no init needed */
#if defined(RCC_PLLP_SUPPORT)
  uint32_t pllp = 0U;
#endif /* RCC_PLLP_SUPPORT */

  /* Handle SAIs */
  if(PeriphClk == RCC_PERIPHCLK_SAI1)
  {
    srcclk = __HAL_RCC_GET_SAI1_SOURCE();
    if(srcclk == RCC_SAI1CLKSOURCE_PIN)
    {
      frequency = EXTERNAL_SAI1_CLOCK_VALUE;
    }
    /* Else, PLL clock output to check below */
  }

  

__HAL_RCC_GET_SAI1_SOURCE() reads the register RCC->CCIPR and I can see SAI1SEL is 0x0...

 

freeflyer_0-1766837149716.png

So why is SAI1SEL not set to '11' as shown in the refernce manual...

freeflyer_1-1766837355102.png

 

I've selected it CubeMX....

 

freeflyer_2-1766837461389.png

 

 

UPDATE:  I set a breakpoint in the function RCCEx_GetSAIxPeriphCLKFreq and manually changed the value of SAI1SEL from 0x0 to 0x3 and the SCK and FS now work and the BT audio plays.

However, the problem still exists despite using the oscillator instead of MSI.

It plays the audio from the BT401 and for ~20 seconds its fine and the next ~20 seconds it still sounds like static/stutter. This cycle repeats

And I still don't know why SAI EXT CLOCK is not being configured by the software, I only got it working by manually modifying the SAI1SEL register.

 

UPDATE 2: It looks like the SAI EXT CLOCK is defined and exectuted in HAL_SAI_MspInit....

 

void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/* SAI1 */
    if(hsai->Instance==SAI1_Block_A)
    {
    /* Peripheral clock enable */

  /** Initializes the peripherals clock
  */
    PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PIN;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

   

But it seems that HAL_RCCEx_PeriphCLKConfig is not writing the value RCC_SAI1CLKSOURCE_PIN to the SAI1SEL register ???


@freeflyer wrote:

It plays the audio from the BT401 and for ~20 seconds its fine and the next ~20 seconds it still sounds like static/stutter. This cycle repeats

And I still don't know why SAI EXT CLOCK is not being configured by the software, I only got it working by manually modifying the SAI1SEL register.


You test now Master BT401 and passthrough SAI A -> B or what?

And EXT CLOCK dont work because HAL and MX is buggy as any other sw on the world.

If you test Master then connect MCLK BT401 out to EXT Clock in STM and dont use any MSI or oscilator for SAI...

freeflyer
Senior III

I found the reason why the SAI EXT CLOCK was not being configured.

The generated code in the file stm32l4xx_hal_msp.c configures all the periperhal clocks.

When initialising each peripheral, the clock selection is defined by PeriphClkInit.PeriphClockSelection.

But for some reason, when initialising the SAI peripheral (HAL_SAI_MspIni) there is no value assigned to PeriphClkInit.PeriphClockSelection

A value is assigned to PeriphClkInit.Sai1ClockSelection (RCC_SAI1CLKSOURCE_PIN) but because PeriphClkInit.PeriphClockSelection is zero the SAI clock peripheral never gets configured

So I manually added the line (for SAI block B)... 

 

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;

 

So is this a bug ?  All other periphals (e.g. USB, UART, I2C etc) are assigned with a value for PeriphClkInit.PeriphClockSelection with the exception of the SAI.

HAL_SAI_MspIni is shown below (with my manually added line for block B)...

void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/* SAI1 */
    if(hsai->Instance==SAI1_Block_A)
    {
    /* Peripheral clock enable */

  /** Initializes the peripherals clock
  */
    PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PIN;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    if (SAI1_client == 0)
    {
       __HAL_RCC_SAI1_CLK_ENABLE();
    }
    SAI1_client ++;

    /**SAI1_A_Block_A GPIO Configuration
    PB13     ------> SAI1_SCK_A
    PA9     ------> SAI1_FS_A
    PA10     ------> SAI1_SD_A
    */
    GPIO_InitStruct.Pin = BT_I2S_SCK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
    HAL_GPIO_Init(BT_I2S_SCK_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = BT_I2S_FS_Pin|BT_I2S_SD_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

      /* Peripheral DMA init*/

    hdma_sai1_a.Instance = DMA2_Channel1;
    hdma_sai1_a.Init.Request = DMA_REQUEST_1;
    hdma_sai1_a.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_sai1_a.Init.Mode = DMA_CIRCULAR;
    hdma_sai1_a.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
    {
      Error_Handler();
    }

    /* Several peripheral DMA handle pointers point to the same DMA handle.
     Be aware that there is only one channel to perform all the requested DMAs. */
    __HAL_LINKDMA(hsai,hdmarx,hdma_sai1_a);

    __HAL_LINKDMA(hsai,hdmatx,hdma_sai1_a);

    }
    if(hsai->Instance==SAI1_Block_B)
    {
      /* Peripheral clock enable */

  /** Initializes the peripherals clock
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1; // MANUALLY ADDED 
    PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PIN;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

      if (SAI1_client == 0)
      {
       __HAL_RCC_SAI1_CLK_ENABLE();
      }
    SAI1_client ++;

    /**SAI1_B_Block_B GPIO Configuration
    PA4     ------> SAI1_FS_B
    PB3 (JTDO-TRACESWO)     ------> SAI1_SCK_B
    PB5     ------> SAI1_SD_B
    */
    GPIO_InitStruct.Pin = AMP_I2S_FS_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
    HAL_GPIO_Init(AMP_I2S_FS_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = AMP_I2S_SCK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
    HAL_GPIO_Init(AMP_I2S_SCK_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = AMP_I2S_SD_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
    HAL_GPIO_Init(AMP_I2S_SD_GPIO_Port, &GPIO_InitStruct);

      /* Peripheral DMA init*/

    hdma_sai1_b.Instance = DMA2_Channel2;
    hdma_sai1_b.Init.Request = DMA_REQUEST_1;
    hdma_sai1_b.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_sai1_b.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sai1_b.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sai1_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_sai1_b.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_sai1_b.Init.Mode = DMA_CIRCULAR;
    hdma_sai1_b.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_sai1_b) != HAL_OK)
    {
      Error_Handler();
    }

    /* Several peripheral DMA handle pointers point to the same DMA handle.
     Be aware that there is only one channel to perform all the requested DMAs. */
    __HAL_LINKDMA(hsai,hdmarx,hdma_sai1_b);
    __HAL_LINKDMA(hsai,hdmatx,hdma_sai1_b);
    }
}

 

Yes Im currently testing Master BT401 and passthrough SAI A (slave receive) -> B (master transmit) using the SAI EXT CLOCK (pin PA0) which SAI B uses. 

But the audio goes through alternating phases of poor playback which I assume is due to  having two different clock sources

i.e. one for BT401 and one for STM32, even though I am using a more accurate oscillator for STM32 instead of MSI)

 


@MM..1 wrote:

If you test Master then connect MCLK BT401 out to EXT Clock in STM and dont use any MSI or oscilator for SAI...

I am trying to avoid using the BT401 to drive the clocks of the STM32 SAI for reasons explained in previous posts (SAI also plays voice prompts from external flash connected to STM32 and these are critical, I also want to power down the BT401 but still have voice prompts working)

You are correct, it does not exclusively say it is not required. It just says the BCLK and LRCLK are required. I asked my contact in china who provides these parts and firmware and he said MCLK is not required when BT401 is slave. But I could test it in case it is required.  

Thanks, I have now got the external SAI clock (on pin PA0) working which was due to a bug in the generated code (as mentioned in previous posts).

So the STM32 SAI is now running from the external oscillator (12.288MHz), but the playback is the same.  

i.e. it plays the audio from the BT401 and for ~20 seconds its fine and the next ~20 seconds it still sounds like static/stutter. This cycle repeats

I assume the issue is due to using two different clock sources (i.e. the BT401 clock and the STM32 external oscillator).  Therefore this configuration does not seem to work

 

 

>And I still don't know why SAI EXT CLOCK is not being configured by the software, I only got it working by manually modifying the SAI1SEL register.

Looks like an error in Cube generated code; but no big problem, as you anyway want lower power and so not using the ASFL1-12.288MHZ-EC-T , with its 8mA extra.

So running now on MSI  gives good audio -for you- ?

About the "fine and /stuttered" sound we try a simple sync :

1. make a global info : volatile int buffer_ready_ number ;

2. in BT receive callbacks write buffer_ready_ number = 1; in half..callback, buffer_ready_ number = 2; in full...callb.

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
    buffer_ready_ number = 1;
}

...and in -full callback ... =2 .

3. in the play callbacks now look, which buffer is "ready" , then use buffer_ready_ number to copy this buffer to this playbuffer/half.

 

void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
   if (buffer_ready_number == 1)
	memcpy(&a2dpTxBuffer[0],&a2dpRxBuffer[0],AUDIO_STEREO_INT16_HALF * sizeof(int16_t));
   else
      memcpy(&a2dpTxBuffer[0],&a2dpRxBuffer[AUD..HALF],AUDIO_STEREO_INT16_HALF * sizeof(int16_t));
}

 ...and in -full ....same for ...

&a2dpTxBuffer[AUDIO_STEREO_INT16_HALF]

 

If you feel a post has answered your question, please click "Accept as Solution".

For voice promtpts you can simply use MSI = dont require hifi audio and for audio switch to clock from BT401 = most energy effective variant because this clock exist always in master ... Clock sources isnt fixed as MX set it, you can in code manipulate all. MX is only startup wizard.