本帖最后由 cruelfox 于 2018-12-28 22:24 编辑
参考资料: RM0351 STM32L4x6 Reference Manual 第38章节
SPI是很基本的串行通信协议之一,STM32具有硬件SPI模块(有的还带有I2S模式,这里不考虑),可是好用否?通过对手册的学习,我梳理一下平常用得到的如下特点:
(1) 支持常规的 SPI 信号:SCK/MISO/MOSI 三线全双工模式,可以作主机,也可以作从机。可配置时钟相位、空闲时的时钟状态、MSB或LSB在前的模式。
(2) 支持两线模式,可以用单条数据线实现收和发(半双工)
(3) 支持 4-bit 到 16-bit 的字长
(4) 具有NSS (Slave Select,即片选信号)管理,还可以借助它实现主从角色的自动切换。
(5) 可选的硬件CRC功能
(6) 带有FIFO, 支持DMA操作. 硬件状态查询和中断产生等功能等(属于应该有的)
最常见的连接方式是下图这样(如果是两个STM32用SPI通信)
按照常规的理解,CPOL, CPHA, 字长等基本参数都容易设置,按寄存器说明配置就可以了。双向通信需要把接收和发送都允许。
但是 NSS 脚的使用,在SPI传输里面它并不是必须有的。没有 SS 信号,从机一直处于侦听状态,靠 SCK 实现字同步没有问题。启用 SS 信号,则从机只在 SS 为低时才会管 SCK 上的时钟信号。作为从机,将 CR1 中 SSM 位写1,则不再使用 NSS 脚的信号,而靠 CR1 中 SSI 位来代替,如下的叙述:
作为主机时,对 NSS 脚有两种硬件操作方式:一是SSOE为1,它输出片选信号(但是并不是在有数据发送时变低,而是SPI硬件使能,即 CR1 的 SPE 打开就变低,即使没有数据传输。个人以为意义不大,用一个GPIO代替都够了);二是SSOE为0,它作为输入信号,用来判断总线冲突,这样允许多个主机同时连接(当然属于非标准的用法了)。
作主机时SCK信号的产生,有两种情况。一是双向通信或仅有发送时,向数据寄存器 DR 写数会自动启动传输,根据数据量产生若干个周期的 SCK 脉冲信号,然后结束。另一种情形是单向接收(包括半双工模式下的方向选择为接收)时,SCK信号会一直持续——不能控制SCK的脉冲个数,或者说传输量。因为在实际项目中用过,个人觉得这在半双工模式下是个缺憾。
作为 SPI 主机,向数据寄存器DR写数据,开始一次传输,数据从 MOSI 脚逐位发送出去,然后从数据寄存器可以读到这一次同时从 MISO 脚收到的数据,这个行为是很明确的。但若作为从机,当外面有 SCK 信号来之前,必须将要发送的数据已写入数据寄存器DR(实际上是写入内部FIFO),才能正确地发送。 如果没有先写好,并且FIFO是空的,那么从 MOSI 发送出去的是什么数据呢?我在手册中没有看到相关的叙述。如果发送出去是全0倒好了,从我实际应用的结果来看,送出的是以前写过的数据,但不一定是最后一次写的数据。还有问题是,STM32 的 SPI 没有提供一个“把FIFO清空”的办法,这在FIFO中有数据但不想要的时候是有用的(我不想把整个SPI给Reset)
SPI 内部这个 FIFO 有4字节的容量,但是在 APB 总线上,DR 是一个16-bit的寄存器,同时又可以以8-bit模式访问。这里尤其要小心了。
比如常规的 8-bit 字长模式,向 DR 寄存器可以写两个字节,然后低字节先发送,高字节随后发送。在读取的时候,一次也能读FIFO中的两个字节数据。如果是像8位机那样,或者是为了软件写起来简单,一次只读或者写一个字节;以及只需要发送一个字节的数据时,怎么办?好在 DR 寄存器是可以字节(8-bit模式)访问的,那么下面这样写:
void SPI_transmit(char data)
{
SPI1->DR = data;
}
正确吗?
我起初这么写,没有像我意料的那样工作。后来明白了 SPI1->DR 这是一个 16-bit 寄存器(可以看头文件中的结构体定义),上面这条语句会把 data 转换成 16-bit 的类型然后写到寄存器。读 DR 寄存器也会有类似的问题,而且一次读来的两个字节是否都是有效数据。在 SPI 收到数据的时候,依靠状态寄存器中 RXNE 位来指示 FIFO 有数据可以读取,而又取决于 FRXTH 的设置的阈值。
SPI 的寄存器组挺少的,只要不涉及CRC功能,仅有 CR1, CR2, SR 和 DR 四个16-bit寄存器。 其中 SR 里面的状态位也比较少。我觉得缺少一个从机模式下 NSS 变高引起的“传输结束”标志,以及一个完整的字传输未完成时 NSS 变高的异常标志。SPI 不提供就只好用 EXTI 去代替了。总之,这个硬件的不足(不好用)之处在我看来还是有一些。
|