关于 STM32 中 SPI 做从机和半双工的理解和使用

前言

最近调试工作上用到和很多SPI,不同传感器的、不同控制平台之间的。遇到了不少奇奇怪怪的问题,记录一下。主要是stm32上这个平台上的使用,当然对于SPI这个常用的通信协议这里不多介绍了,可以找一下相关的资料熟悉相关概念和使用,全双工下的使用无论是中断还是DMA都比较简单没有啥好说的,这里就讲一下做从机和使用半双工的一些问题。

从机

由于使用 stm32 做从机的时候使用的是软件去操作CS引脚,出现的问题是从机接收端会出现错误的数据,对比发现是数据错位了,出现这种情况可能是 SPI 主机复位、主机重新重新上电、意外的时钟电平干扰都可能会让 SPI 从机接受数据的时候错位,整体数据右移了。所以一开始的解决思路就是看有没有相关的操作可以在每次清空掉RX - Shift Register里面的错误数据,但是查了很多资料都没有相关的描述。

尝试了如下的方式:

  • 关闭使能关闭后重新打开
  • 关闭SPI时钟后重新初始化(deinit)

结论是关闭SPI使能位SPE后在打开使能,问题依旧仍是接受到了错误数据;而使用后面的操作可以清除掉原来移位寄存器中的数据,为了速度直接使用寄存器赋值的操作直接先关闭 spi 时钟后再重新打开,然后对 CR1 赋值完成初始化,实测第二种方式解决了我的问题

半双工

关于半双工的使用是因为使用 TLE5012B 这个磁编码器的时候它使用的是3线的 SPI ,发送和接受数据都在同一条数据线上分时复用也就是半双工的方式,但是看了网上很多资料发现他们的操作都是使用下面这种方式,在MOSI和MISO之间串联一个电阻这样就可以使用全双工的SPI去通信了。不过我印象中stm32 的SPI 是支持半双工通信的,所以尝试了一下使用半双工的方式来通信。

  • 更改后使用如下的连接方式

  • 半双工和只读模式

    这里配置成双向数据线

  • 发送和接受的切换

    有前面可以知道传输的方向是由 BIDIOE 位控制的所以只需要在发送完成的时候切换成接受状态就可以了,那什么时候可以切换状态呢,首先先等待TXE = 1 ,这表明数据移入到移位寄存器中开始发送,接着等待 BSY = 0,这表明物理线上的传输已经结束了,这时候我们就可以切换状态到接受模式了,在hal库和LL库中都有相应的操作,当然也可以直接操作 BIDIOE 这个位进入到接受模式。

  • 时钟信号的控制

可以看出在双向模式接受的是时候只要使能了SPE,那么就意味着传输开始了,SPI主机会自动发送时钟这时候就可以接受到数据

  • 参考代码
uint8_t send_and_read_byte(uint8_t cmd)
{
    uint8_t result;
    GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS low
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //wait buffer empty
    SPI_SendData8(SPI1, cmd);
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); //wait finish sending
    // Read receiving FIFO until it is empty
    while (SPI_GetReceptionFIFOStatus(SPI1) != SPI_ReceptionFIFOStatus_Empty)
        SPI_ReceiveData8(SPI1);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
    while (!(SPI1->SR & SPI_I2S_FLAG_RXNE)) ; // wait data received
    GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS high
    SPI1->CR1 |= SPI_Direction_Tx;  // Set Tx mode to stop Rx clock
    result = SPI_ReceiveData8(SPI1);
    return result;
}  

参考:HOWTO: Use STM32 SPI half duplex mode – Digital Me (ba0sh1.com)

posted @ 2021-06-16 10:24  齐刃  阅读(7711)  评论(2编辑  收藏  举报