AnsweredAssumed Answered

SPI receives bytes twice when switching 8/16bit

Question asked by Sven Pauli on Feb 20, 2018
Latest reply on Feb 22, 2018 by waclawek.jan

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++ statements below (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

Outcomes