STM32F407 – I2S data synchronization problem between PCM1808 and MCU - Page 1
Forum section: electronics / general

author: balu
author_rank: Regular Contributor
timestamp: January 17, 2021, 11:12:26 am
content: Hi,I’m working currently with a simple setup of a STM32F407 MCU and a PCM1808 audio ADC (two channel). The ADC is connected with the MCU via a I2S interface.The MCU is the master and the ADC is the slave. There is no problem with the I2S timing and clock setup within the MCU. The MCU can gather the ADC data for theleft and right channel with sample rates between 8kHz and 96kHz. The following first picture shows the setup and the following code fragment shows the way to gather240 samples.Code:uint16_t adc_buf; // PMC1808 value bufferHAL_StatusTypeDef adc_status; // I2S Statusint main(void){/* Init and config */while(1){/* Set output PB14 to trigger the logic analyser */TRIGGER_SET;/* Gather ADC data from PCM1808 via I2S interface */adc_status = HAL_I2S_Receive(&hi2s2, adc_buf, 240, 100);/* Reset PB14 and short delay for logic analyser */TRIGGER_RST;HAL_Delay(1);}}The current issue is the alignment of channel data in the memory. Sometimes the adc_buf starts with values for the left channel and sometimes the adc_buf will startwith right channel. Sometimes the ad_buf will start with a half word of a left or right channel. The following pictures 2 and 3 will show this behavior.The TRG signal is just a trigger for the logic analyzer. The second picture shows the output of a logic analyzer and a part of the debug output. In this picture adc_buf = 0x1bf and adc_buf = 0x1a00 this is the 24bit ADC value of the left channel (left aligned in a 32 word). The adc_buf = 0x1bf and adc_buf = 0x1c00 and this is the 24bit ADC value of the right channel.The third picture shows the data gathering result of a second scan. The value of ad_buf = 0x9200 and this is the value of the lower half-word of the right channel.I can not to ensure with channel will be the first one in a data gathering cycle. I need a defined alignment of the data for my project, because I will measure the signal level and phase shift of the two channel.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 17, 2021, 03:38:51 pm
content: I haven't used the F4 series so far so I don't know how HAL_I2S_Receive() is implemented. I've used the L4 series and there is no such function, it's called HAL_SAI_xxxx instead.But if the implementation is similar, what the function does is just read N words from the corresponding peripheral. There is no initialization of any kind. And what I don't know is how I2S works on the F4, but generally speaking, I2S is a continuous streaming protocol, so it's not like SPI. Once I2S streaming has started, it will stream continuously and clocking won't be triggered by data transfers (as it would work with SPI). Again I'm not sure how I2S is implemented on the F4, but this is how it works in general, so I'm guessing once the I2S peripheral is initiliazed, it will start streaming no matter what the data buffers contain (to be checked in the F4's ref manual of course.)I see no way of ensuring channel sync the way you do it here. So, either you should init. the I2S peripheral at each iteration prior to calling HAL_I2S_Receive() - which is kind of clumsy - or use DMA or interrupt reception instead of using the blocking version HAL_I2S_Receive().

author: balu
author_rank: Regular Contributor
timestamp: January 18, 2021, 02:54:36 pm
content: Thank you SiliconWizard.I have the same behavior if I use the DMA functionality for data transfer from I2S interface into memory.I have found a solution for my problem. It is not the best, but it will work. The code fragment will show my work around. With the execution of the dummy read withadc_status = HAL_I2S_Receive(&hi2s2, adc_buf, 2, 100) the I2S interface will start the master clock, bit clock and lef/right clock signal. After approx 8960/fs seconds (fs is the sample rate of the ADC) the ADC will start continuously converting the input voltage of the left and right channel. The MCU will wait for left channel data with two while-loops requesting the level of the I2S_WS (left/right clock signal) port. If the level goes from high to low the data gathering will start reading data from the left channel into the memory.Code:uint16_t adc_buf; // PMC1808 value bufferHAL_StatusTypeDef adc_status; // I2S Statusint main(void){// Init and config// Dummy read values from I2S to start the master clock, bit clock and left/right clock signaladc_status = HAL_I2S_Receive(&hi2s2, adc_buf, 2, 100);while(1){// Set output PB14 to trigger the logic analyserTRIGGER_SET;while (HAL_GPIO_ReadPin(I2S_WS_GPIO_Port, I2S_WS_Pin) == 0); // wait until I2S_WS = 1while (HAL_GPIO_ReadPin(I2S_WS_GPIO_Port, I2S_WS_Pin) == 1); // wait until I2S_WS = 0, start of left channel data// Gather ADC data from PCM1808 via I2S interface// read left channel data firstadc_status = HAL_I2S_Receive(&hi2s2, adc_buf, 240, 100);// Reset PB14 and short delay for logic analyserTRIGGER_RST;HAL_Delay(1);}}Not the best way to ensure to have the right data in the right memory place, but it works.

author: aheid
author_rank: Regular Contributor
timestamp: January 19, 2021, 10:15:54 pm
content: I've never used I2S before, but I have two observations.First, the inline documentaiton for HAL_I2S_Receive for the STM32F303 (current project I was in) states "The I2S is kept enabled at the end of transaction to avoid the clock de-synchronization between Master and Slave(example: audio streaming)."So the I2S peripheral will keep running while you HAL_Delay.From the STM32F407 reference manual it says "CHSIDE is updated after each reception. It is sensitive to the WS signal generated by the I2S cell."Sadly, the HAL_I2S_Receive does not let you know about the state of CHSIDE for the first sample in the buffer, because that would have been nice...I guess you could copy the HAL_I2S_Receive function, and make it return the state of the CHSIDE flag for the first sample it copies. Or you can check it before calling HAL_I2S_Receive and pray it doesn't change in the meantime (seems risky to me).How to sync this up in DMA I have no idea, I guess you need to just never let your code go out of sync, and if it does do a full I2S reset?