Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading values of SPI data register of STM32 MCU

There are quite a few similar questions, but none seem to have quite the same issue. I am connecting an STML4 MCU to a 6-axis sensor (LSM6DS3). I have successfully implemented everything in I2C, but would like the extra speed of SPI (and DMA, if I can get these first steps working...). So for a first step, I am trying to read the WHO_AM_I register (0x0F) of the device, which should reply with 0x69. Here is the code:

uint8_t who = 0;

// Sanity check/debugging aid should get 0x5D
who = 0x43 + 0x1A;

// Set SS low
GPIO_WritePin (GPIOB, LL_GPIO_PIN_7, GPIO_PIN_RESET);

// while tx buffer is in use, wait
while (!LL_SPI_IsActiveFlag_TXE(SPI1));

// Send READ command to the WHO_AM_I register
(SPI1->DR) = 0x8F;

// while rx buffer is in use, wait
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));

// Get data off the register
who = (SPI1->DR);

// Wait for everything to wrap up before setting SS high again
while (LL_SPI_IsActiveFlag_BSY(SPI1));

// OK, now we can set SS high
GPIO_WritePin (GPIOB, LL_GPIO_PIN_7, GPIO_PIN_SET);

On the scope/analyzer, I see everything run as expected, including the sensor sending back 0x69. However, when I set a break on the other side of this code block, I see that who goes from 0 to 0x5D to 0xFF. It never reads the 0x69. I looked through other code examples and some people have a second transmit with the data set to some dummy value (usually 0xFF or 0x0), so I also tried that, but the sensor seems to get confused during the second attempt and who ends up being 0x48. I have tried every permutation of waiting for the RXNE/TXE/BSY flags that I could have, as well as many many other things to get the variable to correctly read the SPI1 data register, including reading other registers off the sensor, but all to no avail.

So then the question is, how does one correctly read values off this register?

I should also mention that I can successfully write to the device's registers. I can send the command I want, then read it back and see that it worked on the scope, even though I can never get the values assigned to a variable in code. I always get 0xFF.

I include a screen of my analyzer showing the sensor sending back 0x69 from a single read request, as well as the garbled mess it sends if I try the "dummy transmit" method.Correcly sending me the WHO_AM_I value Sending back <code>0x48</code> on "double" transmit

like image 370
TrivialCase Avatar asked Dec 13 '25 17:12

TrivialCase


1 Answers

SPI always (if the receiver is enabled) receives the data when you transfer.

This is the problem with the libraries that you do not know what is there. SPI is a lot easier to program using registers.

I assume that your data is 8bits.

You need to set the 1/4 (one byte) FIFO threshold during the SPI initialization by:

 SPI1 -> CR2 |= SPI_CR2_FRXTH;

next you need to read the data from the FIFO after every write (you need also to force the compiler to use the correct size (in this case 8bits) load and store instructions):

*(volatile uint8_t *)&SPI1->DR = 0x8F;  // write exactly 8 bits to the FIFO
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));
dummy = *(volatile uint8_t *)&SPI-> DR;
*(volatile uint8_t *)&SPI1->DR = 0;  // dummy write
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));
who = *(volatile uint8_t *)&(SPI1->DR);

I do not know what is the point of using the LL libraries.

instead of

while (!LL_SPI_IsActiveFlag_RXNE(SPI1));

use registers

while (!(SPI1 -> SR & SPI_SR_RNE));

You can also wrap it into the function:

uint8_t SPI_ReadWrite8(SPI_TypeDef *spi, uint8_t data)
{
    while(!(spi -> SR & SPI_SR_TXE));
    *(volatile uint8_t *)&spi->DR = data; 
    while (!(spi -> SR & SPI_SR_RNE));
    return *(volatile uint8_t *)&spi-> DR;
}

And

SPI_ReadWrite8(SPI1, 0x8f);
who = SPI_ReadWrite8(SPI1, 0);
like image 96
0___________ Avatar answered Dec 16 '25 00:12

0___________



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!