news 2026/10/15 4:18:56

DAYS 55【嵌入式通信协议】SPI 协议深度拆解:从 CPOL/CPHA 时序本质,到寄存器读写实战全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DAYS 55【嵌入式通信协议】SPI 协议深度拆解:从 CPOL/CPHA 时序本质,到寄存器读写实战全流程

前言

在嵌入式常用外设通信协议中,SPI 凭借全双工传输、速率高、协议简单的优势,广泛应用于 SPI Flash、OLED 屏幕、加速度传感器、ADC 芯片等场景。但很多初学者接触 SPI 时,总会被 CPOL、CPHA 的四种模式绕晕,也始终搞不懂为什么读操作一定要发 “哑元(Dummy)”。

本文结合完整时序波形图,从最底层的时钟极性、相位原理讲起,再到寄存器写操作、读操作的完整流程拆解,把 SPI 的核心逻辑彻底讲透。

一、SPI 基础:四根信号线的分工

SPI 是同步串行外设接口,采用典型的一主多从架构,标准 SPI 由 4 根核心信号线组成:

  • SCLK:串行时钟,由主机产生,为数据传输提供同步节拍
  • CS/SS:片选信号,低电平有效,主机拉低对应从机的 CS 引脚,才能与该从机建立通信
  • MOSI(Master Out Slave In):主发从收通道,主机输出数据、从机接收数据
  • MISO(Master In Slave Out):主收从发通道,从机输出数据、主机接收数据

SPI 是全双工通信:在 SCLK 的驱动下,MOSI 和 MISO 可以同时传输数据,发送和接收是同步进行的。这也是 SPI 读操作必须发送数据的根本原因。

二、核心难点:CPOL 与 CPHA 的本质

SPI 的四种工作模式,本质就是时钟极性(CPOL)和时钟相位(CPHA)的不同组合。这两个参数是 SPI 通信双方必须严格一致的配置,参数不匹配会导致数据完全错乱。

1. 时钟极性 CPOL

CPOL 决定了总线空闲状态下,SCLK 的电平:

  • CPOL = 0:空闲时 SCLK 保持低电平
  • CPOL = 1:空闲时 SCLK 保持高电平

简单理解:CPOL 就是时钟的 “初始状态”—— 通信未启动、CS 未拉低时,时钟线停驻的电平。

2. 时钟相位 CPHA

CPHA 决定了数据在第几个时钟边沿被采样:

  • CPHA = 0:数据在 SCLK 产生后的第一个边沿被采样
  • CPHA = 1:数据在 SCLK 产生后的第二个边沿被采样

这里的 “边沿” 是上升沿还是下降沿,由 CPOL 共同决定,这也是很多人死记硬背却容易混淆的地方。我们结合两种极性分别拆解:

情况 1:CPOL=0(空闲低电平)

时钟从低电平启动,第一个边沿是上升沿,第二个边沿是下降沿

  • CPHA=0:上升沿采样数据,下降沿切换下一位数据
  • CPHA=1:下降沿采样数据,上升沿切换下一位数据
情况 2:CPOL=1(空闲高电平)

时钟从高电平启动,第一个边沿是下降沿,第二个边沿是上升沿

  • CPHA=0:下降沿采样数据,上升沿切换下一位数据
  • CPHA=1:上升沿采样数据,下降沿切换下一位数据

3. 四种 SPI 模式总结

表格

SPI 模式CPOLCPHA空闲时钟电平数据采样边沿实际使用频率
模式 000低电平上升沿最常用
模式 101低电平下降沿较少用
模式 210高电平下降沿较少用
模式 311高电平上升沿较常用

开发小贴士:绝大多数外设默认支持模式 0 或模式 3,二者都是上升沿采样,区别仅在于空闲电平不同。

三、实战时序:SPI 寄存器读写全流程

搞懂时钟相位的逻辑,我们来看实际开发中最核心的场景:通过 SPI 读写外设内部寄存器。下面以最通用的模式 0(CPOL=0,CPHA=0)为例,完整拆解写操作与读操作的时序。

1. 写寄存器操作

