Skip to main content
Graduate II
July 23, 2021
Question

Hello, Can anyone please help me in Low level(LL) SPI driver for STM32F7 microcontroller?

  • July 23, 2021
  • 4 replies
  • 4069 views

I have been trying to establish an SPI communication using STM32F7 microcontroller using Low level drivers in STM32CubeMX. I am facing issue Reading from the DR register. I need to read 8-bits but the values are getting stored as 16-bits. Also the DR register is getting update only after transmitting the command 3 times.

This is the SPI driver which I have written.

void SPITransmitData(char *p8Data, int i16Length)

{

while(SET == LL_SPI_IsActiveFlag_BSY(SPI2);

do

{

while(RESET == LL_SPI_IsActiveFlag_TXE(SPI2);

LL_SPI_TransmitData8(SPI2, *p8Data++);

i16Length--;

}while(i16Length != 0);

while(SET == LL_SPI_GetTxFIFOLevel(SPI2);

while(SET == LL_SPI_GetRxFIFOLevel(SPI2);

LL_SPI_ClearFlag_OVR(SPI2);

return;

}

void SPIReceiveData(char *p8Data, int i16Length)

{

do

{

LL_SPI_SetRxFIFOThreshold(SPI2, LL_SPI_RX_FIFO_TH_QUARTER);

while(RESET == LL_SPI_IsActiveFlag_TXE(SPI2);

LL_SPI_TransmitData8(SPI2, (uint8_t)*(p8Data+i16Count));

while(RESET == LL_SPI_IsActiveFlag_RXNE(SPI2);

*(p8Data+i16Count) = (uint8_t)(LL_SPI_ReceiveData16(SPI2);

i16Count += 1;

i16Length--;

}while(i16Length != 0);

while(SET == LL_SPI_GetTxFIFOLevel(SPI2);

while(SET == LL_SPI_GetRxFIFOLevel(SPI2);

return;

}

Please can anyone help.

-Regards

    This topic has been closed for replies.

    4 replies

    Super User
    July 23, 2021

    > I need to read 8-bits but the values are getting stored as 16-bits.

    > *(p8Data+i16Count) = (uint8_t)(LL_SPI_ReceiveData16(SPI2);

    Use LL_SPI_ReceiveData8 instead of LL_SPI_ReceiveData8.

    Note also that, as written, that line won't compile. It amazes me the amount of "copy/paste errors" when users post code here.

    > while(RESET == LL_SPI_IsActiveFlag_TXE(SPI2);

    > while(RESET == LL_SPI_IsActiveFlag_RXNE(SPI2);

    These lines also will not compile as-written since the parentheses are not balanced.

    HDesa.1Author
    Graduate II
    July 26, 2021

    Thank you for the reply.

    I apologise for the parentheses. It might have missed while coping. This will be taken care of in future. But I confirm that there are not compilation error in the code.

    I tried using LL_SPI_ReceiveData8, but it is always reading 0x00. I observed that the data is being read as MSB for the 16-bit register. I had to do (uint8_t)((LL_SPI_ReceiveData16(SPI2)>>8); in order to read it properly. And that too after sending same command thrice.

    Could you please tell if I am missing any settings. Below are the SPI settings which I have done.

    LL_SPI_InitTypeDef SPI_InitStruct = {0};

    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;

    SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;

    SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;

    SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;

    SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;

    SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;

    SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;

    SPI_InitStruct.CRCPoly = 7;

    SPI_InitStruct.ClockPolarity=LL_SPI_POLARITY_HIGH;

    SPI_InitStruct.ClockPhase=LL_SPI_PHASE_2EDGE;

    LL_SPI_Disable(SPI2);

    LL_SPI_Init(SPI2, &SPI_InitStruct);

    LL_SPI_SetStandard(SPI2, LL_SPI_PROTOCOL_MOTOROLA);

    LL_SPI_EnableNSSPulseMgt(SPI2);

    LL_SPI_Enable(SPI2);

    HDesa.1Author
    Graduate II
    July 29, 2021

    Hi,

    Can anyone please tell where I am going wrong in? Why am I not able to read data?

    Visitor II
    June 23, 2022

    I had a similar issue reading the DR register correctly.

    Here's the settings I had:

    LL_SPI_InitTypeDef SPI_InitStruct = {0};
    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
    SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
    SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH;
    SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
    SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
    SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16;
    SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
    SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitStruct.CRCPoly = 7;
    LL_SPI_Init(SPI1, &SPI_InitStruct);
    LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
    LL_SPI_DisableNSSPulseMgt(SPI1);

    I was careful to keep the FRXTH bit of SPIx_CR2 register in line with the call made to read the data from the DR register (LL_SPI_ReceiveData8 and LL_SPI_ReceiveData16) but when invoking the LL_SPI_ReceiveData8 function, the returned value was always garbage although I could see that the register value was correct by stepping with the debugger. After a while of hair pulling, I tried to create a temporary variable and read the register like so:

    volatile uint8_t *const spiDR8Bits = (volatile uint8_t*) &SPI1->DR;
    uint8_t receivedByte = *spiDR8Bits;

    This worked !

    I think the issue had to do with the compiler not generating the correct instructions but It's no clear to me why and how.

    I noticed that the LL_SPI_TransmitData8 and LL_SPI_TransmitData16 functions were defined like this:

    __STATIC_INLINE void LL_SPI_TransmitData8(SPI_TypeDef *SPIx, uint8_t TxData)
    {
    #if defined (__GNUC__)
     __IO uint8_t *spidr = ((__IO uint8_t *)&SPIx->DR);
     *spidr = TxData;
    #else
     *((__IO uint8_t *)&SPIx->DR) = TxData;
    #endif /* __GNUC__ */
    }
     
    __STATIC_INLINE void LL_SPI_TransmitData16(SPI_TypeDef *SPIx, uint16_t TxData)
    {
    #if defined (__GNUC__)
     __IO uint16_t *spidr = ((__IO uint16_t *)&SPIx->DR);
     *spidr = TxData;
    #else
     SPIx->DR = TxData;
    #endif /* __GNUC__ */
    }

    But the LL_SPI_ReceiveData8 and LL_SPI_ReceiveData16 functions didn't make a distinction on the compiler so I finally created my own inline functions to read the DR register like this:

    static inline uint8_t SPI_ReceiveData8(SPI_TypeDef *SPIx)
    {
    #if defined (__GNUC__)
     volatile uint8_t *spidr = ((volatile uint8_t*) &SPIx->DR);
     return *spidr;
    #else
     return (*((volatile uint8_t *)&SPIx->DR));
    #endif /* __GNUC__ */
    }
     
    static inline uint16_t SPI_ReceiveData16(SPI_TypeDef *SPIx)
    {
    #if defined (__GNUC__)
     volatile uint16_t *spidr = ((volatile uint16_t*) &SPIx->DR);
     return *spidr;
    #else
     return (*((volatile uint16_t*) &SPIx->DR));
    #endif /* __GNUC__ */
    }

    Super User
    June 23, 2022

    The SPI data register indeed is sensitive to the write width (8 vs 32 bits).

    In your example this does a 8-bit read

    volatile uint8_t *spidr = ((volatile uint8_t*) &SPIx->DR);
    return *spidr;

    This should do a 8-bit write:

    *((volatile uint8_t *)&SPIx->DR) = TxData;

    but this won't do a 16-bit read (can you see why?)

     return (uint16_t)(READ_REG(SPIx->DR));

    Visitor II
    June 23, 2022

    @Pavel A.​ 

    "but this won't do a 16-bit read (can you see why?)"

    return (uint16_t)(READ_REG(SPIx->DR));

    I meant :

    return (*((volatile uint16_t *)&SPIx->DR));

    Super User
    June 23, 2022

    OK then