cancel
Showing results for 
Search instead for 
Did you mean: 

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

HDesa.1
Senior

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

-Regards
Hrishikesh
7 REPLIES 7
TDK
Guru

> 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.

If you feel a post has answered your question, please click "Accept as Solution".

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);

-Regards
Hrishikesh
HDesa.1
Senior

Hi,

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

-Regards
Hrishikesh
JNico.4
Associate

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__ */
}

Pavel A.
Evangelist III

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));

@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));

OK then