2020-11-13 09:33 PM
Dear Experts,
I ported I2S RX and TX program from F401 to F302R8 Nucleo board.
And I encountered **unexpected** phenomenon.
When I use I2S for receiving data (Half-duplex, Master-Receive mode, 48kHz, 24bit data on 32bit frame), it works fine.
That means, CubeMx initialize I2S as I2SxPR = 0x10b, I2SxCFGR = 0Xb03 and output frequencies are I2S_CK = 3.130 MHz and I2S_WS = 48.91kHz. (==> OK)
However, when I set it in Master-Transmit mode (rest are no change), CubeMx initialize I2S with the same parameter I2SxPR = 0x10b, I2SxCFGR = 0xa03 [changed as master-transmit], but OUTPUT FREQUENCIES are HALF of correct one. (I2S_CK = 1.535 MHz and I2S_WS = 23.99kHz). Master clock out is also changed from 11.99MHz to 6.143MHz.
And if I use lower sampling rate such as 16kHz or 8kHz, such frequencies deviate further. (some are one-third of correct one.)
I have not found any problem on F401 MCU like this.
Can anyone kindly let me know how I can avoid this phenomenon. (Or does STM issued any errata on this issue?).
Thanks for your kind advice in advance.
K.A.
Solved! Go to Solution.
2020-12-01 03:12 PM
Dear Mr. Houssem,
Thank you very much for your kind mail.
However, I found the reason of the article above last week.
The same program works "almost" perfectly on F401, but does not run on F302. And only difference between them were I2S clock (fs).
Program sends 48 set of data (L+R) to I2S output every 1mSec after receiving timer interrupt.
Although HAL has some overhead to start DMA (about 6 uSec), it will be a constant delay and out put data will be transmitted without break.
However, what I found was, I2S clock on STM32 MCUs (fs=48kHz in this case) has some error and it is not exactly 48kHz. (In this case -2.34% according to RM0365)
This means actual transmit time of I2S is longer than 1 mSec. (I2S clock error (-2.34%) means I2S needs 23.4 uSec longer time for 1 mSec of data transfer.)
Therefore,
1) 1st data (48 data set for 1 mSec) was sent out to I2S using DMA.
2) after 1 mSec, next timer interrupt invoked
3) 2nd data was sent to I2S using DMA, however, DMA or I2S is not READY and it returns error.
Like above, 1st, 3rd, 5th... data will be sent, but 2nd, 4th, 6th... data will fail. That is the reason of the article above, I suppose.
( I tried to check READY status of I2S before starting DMA, but as transfer time is always longer by 23.4 uSec and DMA overhead will be 6 uSec, it will be 30 uSec of time gap between data transfer. (And this will not work.) )
Looking into the timing of DMA transfer more precisely,
1) DMA will send 48 data set (32bit*2ch*48) to I2S as 192 of 16bit data (16bit * 2 * 2ch * 48), and DMA will complete (TCIF) when it wrote last data to I2S Data Register. And there will be a room of 5.2 uSec before starting next DMA transfer. (1msec/192 =5.2uSec)
However,
2) DMA transfer overhead time (using HAL) is about 6 uSec on F302 with 72MHz. And it will consume all time above. (When DMA transfer statement were started, and check the DMA status and/or I2S status, it is not READY.)
Therefore, we cannot use DMA_TransferCpltCallback() instead of Timer trigger.
After struggling a week or more, I finally found the Answer.
It is to set I2S (Tx) in DMA Circular mode.
To synchronize I2S (Rx) and I2S (Tx), after starting I2S (Rx) in DMA Circular mode, and RxHalfCpltCallback() was called first time, I started I2S (Tx) in DMA Circular mode.
After this, Rx and Tx are automatically executed by DMA (without any MCU program).
Only I need to was to process Input data and write it to Out Buffer in time for Tx.
Thank you,
K.A.
2020-12-01 02:02 AM
Dear @KAJIK.1 ,
Thank you for these details.
Could you please share your 2 .ioc files.
Kind regards,
Houssem
2020-12-01 03:12 PM
Dear Mr. Houssem,
Thank you very much for your kind mail.
However, I found the reason of the article above last week.
The same program works "almost" perfectly on F401, but does not run on F302. And only difference between them were I2S clock (fs).
Program sends 48 set of data (L+R) to I2S output every 1mSec after receiving timer interrupt.
Although HAL has some overhead to start DMA (about 6 uSec), it will be a constant delay and out put data will be transmitted without break.
However, what I found was, I2S clock on STM32 MCUs (fs=48kHz in this case) has some error and it is not exactly 48kHz. (In this case -2.34% according to RM0365)
This means actual transmit time of I2S is longer than 1 mSec. (I2S clock error (-2.34%) means I2S needs 23.4 uSec longer time for 1 mSec of data transfer.)
Therefore,
1) 1st data (48 data set for 1 mSec) was sent out to I2S using DMA.
2) after 1 mSec, next timer interrupt invoked
3) 2nd data was sent to I2S using DMA, however, DMA or I2S is not READY and it returns error.
Like above, 1st, 3rd, 5th... data will be sent, but 2nd, 4th, 6th... data will fail. That is the reason of the article above, I suppose.
( I tried to check READY status of I2S before starting DMA, but as transfer time is always longer by 23.4 uSec and DMA overhead will be 6 uSec, it will be 30 uSec of time gap between data transfer. (And this will not work.) )
Looking into the timing of DMA transfer more precisely,
1) DMA will send 48 data set (32bit*2ch*48) to I2S as 192 of 16bit data (16bit * 2 * 2ch * 48), and DMA will complete (TCIF) when it wrote last data to I2S Data Register. And there will be a room of 5.2 uSec before starting next DMA transfer. (1msec/192 =5.2uSec)
However,
2) DMA transfer overhead time (using HAL) is about 6 uSec on F302 with 72MHz. And it will consume all time above. (When DMA transfer statement were started, and check the DMA status and/or I2S status, it is not READY.)
Therefore, we cannot use DMA_TransferCpltCallback() instead of Timer trigger.
After struggling a week or more, I finally found the Answer.
It is to set I2S (Tx) in DMA Circular mode.
To synchronize I2S (Rx) and I2S (Tx), after starting I2S (Rx) in DMA Circular mode, and RxHalfCpltCallback() was called first time, I started I2S (Tx) in DMA Circular mode.
After this, Rx and Tx are automatically executed by DMA (without any MCU program).
Only I need to was to process Input data and write it to Out Buffer in time for Tx.
Thank you,
K.A.