云计算百科
云计算领域专业知识百科平台

SPI 模式错收发错位:CPOL/CPHA 与从设备相反,对照 datasheet 时序图对齐

SPI 模式错收发错位:CPOL/CPHA 与从设备相反,对照 datasheet 时序图对齐

一、开篇:一个真实翻车现场

用 SPI 读一片 ADC(STM32F4 主,ADC 从),发命令、收数据,结果读出来的值整体错位一位,或者干脆全 0xFF / 全 0x00。我确认过接线、CS 时序、字节序都对,折腾半天才发现:SPI 的时钟极性/相位(CPOL/CPHA)和 ADC datasheet 要求的不一致——主机在错误的边沿采样,数据整体错一位。

把 SPI_InitStruct.CLKPhase 和 CLKPolarity 按 datasheet 时序图改对后,数据立刻正常。这是 SPI 最经典也最容易被忽略的坑。

根因一句话:SPI 有 4 种模式(由 CPOL 时钟极性、CPHA 时钟相位组合),决定"空闲时时钟电平"和"在时钟的哪个边沿采样数据"。主机和从机模式必须一致,否则采样边沿错一位,收到的数据整帧错位/反相。

适用读者:SPI 通信读出乱数据、全 0xFF/0x00、错位一位的同学。
读完你能做:对照从机 datasheet 时序图,确定 CPOL/CPHA,把主机 SPI 模式配一致,根治错位。

二、先搞懂:CPOL/CPHA 到底定义什么(认知)

2.1 两个参数

  • CPOL(时钟极性):0=空闲时 SCK 低;1=空闲时 SCK 高。
  • CPHA(时钟相位):0=在 SCK 第一个边沿(CPOL 决定的"起始边沿")采样数据;1=在 SCK 第二个边沿采样。

组合出 4 种模式(0~3)。主从必须同一模式,否则采样边沿差半个时钟,数据错一位。

2.2 为什么错位

假设从机在"上升沿锁存、下降沿输出",要求模式 (CPOL=0, CPHA=1)(第二个边沿=下降沿采样,从机在上升沿改变数据)。若主机配成模式 0(第一个边沿=上升沿采样),它会在从机"刚改变数据"的瞬间去采样 → 采到的是上一比特的尾巴 → 整帧错位。

模式CPOLCPHA采样边沿
0 0 0 上升沿(第一个)
1 0 1 下降沿(第二个)
2 1 0 下降沿(第一个)
3 1 1 上升沿(第二个)

#mermaid-svg-X5Pagf30ZqCbIb3C{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-X5Pagf30ZqCbIb3C .error-icon{fill:#552222;}#mermaid-svg-X5Pagf30ZqCbIb3C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-X5Pagf30ZqCbIb3C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-X5Pagf30ZqCbIb3C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-X5Pagf30ZqCbIb3C .marker.cross{stroke:#333333;}#mermaid-svg-X5Pagf30ZqCbIb3C svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-X5Pagf30ZqCbIb3C p{margin:0;}#mermaid-svg-X5Pagf30ZqCbIb3C .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C .cluster-label text{fill:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C .cluster-label span{color:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C .cluster-label span p{background-color:transparent;}#mermaid-svg-X5Pagf30ZqCbIb3C .label text,#mermaid-svg-X5Pagf30ZqCbIb3C span{fill:#333;color:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C .node rect,#mermaid-svg-X5Pagf30ZqCbIb3C .node circle,#mermaid-svg-X5Pagf30ZqCbIb3C .node ellipse,#mermaid-svg-X5Pagf30ZqCbIb3C .node polygon,#mermaid-svg-X5Pagf30ZqCbIb3C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-X5Pagf30ZqCbIb3C .rough-node .label text,#mermaid-svg-X5Pagf30ZqCbIb3C .node .label text,#mermaid-svg-X5Pagf30ZqCbIb3C .image-shape .label,#mermaid-svg-X5Pagf30ZqCbIb3C .icon-shape .label{text-anchor:middle;}#mermaid-svg-X5Pagf30ZqCbIb3C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-X5Pagf30ZqCbIb3C .rough-node .label,#mermaid-svg-X5Pagf30ZqCbIb3C .node .label,#mermaid-svg-X5Pagf30ZqCbIb3C .image-shape .label,#mermaid-svg-X5Pagf30ZqCbIb3C .icon-shape .label{text-align:center;}#mermaid-svg-X5Pagf30ZqCbIb3C .node.clickable{cursor:pointer;}#mermaid-svg-X5Pagf30ZqCbIb3C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-X5Pagf30ZqCbIb3C .arrowheadPath{fill:#333333;}#mermaid-svg-X5Pagf30ZqCbIb3C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-X5Pagf30ZqCbIb3C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-X5Pagf30ZqCbIb3C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X5Pagf30ZqCbIb3C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-X5Pagf30ZqCbIb3C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X5Pagf30ZqCbIb3C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-X5Pagf30ZqCbIb3C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-X5Pagf30ZqCbIb3C .cluster text{fill:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C .cluster span{color:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-X5Pagf30ZqCbIb3C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-X5Pagf30ZqCbIb3C rect.text{fill:none;stroke-width:0;}#mermaid-svg-X5Pagf30ZqCbIb3C .icon-shape,#mermaid-svg-X5Pagf30ZqCbIb3C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X5Pagf30ZqCbIb3C .icon-shape p,#mermaid-svg-X5Pagf30ZqCbIb3C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-X5Pagf30ZqCbIb3C .icon-shape rect,#mermaid-svg-X5Pagf30ZqCbIb3C .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X5Pagf30ZqCbIb3C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-X5Pagf30ZqCbIb3C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-X5Pagf30ZqCbIb3C :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

