Skip to main content
Visitor II
March 28, 2024
Solved

STM32U5xx: SPDIF out - how to configure?

  • March 28, 2024
  • 1 reply
  • 2885 views

I am struggling since days to get "my audio" out via SPDIF (electrical).

What I see on scope (there is a voltage divider and cap for DC-free on PC12, Vpp is 0.6V, approx. 75 Ohm impedance):

MCU_SPDIF.png

It makes sense - but my SPDIF receiver (a STM32F769I-DISCO board, which works fine as SPDIF receiver with another source) does not get any audio (but it seems to sync and realize there is SPDIF signal, but completely silent).

Intention:

  • any audio I have in MCU (it will be PDM MIC via CODEC to I2S, as 24bit, 48 KHz) should be forwarded via SPDIF Tx
  • I want to use SAI2_B as SPDIF Tx (with PC12), also as 24bit (both are 48 KHz)
  • I tried with "faking", sending buffer content for SPDIF Out as a sine wave (24bit, 48 KHz)

 

#ifdef SPDIF_TEST
#define SPDIF_TIME		100
#define SPDIF_WORDS		4		/* 24bit values, but 32bit word */
#define SPDIF_CHANNELS	2
#define	SPDIF_AUDIOFREQ	48
#define SPDIF_DOUBLEBUF	2
int32_t SPDIF_out_test[SPDIF_CHANNELS * SPDIF_AUDIOFREQ * SPDIF_TIME * SPDIF_DOUBLEBUF] __aligned(4);
void GenerateSPDIFOut(void)
{
	int i;
	int32_t val = 0;
	double d;

	for (i = 0; i < (SPDIF_CHANNELS * SPDIF_AUDIOFREQ * SPDIF_TIME); i++)
	{
		d = sin(((2 * M_PI) * i) / SPDIF_AUDIOFREQ);
		d *= (double)0x00700000;		//volume scaling
		val = (int32_t)d;
		val &= 0x00FFFFFF;
		val |= 0x01000000;
		SPDIF_out_test[2 * i + 0] = val;
		SPDIF_out_test[2 * i + 1] = val;
	}
}
#endif

 

I play this via this code (using DMA Ch5):

 

ifdef SPDIF_TEST
	GenerateSPDIFOut();
	MX_SAI_Init();
#endif
#ifdef SPDIF_TEST
	HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint8_t *)SPDIF_out_test, (uint16_t)sizeof(SPDIF_out_test) / sizeof(uint32_t));
#else

 

The configuration is this:

 

 hsai_BlockB1.Instance = SAI2_Block_B;
 hsai_BlockB1.Init.Protocol = SAI_SPDIF_PROTOCOL;
 hsai_BlockB1.Init.AudioMode = SAI_MODEMASTER_TX;
 hsai_BlockB1.Init.Synchro = SAI_ASYNCHRONOUS;
 hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
 hsai_BlockB1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;	//SAI_MASTERDIVIDER_ENABLE;
 hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
 hsai_BlockB1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
 ////hsai_BlockB1.Init.DataSize = SAI_DATASIZE_32;
 hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
 hsai_BlockB1.Init.MckOutput = SAI_MCK_OUTPUT_DISABLE;
 hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
 hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
 hsai_BlockB1.Init.PdmInit.Activation = DISABLE;
 hsai_BlockB1.Init.PdmInit.MicPairsNbr = 1;
 hsai_BlockB1.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK1_ENABLE;
 if (HAL_SAI_Init(&hsai_BlockB1) != HAL_OK)
 {
	 Error_Handler();
 }
 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI2;
 PeriphClkInit.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLL3;
 PeriphClkInit.PLL3.PLL3Source = RCC_PLLSOURCE_HSE;
 PeriphClkInit.PLL3.PLL3M = 1;

 /* 48 KHz SPDIF with scope CubeMX cfg */
 PeriphClkInit.PLL3.PLL3N = 18;				//36;
 PeriphClkInit.PLL3.PLL3P = 96;				//96;
 PeriphClkInit.PLL3.PLL3Q = 2;
 PeriphClkInit.PLL3.PLL3R = 2;
 PeriphClkInit.PLL3.PLL3RGE = RCC_PLLVCIRANGE_1;
 PeriphClkInit.PLL3.PLL3FRACN = 3544;		//7080

 PeriphClkInit.PLL3.PLL3ClockOut = RCC_PLL3_DIVP;
 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
 {
 Error_Handler();
 }
