恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
SPI四种模式详解:从CPOL/CPHA原理到实战配置与避坑指南
首页
资讯中心
/
SPI四种模式详解:从CPOL/CPHA原理到实战配置与避坑指南
SPI四种模式详解:从CPOL/CPHA原理到实战配置与避坑指南
发布时间:2026/9/24 8:38:06
SPI 这玩意儿说简单也简单四根线一接时钟一给数据就能跑起来。但说坑也真坑我见过太多人调试 SPI 的时候示波器一挂波形全对数据就是读不出来最后发现是模式选错了。Mode 0 和 Mode 3 在大多数芯片上都能凑合用但一旦遇到对时序敏感的外设比如某些 ADC、Flash 或者传感器模式不对就是死活不通。这篇文章就把 SPI 的四种模式彻底拆开讲清楚从时钟极性和相位的基本定义到实际抓波形的对比再到不同芯片上的配置方法和踩坑记录争取让你看完之后不用再靠“四个模式挨个试”这种笨办法来调 SPI。1. SPI 四种模式到底在说什么1.1 从两根线的默认状态说起SPI 协议的核心就四根线SCLK、MOSI、MISO、CS。其中最容易让人迷糊的就是 SCLK 这根时钟线。很多人以为时钟就是方波高低翻转就完事了但实际上 SPI 协议对时钟线有两个维度的定义时钟极性CPOL和时钟相位CPHA。这两个参数一组合就是四种模式。先说 CPOL它定义的是时钟线在空闲状态时是高电平还是低电平。CPOL0 表示空闲时 SCLK 保持低电平CPOL1 表示空闲时 SCLK 保持高电平。这个“空闲”指的是 CS 拉低之前、以及数据传输间隙中时钟线的状态。你可以把它理解为“没开始干活的时候时钟线默认待在哪一边”。再说 CPHA它定义的是数据在时钟的第几个边沿被采样。CPHA0 表示在第一个边沿采样CPHA1 表示在第二个边沿采样。这里的“第一个边沿”和“第二个边沿”是相对于数据传输开始后的时钟翻转来说的。如果 CPOL0第一个边沿就是上升沿如果 CPOL1第一个边沿就是下降沿。这两个参数一交叉就得到了四种组合模式CPOLCPHA空闲电平采样边沿移出边沿Mode 000低上升沿下降沿Mode 101低下降沿上升沿Mode 210高下降沿上升沿Mode 311高上升沿下降沿这张表建议你直接记在脑子里或者贴在工位上。因为不管你用的是 STM32、FPGA 还是 Linux 上的 spidev配置的时候都绕不开这四个组合。1.2 为什么采样边沿和移出边沿是错开的这里有一个很多人没想明白的问题为什么数据采样和移出要在不同的边沿能不能在同一个边沿既采样又移出答案是不行。SPI 是全双工通信主机和从机同时发送和接收数据。如果采样和移出在同一个边沿那么发送方刚把数据推出去接收方就要在同一时刻采样这时候信号线上的电平可能还没稳定采到的就是亚稳态或者错误数据。所以 SPI 协议规定一个边沿用来移出数据也就是发送方改变数据线电平另一个边沿用来采样数据接收方读取数据线电平。两个边沿之间隔了半个时钟周期给了信号足够的建立时间和保持时间。这个设计思路和 I2C 有点像但 I2C 是半双工而且有应答机制SPI 没有应答全靠时序对齐。所以一旦模式配错数据就全乱了而且没有任何错误提示你只能看到读出来的数据是 0x00 或者 0xFF或者一堆乱码。1.3 四种模式的实际使用分布虽然协议定义了四种模式但实际项目中并不是平均分布的。根据我这些年的经验Mode 0 和 Mode 3 占了绝大多数场景Mode 1 和 Mode 2 相对少见但偏偏有些芯片就只支持这两种。常见器件的模式分布大概是这样的SPI FlashW25Q 系列、GD25 系列通常支持 Mode 0 和 Mode 3手册里一般写“Mode 0 or Mode 3”。ADC 芯片如 MCP3208、ADS1256MCP3208 是 Mode 0/1ADS1256 是 Mode 1。传感器如 MPU6500、BMP280多数是 Mode 0 或 Mode 3。显示屏如 ST7789、ILI9341通常是 Mode 0 或 Mode 3。某些专用芯片如 MT6701 磁编码器Mode 1 或 Mode 3具体要看手册。所以你在选型或者调试的时候第一件事就是翻手册确认从机支持哪种模式。如果手册写了“支持 Mode 0 和 Mode 3”那你就优先用 Mode 0因为大多数 MCU 的默认配置就是 Mode 0省事。2. 时钟极性和相位的底层逻辑2.1 CPOL 决定了时钟线的“休息状态”CPOL 这个参数看起来简单但它影响的是整个通信过程中的电平基准。如果 CPOL0时钟线在空闲时是低电平那么第一个有效边沿就是上升沿。如果 CPOL1时钟线空闲时是高电平第一个有效边沿就是下降沿。这里有一个容易忽略的细节CPOL 不仅影响空闲状态还影响 CS 拉低到第一个时钟边沿之间的电平。有些从机芯片对这段时间的时钟线状态有要求如果 CPOL 配错了从机可能会误判为一次无效的起始条件直接忽略后面的数据。我遇到过这样一个案例用 STM32F103 驱动一颗国产 SPI FlashMode 0 死活读不出 ID换成 Mode 3 就正常了。后来用示波器抓波形发现Mode 0 下 CS 拉低后时钟线有一个短暂的毛刺虽然很窄但 Flash 芯片把它当成了一个时钟边沿导致后续数据错位。换成 Mode 3 之后空闲电平是高毛刺被淹没了通信就正常了。这个案例说明CPOL 的选择不只是协议匹配问题还和硬件信号质量有关。2.2 CPHA 决定了数据在哪个边沿被锁存CPHA 的选择直接决定了数据采样时刻。CPHA0 时数据在第一个边沿被采样也就是说发送方必须在第一个边沿之前就把数据准备好。CPHA1 时数据在第二个边沿被采样发送方可以在第一个边沿时改变数据接收方在第二个边沿采样。用生活化的例子来理解假设你和另一个人隔着一条传送带传递零件。CPHA0 就像是你先把零件放在传送带上然后按下启动按钮第一个边沿对方在按钮按下的瞬间就把零件取走。CPHA1 就像是你按下启动按钮第一个边沿之后传送带开始动你在传送带移动的过程中把零件放上去对方在传送带停止第二个边沿时取走零件。这个类比不完全精确但能帮你理解为什么 CPHA1 时数据建立时间更充裕。实际上CPHA1 的时序对高速通信更友好因为发送方有半个时钟周期的时间来稳定数据线。但 CPHA0 的时序更简单适合低速场景。2.3 四种模式的波形对比光看表格不够直观我们直接看波形。假设要发送一个字节 0xA5二进制 10100101MSB 先发四种模式的波形差异如下Mode 0CPOL0, CPHA0时钟空闲低电平第一个上升沿采样。数据在下降沿改变上升沿采样。所以你在示波器上会看到数据线在时钟下降沿翻转在上升沿中间位置稳定。Mode 1CPOL0, CPHA1时钟空闲低电平第二个边沿下降沿采样。数据在上升沿改变下降沿采样。数据线的翻转发生在时钟上升沿采样发生在下降沿。Mode 2CPOL1, CPHA0时钟空闲高电平第一个边沿下降沿采样。数据在上升沿改变下降沿采样。和 Mode 0 刚好镜像。Mode 3CPOL1, CPHA1时钟空闲高电平第二个边沿上升沿采样。数据在下降沿改变上升沿采样。和 Mode 1 刚好镜像。如果你手头有示波器或者逻辑分析仪建议实际抓一下四种模式的波形对比看一遍比看十遍文字都管用。没有仪器的话可以用 MCU 的 SPI 外设配合 GPIO 模拟用串口打印时序状态也能大致验证。3. 不同平台上的模式配置实战3.1 STM32 HAL 库配置 SPI 模式STM32 的 HAL 库把 SPI 模式配置封装得很简单在SPI_HandleTypeDef结构体里有两个字段CLKPolarity和CLKPhase。配置 Mode 0 的代码如下hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; // CPOL 0 hspi1.Init.CLKPhase SPI_PHASE_1EDGE; // CPHA 0 hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_8; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 10; HAL_SPI_Init(hspi1);如果要改成 Mode 3只需要把CLKPolarity改成SPI_POLARITY_HIGHCLKPhase改成SPI_PHASE_2EDGE。Mode 1 是SPI_POLARITY_LOWSPI_PHASE_2EDGEMode 2 是SPI_POLARITY_HIGHSPI_PHASE_1EDGE。用 CubeMX 的话更简单在 SPI 配置界面直接选 CPOL 和 CPHA 就行生成的代码会自动填好这两个字段。但要注意CubeMX 默认是 Mode 0如果你用的从机是 Mode 3记得手动改。注意STM32F1 系列的 SPI 在 Mode 0 和 Mode 3 下都能正常工作但 Mode 1 和 Mode 2 在某些批次芯片上可能存在时序偏差建议优先选 Mode 0 或 Mode 3。3.2 FPGA 中手动实现 SPI 时序FPGA 没有现成的 SPI 外设除非用硬核通常需要自己写状态机。这时候四种模式的差异就体现在状态机的状态转移上。以 Mode 0 为例一个简单的发送状态机大概是这样的// Mode 0: CPOL0, CPHA0 // 空闲时 sclk 0 // 第一个上升沿采样下降沿改变数据 always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; sclk 0; mosi 0; end else begin case (state) IDLE: begin sclk 0; if (start) begin state SEND_BIT; bit_cnt 7; shift_reg tx_data; end end SEND_BIT: begin // 下降沿改变数据 sclk 0; mosi shift_reg[7]; shift_reg {shift_reg[6:0], 1b0}; state SAMPLE_BIT; end SAMPLE_BIT: begin // 上升沿采样 sclk 1; rx_data {rx_data[6:0], miso}; if (bit_cnt 0) begin state IDLE; end else begin bit_cnt bit_cnt - 1; state SEND_BIT; end end endcase end end如果是 Mode 3只需要把空闲时的sclk改成 1采样和改变的边沿对调即可。FPGA 的好处是你可以精确控制每个边沿的时刻不受 MCU 外设限制。但坏处是代码量大调试麻烦尤其是高速通信时时序约束要写得很仔细。3.3 Linux spidev 下的模式设置在 Linux 系统上SPI 设备通常通过 spidev 接口访问。模式设置是在打开设备之后通过ioctl调用SPI_IOC_WR_MODE来完成的int fd open(/dev/spidev0.0, O_RDWR); uint8_t mode SPI_MODE_0; // 或者 SPI_MODE_1, SPI_MODE_2, SPI_MODE_3 ioctl(fd, SPI_IOC_WR_MODE, mode);Linux 内核里定义的SPI_MODE_0到SPI_MODE_3宏分别对应四种模式。如果你用的是设备树Device Tree也可以在设备树节点里直接指定spi-cpol和spi-cpha属性spi0 { status okay; spidev0 { compatible rohm,dh2228fv; reg 0; spi-max-frequency 1000000; spi-cpol; // CPOL 1 spi-cpha; // CPHA 1 }; };上面这个配置就是 Mode 3。如果只写spi-cpol不写spi-cpha那就是 Mode 2。两个都不写就是 Mode 0。提示在 Linux 上调试 SPI 时如果发现数据不对先用spidev_test工具跑一下自发自收测试确认模式配置正确后再接实际从机。4. 模式选错会怎样典型故障与排查4.1 读出来全是 0x00 或 0xFF这是最常见的症状。模式配错之后从机根本没有正确采样到主机发送的命令或者主机采样从机数据时采到了错误的位置导致读回来的数据全是 0x00 或者 0xFF。这时候不要急着怀疑硬件连接先检查模式配置。排查步骤很简单拿示波器或者逻辑分析仪抓 CS、SCLK、MOSI、MISO 四根线。先看 CS 拉低之后SCLK 的第一个边沿是上升还是下降判断 CPOL 是否匹配。再看 MOSI 上的数据是在哪个边沿稳定的判断 CPHA 是否匹配。如果 MOSI 数据在第一个边沿还没稳定就被采样了那就是 CPHA 配错了。4.2 数据偶尔正确偶尔错误这种间歇性故障最让人头疼。可能的原因有几个一是模式虽然对了但时钟频率太高信号建立时间不够二是 CPHA 配置在临界点上某些批次芯片能容忍某些不能三是硬件上缺少上拉或下拉电阻导致空闲电平时钟线不稳定。我遇到过一个案例用 STM32F103 通过 SPI 读取 MT6701 磁编码器的角度数据Mode 1 配置下低速时数据正常一旦把 SPI 时钟提到 10MHz 以上数据就开始跳变。后来用示波器看波形发现 MISO 上的数据在采样边沿附近有振铃导致采样不稳定。解决方法是在 MISO 线上串了一个 22 欧姆的电阻振铃明显减弱数据就稳定了。这个案例说明模式配置只是第一步信号完整性同样重要。4.3 从机完全无响应如果从机连 ID 都读不出来CS 拉低后 MISO 一直是高阻态或者固定电平那可能是模式差异太大从机根本没有进入通信状态。这时候可以尝试把四种模式挨个试一遍但更高效的方法是直接翻手册确认从机支持的模式。有些芯片的手册写得很隐晦比如只写“数据在时钟上升沿采样”没有明确说 CPOL 和 CPHA。这时候你需要根据这句话推断如果上升沿采样且空闲电平是低那就是 Mode 0如果空闲电平是高那就是 Mode 3。如果手册写了“数据在时钟下降沿采样”空闲低就是 Mode 1空闲高就是 Mode 2。4.4 常见问题速查表症状可能原因排查方法解决方案读出全 0x00CPHA 配错抓 MOSI 波形看数据稳定时刻切换 CPHA读出全 0xFFCPOL 配错抓 SCLK 空闲电平切换 CPOL数据偶尔错误时钟太快或信号振铃降低时钟频率测试降频或加匹配电阻从机无响应模式完全不匹配查手册确认支持模式换模式或换芯片多字节传输错位CS 时序问题抓 CS 和 SCLK 关系调整 CS 建立/保持时间5. 硬件片选与软件片选的模式影响5.1 硬件片选下的时序特点硬件片选NSS由 SPI 外设自动控制CS 拉低和拉高的时刻由硬件决定通常和时钟边沿对齐得很好。在 Mode 0 和 Mode 3 下硬件片选的时序比较标准不容易出问题。但在 Mode 1 和 Mode 2 下有些 MCU 的硬件片选会在第一个时钟边沿之前就拉低导致从机误判起始条件。STM32 的硬件 NSS 在 Mode 1 下有一个已知问题NSS 拉低后第一个时钟边沿是上升沿但 NSS 的拉低时刻可能和时钟边沿太近导致从机来不及准备。解决方法通常是改用软件片选手动控制 CS 的拉低和拉高时刻在 CS 拉低之后加一个小延时再开始时钟。5.2 软件片选的实际操作软件片选就是用一个普通 GPIO 来控制 CSSPI 外设只负责时钟和数据。这样做的好处是你可以精确控制 CS 的时序比如在 CS 拉低之后延时 1 微秒再发时钟在最后一个时钟之后延时 1 微秒再拉高 CS。很多对时序敏感的芯片比如某些 ADC 和 Flash都要求 CS 建立时间和保持时间硬件片选可能满足不了软件片选就灵活得多。用 STM32 HAL 库实现软件片选的代码大概是这样// CS 拉低 HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); // 延时满足建立时间 for (volatile int i 0; i 10; i); // 发送数据 HAL_SPI_Transmit(hspi1, tx_buf, len, 100); // 延时满足保持时间 for (volatile int i 0; i 10; i); // CS 拉高 HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);这个延时循环的时长需要根据从机手册的时序要求来定。一般来说CS 建立时间tCSS和保持时间tCSH在几十纳秒到几微秒之间具体看芯片。5.3 DMA 传输下的模式注意事项用 DMA 传输 SPI 数据时模式配置本身不变但要注意 DMA 的传输完成中断和 CS 拉高的时机。如果 DMA 传输还没完成就拉高 CS最后一个字节可能丢失。STM32 的 HAL 库提供了HAL_SPI_Transmit_DMA和HAL_SPI_Receive_DMA在 DMA 完成回调里拉高 CS 比较稳妥。另外DMA 传输时如果模式配错症状和普通传输一样但排查起来更麻烦因为 DMA 是后台搬运你看不到中间状态。建议先用普通传输调通模式再切换到 DMA。6. 高速通信下的模式选择与信号完整性6.1 模式对最高时钟频率的影响四种模式对最高时钟频率的容忍度是不一样的。一般来说Mode 0 和 Mode 3 能跑到更高的频率因为采样边沿和移出边沿的间隔比较标准。Mode 1 和 Mode 2 在某些芯片上高速时容易出现建立时间不足的问题。以 STM32F103 为例SPI1 最高可以跑到 18MHzAPB2 72MHz 分频 4但在 Mode 1 下实测超过 12MHz 就开始出现误码。Mode 0 和 Mode 3 可以稳定跑到 18MHz。这个差异在选型阶段就要考虑进去如果从机只支持 Mode 1那就要评估 MCU 能不能在目标频率下稳定工作。6.2 信号完整性对模式匹配的干扰高速 SPI 通信中信号完整性是绕不开的话题。时钟线上的过冲、振铃、串扰都会影响采样时刻的电平判断。如果 CPHA 配置在临界点上信号完整性问题会被放大导致间歇性误码。改善信号完整性的常用手段包括在时钟线上串匹配电阻通常 22 到 33 欧姆缩短走线长度增加地平面避免时钟线和数据线平行走线。这些措施在 Mode 0 和 Mode 3 下效果比较明显因为采样边沿和信号稳定区间的重叠度更高。6.3 实测波形对比与经验值我用逻辑分析仪抓过四种模式在 10MHz 下的波形发现一个规律Mode 0 和 Mode 3 的数据线在采样边沿附近最稳定眼图张开度最大。Mode 1 和 Mode 2 的数据线在采样边沿附近有轻微抖动眼图张开度小一些。这个差异在低速时不明显但频率越高越显著。所以我的经验是如果从机同时支持 Mode 0 和 Mode 3优先选 Mode 0如果从机只支持 Mode 1 或 Mode 2尽量把时钟频率降到 10MHz 以下并做好信号完整性处理。7. 几个容易踩的坑和实操心得7.1 不要迷信“四个模式挨个试”很多教程教你“SPI 不通就把四个模式挨个试一遍”这个方法在低速、短距离、单从机的情况下确实能碰对但效率极低而且掩盖了真正的问题。正确的做法是先查手册确认从机支持的模式再用示波器验证波形最后才考虑换模式。7.2 注意从机手册里的“隐藏模式”有些芯片手册写的是“支持 Mode 0 和 Mode 3”但实际上只支持其中一种另一种是兼容性描述。比如某些国产 Flash 芯片手册写支持 Mode 0/3但实测 Mode 3 下写入操作会失败只有 Mode 0 能正常读写。这种情况只能通过实测发现所以新芯片上手时建议把手册声称支持的模式都测一遍。7.3 多从机共享 SPI 总线时的模式冲突如果一条 SPI 总线上挂了多个从机而它们的模式要求不一样那就麻烦了。SPI 总线上的所有设备必须使用相同的模式因为时钟极性和相位是总线级别的参数不能针对每个从机单独配置。如果从机模式冲突只能通过软件切换模式或者在两次通信之间重新配置 SPI 外设。这种做法在 Linux 上比较常见通过 spidev 的 ioctl 动态切换模式但切换开销比较大不适合高频通信。7.4 逻辑分析仪的采样率要足够高用逻辑分析仪抓 SPI 波形时采样率至少要是 SPI 时钟频率的 10 倍以上。比如 SPI 跑 10MHz逻辑分析仪采样率至少要 100MHz否则抓到的波形会失真边沿位置判断不准反而误导排查方向。我早期用过一个 24MHz 采样率的逻辑分析仪抓 8MHz 的 SPI波形看起来全是毛刺换了 200MHz 的之后才发现实际波形很干净。7.5 模式配置和 GPIO 复用要一起检查STM32 的 SPI 引脚需要配置为复用推挽输出AF_PP如果配成了普通推挽输出时钟和数据线可能出不来波形或者波形幅度不对。这个问题在 CubeMX 里一般不会出现因为 CubeMX 会自动配置复用功能但手动写初始化代码时容易漏掉。检查方法是看 GPIO 的Mode字段是不是GPIO_MODE_AF_PP。8. 从模式选择延伸到协议理解SPI 的四种模式看似只是两个参数的组合但它背后反映的是同步串行通信的基本设计哲学发送方和接收方必须对“什么时候数据有效”达成一致。CPOL 解决的是“空闲时线在哪”CPHA 解决的是“第几个边沿算数”。这两个问题在任何同步通信协议里都存在只是 SPI 把它显式地参数化了。理解了这一点你再去看 I2C、UART 甚至一些自定义的同步协议会发现很多相似的设计思路。比如 I2C 的时钟极性是固定的空闲高但数据采样边沿也是固定的上升沿所以 I2C 不需要模式配置。UART 是异步通信没有时钟线靠波特率对齐所以也不需要模式配置。SPI 之所以有四种模式是因为它把时钟的灵活性留给了用户代价就是配置复杂度增加。我在实际项目中养成了一个习惯拿到任何 SPI 从机第一件事不是写代码而是翻到时序图那一页用笔把 CPOL 和 CPHA 标出来然后对照 MCU 的配置选项确认一遍。这个习惯帮我省了很多调试时间也避免了不少“明明代码没问题但就是不通”的困惑。最后分享一个小技巧如果你手头没有示波器可以用 MCU 的一个 GPIO 在 SPI 传输的关键时刻翻转电平然后用另一个 GPIO 读取从机的 MISO 状态通过串口打印出来。虽然精度不高但能大致判断数据是在哪个边沿被采样的。这个方法在紧急调试时救过我好几次。