2
votes

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 );
}
1
Maybe in LL_SPI_ReceiveData8(spi) you do not read the correct byte. Either Most Significant Byte or Least Significant Byte of the 16bits SPI registerNGI
The TXE flag is asserted when the byte in SPI_DR register is moved to the shift register, i.e. at the start of the byte transmission. The RXNE flag is asserted when the byte from the shift register has been moved to the SPI_DR register, i.e. at the end of the transmission. Your code might in fact be reading byte 2 instead of byte 3.Codo
We use a kind of STM32 in House but I am not the one that wrote these drivers. But once on another microcontroller I had to declare the SPI CLK Pin as both an output and both an input, so that CLK re-entered internally to enable sampling of MISONGI
@NGI Thanks everyone for the input! Turns out the correct byte is in the MSB of the 16 bit register. What NGI suggested was correct. I'm now trying to understand why this is the case. I would have thought setting the FIFO to LL_SPI_RX_FIFO_QUARTER_FULL would place the shifted in data to the lower part of the register. Any ideas?Mackenzie Goodwin

1 Answers

1
votes

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