void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{
 if(hsai->Instance==SAI2_Block_B)
 {
 /* Peripheral clock enable */
 if (SAI2_client == 0)
 {
 	 __HAL_RCC_SAI2_CLK_ENABLE();

 	 /* Peripheral interrupt init*/
 	 HAL_NVIC_SetPriority(SAI2_IRQn, 0, 0);
 	 HAL_NVIC_EnableIRQ(SAI2_IRQn);
 }
 SAI2_client++;

 /**SAI1_B_Block_B GPIO Configuration
 PB5 ------> SAI1_SD_B
 */

 GPIO_InitStruct.Pin = GPIO_PIN_12;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.Alternate = GPIO_AF13_SAI2;
 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

 /* Peripheral DMA init*/
 NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
 NodeConfig.Init.Request = GPDMA1_REQUEST_SAI2_B;
 NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
 NodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
 NodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
 NodeConfig.Init.DestInc = DMA_DINC_FIXED;
 NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
 NodeConfig.Init.DestDataWidth = DMA_SRC_DATAWIDTH_WORD;
 NodeConfig.Init.SrcBurstLength = 1;
 NodeConfig.Init.DestBurstLength = 1;
 NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
 NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 NodeConfig.Init.Mode = DMA_NORMAL;
 NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
 NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
 NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
 if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel5) != HAL_OK)
 {
 Error_Handler();
 }

 if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel5, NULL, &Node_GPDMA1_Channel5) != HAL_OK)
 {
 Error_Handler();
 }

 if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel5) != HAL_OK)
 {
 Error_Handler();
 }

 handle_GPDMA1_Channel5.Instance = GPDMA1_Channel5;
 handle_GPDMA1_Channel5.InitLinkedList.Priority = DMA_LOW_PRIORITY_MID_WEIGHT;
 handle_GPDMA1_Channel5.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
 handle_GPDMA1_Channel5.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
 handle_GPDMA1_Channel5.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 handle_GPDMA1_Channel5.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
 if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel5) != HAL_OK)
 {
 Error_Handler();
 }

 if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel5, &List_GPDMA1_Channel5) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_LINKDMA(hsai, hdmatx, handle_GPDMA1_Channel5);

 if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel5, DMA_CHANNEL_NPRIV) != HAL_OK)
 {
 Error_Handler();
 }
 }
}

 

Besides the issue - "why I do not get the SPDIF signal on receiver", there are some major questions:

  • I do not need to set the datasize "as I understand" - forced anyway), e.g. via:

 

////hsai_BlockB1.Init.DataSize = SAI_DATASIZE_32;​

 

It should be forced automatically to 32bit (actually, the STM docs talk about 64bit and I see in drivers something with 64bit is used, I understand also the there is a DIV factor of 64 for SPDIF)

  • So, assuming the SPDIF needs really a SAI2 clock as 48 KHz * 64 = 3,072 KHz - I decided to use PLL3 and set the correct PLL out frequency for the SAI2 as SPDIF:
    Reusing PLL2, used for SAI1 and OCTOSPI cannot work, because I would not be able to set exactly this SAI_CLK value. So, PLL3 for the "nominal" SAI2 clock needed, just for SPDIF.
  •  Not really clear how to launch the DMA (with circular buffer):
    It the size the "number of words/samples" or the "number of bytes"? (I am using now "number of sample words", as seen that "size" is multiplied in driver with DMA word size)

 

HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint8_t *)SPDIF_out_test, (uint16_t)sizeof(SPDIF_out_test) / sizeof(uint32_t));​

 

  • The biggest confusion comes with CubeMX generated code!

If I use CubeMX and configure all (for my 8 MHz HSE) - I get this config:

 

PLL3P : PPL3M = 1
 PLL3N = 36
 PLL3P = 96
 factional = 7080
//resulting in: 3.07021 MHz, audio as: 48.0 KHz (minimum error)

 

I tried to trim in CubeMX that audio frequency error is minimum (or 0).

