2020-06-08 05:02 AM
I've been struggling for quite a while now on my SPI setup.
The setup is as follows:
- SPI Slave is a Nucleo STM32H743 in simplex mode, clocks at maximum (sysclk 400MHz, hclck 200Mhz, APB clock 100MHz)
- SPI master is another identical Nucleo in simplex mode, clocks divided by 2: sysclk 200MHz, etc... and spi_ker_clk = 100MHz
- SPI master with a prescaler of 16 ie. clock SPI around 6MHz. CRC enabled. Data frame 8 bits. FIFO threshold 4 bytes
- SPI slave: CRC enabled, 8 bits. FIFO threshold 4 bytes
There is no Slave Select signal to control the slave.
Here is the code for the master. Everything done in polling and I've added some delay to let time to the Slave to work. The following function is called in loop, nothing else is done by the Master (in this simplified version that I used for debug):
uint32_t SPI_EnvoiCommandeTest(void)
{
uint32_t resp;
uint8_t statut;
SPI1->CFG2 |= SPI_CFG2_COMM_0;
SPI1->CFG2 &= ~SPI_CFG2_COMM_1;
SPI1->CR2 = 4;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
SPI1->TXDR = 0x12345678;
while ( (SPI1->SR & SPI_SR_EOT) == 0 );
if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
{
return ( SPI1->SR & SPI_SR_ERR_MASK);
}
SPI1->IFCR = 0xFFFFFFFF;
SPI1->CR1 &= ~SPI_CR1_SPE;
Delay(1000);
SPI1->CFG2 |= SPI_CFG2_COMM_1;
SPI1->CFG2 &= ~SPI_CFG2_COMM_0;
SPI1->CR2 = 5;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
while ( (SPI1->SR & SPI_SR_EOT) == 0 );
resp = SPI1->RXDR;
statut = *((__IO octet *)&(SPI1->RXDR));
if ( resp != 0x9ABCDEFF)
while(1);
if ( statut != 0x77)
while(1);
while ( (SPI1->SR & SPI_SR_EOT) == 0 );
if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
{
return ( SPI1->SR & SPI_SR_ERR_MASK);
}
SPI1->IFCR = 0xFFFFFFFF;
SPI1->CR1 &= ~SPI_CR1_SPE;
Delay(1000);
return 0;
}
For the Slave, the reception is done by the interrupt handler. The main thread is just waiting for a flag to be set (set by `SPI_StopReception()`) and send 5 bytes of answer.
static void SPI_GenericHandler(SpiId_e SpiId)
{
SPI_TypeDef *Spi = SpiMgt[SpiId].SpiInstance;
uint32_t trigger = Spi->IER & Spi->SR;
uint32_t cmd;
uint8_t stat;
if (trigger & SPI_SR_RXP)
{
cmd = Spi->RXDR;
if (cmd != 0x12345678)
while(1);
while((Spi->SR & SPI_SR_EOT) == 0);
if (Spi->SR & SPI_SR_CRCE)
while(1);
SPI_StopReception(SpiId);
}
}
(...)
My problem is the following.
The communication is working fine hundreds of thousands of times and then fails at Slave side: instead of reading bytes 78 56 34 12 from the SPI FIFO, I read for example 34 12 00 00 or 56 34 12 00.
At first glance one would say it is simply the Slave that is too slow and missed some bytes BUT what is weird is that:
- I get a RXP interrupt which means the Slave has detected correctly the SPI clock during 4 bytes and has sampled the 4 bytes.
- there is no CRC error which means the Slave received the correct bits. For example, when I read 56 34 12 00 from the FIFO the RXCRC was 0x08 which is the CRC of the complete frame 78 56 34 12
It is like there is a problem in the reading of the FIFO.
I've used a logic analyser and didn't identify any electrical issue.
Any idea or suggestion ?
2020-06-08 05:34 AM
Read out and post content of SPI registers at slave side.
Have you checked the errata?
JW
2020-06-08 05:50 AM
Yes I've checked the errata. The only one that applies to my setup is "Master data transfer stall at system clock much faster than SCK". I've implemented the woraround (disable SPI after EOT).
Registers at slave side at slave side, during a faulty reception (more precisely I break in the SPI RXP interrupt handler). In this occurence, I read 34 12 00 00
Values of CTSIZE and RXPLVL are not really consistent (at least I don't understand them): since FTHVL=3 (4-data) and TSIZE=4 (same at Master side) , when I get a RXP event, I should have received at least 4 bytes. I don't see how CTSIZE can be 1 , neither why there would be another byte left in the FIFO (RXPLVL=1).
2020-06-08 07:08 AM
I can't see anything wrong here. What's concerning indeed is that while FTHVL=3, it appears that you've read (i.e. had SPI_SR.RXP set) while the FIFO did *not* contain all 4 bytes.
Maybe this is related to the CRC somehow?
However, I don't have experience with 'H7 so I can't help with this more, sorry.
JW
2020-06-08 07:24 AM
Actually it's the exact same thing without CRC enabled.
Thank you !
2020-06-08 07:43 AM
If you've actually read out only 3 bytes from FIFO, shouldn't the 4th be still there - that would explain RXPLVL and CSIZE, indicating the RXP flag being set prematurely.
At this point, you might want to talk to ST directly, through web support fgorm or through FAE.
JW
2020-06-10 06:46 AM
Do you confirm that, on slave mode, since I don't need Slave Select, the value of bit SSI does not matter ? (SSM is 1)
2020-06-10 07:32 AM
When SSM=1 on the slave, it treats SSI exactly as a chip select/slave select line.
When SSM=1 and SSI=1, CLK and MOSI are ignored, MISO is not driven.
When SSM=1 and SSI=0, MISO is actively driven, bits are shifted in and out on CLK edges.
2020-06-10 07:42 AM
Thank you, crystal clear.
2020-06-10 07:59 AM
For the record, I've done many testing, and also checked my code with my colleagues and I'm starting to believe there might be a bug in the SPI peripheral in Slave mode.
I've simplified both slave and master softwares to do only SPI communication in polling. No interrupts or other tasks.
Each exchange consists of:
Once in a while, as I described, instead of reading for example 78 56 34 12 I read 34 12 00 00 or 12 00 00 00. There is no error status, neither CRCE nor UDR or OVR. The CRC (RXCRC) is always correct.
On 11 millions commands I've got around 100 errors.
I've tried to add a delay after the bit EOT is set. I tried to read byte by byte (LDRB instead of LDR). I've checked the assembly code. It is always the same thing.
I've entered a support request. Waiting for their answer.