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
  • BT401 is I2S slave
  • STM32 SAI A is I2S master receive
  • STM32 SAI B is I2S slave transmitt
  • STM32 clock is MSI RC 12.288MHz

 

freeflyer_0-1766662502542.png

 

Below is the SAI configuration...

 

freeflyer_1-1766662987675.png

 

 

The BT401 doesn't just play corrupted noise or refuse to play, it also looses the bluetooth connection.  

After multiple attempts at trying to play audio through the BT401, the bluetooth connection is lost to the BT401 device. The status LED on the BT401 turns off, then starts flashing (indicating its waiting for a bluetooth connection)

I have to remove the BT401 device from the phone as it refuses to connect, perform a power reset of the BT401, then add the BT401 device on the phone again.  

So its not just a playback issue, its also the bluetooth connection issue which happens when attempting to play audio throught the BT401.  

Yet if I configure the BT401 to be the I2S master and the STM32 to be the I2S slave, it works correctly, but this is not the ideal setup as previously mentioned.

 

(me) >If it cannot work really good in slave mode, we have to change the procedure .

+

>if I configure the BT401 to be the I2S master and the STM32 to be the I2S slave, it works correctly,

So new plan: BT401 to be the I2S master    +  L433 master transmit to DAC (with 12.288 clock, HSI or HSE byp.) .

Both run DMA on circular buffers, lets say: 8KB, 2x 4KB half buffers; 

At first try this setting, to play the output : copy/set in I2S callbacks zeros for silence or your "voice" for numbers.

If this working fine, then try copy from BT input buffer -> to I2S play buffer, to get the musik as sound.

ok?

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

Eveyrthing works correctly when the BT is master and the STM32 is slave, I've already got that working without any problems. 

But I would prefer to use the BT as slave and the STM32 as master, but the BT401 does not behave when it is the slave.

I was hoping it was due to clock drift due to using the MSI RC oscillator on the STM32, but when I tried using the external 12.288MHz oscillator instead of the MSI RC oscillator the BT401 still did not behave

So it does not seem to be due to clock drift.

I have attached some scope captures when the BT401 is slave and the STM32 is master, but I don’t think it tells you much ?

The scope was set to a single trigger on the rising edge of the BT401 SD output (CH4) i.e. when audio is intially played from the phone.

The BCLK and LRCLK generated by the STM32 are on CH 2 and CH3 respectively.

CH1 is set for the duration of the I2S DMA interrupts (HAL_SAI_RxCpltCallback & HAL_SAI_RxHalfCpltCallback). 

freeflyer_1-1766682715473.png

freeflyer_2-1766682740676.png

freeflyer_3-1766682804637.png

freeflyer_6-1766682934239.png

 

 

freeflyer_5-1766682846334.png

freeflyer_7-1766682965610.png

The BT401 should output data when the phone plays the audio so long as it has the bit clock and left/right clock from the STM32.  Even iif the data output pin (SD) was not connected to anything, you should still see data out.

But as you can see from the scope captures, the initial data is corrupt and after a very short time the data just stops (as shown in the last scope capture when it stops after ~500ms).  After several attempts of trying to play, the BT401 seems to reset all its settings because the bluetooth connection is lost and it has to be removed from the phone and added again (this also requires a power reset of the BT401).

So the issue is to do with the BT401, but I don't know what the issue is.  Its not due to clock drift as I already proved that by using an oscillator. The STM32 firmware is working correctly, its just that the BT401 does not behave when its in slave mode.

