Skip to main content
Graduate II
June 2, 2025
Question

Trying to connect a WCMCU-5102 (PCM-5102) to STM32H503CBU6

  • June 2, 2025
  • 2 replies
  • 875 views

I successfully configured the I2S2 interface to a sample rate of 48KHz. The attached WCMCU-5102 is connected to these three wires.

I2S2_WS = PA3
I2S2_CK = PA5
I2S2_SDO = PB1

FLT = GND
DMP = GND
SCL = GND
FMT = GND

XMT = Vdd (3.3V)

 

static void MX_I2S2_Init(void)
{

 /* USER CODE BEGIN I2S2_Init 0 */

 /* USER CODE END I2S2_Init 0 */

 /* USER CODE BEGIN I2S2_Init 1 */

 /* USER CODE END I2S2_Init 1 */
 hi2s2.Instance = SPI2;
 hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
 hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
 hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
 hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
 hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_48K;
 hi2s2.Init.CPOL = I2S_CPOL_LOW;
 hi2s2.Init.FirstBit = I2S_FIRSTBIT_MSB;
 hi2s2.Init.WSInversion = I2S_WS_INVERSION_DISABLE;
 hi2s2.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_RIGHT;
 hi2s2.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;
 if (HAL_I2S_Init(&hi2s2) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN I2S2_Init 2 */

 /* USER CODE END I2S2_Init 2 */

}

 

main.c has:

uint16_t test_amplitudes[] = {0,0,65535,65535};
 while (1)
 {
	 HAL_I2S_Transmit(&hi2s2, test_amplitudes, 4 ,5000);
 }

 

My questions:

why don't I see any analog signal? I tested two devices to no avail.
The protocol sends two signals in one sample (L and R, during the I2S2_WS signal high/low phase).
How are the values organized in storage (RAM)? I'm assuming in sequence L,R,L,R ...

I'd be glad someone could enlighten me.

    This topic has been closed for replies.

    2 replies

    chriskukuAuthor
    Graduate II
    June 3, 2025

    I'm having signal! My code looks like

     

    uint16_t sine_amplitudes[] = {
    0 , 0 ,
    804 , 804 ,
    1607 , 1607 ,
    2410 , 2410 ,
    3211 , 3211 ,
    4011 , 4011 ,
    4807 , 4807 ,
    5601 , 5601 ,
    6392 , 6392 ,
    7179 , 7179 ,
    7961 , 7961 ,
    8739 , 8739 ,
    9511 , 9511 ,
    10278 , 10278 ,
    11038 , 11038 ,
    11792 , 11792 ,
    12539 , 12539 ,
    13278 , 13278 ,
    14009 , 14009 ,
    14732 , 14732 ,
    15446 , 15446 ,
    16150 , 16150 ,
    16845 , 16845 ,
    17530 , 17530 ,
    18204 , 18204 ,
    18867 , 18867 ,
    19519 , 19519 ,
    20159 , 20159 ,
    20787 , 20787 ,
    21402 , 21402 ,
    22004 , 22004 ,
    22594 , 22594 ,
    23169 , 23169 ,
    23731 , 23731 ,
    24278 , 24278 ,
    24811 , 24811 ,
    25329 , 25329 ,
    25831 , 25831 ,
    26318 , 26318 ,
    26789 , 26789 ,
    27244 , 27244 ,
    27683 , 27683 ,
    28105 , 28105 ,
    28510 , 28510 ,
    28897 , 28897 ,
    29268 , 29268 ,
    29621 , 29621 ,
    29955 , 29955 ,
    30272 , 30272 ,
    30571 , 30571 ,
    30851 , 30851 ,
    31113 , 31113 ,
    31356 , 31356 ,
    31580 , 31580 ,
    31785 , 31785 ,
    31970 , 31970 ,
    32137 , 32137 ,
    32284 , 32284 ,
    32412 , 32412 ,
    32520 , 32520 ,
    32609 , 32609 ,
    32678 , 32678 ,
    32727 , 32727 ,
    32757 , 32757 ,
    32767 , 32767 ,
    32757 , 32757 ,
    32727 , 32727 ,
    32678 , 32678 ,
    32609 , 32609 ,
    32520 , 32520 ,
    32412 , 32412 ,
    32284 , 32284 ,
    32137 , 32137 ,
    31970 , 31970 ,
    31785 , 31785 ,
    31580 , 31580 ,
    31356 , 31356 ,
    31113 , 31113 ,
    30851 , 30851 ,
    30571 , 30571 ,
    30272 , 30272 ,
    29955 , 29955 ,
    29621 , 29621 ,
    29268 , 29268 ,
    28897 , 28897 ,
    28510 , 28510 ,
    28105 , 28105 ,
    27683 , 27683 ,
    27244 , 27244 ,
    26789 , 26789 ,
    26318 , 26318 ,
    25831 , 25831 ,
    25329 , 25329 ,
    24811 , 24811 ,
    24278 , 24278 ,
    23731 , 23731 ,
    23169 , 23169 ,
    22594 , 22594 ,
    22004 , 22004 ,
    21402 , 21402 ,
    20787 , 20787 ,
    20159 , 20159 ,
    19519 , 19519 ,
    18867 , 18867 ,
    18204 , 18204 ,
    17530 , 17530 ,
    16845 , 16845 ,
    16150 , 16150 ,
    15446 , 15446 ,
    14732 , 14732 ,
    14009 , 14009 ,
    13278 , 13278 ,
    12539 , 12539 ,
    11792 , 11792 ,
    11038 , 11038 ,
    10278 , 10278 ,
    9511 , 9511 ,
    8739 , 8739 ,
    7961 , 7961 ,
    7179 , 7179 ,
    6392 , 6392 ,
    5601 , 5601 ,
    4807 , 4807 ,
    4011 , 4011 ,
    3211 , 3211 ,
    2410 , 2410 ,
    1607 , 1607 ,
    804 , 804 ,
    0 , 0 ,
    -804 , -804 ,
    -1607 , -1607 ,
    -2410 , -2410 ,
    -3211 , -3211 ,
    -4011 , -4011 ,
    -4807 , -4807 ,
    -5601 , -5601 ,
    -6392 , -6392 ,
    -7179 , -7179 ,
    -7961 , -7961 ,
    -8739 , -8739 ,
    -9511 , -9511 ,
    -10278 , -10278 ,
    -11038 , -11038 ,
    -11792 , -11792 ,
    -12539 , -12539 ,
    -13278 , -13278 ,
    -14009 , -14009 ,
    -14732 , -14732 ,
    -15446 , -15446 ,
    -16150 , -16150 ,
    -16845 , -16845 ,
    -17530 , -17530 ,
    -18204 , -18204 ,
    -18867 , -18867 ,
    -19519 , -19519 ,
    -20159 , -20159 ,
    -20787 , -20787 ,
    -21402 , -21402 ,
    -22004 , -22004 ,
    -22594 , -22594 ,
    -23169 , -23169 ,
    -23731 , -23731 ,
    -24278 , -24278 ,
    -24811 , -24811 ,
    -25329 , -25329 ,
    -25831 , -25831 ,
    -26318 , -26318 ,
    -26789 , -26789 ,
    -27244 , -27244 ,
    -27683 , -27683 ,
    -28105 , -28105 ,
    -28510 , -28510 ,
    -28897 , -28897 ,
    -29268 , -29268 ,
    -29621 , -29621 ,
    -29955 , -29955 ,
    -30272 , -30272 ,
    -30571 , -30571 ,
    -30851 , -30851 ,
    -31113 , -31113 ,
    -31356 , -31356 ,
    -31580 , -31580 ,
    -31785 , -31785 ,
    -31970 , -31970 ,
    -32137 , -32137 ,
    -32284 , -32284 ,
    -32412 , -32412 ,
    -32520 , -32520 ,
    -32609 , -32609 ,
    -32678 , -32678 ,
    -32727 , -32727 ,
    -32757 , -32757 ,
    -32767 , -32767 ,
    -32757 , -32757 ,
    -32727 , -32727 ,
    -32678 , -32678 ,
    -32609 , -32609 ,
    -32520 , -32520 ,
    -32412 , -32412 ,
    -32284 , -32284 ,
    -32137 , -32137 ,
    -31970 , -31970 ,
    -31785 , -31785 ,
    -31580 , -31580 ,
    -31356 , -31356 ,
    -31113 , -31113 ,
    -30851 , -30851 ,
    -30571 , -30571 ,
    -30272 , -30272 ,
    -29955 , -29955 ,
    -29621 , -29621 ,
    -29268 , -29268 ,
    -28897 , -28897 ,
    -28510 , -28510 ,
    -28105 , -28105 ,
    -27683 , -27683 ,
    -27244 , -27244 ,
    -26789 , -26789 ,
    -26318 , -26318 ,
    -25831 , -25831 ,
    -25329 , -25329 ,
    -24811 , -24811 ,
    -24278 , -24278 ,
    -23731 , -23731 ,
    -23169 , -23169 ,
    -22594 , -22594 ,
    -22004 , -22004 ,
    -21402 , -21402 ,
    -20787 , -20787 ,
    -20159 , -20159 ,
    -19519 , -19519 ,
    -18867 , -18867 ,
    -18204 , -18204 ,
    -17530 , -17530 ,
    -16845 , -16845 ,
    -16150 , -16150 ,
    -15446 , -15446 ,
    -14732 , -14732 ,
    -14009 , -14009 ,
    -13278 , -13278 ,
    -12539 , -12539 ,
    -11792 , -11792 ,
    -11038 , -11038 ,
    -10278 , -10278 ,
    -9511 , -9511 ,
    -8739 , -8739 ,
    -7961 , -7961 ,
    -7179 , -7179 ,
    -6392 , -6392 ,
    -5601 , -5601 ,
    -4807 , -4807 ,
    -4011 , -4011 ,
    -3211 , -3211 ,
    -2410 , -2410 ,
    -1607 , -1607 ,
    -804 , -804 ,
    };
     int size= sizeof(sine_amplitudes)/sizeof(sine_amplitudes[0]);
    
     /* USER CODE END 2 */
    
     /* Infinite loop */
     /* USER CODE BEGIN WHILE */
     while (1)
     {
    	 //HAL_I2S_Transmit_DMA(&hi2s2, sine_amplitudes, size/2);
    	 HAL_I2S_Transmit(&hi2s2, sine_amplitudes, size,5000);
    	 //HAL_I2S_Transmit(&hi2s2, test_amplitudes, 4 ,5000);
     /* USER CODE END WHILE */
    
     /* USER CODE BEGIN 3 */
     }
     /* USER CODE END 3 */

     

    The timing looks also ok. 48KHz.
    Now my idea is, not to use HAL_I2S_Transmit but use DMA.
    I would like to have two adjacent 16bit RAM locations L,R, and configure a DMA continously running at 48KHz, reading exactly this one RAM location (two, since stereo) with one 32bit gulp and writing it to the output stream of the I2S. So the DMA transfersize would be 1 32bit word. While the DMA is continously writing out the RAM location to the DAC it is the purpose of another process, to update that RAM location with the actual data (from a lookup table e.g.).

    Is the principle correct from the design standpoint? And how would I have to configure the DMA? I don't want to use interrupts or high water marks or such things. Just a  DMA hard as bone continously running.

     

    Ideas, comments?

    Graduate II
    June 3, 2025

    I've never used I²S, but I do use DMA for other things.

    Had the DMA controller in your chosen device had a double-buffer mode, this would have been super simple. Alas, it doesn't.

    However, it does have a linked-list functionality, where you can create a number of different transfers. The linked-list data structure contains a number of settings for the DMA controller, plus the address of the next data structure. (This is what makes it "linked".) When the DMA controller has completed the transfer described in the first linked-list data structure, it loads the data from the second structure and continues from there. This can also be set to run in circular mode, which is what you need.

    My suggestion is that you assign two memory chunks (e.g. in the form of arrays) for the audio data, and then create a linked list, with one linked-list data structure for each audio memory chunk. Then, while the DMA transfer is running, your main code needs to keep track of where the DMA controller is currently getting its data from. I imagine that you can poll the SAR (Source Address Register) to determine this, although I am not completely sure. When your code sees that the DMA controller has switched from audio data chunk 1 to chunk 2, your code can then update the contents of chunk 1. And vice versa.

    Now, this isn't something I've tried myself, but it is the best I've come up with by reading the datasheet for your device. It is possible that there is a simpler solution I've overlooked. However, provided that you can build the linked-list data structure correctly, it doesn't seem to be overly complicated.

    chriskukuAuthor
    Graduate II
    June 3, 2025

    Thanks, @EThom.3 , for your ideas. It would be weird to me to present the data in an array. My idea is

    to write all data in one (due to stereo actually two) memory location(s).  The data appears there like the landscape passes by when you look through a train waggon window (metaphorically speaking).

    Assume the DMA is configured such that it always writes out (samples with the inherent 48KHz rate) that (summing point) memory location representing all analog data summed up from various sources in the application (as a digital sample value). 

    Supplying buffers before putting them out is simply too weird with all the timing constraints etc. I know well the double buffering technique from a data acquisition project I built in the 80ies (using a 68000 ) where data had to be collected at high speed.

    Please, think again about my idea of using a DMA of size 1.

    Graduate II
    June 3, 2025

    Hello @chriskuku 

    Oh... so your code is actually generating audio on-the-fly, rather than playing something predetermined? If that is the case, then you truly won't have much of a buffer. Yeah, I definitely missed something there, but now I think I understand what you are trying to do.

    My understanding is that you want to continuously dump samples in a specific memory location. Whenever there is room in the I²S transmit buffer, you want the DMA to transfer whatever is in that memory location to the I²S transmit buffer. Is that correctly understood?

    For this to work, I guess that you would need to set the DMA controller not to increment the source address (easy enough), and to run continuously. An infinite burst, if you like. However, I can't figure out if the latter is possible.

    I also tried to see if I²S Tx Buffer "not full" could generate an event that could trigger the DMA controller, but couldn't find anything like that.

    There might be a different solution. If the memory location you keep writing the samples to happens to be the transmit data register (TXDR), then I imagine that the sample will enter the transmit buffer only if there is an empty space in it. If there isn't, I guess that the sample is ignored. If this works, then I believe that you get much the same result as with a continuously working DMA controller.

    No guarantees, though.