But when I run this config - it seems to be completely wrong. I check with a scope the SPDIF signal and its frequency. I assume to see this:

  • 48 KHz audio, two channels, 32bit samples (SPDIF uses 24bit, but a 32bit frame) - so, 3,072 KHz
  • but SPDIF is "marked encoded", so that I expect to see on SPDIF the fastest frequency (for values as 1) is doubled, so that a sequence of zeros is 48 KHz, but a sequence of ones is 96 KHz

So, I tried to trim the SPDIF frequency via scope and I had to configure this:

 

PLL3P : PLL3M = 1
 PLL3N = 18
 PLL3P = 96
 fractional = 3546
//resulting in 2x 48 KHz for SPDIF signal

 

So, what I see:

  • The CubeMX config says "correct 48 KHz as audio" but I have to double the frequency for SPDIF. And the PLL frequency seems to be a little bit off, but OK to tweak via "fractional" (or my scope is a bit off).

Also not really clear for me:

  • the SPDIF sample words (e.g. as 24bit) contain also the CS, U, V bits: MSB of audio sample is always at bit 23, but the upper next three bits are also transmitted via SPDIF: if, for instance, valid bit is "wrong" - receiver might ignore it.
  • I tried to play with these bits: no difference
  • assuming it would work (at the end) - there is a "dramatic impact" on my buffer handling:
    I CANNOT simply forward the same buffer: assume I get 24bit samples from my codec and I want to send out exactly the same buffer content as SPDIF - do I have to touch in every sample the bits [26:24] just to set a valid CS, U, V bit?
  • It sounds to as (OK): process the audio buffer received from codec (via I2S), flip the SPDIF bits (or shift the audio sample value? ...) and send out via SPDIF as a different buffer...?
    Not possible to forward a 32bit buffer (with 24bit sample words) via SPDIF...?