写操作是主机主动向外设指定寄存器写入数据,全程有效数据由主机通过 MOSI 发出,MISO 上的数据无实际意义。

完整时序流程:

  1. 拉低 CS 片选:主机将对应从机的 CS 引脚拉低,宣告通信开始,从机开始监听 MOSI 上的数据。
  2. 发送寄存器地址:主机通过 MOSI 串行发送 8 位寄存器地址(通常最高位为读写标志位,0 表示写操作)。
  3. 发送写入数据:地址传输完成后,主机继续通过 MOSI 发送 8 位待写入的数据。
  4. 拉高 CS 结束通信:数据传输完成后,主机拉高 CS 引脚,本次写操作结束,从机停止响应。

全双工特性说明:写操作过程中,MISO 上也会出现电平变化,但这是从机输出的无效数据,主机不需要读取处理。

2. 读寄存器操作

读操作是主机读取外设指定寄存器的数据,这也是 SPI 最容易踩坑的地方:读操作必须发送哑元(Dummy)字节。

为什么必须有哑元?

因为 SPI 是同步全双工通信,没有时钟就没有数据传输。主机想要接收从机的数据,必须主动提供 SCLK 时钟;而提供时钟的同时,MOSI 上必然会同步输出数据。

完整读操作时序流程:

  1. 拉低 CS 片选:主机拉低 CS,启动本次通信。
  2. 发送寄存器地址:主机通过 MOSI 发送 8 位寄存器地址(最高位置 1,表示读操作)。

    此时从机正在解析地址、准备数据,MISO 上的数据无效,主机忽略即可。

  3. 发送哑元,同步接收数据:地址发送完成后,主机发送 1 字节 Dummy 数据(任意值,通常为 0xFF),目的是提供 8 个 SCLK 时钟周期。

    在这 8 个时钟周期里,从机将寄存器数据放到 MISO 线上,主机在采样边沿读取 MISO,得到真正的寄存器值。

  4. 拉高 CS 结束:数据接收完成,主机拉高 CS,通信结束。

一句话总结 SPI 读操作:发地址的时候收不到数据;发哑元的时候,才真正接收数据。这也是 SPI 和 I2C 读操作最核心的区别。

四、代码示例:硬件 SPI 读写寄存器

下面给出一份典型的 32 位 MCU 硬件 SPI 读写寄存器的代码框架,完全对应上面的时序逻辑。

#include "spi_hw.h" /* 片选控制宏,根据实际硬件修改 */ #define SPI_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define SPI_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4) /* * @brief SPI底层发送一个字节(全双工,发的同时收一个字节) * @param data 待发送的字节 * @retval 接收到的字节 */ static uint8_t spi_send_byte(uint8_t data) { /* 等待发送缓冲区空 */ while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, data); /* 等待接收缓冲区非空 */ while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } /* * @brief SPI写单个寄存器 * @param reg_addr 寄存器地址(7位有效,最高位为写标志0) * @param value 要写入的数据 */ void spi_write_reg(uint8_t reg_addr, uint8_t value) { SPI_CS_LOW(); // 拉低片选,启动通信 spi_send_byte(reg_addr & 0x7F); // 发送寄存器地址,最高位清0表示写 spi_send_byte(value); // 发送写入数据 SPI_CS_HIGH(); // 拉高片选,结束通信 } /* * @brief SPI读单个寄存器 * @param reg_addr 寄存器地址(7位有效,最高位为读标志1) * @retval 读取到的寄存器值 */ uint8_t spi_read_reg(uint8_t reg_addr) { uint8_t recv_val; SPI_CS_LOW(); // 拉低片选 spi_send_byte(reg_addr | 0x80); // 发送寄存器地址,最高位置1表示读 recv_val = spi_send_byte(0xFF); // 发送哑元字节,同步接收数据 SPI_CS_HIGH(); // 拉高片选 return recv_val; }

核心说明:spi_send_byte是全双工函数,发送一个字节的同时,会返回总线上接收到的字节。写操作时忽略返回值,读操作时利用哑元发送的返回值得到有效数据。