For reference, below is the code for the SAI DMA interrupts.  The buffer size is 4096 (i.e. 2048 for one half and 2048 for the other half)...

 

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
	if (hsai != &hsai_BlockA1)
		return;

	AUD_WAVBUFF *pData = AUD_pAudioData();
	uint32_t i;
	/* ----------------------------------------------------
	 * 1. Pass-through Bluetooth audio (first half)
	 * ---------------------------------------------------- */
	if (i2s_flg_volumeMutedBT)
	{
	    memset(&a2dpTxBuffer[0], 0,
	           AUDIO_STEREO_INT16_HALF * sizeof(int16_t));
	}
	else
	{
		memcpy(&a2dpTxBuffer[0],&a2dpRxBuffer[0],AUDIO_STEREO_INT16_HALF * sizeof(int16_t));

		/* ----------------------------------------------------
		 * 2. Optional Bluetooth volume reduction
		 * ---------------------------------------------------- */
		if((ALT_getFreefallState()) && (!i2s_flg_volumeMutedBT))
		{
			for (i = 0; i < AUDIO_STEREO_INT16_HALF; i++)
			{
				a2dpTxBuffer[i] = I2S_scaleAudioQ15(a2dpTxBuffer[i], a2dpVolume);
			}
		}
	}
	/* ----------------------------------------------------
	 * 3. Voice prompt injection (LEFT channel only)
	 * ---------------------------------------------------- */
	if (pData->voicePromtStat == VOICE_PROMPT_STATUS_PLAY)
	{
		uint32_t remaining_bytes =
				(pData->wavFileSize > pData->bytestocopy) ?
						(pData->wavFileSize - pData->bytestocopy) : 0;

		uint32_t copy_samples =
				((remaining_bytes / 2) < AUDIO_MONO_SAMPLES_HALF) ?
						(remaining_bytes / 2) : AUDIO_MONO_SAMPLES_HALF;

		uint32_t copy_bytes = copy_samples * 2;

		if (copy_samples > 0)
		{
			if (pData->source == AUDIO_SRC_INTERNAL)
			{
			    memcpy(&vocalBuffer[0],
			           pData->audioStartAddress + pData->bytestocopy,
			           copy_bytes);
			}
			else
			{
			    w25qxx_read_dma(&w25qxx,
			        pData->bytestocopy + (uint32_t)pData->audioStartAddress,
			        (uint8_t *)&vocalBuffer[0],
			        copy_bytes);
			}

			for (i = 0; i < copy_samples; i++)
			{
				a2dpTxBuffer[2*i] =
						I2S_scaleAudioQ15(vocalBuffer[i],
								systemConfig.volumeVP);
			}

			pData->bytestocopy += copy_bytes;
		}

		/* Silence remaining LEFT samples if prompt ended */
		if (copy_samples < AUDIO_MONO_SAMPLES_HALF)
		{
			for (i = copy_samples; i < AUDIO_MONO_SAMPLES_HALF; i++)
			{
				a2dpTxBuffer[2*i] = 0;
			}
			pData->voicePromtStat = VOICE_PROMPT_STATUS_IDLE;
		}
	}
}


void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
	if (hsai != &hsai_BlockA1)
		return;

	AUD_WAVBUFF *pData = AUD_pAudioData();
	uint32_t i;
	uint32_t offset = AUDIO_STEREO_INT16_HALF;

	/* ----------------------------------------------------
	 * 1. Pass-through Bluetooth audio (second half)
	 * ---------------------------------------------------- */
	if (i2s_flg_volumeMutedBT)
	{
	    memset(&a2dpTxBuffer[AUDIO_STEREO_INT16_HALF], 0,
	           AUDIO_STEREO_INT16_HALF * sizeof(int16_t));
	}
	else
	{
		memcpy(&a2dpTxBuffer[offset],&a2dpRxBuffer[offset],AUDIO_STEREO_INT16_HALF * sizeof(int16_t));

		/* ----------------------------------------------------
		 * 2. Optional Bluetooth volume reduction
		 * ---------------------------------------------------- */
		if((ALT_getFreefallState()) && (!i2s_flg_volumeMutedBT))
		{
			for (i = offset; i < offset + AUDIO_STEREO_INT16_HALF; i++)
			{
				a2dpTxBuffer[i] = I2S_scaleAudioQ15(a2dpTxBuffer[i], a2dpVolume);
			}
		}
	}

	/* ----------------------------------------------------
	 * 3. Voice prompt injection (LEFT channel only)
	 * ---------------------------------------------------- */
	if (pData->voicePromtStat == VOICE_PROMPT_STATUS_PLAY)
	{
		uint32_t remaining_bytes =
				(pData->wavFileSize > pData->bytestocopy) ?
						(pData->wavFileSize - pData->bytestocopy) : 0;

		uint32_t copy_samples =
				((remaining_bytes / 2) < AUDIO_MONO_SAMPLES_HALF) ?
						(remaining_bytes / 2) : AUDIO_MONO_SAMPLES_HALF;

		uint32_t copy_bytes = copy_samples * 2;

		if (copy_samples > 0)
		{
			if (pData->source == AUDIO_SRC_INTERNAL)
			{
			    memcpy(&vocalBuffer[AUDIO_MONO_SAMPLES_HALF],
			           pData->audioStartAddress + pData->bytestocopy,
			           copy_bytes);
			}
			else
			{
			    w25qxx_read_dma(&w25qxx,
			        pData->bytestocopy + (uint32_t)pData->audioStartAddress,
			        (uint8_t *)&vocalBuffer[AUDIO_MONO_SAMPLES_HALF],
			        copy_bytes);
			}

			for (i = 0; i < copy_samples; i++)
			{
				a2dpTxBuffer[offset + 2*i] =
						I2S_scaleAudioQ15(
								vocalBuffer[AUDIO_MONO_SAMPLES_HALF + i],
								systemConfig.volumeVP);
			}

			pData->bytestocopy += copy_bytes;
		}

		if (copy_samples < AUDIO_MONO_SAMPLES_HALF)
		{
			for (i = copy_samples; i < AUDIO_MONO_SAMPLES_HALF; i++)
			{
				a2dpTxBuffer[offset + 2*i] = 0;
			}

			pData->voicePromtStat = VOICE_PROMPT_STATUS_IDLE;
		}
	}
}

 

 

 

 

 

 

>scope captures when the BT401 is slave and the STM32 is master, but I don’t think it tells you much ?

Enough ...  try as i told you:

So new plan: BT401 to be the I2S master    +  L433 master transmit to DAC (with 12.288 clock, HSI or HSE byp.) .

Both run DMA on circular buffers, lets say: 2x : 4KB, 2x 2KB half buffers; 

At first try this setting, to play the output : copy/set in I2S callbacks zeros for silence or your "voice" for numbers.

Should be no problem.

If this working fine, then try copy from BT input buffer -> to I2S play buffer, to get the musik as sound.

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

I am so confused ?

The new plan is not new, its already working....

  • BT401 to be the I2S master    +  L433 master transmit to DAC (with 12.288 clock, HSI or HSE byp.)
    • DONE: Although I am using MSI as the clock source
  • Both run DMA on circular buffers, lets say: 2x : 4KB, 2x 2KB half buffers;
    • DONE: Although I am using 2 x 2KB buffers, 2x 1KB half buffers
  • If this working fine, then try copy from BT input buffer -> to I2S play buffer, to get the musik as sound.
    • DONE: Music is copied and plays as well as voice prompts

So I don't understand what you are asking, because that configuration is already working ?

What does not work is when BT401 is a slave, because with that configuration the BT401 refuses to output audio and then crashes.  The issue is with the BT401, not the STM32.

Are you saying dont use the BT401 as a slave and instead use it as master (which ia already working) ?

If so, when the BT401 is a master it does work correctly but that configuration has some disadvantages:

  • Relies on BT401 to generate the I2S clocks
    • Potential risk as voice prompts are critical (i.e. if BT401 fails to generate the I2S clocks, the voice prompts will not work)
  • Unable to power down the BT401 to save valuable battery power (bluetooth is only required for the first half of the operating cycle)
    • BT401 must always be powered as it generates the I2S clocks
      • Therefore during the last half of the operating cycle, it will be running purely to genrete the I2S clocks as I deliverately disable the bluetooth audio during this phase

I am trying to solve the issue when the BT401 as a slave.

There is no issue to solve when the BT401 is a master, because that configuration already works.

So all working fine now ??

I think, you didnt get the way, i tried to explain:

1. BT401 to be the I2S master   , L433 is slave receiver for this stream

+ on other circular buffer ->

2. stm32L433 is (also) master transmitter, to transmit to DAC (can work alone, BT on or off)

for its stream, that playes the audio to the dac. This is a fully independent own stream ! 

 

So it NOT relies on BT working perfect or not - your audio/voice can be played always perfect.

Only if stream from BT is stable, you copy data to the (circ.) output buffers, to hear the BT audio.

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

Hi , some hints 1. module irelevant slave or master use own clk .
2. you have fails in config start with here 

MM1_0-1766692251890.png

audio systems never will work with 2,3% error in clk.

3. Your cyan scoped BCLK seems clock 24 bit instead 16 , that require BT401

4. Using HAL and MX for this isnt optimal way, sometimes impossible. As you see with not working ext clk.

After you correct mistakes i mean slave can work as you plan.

 

Ahh I see, sorry I did not clearly understand your suggestiion.

So you are saying that....

  • BT401 is master
  • STM32 is a slave for the BT401 
  • STM32 is a master for the voice prompts

If I can get that configuration to work then it will meet my requirements, because then the BT401 will behave (unlike misbehaving when its in slave mode) and the voice prompts will play regardless of the BT401 state.

Is this what the configuraton would look like....

 

freeflyer_0-1766693834781.png

At the moment I only use the receive callbacks (HAL_SAI_RxCpltCallback & HAL_SAI_RxHalfCpltCallback) as shown in the code I posted.

With this alternative configuration, would I need to use the transmit callbacks too ?

Apologies, that screnshot I posted was an older version which had the incorrect clock configuration so the SAI showed an error with the 48kHz audio frequency as you pointed out.

Since managing to achieve 12.288MHz for the SAI clock, the audio clock is now correct with no error....

 

freeflyer_1-1766694173909.png

 

The BT401 uses 32-bit data frame but only sends 16-bit date...

freeflyer_2-1766694476499.png

 

The BT401 outputs 16-bit audio data packaged in a 32-bit I2S frame format. The BCK frequency of 3.072 MHz (48 kHz × 64 bits per frame) confirms this 32-bit-per-channel transmission structure.

Are you suggesting I should be using low level rather than HAL and CubeMX ?  Is this due to HAL being too slow or bugs ?

 

Configuration...you understand now. :)

Callbacks ...only the transmit are to handle, as we want play audio. At first .

The receive callbacks...forget , for now. BT receive will run , but we dont use it.

Except, later, if it looks "ok" , then we copy in the (transmit) callbacks not zeros for silence, or "voice" for info, but also then one of the input buffers , to "copy" the input from BT to the play buffer.

(Synchronisation is at first not important, we do it later. Get it working at first, even if BT audio might have some dropouts...or else, not important now. )

We create two independent streams : the important, L433 is master, to play to the dac.

The second, from BT , might run...musik, or not...silence. But output always possible, and voice clear.

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