SPI 主机设计:CPOL/CPHA 四种模式、片选与移位寄存器时序
更新时间:2026-09-19。本文与异步的 UART、开漏的 I2C 对照。SPI 推挽、全双工、无应答、引脚多但速率高,常用于 Flash、ADC/DAC、显示屏、传感器高速数据。
本文要回答的问题
- CPOL 和 CPHA 各决定什么,四种 SPI mode 怎么来?
- 主机移位寄存器怎么做到「发一个同时收一个」?
- 片选 SS 的建立/保持/空闲时间为什么要留?
- SPI 和 I2C 怎么选?最常见的「采到 0xFF/错位」是什么原因?
一、四线与拓扑
| 信号 | 含义 |
|---|---|
| SCLK | 主机产生的串行时钟 |
| MOSI(COPI) | 主机出、从机入 |
| MISO(CIPO) | 从机出、主机入 |
| SS/CS(低有效) | 主机拉低选中某个从机,可多个(每个从机一根) |
推挽驱动(不像 I2C 开漏),单主多从靠独立片选,无应答、无流控、无标准多主仲裁,因此协议极简、速度可达数十~上百 MHz。一收一发同时进行:全双工。
二、CPOL/CPHA:四种模式
- CPOL(时钟极性):空闲时 SCLK=0(CPOL=0)还是 1(CPOL=1)。
- CPHA(时钟相位):在第一个边沿(CPHA=0)还是第二个边沿(CPHA=1)采样数据;另一侧边沿则移位输出新位。
| Mode | CPOL | CPHA | 空闲电平 | 采样沿 | 移位(更新)沿 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 上升沿(第1边沿) | 下降沿 |
| 1 | 0 | 1 | 0 | 下降沿(第2边沿) | 上升沿 |
| 2 | 1 | 0 | 1 | 下降沿(第1边沿) | 上升沿 |
| 3 | 1 | 1 | 1 | 上升沿(第2边沿) | 下降沿 |
bash
Mode0: 空闲低,SS 拉低后主机先在下降沿把第1位摆到 MOSI,
随后上升沿主从双方采样 —— 因此 CPHA=0 时数据必须「先于」第一个 SCLK 边沿就绪。关键细节:CPHA=0(mode0/2)时,SS 拉低即把从机第一位输出到 MISO,主机在第一个 SCLK 边沿采样;CPHA=1(mode1/3)时,从机要等到第一个时钟边沿才输出第一位。主机和从机的 mode 必须完全一致,否则采样点落在数据变化沿附近,轻则读到错位、重排,重则全 0/全 1。mode0 和 mode3 空闲电平相反但都是上升沿采样,实际最常用 mode0。
三、主机 RTL:移位寄存器 + 时钟分频 + FSM
核心是两个「背对背移位寄存器」同时转:
verilog
// 每次使能一个 SCLK 边沿节拍:
// 主机:MOSI = tx_shft[MSB];采样沿锁存 MISO -> rx_shft
always @(posedge shift_clk) begin
if (start) begin tx_shft <= tx_data; bitcnt <= 0; end
else if (shift_en) begin
tx_shft <= {tx_shft[6:0], 1'b0}; // 左移,MSB first(多数器件)
rx_shft <= {rx_shft[6:0], miso}; // 同时收一位
bitcnt <= bitcnt + 1;
end
end
assign sclk = cpol ^ sclk_internal; // 极性翻转
assign mosi = tx_shft[7];- 波特率/时钟:把 APB/系统时钟经分频寄存器产生 SCLK,常做成可配置 2/4/8… 分频;mode 切换即翻转极性与采样相位选择。
- FSM:IDLE → 拉低 SS 并留建立时间 → 移位 8(或 16/24/32)位 → 留保持时间 → 释放 SS → 帧间空闲。
- 一次 8 位传输结束时
rx_shft即为这 8 拍同时收回的字节(哪怕只想读,也要发 dummy/0x00 产生时钟;只想写也会收到字节,丢弃即可)。 - 位序(MSB/LSB first)按器件 datasheet 配置,Flash 多为 MSB first。
四、片选时序约束
datasheet 常规定:
- SS 建立时间 t_su:SS 拉低到第一个 SCLK 边沿的最小间隔(CPHA=0 时尤其重要,第一位要在此期间就位);
- SS 保持 t_hold:最后一个采样沿到 SS 拉高;
- SS 高电平最短(帧间空闲 t_hiccup):连续两次传输之间 SS 至少高多久,从机用它复位位计数/退出连续读。
SPI 控制器一般提供「保持 SS 拉低跨多字节」(burst/连续地址读 Flash)与「每字节松一次 SS」两种模式;后者会让带地址自增的从机每次复位指针。
五、多从机与扩展
- 每个从机一根独立 SS(硬件译码可扩展),同一时刻只拉一个;未选中的从机必须把 MISO 置高阻,避免多从机驱动冲突——三态控制见 三态总线。
- Daisy-chain(移位寄存器级联,如 LED 驱动、某些 ADC):MISO 接下一从 MOSI,共用 SS/SCLK,一次移入 N 字节。
- Quad/OSPI Flash 用 4/8 根数据线,已超出经典 SPI,属专用控制器。
六、SPI vs I2C
| SPI | I2C | |
|---|---|---|
| 线数(N 从) | 3+N(多 SS) | 2(SCL/SDA) |
| 双工 | 全双工 | 半双工 |
| 速率 | 高(数十 MHz+) | 100k/400k/1M |
| 应答/流控 | 无 | ACK、时钟拉伸 |
| 地址 | 硬件片选 | 软件地址(可总线枚举) |
| 引脚/功耗 | 多 | 少 |
| 典型 | Flash、高速 ADC/LCD | EEPROM、传感器、配置 |
七、常见的坑
- CPOL/CPHA 不匹配:头号问题,读全 0xFF、0x00 或字节位移;先确认从机要求的 mode。
- CPHA=0 第一位太晚:SS 拉低后没提前输出,第一个边沿采空;按 t_su 预放置数据。
- 位序弄反:MSB/LSB first 与器件不符。
- 片选时序太紧/太松:没留建立保持,或每字节松 SS 导致地址不连续。
- 未选中从机 MISO 未高阻:多从机总线冲突,读到抖动值。
- 只读不发时钟:SPI 从机不产生时钟,读也必须主机发 dummy 字节产生 SCLK。
- SCLK 过快/走线反射:推挽高速沿 SI 问题,需控边沿速率/匹配,见 片上信号完整性。
相关与延伸
对比见 I2C 主机、UART 波特率与溢出;三态见 三态总线;配置寄存器接口见 APB;Flash 实例见 SPI NOR Flash 读写。
总结
SPI 用主机产生的 SCLK + 推挽 MOSI/MISO + 独立片选做全双工同步传输,CPOL 定空闲电平、CPHA 定第几个边沿采样组合出 mode0–3,主从必须一致;主机就是一个时钟分频器加一对同步移位寄存器,发一位的同时收一位,读也要发 dummy 产生时钟,并按 datasheet 留片选建立/保持与帧间空闲——mode 不匹配是数据采反的头号原因。