cancel
Showing results for 
Search instead for 
Did you mean: 

Wrong RXCRC for SPI3 with 18Mhz

stst9184
Associate II
Posted on April 03, 2012 at 11:57

I've interesting behavior on STM32F105VC. I have SPI3(remmaped) master with SPI auto CRC calculation (RXCRC,TXCRC). For 9Mhz it is fine, for 18Mhz I receive all words correct, including

CRC, but RXCRC register is wrong and CRCERR is set. See below

                       18 MHz result

0690X00000602kUQAQ.jpg

Tx frame :

spiFrameWithoutCRC (matlab endian) = '0400050000000000000000000000000000000000000000000000000000000000000000000000';

calculated crc : 990B

txcrc: 990B

Rx frame:

spiFrameWithoutCRC( matlab endian) = '0100020000000000000000000000000000000000000000000000000000000000000000000000';

calculated crc: 5A0 (binary 10110100000)

rxcrc:              2D0 (binary 1011010000)

data register: 5A0

Here is just to be sure a 9MHz result

0690X00000602gjQAA.jpg

#crc16 #spi
7 REPLIES 7
stst9184
Associate II
Posted on April 03, 2012 at 16:18

One good news: If I switch off a slave and connect MISO with MOSI on master, everything is OK.

stst9184
Associate II
Posted on April 03, 2012 at 17:17

CRC (0x990B) halfwordword for MISO, MOSI from two STMs.

0690X00000602lXQAQ.jpg

A whole frame is send from both slave/master. But master has wrong RXCRC.

0690X00000602kZQAQ.jpg

Does somebody know what can be a reason?

stst9184
Associate II
Posted on April 03, 2012 at 19:08

0690X00000602keQAA.jpg

Is this a problem? Hier is a code to start a transmit, with some subfunctions. But it shall be enough:


void SPI_WriteExtBuf(

SPIDevice_t * pDev,

int16_t * const in, 

const int16_t *out, 

const int32_t size

)

{

DMA_Channel_TypeDef *DmaRx; /* DMA RX channel */

DMA_Channel_TypeDef *DmaTx; /* DMA TX channel */ 

SPI_TypeDef *pSpi; /* Spi peripherie */

uint16_t dummy; /* dummy variable */ 


DMAChannelUpdCfg_t DmaRxUpdCfg;

DMAChannelUpdCfg_t DmaTxUpdCfg;


assert(SPI_ValidDevice(pDev));

assert(SPI_ValidPeripherie(pDev->pSpi));

assert(pDev->pCfg);



DmaRx = pDev->pRxDma;

DmaTx = pDev->pTxDma;

pSpi = pDev->pSpi;


DMADev_ChannelDisable(DmaTx);

DMADev_ChannelDisable(DmaRx);

SPI_Cmd(pSpi, DISABLE);


DmaRxUpdCfg.enableInterrupts = DMA_IT_TC;

DmaRxUpdCfg.buffer = in;

DmaRxUpdCfg.size = size;

DmaRxUpdCfg.clearFlagMask = pDev->clearRxFlagMask;

DMADev_ChannelUpdate(DmaRx, &DmaRxUpdCfg);


DmaTxUpdCfg.enableInterrupts = 0;

DmaTxUpdCfg.buffer = (int16_t*)out;

if (pDev->pCfg->disableCrc == 0)

{

DmaTxUpdCfg.size = size-1;

}

else

{

DmaTxUpdCfg.size = size;

}

DmaTxUpdCfg.clearFlagMask = pDev->clearTxFlagMask;

DMADev_ChannelUpdate(DmaTx, &DmaTxUpdCfg);


/* Clear status flags */

//pSpi->DR = 0;

dummy = pSpi->DR;

pSpi->SR &= ~SPI_SR_CRCERR;

(void)dummy;


pSpi->CR1 &= ~(SPI_CR1_CRCNEXT | SPI_CR1_CRCEN);

/* Reenable CRC calculation and reset Rx&Tx CRC*/

if (pDev->pCfg->disableCrc == 0)

{

pSpi->CR1 |= SPI_CR1_CRCEN;

}


DMADev_ChannelEnable(DmaRx);

DMADev_ChannelEnable(DmaTx);


/* Soft chip select enable */ 

if (pDev->pCfg->fncSoftChipSelect)

{

pDev->pCfg->fncSoftChipSelect();

}


SPI_I2S_DMACmd(pSpi, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx , ENABLE);

SPI_Cmd(pSpi, ENABLE);


pDev->usageStatistic.cntTx++;


}

