cancel
Showing results for 
Search instead for 
Did you mean: 

why is the following SPI implementation not working?

torsten2
Associate II
Posted on May 08, 2015 at 22:03

Hello,

I have trouble to communicate with a BlueNRG device. I�ve tracked it down to my SPI implementation. Assumed I want to have a �usual�, full duplex SPI master interface on a STM32L152, using CMSIS I initialize the interface as follows: 

    void configure_spi( SPI_TypeDef* spi )

    {

        spi->CR1 = 0 ;

        // fPCLK/4 | master | enabled

        spi->CR1 |=

              SPI_CR1_SSI

            | SPI_CR1_SSM

            | SPI_CR1_MSTR;

        spi->SR = 0;

        spi->CR1 |= SPI_CR1_SPE;

    }

According to Figure 247 in the reference manual on page 755, there is some kind of double buffering and the software should start the transaction with two writes and end the transaction with 2 reads:

    void spi::transmit_receive( const std::uint8_t* out_buffer, std::size_t out_buffer_size, std::uint8_t* in_buffer, std::size_t in_buffer_size )

    {

        assert( out_buffer_size != 0 || in_buffer_size != 0 );

        const std::uint8_t* const out_end = out_buffer + out_buffer_size;

              std::uint8_t* const in_end  = in_buffer + in_buffer_size;

        out_buffer = write_byte( out_buffer, out_end );

        for ( std::size_t bytes_transfered = std::max( out_buffer_size, in_buffer_size ) - 1; bytes_transfered; �-bytes_transfered )

        {

            out_buffer = write_byte( out_buffer, out_end );

            in_buffer = read_byte( in_buffer, in_end );

        }

        read_byte( in_buffer, in_end );

    }

With write_byte and read_byte looking like this:

    static const std::uint8_t* write_byte( const std::uint8_t* out_buffer, const std::uint8_t* out_end )

    {

        while ( used_spi->SR & I2C_SR1_TXE == 0 )

            ;

        if ( out_buffer != out_end )

        {

            used_spi->DR = *out_buffer;

            ++out_buffer;

        }

        else

        {

            used_spi->DR = 0;

        }

        return out_buffer;

    }

    static std::uint8_t* read_byte( std::uint8_t* in_buffer, std::uint8_t* in_end )

    {

        while ( used_spi->SR & I2C_SR1_RXNE == 0 )

            ;

        if ( in_buffer != in_end )

        {

            *in_buffer = used_spi->DR;

            ++in_buffer;

        }

        else

        {

            volatile std::uint8_t dummy = used_spi->DR;

            static_cast< void >( dummy );

        }

        return in_buffer;

    }

Do I misread the diagram? With this implementation, the result looks like I�m missing at least the second byte on the receiving side.

kind regards,

Torsten

#spi-double-buffer
3 REPLIES 3
mckenney
Senior
Posted on May 11, 2015 at 15:29

>

  while ( used_spi->SR & I2C_SR1_RXNE == 0 )

This is a big part of the problem. Since I2C_SR1_RXNE is not (ever) == 0, this loop never waits at all, so you're fetching the Rx byte before it's there. Also I2C_SR1_RXNE != SPI_SR_RXNE, i.e. you're checking the wrong bit.

There are analogous problems in your TXE test, but with only 2 bytes in your transaction you probably haven't tripped over them yet.

torsten2
Associate II
Posted on May 11, 2015 at 15:41

Hi Bruce,

thank you very much. Looks like I got the operator precedence wrong :(

cheers,

Torsten
torsten2
Associate II
Posted on May 11, 2015 at 17:56

And in addition, I've used the wrong macros for the bits to test. Two lines of code with 4 bugs :(