2024-03-28 09:55 PM
I think, using SAI for SPDIF Tx, can go wrong (in terms of clock config, the MCLKDIV setting):
Debugging SAI SPDIF Tx config on a STM32U5A5 MCU - I saw this: it seems to me, this bug (in HAL drivers) is there for years: in my old projects, years ago to get it working: I had to set SPDIF audio sampling rate to 96KHz - even it is 48KHz (but now obvious why after debugging the HAL code on this MCU).
In order to get the right frequencies on SPDIF Tx, on a STM32U5A5, I have to set this PLL config:
/* 48 KHz SPDIF with scope/debug CubeMX cfg correct */
PeriphClkInit.PLL3.PLL3N = 36; //36; 36;
PeriphClkInit.PLL3.PLL3P = 24; //96; 24;
PeriphClkInit.PLL3.PLL3Q = 2;
PeriphClkInit.PLL3.PLL3R = 2;
PeriphClkInit.PLL3.PLL3RGE = RCC_PLLVCIRANGE_1;
PeriphClkInit.PLL3.PLL3FRACN = 7077; //7080 7078; -->
// it results in 12,288.004 KHz in FW - MCLKDIV should be 2 at the end - but it fails!
/* but: above 12,288 KHz results in MCLKDIV +1, too large! - so, we have to get a bit below "nominal", resulting in a larger clock offset error:
* -36Hz, compared to value 7078 with just 4 Hz error!
* why I cannot use 7078 with 12,288.004 KHz which would be better? The MCLKDIV is wrong by +1!
*/
PeriphClkInit.PLL3.PLL3ClockOut = RCC_PLL3_DIVP;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
ATTENTION: see the difference between the values generated by CubeMX tool and what works at the end really: CubeMX was trimmed for 0% error for audio via SPDIF - but the generated code is "soooo" wrong.
The HAL driver (stm32xxx_hal_sai.c) does this:
#if defined(SAI2)
if ((hsai->Instance == SAI1_Block_A) || (hsai->Instance == SAI1_Block_B))
{
freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SAI1);
}
else
{
freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SAI2);
}
#else /* SAI2 */
freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SAI1);
#endif /* SAI2 */
/* Configure Master Clock Divider using the following formula :
- If NODIV = 1 :
MCKDIV[5:0] = SAI_CK_x / (FS * (FRL + 1))
- If NODIV = 0 :
MCKDIV[5:0] = SAI_CK_x / (FS * (OSR + 1) * 256) */
if (hsai->Init.NoDivider == SAI_MASTERDIVIDER_DISABLE)
{
/* NODIV = 1 */
uint32_t tmpframelength;
if (hsai->Init.Protocol == SAI_SPDIF_PROTOCOL)
{
/* For SPDIF protocol, frame length is set by hardware to 64 */
tmpframelength = SAI_SPDIF_FRAME_LENGTH;
}
else if (hsai->Init.Protocol == SAI_AC97_PROTOCOL)
{
/* For AC97 protocol, frame length is set by hardware to 256 */
tmpframelength = SAI_AC97_FRAME_LENGTH;
}
else
{
/* For free protocol, frame length is set by user */
tmpframelength = hsai->FrameInit.FrameLength;
}
/* (freq x 10) to keep Significant digits */
tmpval = (freq * 10U) / (hsai->Init.AudioFrequency * tmpframelength);
}
else
{
/* NODIV = 0 */
uint32_t tmposr;
tmposr = (hsai->Init.MckOverSampling == SAI_MCK_OVERSAMPLING_ENABLE) ? 2U : 1U;
/* (freq x 10) to keep Significant digits */
tmpval = (freq * 10U) / (hsai->Init.AudioFrequency * tmposr * 256U);
}
hsai->Init.Mckdiv = tmpval / 10U;
#if 0
/* ===== this seems to be going wrong! ===== */
/* Round result to the nearest integer */
/* I get remainder 9 - so, it ends up with a wrong Mckdiv setting (just half the frequency we need) */
if ((tmpval % 10U) > 8U) /* why 8?? should it be 5! - but it would be still wrong at the end! */
{
hsai->Init.Mckdiv += 1U;
}
#endif
/* For SPDIF protocol, SAI shall provide a bit clock twice faster the symbol-rate */
if (hsai->Init.Protocol == SAI_SPDIF_PROTOCOL)
{
hsai->Init.Mckdiv = hsai->Init.Mckdiv >> 1;
}
}
The #if 0 there is my "temporary bug fix" (when I am very close to the correct nominal PLL clock out).
But it goes wrong!
If you see this code, or you debug this code... what happens is:
if ((tmpval % 10U) > 8U) /* why 8?? should it be 5! - but it would be still wrong at the end! */
{
hsai->Init.Mckdiv += 1U;
}
When I am very close to the "correct" frequency of PLL - I get remainder 9 and it increments the Mckdiv! - it is WRONG!
It seems to be wrong for me, because:
No wonder why all my old projects (since years) work just with 96KHz as SPDIF audio frequency and why I see/hear periodic audio artefacts on SPDIF Tx.
Dear STM team:
please, can you check if this code is really "correct", esp. if it could be better (e.g. using floating point and rounding not via simple integer, instead to look for the smallest offset error with two neighboring settings). Please, make sure the code works if I set a "perfect" PLL clock for SPDIF (and the MCLKDIV is correct, not off by +1, and just half of the needed clock speed at the end, just possible to fix by setting 96KHz audio rate). Thank you.
2024-03-29 12:44 AM - edited 2024-03-29 01:30 AM
Hello @tjaekel ,
Thank you for having reported the issue. Your detailed report and collaboration are much appreciated.
I escalated this internally for investigate via the internal tickets number: 177434 and 177449.
(PS: The internal tickets number are only for reference, not available outside of ST)
2024-03-29 04:46 AM
Hi @tjaekel ,
Could please provide the ioc file for reproducing the issue with the same configuration as you.
2024-03-30 12:49 PM
How to attach the IOC file here?
What I did in CubeMX (and maybe you can replicate):
My entire FW project is here (not the CubeMX project, my real IDE project, see my clock config there):
https://github.com/tjaekel/NUCLEO-U5A5JZ-Q_QSPI
Here the CubeMX config done (for a STM32U5A5 LQFP package, as reference):
Trim the SPDIF clock config for "best" "Real Audio Frequency" - it is wrong (even it shows me perfect 48KHz).
The clock configuration: SAI2_B, PC12, using PLL3_P, with HSE 8 MHz XTAL - resulting in correct "Real Audio Frequency"
But it is (for sure) wrong:
The IOC file for SPDIF config is here:
https://github.com/tjaekel/NUCLEO-U5A5JZ-Q_QSPI/blob/main/STM32U5A5_LQFP100.ioc
I will close as solution when you (STM team) can confirm how to configure SPDIF via CubeMX.
Thank you.
2024-04-22 12:30 PM
Hi @tjaekel and thank you for sharing the details.
Our dev team are working to resolve the reported issue.
I confirm that in CubeMX: some parameters are not displayed on SAI configuration panel when SPDIF protocol is selected and some information concerning audio frequency should be corrected.
As the SAI clock generator described in the STM32U5 reference manual (RM0476):
On HAL SAI source code, depending on the audio frequency asked, MCKDIV is computed.
Computation of MCKDIV depends on asked audio frequency (Fs), frame length (FRL), SAI kernel clock (Fk), master clock no divider parameter (NODIV) and master clock oversampling (OSR):
If NODIV = 1 :
MCKDIV = Fk / (Fs x FRL)
If NODIV = 0 and OSR = 0 :
MCKDIV = Fk / (Fs x 256)
If NODIV = 0 and OSR = 1 :
MCKDIV = Fk / (Fs x 256 x 2)
For specific case of SPDIF protocol, the bit clock must be twice the symbol-rate, so MCKDIV is also divided by 2.
So, for specific SPDIF case, the previous formulas become:
If NODIV = 1 :
MCKDIV = Fk / (Fs x 64 x 2)
If NODIV = 0 and OSR = 0 :
MCKDIV = Fk / (Fs x 256 x 2)
If NODIV = 0 and OSR = 1 :
MCKDIV = Fk / (Fs x 256 x 2 x 2)
It's recommended to configure the SAI kernel clock in order to have as much as possible, an integer value of MCKDIV.
It means for specific case of SPDIF, to configure SAI kernel clock as follow:
If NODIV = 1 :
Fk = N x (Fs x 64 x 2)
If NODIV = 0 and OSR = 0 :
Fk = N x (Fs x 256 x 2)
If NODIV = 0 and OSR = 1 :
Fk = N x (Fs x 256 x 2 x 2)
Because master clock no divider (NODIV) and master clock oversampling (OSR) parameters are not available on SAI configuration panel. These parameters are initialized to 0. It means that you need to configure a SAI kernel clock as a multiple of (Fs x 256 x 2).
Therefore, for an audio frequency at 48KHz, you need to configure SAI kernel clock at around N x 24,576Mhz.
Nevertheless, we will work to resolve the issues on CubeMX side, to add missing parameters and correct information displayed on SAI configuration panel:
1- Add those two parameters "Master Clock No Divider" and 'Master Clock Oversampling" in the list of Parameters settings when SPDIF protocol is selected for SAI in order to calculate the right "Real Audio Frequency".
2-"Master Clock Oversampling" parameter has also to be added when SAI mode is set as "Master" or "Master with master clock out".
Hope my answer is clear!
Do not hesitate to raise any issue and feel free to add your feedback.