Posted on April 03, 2012 at 20:23

Can you present your data being sent in C, not MatLab format. I can't make sense of the computed CRC values.

The code fragment doesn't really touch on how you're waiting for the DMA to complete, and for the last bits to leave the SPI shift register. ie it's far too abstracted for anyone to independently test or evaluate.

Sounds like you have some timing/sequencing issues, to quote the TRM:

''Note: When the SPI is in slave mode, be careful to enable CRC calculation only when the clock is stable, that is, when the clock is in the steady state. If not, a wrong CRC calculation may be done. In fact, the CRC is sensitive to the SCK slave input clock as soon as CRCEN is set, and this, whatever the value of the SPE bit.

 

 

With high bitrate frequencies, be careful when transmitting the CRC. As the number of used CPU cycles has to be as low as possible in the CRC transfer phase, it is forbidden to call software functions in the CRC transmission sequence to avoid errors in the last data and CRC reception. In fact, CRCNEXT bit has to be written before the end of the transmission/reception of the last data.

 

 

For high bit rate frequencies, it is advised to use the DMA mode to avoid the degradation of the SPI speed performance due to CPU accesses impacting the SPI bandwidth.

 

 

When the STM32F10xxx are configured as slaves and the NSS hardware mode is used, the NSS pin needs to be kept low between the data phase and the CRC phase.

 

 

When the SPI is configured in slave mode with the CRC feature enabled, CRC calculation takes place even if a high level is applied on the NSS pin. This may happen for example in case of a multislave environment where the communication master addresses slaves alternately.

 

 

Between a slave deselection (high level on NSS) and a new slave selection (low level on NSS), the CRC value should be cleared on both master and slave sides in order to

 

resynchronize the master and slave for their respective CRC calculation.

 

 

To clear the CRC, follow the procedure below:

 

1. Disable SPI (SPE = 0)

 

2. Clear the CRCEN bit

 

3. Set the CRCEN bit

 

4. Enable the SPI (SPE = 1)''

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
stst9184
Associate II
Posted on April 04, 2012 at 10:38

Hi clive1,

I've removed an abstract level. It is a problem on the master side. The CRC is restarted, see below :


void SPI_WriteExtBuf2(

SPIDevice_t * pDev,

int16_t * const in, 

const int16_t *out, 

const int32_t size

)

{

DMA_Channel_TypeDef *DmaRx; /* DMA RX channel */

DMA_Channel_TypeDef *DmaTx; /* DMA TX channel */ 

SPI_TypeDef *pSpi; /* Spi peripherie */

uint16_t dummy; /* dummy variable */ 


DmaRx = DMA2_Channel1;

DmaTx = DMA2_Channel2;

pSpi = SPI3;


DMA_Cmd(DmaTx, DISABLE); 

DMA_Cmd(DmaRx, DISABLE);

SPI_Cmd(pSpi, DISABLE);


DMA_ClearITPendingBit(DMA2_IT_GL1);

DmaRx->CMAR = (u32)in;

DmaRx->CNDTR = (u16)size;

DMADev_ChannelSetIrqs(DmaRx, DMA_IT_TC, ENABLE);


DMA_ClearITPendingBit(DMA2_IT_GL2);

DmaTx->CMAR = (u32)out;

DmaTx->CNDTR = (u16)size-1;

DMADev_ChannelSetIrqs(DmaTx, 0, ENABLE);


/* Clear status flags */

//pSpi->DR = 0;

dummy = pSpi->DR;

pSpi->SR &= ~SPI_SR_CRCERR;

(void)dummy;


pSpi->CR1 &= ~(SPI_CR1_CRCNEXT | SPI_CR1_CRCEN);

/* Reenable CRC calculation and reset Rx&Tx CRC*/

pSpi->CR1 |= SPI_CR1_CRCEN;



DMA_Cmd(DmaRx, ENABLE);

DMA_Cmd(DmaTx, ENABLE);


/* Soft chip select enable */ 

GPIO_ResetBits(GPIOA, GPIO_Pin_8);


SPI_I2S_DMACmd(pSpi, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx , ENABLE);

SPI_Cmd(pSpi, ENABLE);


}

