Interesting, that is on the F7 which has a FIFO but flushing the data register after stopping the master clock and DMAs could be a solution. Another option would be to reset the SPI peripheral on i2sStart().
Giovanni
Restarting an I2S transfer changes alignement in DMA RX buffer
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
-
- Posts: 35
- Joined: Fri Feb 09, 2018 12:44 am
- Has thanked: 2 times
- Been thanked: 5 times
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
I've managed to recover from this problem. Before starting DMA, I explicitly fetch a few half-words from the I2S data register, and find where the sample start sequence begins. Unfortunately, this solution is not universal: is works for my particular codec chip
Regards,
Vladimir
Regards,
Vladimir
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
Hello,
This is my first post in this forum
I think I just ran into this same problem though I did not fully characterized the issue on an STM32F429 (discovery board) and in RX mode.
I found those two links that seem related:
https://community.st.com/s/question/0D5 ... hing-issue
https://community.st.com/s/question/0D5 ... er-restart
So it seems that the issue comes from the fact that when I2S is turned off some data may still arrive into the DR register and those data will get as first data for next reception and shift all the data by 1 channel.
Also I found that ST dedicated a paragraph on "Disabling the SPI" because it is probably a bit tricky and I suppose it applies to the I2S even more.
The interresting sequence they describe is as follow:
- wait for RXNE=1 (this will be the second to last data receive)
- disable the SPI : SPE=0
- wait for RXNE=1 (this is the last)
And this is how I changed the stop_exchange routine:
What I believe it could happen before is that:
- Disable SPI : SPE=0
- Disable DMA before that last data arrive
- The last data may arrive but nobody to discard it and let the pipeline clean for next exchange
What I believe it fixes the issue:
- Disable DMA
- Wait for last data (so that we are sure it is here and perhaps preventing other to arrive as there is no buffer)
- Disable SPI : SPE=0
- Drain the last data
This seems to work for me.
Again I don't fully understand what's happening only suppositions.
This is my first post in this forum
I think I just ran into this same problem though I did not fully characterized the issue on an STM32F429 (discovery board) and in RX mode.
I found those two links that seem related:
https://community.st.com/s/question/0D5 ... hing-issue
https://community.st.com/s/question/0D5 ... er-restart
So it seems that the issue comes from the fact that when I2S is turned off some data may still arrive into the DR register and those data will get as first data for next reception and shift all the data by 1 channel.
Also I found that ST dedicated a paragraph on "Disabling the SPI" because it is probably a bit tricky and I suppose it applies to the I2S even more.
The interresting sequence they describe is as follow:
- wait for RXNE=1 (this will be the second to last data receive)
- disable the SPI : SPE=0
- wait for RXNE=1 (this is the last)
And this is how I changed the stop_exchange routine:
Code: Select all
void i2s_lld_stop_exchange(I2SDriver *i2sp) {
/* Stop TX DMA, if enabled.*/
if (NULL != i2sp->dmatx) {
dmaStreamDisable(i2sp->dmatx);
/* From the RM: To switch off the I2S, by clearing I2SE, it is mandatory
to wait for TXE = 1 and BSY = 0.*/
while ((i2sp->spi->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE)
;
}
/* Stop RX DMA, if enabled.*/
if (NULL != i2sp->dmarx) {
dmaStreamDisable(i2sp->dmarx);
while ((i2sp->spi->SR & SPI_SR_RXNE) != SPI_SR_RXNE);
}
/* Stop SPI/I2S peripheral.*/
i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE;
/* Drain the last DATA to clean for next exchange */
if (NULL != i2sp->dmarx) {
while (i2sp->spi->SR & SPI_SR_RXNE)
i2sp->spi->DR;
}
}
What I believe it could happen before is that:
- Disable SPI : SPE=0
- Disable DMA before that last data arrive
- The last data may arrive but nobody to discard it and let the pipeline clean for next exchange
What I believe it fixes the issue:
- Disable DMA
- Wait for last data (so that we are sure it is here and perhaps preventing other to arrive as there is no buffer)
- Disable SPI : SPE=0
- Drain the last data
This seems to work for me.
Again I don't fully understand what's happening only suppositions.
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
Hi,
Thanks for experimenting with this, I need to look at that paragraph.
The fix makes sense, I wonder if it should go in the SPI driver too when stopping a continuous transfer.
Giovanni
Thanks for experimenting with this, I need to look at that paragraph.
The fix makes sense, I wonder if it should go in the SPI driver too when stopping a continuous transfer.
Giovanni
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
Hi,
I committed the change in trunk, just removed an extra condition, could you check everything is OK?
Giovanni
I committed the change in trunk, just removed an extra condition, could you check everything is OK?
Code: Select all
void i2s_lld_stop_exchange(I2SDriver *i2sp) {
/* Stop TX DMA, if enabled.*/
if (NULL != i2sp->dmatx) {
dmaStreamDisable(i2sp->dmatx);
/* From the RM: To switch off the I2S, by clearing I2SE, it is mandatory
to wait for TXE = 1 and BSY = 0.*/
while ((i2sp->spi->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE)
;
/* Stop SPI/I2S peripheral.*/
i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE;
}
/* Stop RX DMA, if enabled then draining the RX DR.*/
if (NULL != i2sp->dmarx) {
dmaStreamDisable(i2sp->dmarx);
/* Waiting for some data to be present in RX DR.*/
while ((i2sp->spi->SR & SPI_SR_RXNE) != SPI_SR_RXNE)
;
/* Stop SPI/I2S peripheral.*/
i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE;
/* Purging data in DR.*/
while ((i2sp->spi->SR & SPI_SR_RXNE) != 0)
(void) i2sp->spi->DR;
}
}
Giovanni
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
Hello Giovanni I am sorry I completely forgot about this and did not see the notifications...
I cannot try to run your code immediately but I have been running my version of the change since my first post and it is working when I can very easily reproduce the old issue.
BUT I am only using 1 I2S driver in RX and 1 I2S driver in TX
Carefully reading you code comparing to my patch makes me think that it is absolutely the same IF the I2S driver is exclusively either in RX or TX mode.
I believe that if the driver could operate in both TX and RX mode this would not work, but double checking the Ref Manual there is no RX+TX (full duplex) mode in I2S. Well ... there is but it is done via a I2S_ext peripherals which I think are kind of "virtual". I don't know what is your intention but I think the current version of the driver is only supporting Half-Duplex (only RX or TX) as you don't seem to play with the I2S_ext peripheral.
So if the driver is meant only to operate either the RX or the TX mode then your patch looks fine to me.
Again really sorry for the delay here...
JP:
I cannot try to run your code immediately but I have been running my version of the change since my first post and it is working when I can very easily reproduce the old issue.
BUT I am only using 1 I2S driver in RX and 1 I2S driver in TX
Carefully reading you code comparing to my patch makes me think that it is absolutely the same IF the I2S driver is exclusively either in RX or TX mode.
I believe that if the driver could operate in both TX and RX mode this would not work, but double checking the Ref Manual there is no RX+TX (full duplex) mode in I2S. Well ... there is but it is done via a I2S_ext peripherals which I think are kind of "virtual". I don't know what is your intention but I think the current version of the driver is only supporting Half-Duplex (only RX or TX) as you don't seem to play with the I2S_ext peripheral.
So if the driver is meant only to operate either the RX or the TX mode then your patch looks fine to me.
Again really sorry for the delay here...
JP:
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
Hi,
Don't worry. Let me know if you have a chance to test it.
The driver does not support full duplex mode so it should be fine.
Giovanni
Don't worry. Let me know if you have a chance to test it.
The driver does not support full duplex mode so it should be fine.
Giovanni
- Giovanni
- Site Admin
- Posts: 14444
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1074 times
- Been thanked: 921 times
- Contact:
Re: Restarting an I2S transfer changes alignement in DMA RX buffer
Any news on this?
Giovanni
Giovanni
Last bumped by Giovanni on Sat Feb 13, 2021 10:33 am.
Who is online
Users browsing this forum: No registered users and 10 guests