cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G030 SPI master read issue

KhPaul
Associate II

I use in my project stm32g030f6p6 to communicate with ADXL345 by 4-wire SPI. I have no problem with transmition, yet with reading I run into serious problem, that doesn't mentioned in Errata.

My SPI configuraton is CPOL 1, CPHA 1, speed 1MHz. 

I use Logic-U analizer and ADXL345 for sure respond right, but I receive wrong data, usualy from 1 to 2 bytes.
Only one solution is while reading procedure before togling NSS pin twice read SPI_DR in tthat case RX FiFo is cleared and I receive right data.
I can't understand why I should have done it even once, why it isn't mentioned in Errata.

I also tried to set RXONLY bit right after sending register address and clear it after receiving last byte, but permantely SPI send extra byte it's register address with cleared 7th bit, nor clearing SPE bit neither reading DR doesn't help.

Thus, I wonder what I did wrang If I did of course, and what the solution ST offer.

 

Here it's part of my code for reading data from register

 

#define CAST_IO8(x)    *((__IO uint8_t *)&x)
#define CAST_IO16(x)   *((__IO uint16_t *)&x)

typedef struct {
    pinDef_TypeDef nss;
    uint32_t cr1;
    uint32_t cr2;
    bool halfDuplex;
    bool hardwareNSS;
} spi_t;
// Array to store configuration of 'n' devices for both SPI interfaces
static spi_t  spi_unit[2][4];
// Array of the pointers to current device for both SPI interfaces
static spi_t* spi[2];

/****************************************************************/
/****************************************************************/
static uint8_t getBusID (SPI_TypeDef* pBus)
{
    switch ((uint32_t)pBus) {
        case (uint32_t)SPI1: return 0;
        case (uint32_t)SPI2: return 1;
    }
    return 0xff; // Error
}

/****************************************************************/
/****************************************************************/
__attribute__((always_inline))
static inline void SPI_ClearDR(SPI_TypeDef* pBus)
{
    limited_while (0xffff, !(pBus->SR & SPI_SR_RXNE));
    (void)CAST_IO8(pBus->DR);
}

/****************************************************************/
/****************************************************************/
__attribute__((always_inline))
static inline void SPI_WriteDR(SPI_TypeDef* pBus, uint8_t data)
{
    limited_while (0xffff, !(pBus->SR & SPI_SR_TXE));
    CAST_IO8(pBus->DR) = data;
}

/****************************************************************/
/****************************************************************/
__attribute__((always_inline))
static inline uint8_t SPI_ReadDR(SPI_TypeDef* pBus, uint8_t busId)
{
    if (!spi[busId]->halfDuplex) {
        limited_while (0xffff, !(pBus->SR & SPI_SR_TXE));
        CAST_IO8(pBus->DR) = 0;
    }
    limited_while (0xffff, !(pBus->SR & SPI_SR_RXNE));
    return CAST_IO8(pBus->DR);
}

/****************************************************************/
/****************************************************************/
__attribute__((always_inline))
static inline void SPI_SwitchToRX(SPI_TypeDef* pBus, uint8_t busId)
{
    if (spi[busId]->halfDuplex) pBus->CR1 &=~SPI_CR1_BIDIOE;
}

/****************************************************************/
/****************************************************************/
__attribute__((always_inline))
static inline void SPI_SwitchToTX(SPI_TypeDef* pBus, uint8_t busId)
{
    if (spi[busId]->halfDuplex) pBus->CR1 |= SPI_CR1_BIDIOE;
}

/****************************************************************/
/****************************************************************/
__attribute__((always_inline))
static inline void SPI_NSS_set(SPI_TypeDef* pBus, uint8_t state)
{
    uint8_t bus = getBusID(pBus);

    // Check state for the part of the transaction
    switch (state) {
        // It's end of the transaction - Wait till it's done
        case true: limited_while (0xffff, pBus->SR & SPI_SR_BSY); break;
        // It's start of the transaction - Clear DR.
        // I don't know why I has to do it twice, yet it's only one way to read right data!
        case false: (void)CAST_IO16(pBus->DR); (void)CAST_IO16(pBus->DR); break;
    }

    // It's hardware NSS mode there's nothing to do
    if (spi[bus]->hardwareNSS) return;

    GPIOx_Write(spi[bus]->nss.GPIO, spi[bus]->nss.pin, state);
}
/****************************************************************/
/*  The function for reading data from register in Master mode  */
/* ============================================================ */
/* pBus    - SPI bus (SPI1 / SPI2)                              */
/* addr    - register address to read data                      */
/* data    - pointer to array to write read data                */
/* len     - bytes to read                                      */
/****************************************************************/
void SPI_MasterReadDataFromReg (SPI_TypeDef* pBus, uint8_t addr, uint8_t* data, size_t len)
{
    uint8_t bus = getBusID(pBus);

    SPI_NSS_set(pBus, 0);

    SPI_WriteDR(pBus, addr);
    SPI_ClearDR(pBus);

    // Switch to read mode
    SPI_SwitchToRX(pBus, bus);

    // Read received data
    while(len--) *(data++) = SPI_ReadDR(pBus, bus);

    // Switch to transmit mode
    SPI_SwitchToTX(pBus, bus);

    SPI_NSS_set(pBus, 1);
}

 

 

12 REPLIES 12
KhPaul
Associate II

Before starting the topic, I deeply read reference manual, erratas, and similar topics I found.

No offence, but if you aren't going  to dig into the code either, how could you help me? If you read my code you'll see that I read DR each time I send a byte to DR, besides I wait for TXE before sending, and for RXNE before reading, and for BSY clear befor ending of the transaction. So, what a reason in this kind of help, or you do it just to grow your rate GURU?  

Besides, I gave a simplified alghorithm my reading procedure, step by step, I read all bytes Slave send me, but while next transaction there are some data from previous one in RX FiFo of the SPI.

Thus, just leave it, I'm ready to wait for reasonable response from the comunity.

 

Good luck.

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

I did presented CAST_IO8. Considering  limited_while, here it's 

 

extern uint32_t wdt_while_cnt;

/* ================================================================
 *                          Helper macros
 * ================================================================ */
#define limited_while(itr, cndt)    {wdt_while_cnt=itr; while(wdt_while_cnt-- && (cndt));}

 

As I said, before I deeply checked everything before starting the topic, the issue is clear: RXFIFO is filled with extra data. But, anyway I missed some important information from the refernce manual, there is next note:  Read data until FRLVL[1:0] = 00 (read all the received data) 

So, extra bytes in RXFIFO it is a normal behaviour, and reading DR is a right way to clear it.

I changed limited_while to be able to pass an extra variable argument - expression

 

#define limited_while(itr, cnd, ...)    for(uint32_t __i = (itr); __i && (cnd); __i--){__VA_ARGS__;}

 

And added RXFIFO clearing procedure after data are received

 

limited_while (0xffff, pBus->SR & (SPI_SR_RXNE | SPI_SR_FRLVL), (void)CAST_IO8(pBus->DR));

 

Conclusion: It's necessary to read the documentation more carefully.

Topic is closed.

Thank you everyone, I wish you good luck.