五、SPI 开发常见踩坑总结

  1. CPOL/CPHA 参数不匹配这是最常见的问题,表现为读出来的数据全是 0、全是 1,或者完全乱码。解决方法:严格对照外设数据手册,确认 SPI 工作模式,保证主机和从机参数完全一致。
  2. 读操作忘记发送哑元很多初学者读 SPI 寄存器,发完地址就直接读取结果,永远得不到正确数据。必须记住:SPI 读数据,地址之后一定要发送对应长度的哑元时钟。
  3. CS 时序错误
    • 每次独立读写都必须完整执行 “拉低 CS→传输→拉高 CS”,不能多次读写共用一个低电平 CS(除非外设明确支持地址自增连续读写)
    • CS 拉低后建议增加几微秒延时,保证从机准备就绪再开始传输
  4. 数据位宽与传输顺序错误
    • 绝大多数 SPI 默认 8 位数据、MSB(高位)优先传输
    • 16 位 / 32 位寄存器的外设,要特别注意高低字节的传输顺序
  5. 时钟速率过快初期调试建议将 SPI 时钟调低,确认通信正常后再逐步提高频率,避免硬件布线、电平兼容导致的高速传输错误。

结语

SPI 协议本身并不复杂,核心就是理解 “同步、全双工” 这两个本质。CPOL 和 CPHA 决定了数据采样的时机,哑元是全双工读操作的必然产物。 吃透底层时序原理,再去调试 SPI 驱动,就不会只会照搬例程,遇到问题也能快速定位根因。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 4:18:43

FNF剧情型模组全解析:安装、排错与“争抢BF”的原理

如果你经常逛节奏游戏社区,大概见过这类描述:Pico 和岳父在争夺 BF。第一次看到这种说法的人,很可能会被绕晕——这不是同一个世界观下的角色吗?怎么突然变成了三角关系?这到底是在玩梗,还是模组里真实发生…

作者头像 李华
网站建设 2026/10/15 4:18:42

降AI率实战指南:从检测原理到工具选型的完整方案

写论文翻车名场面:洋洋洒洒两千字初稿,导师看完就一句话:"这段是你让AI写的吧?"你还没来得及否认,导师已经把屏幕转过来,指着第三段说:"别的不说,这一段四句话全是同…

作者头像 李华
网站建设 2026/10/15 4:17:50

燃烧反应工程:原理、设计与污染控制技术

1. 燃烧反应工程概述燃烧反应工程是化学工程领域的重要分支,专注于研究燃烧过程中的物质转化与能量传递规律。作为一名在能源行业工作多年的工程师,我见证了这个领域从基础研究到工业应用的完整发展历程。燃烧反应不仅仅是简单的燃料与氧气结合的过程&am…

作者头像 李华
网站建设 2026/10/15 4:17:46

SAP银企直连落地指南:架构选型、配置联调与高频踩坑

简介:这份PDF聚焦SAP银企直连(EPIC)解决方案,面向SAP财务顾问、企业资金管理人员及银行接口实施人员,旨在帮助读者快速理解SAP中国本地化支付集成的整体思路。内容系统梳理了EPIC的本地化背景,围绕付款、收…

作者头像 李华
网站建设 2026/10/15 4:17:36

全汉化,开浏览器即玩!把经典Roguelike游戏《盗贼寓言》塞进NAS

之前熊猫发过一些NAS部署游戏的教程,就有小伙伴想问还有什么好玩的能部署到NAS上,于是最近熊猫发掘了很多,今天要介绍的就是经典Roguelike游戏《盗贼寓言》。Roguelike游戏不知道大家有没有接触过,不过熊猫是非常喜欢这类型游戏的…

作者头像 李华
网站建设 2026/10/15 4:17:27

DeepSeek从入门到精通:提示词、API与工作流实战指南

简介:由清华大学新闻与传播学院新媒体研究中心出品的《DeepSeek从入门到精通》是一份面向AI应用者与初学者的系统教程,聚焦DeepSeek公司及开源推理模型DeepSeek-R1的功能拆解与实操方法。内容覆盖智能对话、文本生成、语义理解、代码生成补全等应用场景&…

作者头像 李华