2025-08-14 5:49 PM
Hello, I have made my own STM32F732RET6 board that has a USB FS configured as a Stereo, 24 bit 48kHz Audio Interface and SAI2 connected to an SN75176BDR (Differential Bus Transceiver IC) because I want to transmit AES3-AES/EBU (same as SPDIF) audio data coming from the USB). So USB Audio -> SPDIF TX. Problem is I cannot get it to work right and ALWAYS get noise in the output signal (I have hardware that can receive AES3-AES/EBU so I can hear it). I am suspecting this is a clock issue where the frequency of the SPDIF signal is not high enough to support 24 bit stereo 48kHz, from what I've read, this would require an SPDIF frequency of 6.144MHz. I don't know if "Real Audio Frequency" in the .ioc "Pinout & Configuration" on SAI2 should match this number (6.144MHz) or the Audio Sampling Frequency (48kHz). I cannot get "Real Audio Frequency" to be exactly 6.144MHz. I don't know if this should be the frequency the the SAI2 Clock Mux outputs (SAI2 Clock Mux sources from PLLSAI1), I can get PLLSAI1 to output a frequency of 6.144MHz but then "Real Audio Frequency" becomes 96kHz (and when this happens I get no sound, no noise, absolutely nothing on the receiving end). When I get PLLSAI1 to output 192MHz, the signal I get contains some of the audio I sent from the computer (USB) but it is mostly noise. Is there any way that SAI2 works exactly on the frequency provided by PLLSAI1 (through the MUX) without a divider? I read an older post about CubeMX generating wrong Clock Config for SPDIF but I really cannot understand how he fixes it. He also mentions something about having to set the Audio Frequency to 96kHz in order to get working 48kHz but further than that I don't understand.
Below I have posted some files so you can hear what it sound like currently.
Code Below, this is from the usbd_audio_if.c file that sends the USB Audio Data to the SAI DMA, no other function has been touched besides AUDIO_PeriodicTC_FS
static int8_t AUDIO_PeriodicTC_FS(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
if (cmd == AUDIO_OUT_TC)
{
// Convert USB 24-bit stereo to SPDIF 32-bit frames
uint32_t *dst = sai_tx_buffer;
uint8_t *src=pbuf;
for (uint32_t i = 0; i < size / 3; i++)
{
// USB is 24-bit little endian: LSB, Mid, MSB
uint32_t sample = (uint32_t)(src[0] | (src[1] << 8) | (src[2] << 16));
// Sign extend from 24 to 32 bits
if (sample & 0x800000){
sample = sample | 0xFF000000;
}
*dst++ = (uint32_t)sample;
src += 3;
}
// Now transmit aligned SPDIF words
HAL_SAI_Transmit_DMA(&hsai_BlockB2, (uint8_t*)sai_tx_buffer, size / 3);
}
return USBD_OK;
}
Below are both clock configurations, the first with the 3MHz Real Audio Frequency is the one the merely works. Second is the one with 48kHz Real Audio Frequency, matching the Audio Frequency which gives no audio.
192MHz Clock, 3MHz Real Audio Frequency
3.072 Clock, 48kHz Real Audio Frequency
I've been dealing with this for days, any help would be greatly appreciated, Thanks in Advance! :)
Solved! Go to Solution.
2025-08-31 11:52 AM
To begin with the most important, SPDIF Clock.
1) Figure out what was the correct clock in order to transmit 48kHz Stereo Audio.
The frequency which you feed the SAI Clock MUX with. For SPDIF it should be (Sampling Rate*64)*2, giving a frequency of 6.144MHz. We do that x2 because SPDIF also carries the clock in the same signal as the audio, in BMC Encoding, which by spec requires double the frequency thus x2 and resulting in 6.144MHz. For 96kHz it would be 12.288MHz.
2) VERY IMPORTANT THAT TOOK DAYS TO FIGURE OUT BECAUSE THE GENERATED CODE IS WRONG. Assuming you have fed a clock with the correct frequency to the SAI Clock MUX, ignore the "Real Audio Frequency" field in SAI Parameter Settings. The clock fed into the SAI must NOT be divided (the CubeMX Generated code divides it, we have to stop it from doing so). Inside the MX_SAIx_Init() function in main.c add this line:
hsai_BlockB2.Init.NoDivider = SAI_MASTERDIVIDER_DISABLE;
3) Replace the wrong generated usbd_audio_if.c functions with the correct ones.
CubeMX (once again) generated wrong code. The Half and Transfer complete functions inside uisbd_audio_if.c are WRONG and they NEVER RUN. Replace them with these:
void TransferComplete_CallBack_FS(void) -> void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
void HalfTransfer_CallBack_FS(void) -> void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai) and void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai) are ALREADY DEFINED by HAL so no need to define them anywhere.
4) Create the code that handles the buffering and sending the actual USB Audio Data.
I have edited the USB Audio Class Descriptors to support 24 Bit audio (won't get into detail how I did that, if you want more info I can post another reply), this doesn't affect anything related to SPDIF, the buffering and packet handling is just made for 24 Bits.
the Function in usbd_audio_if.c:
static int8_t AUDIO_PeriodicTC_FS(uint8_t *pbuf, uint32_t size, uint8_t cmd)
is called whenever a new USB Packet is received, meaning, all buffer handling must be done inside this function. Here is the complete function that handles double buffering.
static int8_t AUDIO_PeriodicTC_FS(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
if (cmd != AUDIO_OUT_TC) return USBD_OK;
uint32_t sample_count = size / 3U; // Each sample is 3 bytes
if (sample_count == 0) return USBD_OK;
// Convert to 32-bit sign-extended
uint8_t *src=pbuf;
for (uint32_t i = 0; i < sample_count; i++)
{
uint32_t sample = (uint32_t)(src[0] | (src[1] << 8) | (src[2] << 16));
sample &= 0x00FFFFFFU;
if (sample & 0x00800000U) sample |= 0xFF000000U;
sai_temp_buffer[i] = sample;
src += 3;
}
// Copy converted samples into the current writable half
uint32_t idx = 0;
while (idx < sample_count)
{
if (!half_ready[current_half])
{
// Try the other half
if (half_ready[1U - current_half])
{
current_half = 1U - current_half;
half_fill_pos = 0;
}
else
{
// Both halves busy -> drop remaining samples
break;
}
}
uint32_t space = HALF_SIZE - half_fill_pos;
uint32_t to_copy = sample_count - idx;
if (to_copy > space) to_copy = space;
memcpy(&sai_tx_buffer[current_half * HALF_SIZE + half_fill_pos],
&sai_temp_buffer[idx],
to_copy * sizeof(uint32_t));
idx += to_copy;
half_fill_pos += to_copy;
if (half_fill_pos >= HALF_SIZE)
{
half_ready[current_half] = 0; // Mark this half as full (DMA will play it soon)
current_half = 1U - current_half; // Switch to other half
half_fill_pos = 0;
}
}
return USBD_OK;
}
After making these changes, it worked properly. I will post the usbd_audio_if.c so you can see everything in detail. NOTE THAT THIS ONLY WORKS FOR 24 BIT AUDIO. IF YOU WANT MORE INFORMATION TO CHANGE THE DEFAULT GENERATED 16 BIT USB AUDIO DEVICE CLASS TO 24 BIT I CAN POST ANOTHER GUIDE EXPLAINING EVERY DETAIL.
2025-08-21 7:47 AM
Hi @nt2ds
Did you use CubeMX to generate your code? If so, would you attach ioc file to check PLLSAI1 dividers?
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-08-21 9:59 AM - edited 2025-08-21 10:01 AM
I used CubeMX inside CubeIDE to generate the code. The .ioc I have attached now is with the default generated clock for SAI2 which gives a Real Audio Frequency of 3MHz, SAI2 target settings are 48kHz Audio Frequency, 24bit, Stereo, I don't know what Real Audio Frequency should be. I have read an older post about the code generation using a MCLKDIV on SAI but I didn't really understand, I also read that in order to get proper 48kHz I have to set Audio Frequency to 96kHz. Anyway, in the .ioc I have attached I have set the SAI2 settings to how it should be working under ideal conditions.
2025-08-22 3:41 AM
Hi @nt2ds
While I’m not audio expert, I suggest you adjusting PLLSAIN to 246 instead of 240 ( PeriphClkInitStruct.PLLSAI.PLLSAIN = 246; in your code). This change should yield a clock frequency around 196.8 MHz, which can help generate a more accurate SPDIF bit clock close to 3.075 MHz.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-08-22 3:42 AM
I did try that but I will again in case I missed something the first time and will keep you updated!
2025-08-25 5:52 AM
Hello, sorry for the later answer (I work in PA that why I need this kind of thing and was working those days). I did try that and it is noticeably better but still the problem persists. What happens with this clock now is some packets are played distorted while very few packets after a short period of time are played properly and this repeats. I cannot measure how often this happens but when I hear something (distorted) for a second or two, for a short moment it plays properly which indicates a clock problem because they are received in a wrong time. I have attached a .wav file which plays a song so you can listen the problem yourself (for reference the song is Tiesto - The Motto). On this post I see he feeds the SAI Clock with the a Frequency the Real Audio Frequency must be (feeding 3.072MHz to SAI, not a bigger value like in my case). I also read that he tells about problems with MCKDIV. What is MCKDIV? From what I understand it is a divider on SAI2 PLL that is not visible on the Clock Configuration windows. Can I make that MCKDIV 1 so that the clock I feed into SAI2 PLL is not divided?
2025-08-25 8:26 AM
This sounds more like a buffering and / or sync problem.
Maybe you should build an intermediate buffer which collects some more USB data before going to SPDIF.
And for better clock accuracy, use the extra audio clock input - if this F7 has that. With probably 24.576 MHz (for 48 / 96 kHz).
2025-08-25 10:49 AM
I know its not a buffer issue. If it were a buffer issue there would be spaces between audio not noise and distortion. Did you listen to the files I posted on the first message?
2025-08-25 11:24 PM
Okay.
I cannot download these files at work.
Have you used some "test signals" which are easier to identify when looking at raw data or raw wav files?
I recommend using digitally created simple signals like a sawtooth (+1 LSB per sample) or a square wave (+-FS). Then check the SPDIF output and all the buffers inbetween.
2025-08-26 4:03 AM
Hi @nt2ds
About MKDIV, Master clock divider, bits are set and cleared by software (as part of SAI peripheral). They have to be configured when the audio block is disabled
= 0000: Divides by 1 the master clock input;
Otherwise equals to
You need to set NODIV=1 and leave MCKDIV = 0000 to feed the kernel clock input as it is.
the whole block diagram:
I'm adding @JonathanC, maybe he can bring some help on this.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.