Mackenzie Goodwin
Mackenzie Goodwin

Reputation: 33

STM32 SPI Driver Receiving Data Always 0

I'm trying to write an SPI driver using the stm32 LL libraries (for the STML4 system). I'm testing the SPI driver by writing 2 bytes to the MOSI line and listen for 1 byte on the MISO line. Using an oscilloscope I was able to verify the 2 bytes were correctly transmitted and the SPI slave device responds with a byte. See attached screenshot:

enter image description here

In the image, I am probing the SCLK and MISO lines. Clearly, the last 8 bits on the MISO line is 0b01110011 which is the expected data. The driver I wrote does not reflect this and is always reading a 0 from the SPI DR register. I'm having issues trying to run the code in debug mode due to the SPI peripheral (maybe I'm missing something) and can't output values using printf (don't have access to a UART interface). I was hoping for some ideas on what issues there could be.

void spi_transmit_receive( SPI_TypeDef* spi, uint8_t* tx, uint8_t* rx, uint16_t size )
{

    if( !LL_SPI_IsEnabled( spi ) )
    {
        LL_SPI_Enable( spi );
    }

    if( size > 1 )
    {
        LL_SPI_SetRxFIFOThreshold( spi, LL_SPI_RX_FIFO_HALF_FULL );
    }
    else
    {
        LL_SPI_SetRxFIFOThreshold( spi, LL_SPI_RX_FIFO_QUARTER_FULL );
    }


    uint8_t* rx_ptr = rx;
    uint8_t* tx_ptr = tx;

    uint16_t tx_count = size;
    uint16_t rx_count = size;

    while( tx_count > 0 || rx_count > 0 )
    {
        if( tx_count > 0 && LL_SPI_IsActiveFlag_TXE( spi ) )
        {
            if( tx_count > 1 )
            {
                LL_SPI_TransmitData16( spi, *((uint16_t*)tx_ptr) );

                tx_ptr += sizeof( uint16_t );

                tx_count -= 2;
            }
            else
            {
                LL_SPI_TransmitData8( spi, *tx_ptr );

                tx_count -= 1;
            }
        }

        if( rx_count > 0 && LL_SPI_IsActiveFlag_RXNE( spi ) )
        {
            if( rx_count > 1 )
            {
                *((uint16_t*)rx_ptr) = LL_SPI_ReceiveData16( spi );
                rx_ptr += sizeof( uint16_t );
                rx_count -= 2;

                if( rx_count <= 1 )
                {
                    // Switch to 8 bit mode for last 8 bits
                    LL_SPI_SetRxFIFOThreshold( spi, LL_SPI_RX_FIFO_QUARTER_FULL );
                }
            }
            else
            {
                *rx_ptr = LL_SPI_ReceiveData8( spi );
                //rx_ptr += sizeof(uint8_t);
                rx_count -= 1;
            }
        }
    }

    while( LL_SPI_IsActiveFlag_BSY( spi ) ) {}

    LL_SPI_Disable( spi );
}

Upvotes: 3

Views: 2344

Answers (1)

Clifford
Clifford

Reputation: 93476

You do not specify the specific STM32 part, so it is not possible to provide a specific user manual reference, there are at least two SPI peripheral variants across the range of STM32 parts, so it is not possible to be definitive for your part, but documentation for the STM32F1xx and STM32F2xx SPI_DR states (my emphasis):

Depending on the data frame format selection bit (DFF in SPI_CR1 register), the data sent or received is either 8-bit or 16-bit. This selection has to be made before enabling the SPI to ensure correct operation.

That is it is not valid to change the frame from 16 to 8 bit in the middle of a transaction. It seems that you should operate this device exclusively in 8 bit mode and simply send two bytes. I think, the use of a 16 bit frame is appropriate only if all transfers are a multiple of 16 bits.

I imagine what is happening is the device remains in 16 bit mode and the data being sent MSB first ends up in the MSB of SPI_DR. But since the documentation suggests that this is undefined behaviour, all bets are off.

enter image description here

Upvotes: 2

Related Questions