Th SPI frames (Tx/Rx are same): /* Last word is auto TXCRC */ uint16_t data[20] = { 4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; And here is a simplified version of the code for a master and slave: volatile uint32_t crcError = 0; volatile uint32_t rxOK = 0; volatile int32_t go = 1; /* Function implementation ---------------------------------------------------*/ int_T main(int_T argc, const char_T *argv[]) { volatile uint32_t x = 400; OS_vInitCriticalSection(); OS_vEnterCriticalSection(); /* Initialize ECU */ vECU_Init(); /* Last word is auto TXCRC */ uint16_t txdata[20] __attribute__ ((aligned(8))) = { 4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; uint16_t rxdata[20] __attribute__ ((aligned(8))) = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; volatile uint16_t rxCrc = 0; if (BspParams.BspNodeId == 0) { while(go) { rxdata[0] = 0; rxdata[1] = 0; rxdata[19] = 0; SPI_WriteExtBuf2(SPI_3, rxdata, txdata, 20); while((DMA2->ISR & 2) == 0); GPIO_SetBits(GPIOA, GPIO_Pin_8); SPI3->CR1 &= ~ SPI_CR1_CRCEN; if ((SPI3->SR & SPI_SR_CRCERR) == SPI_SR_CRCERR) { rxCrc = SPI3->RXCRCR; crcError++; } else { rxOK++; } while(x--); x = 400; } } else { while(go) { while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8) == 0); rxdata[0] = 0; rxdata[1] = 0; SPI_WriteExtBuf2(SPI_3, rxdata, txdata, 20); while((DMA2->ISR & 2) == 0); if ((SPI3->SR & SPI_SR_CRCERR) == SPI_SR_CRCERR) { crcError++; } else { rxOK++; } } if (rxCrc) { rxOK = 0; } } } 0690X00000602kjQAA.jpg
stst9184
Associate II
Posted on April 05, 2012 at 17:46

I am sending a same frame in both directions: tx16b[20] = {4,5,0,...0}, for tx last two contains a crc. I have debugger on a master. I've two breakpoints one before SPI enabled(Tx start) and one in the DMARx interrupt (TX finished). I see an interesting behavior of the RXCRC:

Step RXCRC

1      0x854D

2      0x8C87

3      0x0514

4      0x0514

...

I can reproduce this RXCRC error sequence. Again received data are ok:(

Posted on April 05, 2012 at 20:25

Well the 0x990B computation now make sense.

Problem here is I can't see how you're handling the DMA, and setting the SPI_CR1_CRCNEXT at the appropriate point in the transmission.

The ST example CRC code doesn't use DMA, but does some very specific sequencing of data, and setting of CRCNEXT after the last word signals TXE.

Less magic is involved on the RX side, provided the RXCRC is clear prior to reception, the value of RXCRC after the total payload including CRC (ie 0x990B) should be ZERO, indicating the data divided into the polynomial completely without a remainder.

My best guess is that you DMA the first 18 words, and in the TC interrupt you check the SPI TXE flags, and then SPI_I2S_SendData() the last data word (19), and then set the CRCNEXT, which will cause a 20th CRC word to be sent immediate afterward.

Also remember that TXCRCR will not contain a valid value until all 16 bits pending have been clocked through it. I would imagine if you read RXCRCR will also contain indeterminate data if all the in-bound bits have not come in completely, or it starts on another word.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..