读取STM32微控制器SPI数据寄存器的数值。

3
有很多类似的问题,但似乎没有一个问题完全相同。我正在将STML4 MCU连接到6轴传感器(LSM6DS3)。我已经成功地实现了I2C中的所有内容,但希望使用SPI的额外速度(如果我能让这些第一步工作...)。因此,作为第一步,我正在尝试读取设备的WHO_AM_I寄存器(0x0F),它应该回复0x69。以下是代码:
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);

在作用域/分析器上,我看到一切都按预期运行,包括传感器发送回的0x69。然而,当我在此代码块的另一侧设置断点时,我看到who0变为0x5D再到0xFF。它从未读取0x69。我查看了其他代码示例,有些人使用了第二个传输,并将数据设置为某些虚拟值(通常为0xFF0x0),因此我也尝试了这样做,但是传感器似乎在第二次尝试期间变得混乱,并且who最终成为0x48。我已经尝试了所有可能的等待RXNE/TXE/BSY标志的排列组合,以及许多其他方法来正确读取SPI1数据寄存器中的变量,包括从传感器读取其他寄存器,但都无济于事。

那么问题来了,如何正确地从此寄存器中读取值?

我还应该提到,我可以成功地写入设备的寄存器。我可以发送所需的命令,然后读取它返回的数据,并在示波器上看到它的工作情况,尽管我无法将变量中的值分配给代码。我总是得到0xFF
我包含了一个屏幕截图,显示传感器从单个读取请求中发送回0x69,以及如果我尝试“虚拟传输”方法时发送的混乱信息。Correcly sending me the WHO_AM_I value Sending back <code>0x48</code> on "double" transmit
1个回答

3

如果接收器启用,SPI总是在传输时接收数据。

这是使用库的问题,你不知道里面有什么。使用寄存器编程可以更轻松地编程SPI。

我假设你的数据是8位。

在SPI初始化期间,您需要设置1/4(一个字节)FIFO阈值,方法如下:

 SPI1 -> CR2 |= SPI_CR2_FRXTH;

接下来,您需要在每次写入后从FIFO中读取数据(您还需要强制编译器使用正确大小(在本例中为8位)的载入和存储指令):

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

我不知道使用LL库的意义是什么。
而不是:
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));

使用寄存器

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

你也可以将其封装成函数:

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

谢谢!实际上,除了设置四分之一FIFO阈值外,您在此处提到的所有内容我都已经完成了。我之前将其以LL形式给出的唯一原因是为了清晰明了。我也尝试过将其放在自己的函数中,只是认为将其全部放在同一个代码块中会更容易。然而,我按照您在此处的方式进行了尝试(修复了函数中的类型 - 应该是RXNE),虽然现在最终who被设置为0x0,但几乎得到了相同的响应。不过,作用域信号看起来是一样的。 - TrivialCase
你设置了阈值吗? - 0___________
是的,我已经在做那个了。由于您的回复似乎引导我找到了正确的答案,如果您将其修改为使用 SPI_SR_BSY 而不是 SPI_SR_RNE,我将接受它作为答案。如果您能解释为什么会这样工作,那就更好了。 - TrivialCase
它能工作是因为它刷新了另一个数据。但这不是正确的方式。在相同的STMs上,BSY比从接收移位寄存器传输到数据寄存器的数据更早地被清除。要检测数据,请使用RXNE。如果由于某些原因它无法工作 - 这表明你代码中还有另一个问题。 - 0___________
让我们在聊天中继续这个讨论。链接:https://chat.stackoverflow.com/rooms/177293/discussion-between-trivialcase-and-p-j。 - TrivialCase
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接