居然都第 4 篇了(第三篇是 FPGA 串并这个,为了观看体验换了位置),先回忆一下我们的工作有哪些?
先从 bit 开始,讲了数据的组织方式,然后学了最常见的 HAL 库里面的发送函数,为了最大化的接收效率(使用了中断处理技术,以及引入了 DMA 和双缓冲区技术);第二篇,开始说了几种编程模型,接着研究了QSPI 这一个常见的 SPI 变种,从物理的角度分析了它的过程,发现其中的奥秘在串和并的处理上,为了更加本质的讨论这个话题,拿出了高云 FPGA 的 IO 原语;限于篇幅,在第三篇专门写了一个 FPGA 的 demo 来演示。(难吗?应该是不难,其实就是bit 的拼接)
对于别人可能这个话题就结束了,但是对我来说还有的写,SPI 是一个还没有完结的系列。
第二篇里面的 QSPI, 是 NXP(买了摩托罗拉的 MCU 业务),推出来的 ColdFire:

上面搭载的
另外这个 SPI 协议虽然大家都用,但是 SPI 协议并非由国际标准组织(如 IEEE 或 ISO)制定的公开标准,而是由 摩托罗拉 (Motorola) 公司提出的私有技术,因此它没有像 I2C(由飞利浦制定)那样拥有正式的 RFC 或标准协议白皮书,目前业界没有公认的“非官方标准”。
SPI 本质是一个带片选的同步移位寄存器链,它的祖先可以追溯到:1970 年代的 shift register 总线,早期微处理器并行 I/O 串行扩展,74HC595 / 74HC165 移位器结构。
所以它并不是“突然发明的”,而是:
把移位寄存器 + 时钟 + 片选,封装成 MCU 外设
1983–1985-Motorola 在 68HC 系列 MCU 中加入 SPI
1988-SPI 已成为 Motorola MCU 标准外设
1990s-被 Microchip / Atmel / TI 等采用
2000s-成为事实标准(de facto standard)

MC68HC11

就是这个老芯片

这样看,SPI 活了好多年了
时间回到 1983–1985 年,当时:MCU 刚刚兴起,外设(EEPROM / ADC / DAC)越来越多,并行总线太贵,UART 太慢;Motorola 需要一种:低成本,同步,容易硬件实现,占用引脚少的片内外设接口。
于是:
SPI 被定义为 MCU 内部硬件模块
它最初甚至不是“协议”,只是:
带片选的同步移位寄存器接口
没有:帧结构,地址字段,校验机制,仲裁机制,电气规范文档,统一时序标准所以它很难被定义成“标准协议”。(这些文章里面会补全)
因为它太简单了,任何厂商都可以轻松实现:一个移位寄存器,一个时钟,一个片选,没有专利限制,没有复杂规范。
于是它变成:
de facto standard(事实标准)
而不是:
de jure standard(法律/组织标准)
首先就是时序宽松,有的设备:要求 SS 提前一个周期,有的要求延迟,有的支持连续模式,有的不支持。
SPI 设备协议完全各厂商自定:8 bit,16 bit,24 bit,连续流,带 CRC,不带 CRC
所以:
SPI 是“物理通道”,不是“协议层”
已经被广泛使用,所以简单性是最大优势,强制标准会增加复杂度,而且各厂商不愿被约束;如果真的标准化:必须统一时序,必须统一电气参数,必须定义帧结构。
但是SPI 的价值就在于:
“不约束”
答案并不是“忘了设计”,而是:
SPI 从结构上就不是一个需要仲裁的共享总线系统。
仲裁出现的前提是:
多个设备可能同时驱动同一条物理总线。
例如 I²C:多主机,所有设备共用 SDA/SCL开漏结构,可能两个 Master 同时发送。

串一堆
于是必须比较电平,失去仲裁者退出,胜者继续
这叫:
竞争型共享总线(Contention Bus)
SPI 是:
1 Master
N Slave
结构特点:只有 Master 产生 SCK,Slave 不能产生时钟,每个 Slave 有独立 SS,只有被选中的 Slave 驱动 MISO,其他 Slave 输出三态。

因此:
不存在两个驱动源同时竞争
没有竞争,就不需要仲裁。
SPI 架构是:
单一控制源被动响应节点
它是:
严格的主从系统
不像 I²C:
多个可能的主动节点
SPI 中Slave 永远不能主动发起通信。Slave 永远不能抢占总线,所有时序由 Master 决定
所以:
冲突结构上被禁止
SPI 可以建模为:
状态转移仅由 SCK 决定,没有:异步竞争输入,多源控制,冲突检测状态
它是:
单输入同步状态机
仲裁只存在于:
多输入竞争系统
SPI 没有多输入。
回到 1980s,SPI 的设计目标是:MCU ↔ 外设,MCU ↔ EEPROM,MCU ↔ ADC
典型拓扑是:
MCU
├── ADC
├── DAC
├── EEPROM
└── Display
根本不存在两个 MCU 同时抢总线的需求;如果有两个 MCU:设计师会明确规定一个是 Master,另一个只能 Slave
SPI 假设:
系统设计者负责拓扑规划
而 I²C 假设:
系统允许动态竞争
SPI 满足:
严格确定。
如果存在仲裁:
其中:
不可预测
这就破坏了:
确定性实时系统性质
这个和上面的问题有点重合,时间还是回到 1980s,最初的设计目标是:PCB 内芯片互联,短距离,高速,低复杂度,MCU 引脚够用;那时候:8-bit MCU 40 引脚,多几个 SS 完全可接受;如果加地址机制:需要协议解析,需要帧结构需要仲裁,需要 ACK,增加硬件复杂度,降低最高速度。而 Motorola 的目标是简单 + 快,所以选择物理片选。
如果给 SPI 加地址,会发生什么?每次通信前必须发送地址,所有设备都要监听,必须加入冲突机制,必须加入 ACK,时钟边沿不能太快。
因为没有地址机制,不需要帧开销,不需要 ACK 等待,没有仲裁延迟,时钟可以连续,这使得:SPI 可以做纯数据流接口。
我们从数学上看。
假设:
每次通信需要 8 bit 地址
则:
有效带宽:
R_effective = R_spi × (payload / (payload + address))
对于小数据:50% 带宽浪费;对于高速流:完全不可接受。