Even I saw the SPDIF working already (with a lot of distortion) - I cannot replicate neither send a test signal via SPDIF (from a prefilled buffer with sine wave samples).
What is wrong? (I want to see SPDIF working, esp. when I am sure, the SPDIF out via SAI2_B is generated and out there)

 

    This topic has been closed for replies.
    Best answer by tjaekel

    OK, I can confirm:

    1. SPDIF Out on SAI2_SD_B works:
      Even on STM32U5A5 LQFP64 package ("officially" not a SAI2 and CubeMX does not let me configure pins for SAI2) - the SAI2_SD_B can be used as SPDIF out - IT WORKS! (even on the LQFP64 package!)
    2. Yes, the CubeMX generated clock config for SPDIF is wrong - but I could fix
    3. There is no need to set the CS bits: even I have assumed it is needed to add the V, U, CS bits: no: they can remain all zero (getting 24bit audio samples from SAI1 and forward to SAI2 without modification - great) - IT WORKS!

    Just:

    The issue is!:
    The DMA from memory to SAI_SD_B as SPDIF Out FAILS!
    It writes all zero (or nothing), even the memory is properly filled with audio samples.

    I have used a scope (SALEAE) and SPDIF decoder: yes, I see all words on SPDIF signal are zero on scope!

    Why? No idea: the DMA from memory to SAI2 peripheral as SPDIF out is the issue.
    I have generated the code via CubeMX, as circular buffer - but it does not work.

    When I use Polling mode or INT mode to send the audio to SAI2_B - all works fine. Just the DMA fails.
    Here, what I do now:

    #if 1
    	////HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint8_t *)SPDIF_out_test, (uint16_t)(sizeof(SPDIF_out_test) / sizeof(uint32_t)));		//FAILS!!!!!!!!
    	HAL_SAI_Transmit_IT(&hsai_BlockB1, (uint8_t *)SPDIF_out_test, (uint16_t)(sizeof(SPDIF_out_test) / sizeof(uint32_t)));			//WORKS!
    #else
    	while (1)
    	{
    		HAL_SAI_Transmit(&hsai_BlockB1, (uint8_t *)SPDIF_out_test, (uint16_t)(sizeof(SPDIF_out_test) / sizeof(uint32_t)), 5000);	//WORKS!
    	}
    #endif

    Why is the DMA to SAI2 not working?
    It looks like I have also trouble meanwhile - even it was working - to get PDM MIC via SAI1 into memory - the DMA is "so crazy" (and seems to be the main issue when it does not work).

    BTW: I run the MCU with 1V8: with a modified voltage divider for SPDIF out (and the cap) - it works fine.
    I can receive with a STM32F769I-DISCO board and the SPDIF signal level is similar to running with 3V3.
    Maybe just a need to set the speed to fastest for SAI2_B_SD out (PC12).

    So,
    YES, even on a STM32U5A5RJT6Q (LQFP64 package) - there is an SAI2 and SPDIF out on it works!
    (the STM datasheet is wrong!)

    I will close this thread and open a new one for DMA issue.

    1 reply

    tjaekelAuthor
    Visitor II
    March 28, 2024

    If I understand correctly:

    • 48KHz, 2 channels, a 32bit (for SPDIF) = 3,072 KHz (bit clock)
    • SPDIF seems to take 64 (bit or clock ratio), so, also = 48 * 64 = 3,072 KHz
    • On SPDIF, with the "bi-marking", I should see sequences (for zeros) as 3,072 KHz and sequences (for ones) as doubled, as 6,144 KHz

    But, with the setting as 48KHz:

    hsai_BlockB1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;

    I can never reach this frequency on SPDIF (via tweaking the PLL).

    Remark:

    I have to set to 96K:

    hsai_BlockB1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_96K;

    Now I can trim the PLL to get 3,072 KHz on SPDIF (for the zeros):

     /* 48 KHz SPDIF with scope CubeMX cfg */
     PeriphClkInit.PLL3.PLL3N = 36;		//36;
     PeriphClkInit.PLL3.PLL3P = 24;		//96;
     PeriphClkInit.PLL3.PLL3Q = 2;
     PeriphClkInit.PLL3.PLL3R = 2;
     PeriphClkInit.PLL3.PLL3RGE = RCC_PLLVCIRANGE_1;
     PeriphClkInit.PLL3.PLL3FRACN = 7400;	//7080

    Remark:
    This setting, to doubled audio frequency (96KHz), even all is 48KHz - I saw already on other projects, on older HAL drivers. It seems to me, the "bug" that the SPDIF clock config is not correct and needs a setting for the doubled audio frequency is still there.

    Never mind - even I get now reasonable waveform on SPDIF, with 3.086 MHz and 3.06 MHz frequencies (as average 3.074 MHz - it should be 3.072 MHz but not possible to tweak further) - I still do not see any audio volume on receiver.

    MCU_SPDIF_2.png

    tjaekelAuthor
    Visitor II
    March 28, 2024

    I am so lost how to get SPDIF working (and why my receiver does not get any audio samples, even it seems to sync).

    BTW:
    I "trust" the DMA: during debugging I saw that the DMA src pointer (from memory) is moving and seems to transmit the audio samples to SPDIF DR (data register, also correct address - but no idea what was transferred as value).

    I am not sure, if 32bit with DMA works really: I have another (separate issue) where a 32bit word transfer on I2S, from Codec into memory, fails as well (I get just all zeros, even the I2S signal is completely fine).
    Based on the changed bit pattern on scope (due to my sine wave in test buffer for SPDIF out) - I assume it should come out on SPDIF as well as a sine audio signal (1 KHz).

    Any ideas why my SPDIF receiver does not see any audio samples?
    (I have debugged a bit my STM32F769I-DISCO as SPDIF Rx: I do not see any error flags set but the receiver buffer is really all zero - why?) I trust a bit this SPDIF receiver because it works if I play SPDIF from another setup (mainly the same used as Tx) and it works.

    So, something wrong with my new STM32U5A5 setup, maybe the signal levels (external circuit) or the clock configuration for SPDIF, or the buffer content, or the STM32U5xx has an issue with SPDIF via SAI.

    If you have any clues... very appreciated: I am out of ideas.

    tjaekelAuthor
    Visitor II
    March 28, 2024

    Meanwhile I think: the CubeMX generated code - even I tweak for best match of 48 KHz as audio frequency in CubeMX - is completely wrong. I get just 48 KHz (or 96 KHz) as bit clock on SPDIF signal (instead of 3,072 KHz or doubled). It does not make any sense. Also this "trick" to set 96KHz as audio frequency for SPDIF makes me suspicious.

    Is there any example for SPDIF on STM32U5xx MCU?
    (no need to reference SPDIF sample code for STM32F769 etc. - I have this working since years).