cancel
Showing results for 
Search instead for 
Did you mean: 

SPI receives bytes twice when switching 8/16bit

Sven Pauli
Associate II
Posted on February 20, 2018 at 19:26

On an STM32F103RB, after switching the SPI1 from 8 bit to 16 bit the last byte received while still in 8 bit mode is received again in 16 bit mode.

SPI1 is configured as a plain master as follows:

 RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
 SPI1->CR2 = 0;
 SPI1->CR1 =
 SPI_CR1_SSM |
 SPI_CR1_SSI |
 SPI_CR1_SPE |
 BR_IDENT |
 SPI_CR1_MSTR;�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

When then calling the rxblock() function below, the byte that has already been received in token() (as the first non-0xFF byte) is received once again in rxblock() and stored to the first location in *data. I verified this using a logic analyzer and found out that the byte is definitely not transmitted twice. For instance, a typical stream of bytes on MISO looks like this:

FF FF FF AA 00 00 00 00 00 00 00 00 00 ....

So the first 0xFF are discarded by token() until it receives and returns the 0xAA. The rxblock() should then only store a range of 0x00 to *data, but in fact I end up with the first byte of *data being 0xAA...

Even more funny, when I uncomment the sequence of bogus i++ statementsbelow (i being volatile to have an effect) everything works as expected and *data only contains 0x00 bytes.

Relevant source code:

static void flush(void)
{
 /* Wait for SPI to complete */
 while (!(SPI1->SR & SPI_SR_TXE));
 while (SPI1->SR & SPI_SR_BSY);
 (void) SPI1->DR;
}
static uint8_t token(uint32_t timeout)
{
 flush();
 for (uint8_t retry = 0; retry < 100; retry++) {
 /* Send dummy 0xFF */
 while (!(SPI1->SR & SPI_SR_TXE));
 SPI1->DR = 0xFF;
 while (!(SPI1->SR & SPI_SR_RXNE));
 uint8_t response = SPI1->DR;
 if (response != 0xFF)
 return response;
 }
 /* Timeout */
 return 0xFF;
}
static bool rxblock(uint8_t *data, uint16_t n)
{
 /* Wait for data start token */
 if (token() != 0xAA)
 return false;
 //*
 volatile uint8_t i = 0;
 i++;
 i++;
 i++;
 i++;
 i++;
 i++; //*/
 /* Configure SPI in 16 bit mode with CRC */
 flush();
 SPI1->CR1 &= ~SPI_CR1_SPE;
 SPI1->CR1 |= (SPI_CR1_DFF | SPI_CR1_SPE);
 /* Transfer */
 (void) SPI1->DR;
 for (uint16_t transfers = n / 2; transfers--; ) {
 while ( !(SPI1->SR & SPI_SR_TXE) );
 SPI1->DR = 0xFFFF;
 while ( !(SPI1->SR & SPI_SR_RXNE) );
 uint16_t word = SPI1->DR;
 *data++ = word >> 8;
 *data++ = word;
 }
 /* Restore 8 bit mode */
 flush();
 SPI1->CR1 &= ~SPI_CR1_SPE;
 SPI1->CR1 &= ~SPI_CR1_DFF;
 SPI1->CR1 |= SPI_CR1_SPE;
 return true;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

What am I missing?

Regards,

Sven

1 REPLY 1
Posted on February 22, 2018 at 17:17

I haven't spent time to understand your problem fully, but if you have a suspicion that there is an internal flip/flop set which won't be cleared by disabling/enabling SPI, you may perhaps resort to extreme measures - resetting the SPI through the reset register in RCC.

JW