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-10 08:11 AM
The latest version of code (might be useful for STMicro FAE):
Master side:
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 = value++;
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(...);
SPI1->CFG2 |= SPI_CFG2_COMM_1;
SPI1->CFG2 &= ~SPI_CFG2_COMM_0;
SPI1->CR2 = 4;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
while ( (SPI1->SR & SPI_SR_EOT) == 0 );
err_reception = 0;
resp = SPI1->RXDR;
if ( resp == 0x77777777 )
{
compt_err ++;
}
else
{
if ( resp != 0x9ABCDEFF)
__NOP();
}
if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
{
return ( SPI1->SR & SPI_SR_ERR_MASK);
}
// clear flags
SPI1->IFCR = 0xFFFFFFFF;
// disable SPI
SPI1->CR1 &= ~SPI_CR1_SPE;
Delay(...);
Slave side:
while(1)
{
crc=0;
Spi->CFG2 |= SPI_CFG2_COMM_1;
Spi->CFG2 &= ~SPI_CFG2_COMM_0;
Spi->CFG1 = ((Spi->CFG1) & ~SPI_CFG1_FTHLV) | SPI_FIFO_THRESHOLD_04DATA;
Spi->CR2 = 4;
Spi->CFG1 |= SPI_CFG1_CRCEN;
Spi->UDRDR = 0x66666666;
Spi->CR1 |= SPI_CR1_SPE;
while((Spi->SR & SPI_SR_EOT) == 0);
cmd = Spi->RXDR;
crc = Spi->RXCRC;
if (cmd != expected_value)
{
erreur ++;
}
else
{
erreur = 0;
}
expected_value++;
if (erreur >=2)
__NOP();
if (Spi->SR & SPI_SR_CRCE)
while(1);
Spi->IER = 0;
Spi->IFCR = 0xFFFFFFFF;
Spi->CR1 &= ~SPI_CR1_SPE;
Spi->CFG2 |= SPI_CFG2_COMM_0;
Spi->CFG2 &= ~SPI_CFG2_COMM_1;
Spi->CR2 = 4;
Spi->CFG1 |= SPI_CFG1_CRCEN;
Spi->CFG1 = ((Spi->CFG1) & ~SPI_CFG1_FTHLV) | SPI_FIFO_THRESHOLD_04DATA;
Spi->CR1 |= SPI_CR1_SPE;
if (erreur)
Spi->TXDR = 0x77777777;
else
Spi->TXDR = 0x9ABCDEFF;
while((Spi->SR & SPI_SR_EOT) == 0);
Spi->IER = 0;
Spi->IFCR = 0xFFFFFFFF;
Spi->CR1 &= ~SPI_CR1_SPE;
}
2020-06-11 12:52 AM
Same behaviour in full duplex mode. Same behaviour with slower spi_pclk on slave side.
BTW the ref manual is really not clear regarding the SPI clocks:
"There is no specific constraint concerning the frequency ratio between these clock signals. The user
has to consider a ratio compatible with the data flow speed in order to avoid any data
underrun or overrun events only."
No constraint but you have to consider the good ratio ???
And, later:
"That is why the SPI slave is able to receive and send data even when the spi_pclk and spi_ker_ck clock
signals are inactive.'"
As far as I understood you activate spi_pclk by setting the bit SPI1EN in RCC_APB2_ENR. If you don't do that you can't even write SPI1 registers, so how can you receive anything in slave mode if the spi_pclk is not active ?
2020-06-11 02:26 AM
> No constraint but you have to consider the good ratio ???
There is no specific constraint like 1 MHz or 1:128 pclk:ker_ck, but if the FIFO is filled up in the time needed to even read SR and DR (+ software overhead), you lose.
> you activate spi_pclk by setting the bit SPI1EN in RCC_APB2_ENR. If you don't do that you can't even write SPI1 registers
Set RCC_APB2ENR_SPI1EN, write SPI registers, clear RCC_APB2ENR_SPI1EN. It might also mean that RCC_APB2ENR_SPI1EN remains set, but the source of pclk is stopped.
I guess you could then put the MCU in a low-power mode stopping HSE/HSI, and wake up on the next NSS rising edge if the received packet is expected to fit in the FIFO, falling edge if it isn't.