2023-11-30 11:24 PM - edited 2023-12-01 12:06 AM
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);
}
Solved! Go to Solution.
2023-12-01 09:35 AM - edited 2023-12-01 09:44 AM
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.
2023-12-01 09:45 AM - edited 2023-12-01 09:46 AM
Good luck.
2023-12-01 10:24 AM - edited 2023-12-01 12:08 PM
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.