如 CPOL0 CPHA1

读datasheet时序图

从机模式?

主机配Mode1

采样边沿一致 数据正确

主机配错模式

采样错位一位 乱数据

图 1:SPI 模式对齐流程(如图 1 所示,配错即错位)。

[注意] 很多 datasheet 不直接写"Mode 1",而是画时序图:看"空闲 SCK 电平"(定 CPOL)+“数据在时钟哪个边沿稳定/变化”(定 CPHA)。要学会从图读,而非盲猜。

三、为什么会错位(原理 + 真实错误代码)

3.1 我最初的写法(错误版)

// ❌ 错误写法:随手配了 Mode0,没对照从机 datasheet
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0 → Mode0
// 实际从机 ADC 要求 Mode3(CPOL=1,CPHA=1),采样边沿全错

错在哪:

  • 我按"常见默认"选了 Mode0,没查 ADC 手册。
  • ADC 实际在 Mode3 下才正确锁存:它在 SCK 下降沿改变数据、上升沿(第二个,因 CPOL=1 空闲高)被主机采样。
  • 主机 Mode0 在第一个上升沿就采样,此时从机数据还没稳定 → 采到错位比特。
  • 结果整帧错位,表现为值乱、或边界位全 0/全 1。
  • [坑] 铁律:SPI 主从模式必须一致。配之前先读从机 datasheet 时序图定 CPOL/CPHA,别用"默认 Mode0"盲配。

    3.2 为什么"有时看起来对"

    某些从机对模式容错高(如纯移位寄存器类),或数据本身多位相同,错位一位不易察觉;一旦数据变化频繁/边界位重要,立刻暴露。也有的从机内部做了双边沿采样兜底,掩盖了问题——但别依赖运气。

    配置结果
    主从模式一致 正确
    主从模式差一位 数据错位

    四、正确解法:对照 datasheet 配模式(完整落地)

    4.1 方案对比

    做法可靠性
    盲配 Mode0 易错
    读时序图定 CPOL/CPHA 正解

    4.2 配置(照着点)

    CubeMX:SPI → Configuration → Parameter Settings 里 CPOL / CPHA 按从机手册选。或代码里 hspi1.Init.CLKPolarity/CLKPhase 设对后 HAL_SPI_Init。

    4.3 对照方法(非代码,关键步骤)

  • 打开从机 datasheet 的 SPI 时序图。
  • 看"CS 无效(高)时 SCK 电平" → 定 CPOL(低=0,高=1)。
  • 看"数据在 SCK 哪个边沿保持稳定让主机采":若数据在第一个边沿前已稳定 → CPHA=0;若数据在第一个边沿改变、第二个边沿稳定 → CPHA=1。
  • 主机配相同 CPOL/CPHA。
  • [坑] 两个翻车点收好:

  • 不读 datasheet 盲配 Mode0——多数从机不是 Mode0,必错;先读图。
  • 只看文字"在上升沿采样"没结合 CPOL 判断是第几个边沿——CPHA=0 的"上升沿"在 CPOL=0 是第一个、在 CPOL=1 是第二个,必须连同 CPOL 看。
  • 4.4 改完的实测对比

    指标改前(Mode 错)改后(对齐)
    读出数据 错位/全 FF 正确
    抗变速 随速率变 稳定
    需重配 反复试 一次对照定

    五、收尾

    要点复盘

  • SPI 读出乱数据,先看 CPOL/CPHA 是否和从机一致——这是头号原因。
  • 4 种模式由 CPOL(空闲电平)+ CPHA(采样边沿)决定,主从必须相同。
  • 配之前读从机 datasheet 时序图定模式,别盲配 Mode0。
  • 最佳实践清单

    • SPI 接线后第一件事:对照从机时序图定 CPOL/CPHA。
    • CPOL 看空闲 SCK 电平,CPHA 看采样是第一个还是第二个边沿。
    • 主从模式不一致 → 数据错位一位,典型表现全 0xFF/0x00/乱值。
    • 多个不同从机时,每次切换 CS 前重配 SPI 模式(或选兼容模式的总线)。
    • 示波器抓 SCK+MISO,看采样边沿数据是否稳定,一眼验证模式对错。

    进阶延伸:有些器件支持"任意模式"(自同步),但多数严格;SPI 还有 MSB/LSB 先行(帧格式)也需和从机一致,别忘了查。

    互动:你 SPI 还踩过啥?MSB/LSB 反了、Word Size 不对、片选极性反?评论区交流。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » SPI 模式错收发错位:CPOL/CPHA 与从设备相反,对照 datasheet 时序图对齐
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!