SPI通信协议详解:从四线制到实战避坑指南

📅 发布时间:2026/8/25 10:34:29
SPI通信协议详解:从四线制到实战避坑指南
1. 从“一根线”到“一捆线”为什么我们需要SPI如果你玩过单片机或者FPGA肯定对“通信协议”这个词不陌生。在数字世界里芯片之间要“说话”就得遵守一套约定好的规则。最开始你可能接触的是UART也就是串口它简单只需要两根线TX和RX就能实现全双工通信但速度慢而且一个主机通常只能和一个从机“单聊”。后来你遇到了I2C它用两根线SDA和SCL就能挂上一堆设备通过地址来寻址像个小型局域网但它的速度也有限而且协议开销相对复杂。那么当你的传感器数据量剧增比如高精度ADC、你的显示屏需要高速刷新比如OLED、或者你的存储芯片需要被快速读写比如Flash时UART和I2C可能就有点力不从心了。这时候你就需要一种更“直接”、更“快”的聊天方式——这就是SPISerial Peripheral Interface串行外设接口。SPI的本质是一种高速、全双工的同步串行通信总线。它不像I2C那样有复杂的起始、停止、应答信号它的通信更像是一种“流式”的、基于时钟节拍的数据交换。主机产生时钟从机在时钟的边沿采样或输出数据简单粗暴效率极高。你可以把它想象成工厂里的流水线主机是流水线的控制台提供节拍时钟从机是线上的工人根据节拍同步操作数据就是传送带上的零件在时钟的每一个“滴答”声中一位数据被稳稳地传递过去。这种设计带来的直接好处就是速度。SPI的时钟频率可以轻松达到几十MHz甚至上百MHz取决于芯片和硬件设计远高于常见的I2C通常400kHz或1MHz和UART通常以几百kbps为上限。同时因为是全双工数据可以同时收和发进一步提升了吞吐效率。当然天下没有免费的午餐SPI的高速和简单是以牺牲引脚资源和硬件复杂度为代价的——它通常需要4根线而且每个从机都需要一根独立的片选线。当从机数量多时引脚占用会非常可观。所以当你看到项目里出现AD2S1210旋变解码芯片、SPI Flash、SD卡、WS2812有些驱动芯片支持SPI模式这些器件时SPI几乎就是标配。在STM32、ESP32、HC32、国民芯片等MCU上在Vivado里配置FPGA的SPI控制器时你都是在和这个高效而直接的协议打交道。接下来我们就一层层剥开SPI的“洋葱”看看它到底是怎么工作的以及在实际项目中如何用好它。2. SPI的“四线制”与核心工作模式要理解SPI首先得认清它的物理构成。标准的SPI接口需要4根信号线这也是它和UART、I2C最直观的区别。2.1 四根关键信号线详解SCK (Serial Clock串行时钟)由主机产生并输出给所有从机。这是整个通信的“心跳”所有数据的移入和移出都严格跟随这个时钟的边沿进行。没有时钟通信就无法进行。MOSI (Master Out Slave In主机输出从机输入)数据从主机流向从机。在时钟的驱动下主机通过这根线一位一位地把数据发送给从机。MISO (Master In Slave Out主机输入从机输出)数据从从机流向主机。同样在时钟驱动下从机通过这根线把数据发回给主机。注意在全双工通信中MOSI和MIO是同时工作的。CS/SS (Chip Select / Slave Select片选/从机选择)这是关键中的关键。通常由主机控制低电平有效。当主设备需要和某个从设备通信时就把对应从设备的CS线拉低激活其他从设备的CS线保持高电平无效。被选中的从机才会响应SCK时钟并在MISO上输出数据未被选中的从机必须将其MISO引脚置于高阻态以避免总线冲突。这就是为什么SPI从机多了会占用大量GPIO的原因——每个从机都需要一根独立的CS线。有些器件可能只有3根线比如只有单向数据通信或者把MOSI和MISO合并成一根双向数据线半双工模式但四线制是全功能SPI的典型配置。2.2 时钟极性(CPOL)与时钟相位(CPHA)SPI的四种“方言”这是SPI最让人困惑但也必须彻底搞清楚的概念。SPI协议本身没有严格规定数据在时钟的哪个边沿采样这给了器件厂商灵活性但也导致了兼容性问题。CPOL和CPHA这两个参数共同定义了数据采样和锁存的时刻。CPOL (Clock Polarity时钟极性)定义SCK线在空闲状态即CS有效、但数据传输尚未开始或已经结束时的电平。CPOL0SCK空闲时为低电平。CPOL1SCK空闲时为高电平。CPHA (Clock Phase时钟相位)定义数据在时钟的第几个边沿被采样。CPHA0数据在时钟的第一个边沿对于CPOL0是上升沿对于CPOL1是下降沿被采样在第二个边沿发生切换。CPHA1数据在时钟的第二个边沿被采样在第一个边沿发生切换。它们组合起来就形成了SPI的四种模式通常称为Mode 0, Mode 1, Mode 2, Mode 3。模式CPOLCPHA空闲时SCK电平数据采样时刻数据切换时刻常见器件Mode 000低电平第一个边沿上升沿第二个边沿下降沿很多SPI Flash如W25Q系列、SD卡初始化后Mode 101低电平第二个边沿下降沿第一个边沿上升沿一些ADC、传感器Mode 210高电平第一个边沿下降沿第二个边沿上升沿较少见Mode 311高电平第二个边沿上升沿第一个边沿下降沿一些RFID芯片、特定型号Flash一个快速记忆和判断的方法看数据手册的时序图。找到数据线如MOSI稳定Setup和保持Hold的时间窗口这个窗口的中心点所对应的SCK边沿就是采样边沿。确定了采样边沿再看SCK空闲电平就能推出CPOL和CPHA。为什么模式必须匹配如果主机和从机的模式不匹配采样边沿就对不上。主机可能在上升沿发送数据但从机却在下降沿采样读到的就会全是错位的数据可能全是0或1或者乱码。在初始化任何SPI外设时第一件事就是根据从机数据手册确定其工作模式并将主机配置为相同模式。3. 深入时序数据是如何流动的理解了四根线和四种模式我们来看数据在线上具体是如何传输的。SPI通信以字节8位或字16位等为单位进行。我们以最常用的Mode 0 (CPOL0, CPHA0)为例拆解一个字节的传输过程。准备阶段主机将目标从机的CS线拉低。此时SCK线处于空闲低电平CPOL0。第一个时钟周期第一个上升沿由于CPHA0数据在**第一个边沿上升沿**被采样。因此在SCK上升沿到来之前发送方主机在MOSI上从机在MISO上必须已经将第一位数据通常是最高位MSB或最低位LSB由器件决定放到数据线上并保持稳定满足建立时间t_SU。SCK上升沿到来时接收方从机在MOSI上主机在MISO上采样数据线上的电平将其读入移位寄存器。第一个时钟周期随后的下降沿这是数据切换的时刻。发送方可以在这个边沿将下一位数据放到数据线上。重复步骤2和3在接下来的7个时钟周期里重复“上升沿采样下降沿切换”的过程直到8位数据全部传输完毕。结束阶段传输完成后主机将CS线拉高。SCK恢复到空闲低电平。关于数据位顺序SPI协议本身没有规定先传最高位(MSB)还是最低位(LSB)。这完全由器件决定必须在数据手册中确认。例如很多SPI Flash默认是MSB first而有些ADC可能是LSB first。如果顺序搞反你读到的数据值将是完全错误的位序颠倒。关于全双工在上述过程中MOSI和MISO是同时工作的。主机在发送一个字节的同时也从从机那里接收一个字节。这个接收到的字节可能是有意义的比如读取传感器数据也可能是“哑元”Dummy Byte通常为0xFF或0x00具体取决于从机在接收到命令后的响应机制。许多读操作命令都是主机先发一个命令字比如Flash的读指令0x03然后发一个地址接着主机继续发时钟可能伴随发送哑元从机则在MISO上返回数据。4. 硬件SPI与软件模拟SPI如何选择与实现当你需要在MCU或FPGA上使用SPI时通常有两种实现方式硬件SPI和软件模拟SPIBit-Banging。4.1 硬件SPI效率之王现代MCU如STM32、ESP32、HC32F460、国民芯片等内部都集成了硬件SPI控制器。以STM32的SPI外设为例你只需要在CubeMX或代码中配置几个参数波特率预分频器决定SCK的频率。SCK频率 APB总线时钟 / 预分频值。不能超过从机支持的最大SCLK频率。数据帧格式数据位宽8位或16位、MSB/LSB顺序。CPOL和CPHA选择对应的工作模式。NSS管理片选信号的管理方式。可以是硬件管理NSS引脚自动控制也可以是软件管理用任意GPIO手动控制CS。在实际项目中我强烈推荐使用软件管理NSS即用普通GPIO当CS因为硬件NSS有时序限制不够灵活特别是需要操作多个从机时。配置好后你只需要把要发送的数据写入发送数据寄存器DR硬件就会自动完成时钟生成、数据移位、接收数据填充等一系列操作通常还会配合DMA来解放CPU实现高速、不间断的数据流传输。这是处理大数据量如图像刷新、音频流、大文件读写的首选方案。4.2 软件模拟SPI灵活性的救星软件模拟SPI就是用普通的GPIO口通过代码控制其高低电平变化来模拟出SCK、MOSI、MISO和CS的时序。比如在STC15这类没有硬件SPI的51内核单片机上或者当你MCU的硬件SPI引脚被其他功能占用时就必须用软件模拟。// 以Mode 0 MSB first为例的软件SPI发送一个字节函数伪代码 void Soft_SPI_WriteByte(uint8_t dat) { uint8_t i; CS_LOW(); // 拉低片选开始通信 for(i0; i8; i) { SCK_LOW(); // 确保在第一个边沿上升沿前数据已稳定 if(dat 0x80) // 判断最高位 MOSI_HIGH(); else MOSI_LOW(); Delay_Short(); // 短暂延时满足建立时间 SCK_HIGH(); // 产生上升沿从机在此刻采样MOSI dat 1; // 左移准备下一位 Delay_Short(); // 保持时间 SCK_LOW(); // 产生下降沿为下一位数据切换做准备 } CS_HIGH(); // 拉高片选结束通信 }软件模拟的优点显而易见引脚任意指定不受硬件限制。你可以把SPI信号映射到任何空闲的GPIO上。但缺点同样突出速度慢CPU占用率高。因为每一位数据的收发都需要CPU执行多条指令来操作GPIO速度很难超过1MHz且通信期间CPU被完全占用。它只适用于低速、非实时的场景。4.3 FPGA中的SPI实现极致的控制在FPGA如使用Verilog/VHDL中实现SPI又是另一番天地。这里没有现成的“外设”你需要从底层设计状态机来精确控制每一根信号线的时序。SPI主机设计你需要一个状态机来产生SCK根据模式CPOL/CPHA在正确的时钟边沿切换MOSI数据并在对应的边沿采样MISO数据。同时还要管理CS信号。在Vivado中你可以编写一个SPI Master的IP核精确控制时钟分频、数据位宽、模式等。这对于需要非常规SPI时序如非标准模式、极高速率、自定义帧格式的应用至关重要。SPI从机设计这比主机更有挑战性。从机需要检测CS信号下降沿然后在主机提供的SCK下同步地接收MOSI数据并输出MISO数据。你需要设计一个能同步于外部异步时钟SCK的电路并处理好跨时钟域的数据传递问题。网上能找到的Verilog SPI从机代码核心就是一个由SCK驱动的移位寄存器以及控制这个寄存器何时加载并行数据、何时输出串行数据的逻辑。经验之谈在MCU项目中优先使用硬件SPIDMA。只有当引脚冲突或从机时序非常特殊时才考虑软件模拟。在FPGA项目中如果只是读写标准SPI Flash通常使用Xilinx提供的AXI Quad SPI这类现成IP。如果需要定制化自己写状态机是基本功务必画好时序图再写代码。5. 多从机连接硬件片选与软件片选菊花链如何用一个SPI主机连接多个从机主要有三种拓扑结构。5.1 独立片选最常用这是最直观的方式。主机为每个从机分配一个独立的GPIO作为CS线。通信时只拉低目标从机的CS其他保持高电平。优点是逻辑简单各从机完全独立互不影响。缺点就是占用GPIO资源多。前面提到的hc32f460 spi、stm32f103c8 spi驱动tf卡等例程如果只驱动一个设备就是这种模式。5.2 硬件片选与地址解码有些高级的SPI外设或专用的SPI开关芯片支持通过SPI总线发送地址字节来选中内部的不同子设备或通道。例如一些多通道ADC芯片你首先通过硬件CS选中该芯片然后在发送的命令字节中包含通道选择位。这可以节省GPIO但需要从机支持该功能。spi channel 与 spi job这个概念在一些复杂的SPI驱动框架如Linux的SPI子系统中会出现一个物理SPI控制器上可以配置多个逻辑“通道”每个通道对应不同的片选和时序配置由驱动框架来调度和管理这些“job”。5.3 菊花链Daisy-Chain这是一种非常巧妙但有限制条件的方式。它将多个从机的SPI接口串接起来主机的MOSI连接到第一个从机的MOSI第一个从机的MISO连接到第二个从机的MOSI以此类推最后一个从机的MISO连接回主机的MISO。所有从机共享SCK和CS信号。工作原理主机拉低CS然后发出N个时钟周期N 从机数量 × 数据帧长度。数据从主机的MOSI进入第一个从机经过其移位寄存器后从它的MISO输出到第二个从机的MOSI……就像一个长长的移位寄存器组。同样从机返回的数据也依次通过这个链传回主机。优点极致节省连线只需要4根线SCK MOSI MISO CS即可连接任意多个从机。缺点所有从机必须同时工作。主机每次通信链上所有从机都会移位无法单独访问某一个。数据是广播和串联的。主机发送的数据第一个从机收到的是第一个字第二个从机收到的是第二个字即第一个从机移出的数据以此类推。编程模型复杂。对从机有要求。不是所有SPI从机都支持菊花链模式需要其数据输入和输出在内部是直通的。 这种模式常用于LED驱动芯片如某些WS2812的SPI控制方案、多个级联的移位寄存器等场景。6. 实战避坑指南从配置到走线的常见问题理论懂了一上手就懵下面这些坑我几乎每个都踩过。6.1 模式与相位配置错误这是新手第一坑。症状能读到数据但数据全是错的、固定的0xFF或0x00或者毫无规律。排查反复核对从机数据手册的时序图确认CPOL和CPHA。用逻辑分析仪或示波器抓取SCK、MOSI、CS波形对照时序图看数据建立和采样边沿是否对齐。一个技巧如果发现读到的数据总是0xFF很可能从机根本没有在MISO上输出数据检查CS是否有效、从机是否上电、模式是否正确。6.2 片选(CS)时序问题CS信号不仅仅是“开关”。它的时序也很关键。建立时间在SCK开始产生之前CS必须已经有效拉低一段时间。保持时间在最后一个SCK时钟边沿之后CS必须继续保持有效一段时间才能拉高。两次传输之间的间隔CS拉高后需要等待一段时间才能再次拉低开始下一次传输。 许多芯片的数据手册会明确给出这些时间参数如t_CS_SU, t_CS_H。如果忽略在高速通信时可能导致数据错位或丢失。软件模拟SPI时要在CS操作前后加短暂延时硬件SPI配置时注意NSS信号的管理模式。6.3 时钟频率过高“我的SPI配置成了10MHz为什么通信不稳定” 请检查以下几点从机最大支持频率每个SPI从机都有最高SCLK频率限制比如有些Flash是50MHz有些ADC只有2MHz。超频运行会导致采样失败。PCB走线质量SPI是高速信号。如果走线过长、过细或者有尖锐弯角会产生信号反射、边沿退化在高频下导致误码。spi走线有讲究应尽量短、粗、直远离干扰源必要时串联小电阻如22欧姆进行阻抗匹配减少振铃。电源噪声高速数字通信对电源干净度要求高。确保电源引脚有足够的去耦电容如0.1uF陶瓷电容紧贴芯片电源引脚。6.4 数据位顺序(MSB/LSB)弄反症状读出的数据看起来“像是”正确的但数值完全对不上或者某些位序看起来是颠倒的。比如读一个16位传感器数据0x1234结果读出来是0x2C480x1234的位反转。务必检查数据手册的“Bit Order”说明。6.5 未正确处理MISO高阻态当SPI总线上挂有多个从机时未被选中的从机必须将其MISO引脚置于高阻态三态。如果某个从机的MISO是推挽输出且未被禁用它会和当前被选中的从机在MISO总线上“打架”导致数据错误甚至损坏硬件。确保你的从机驱动代码或硬件在CS无效时正确配置MISO引脚为高阻输入或关闭输出。6.6 FPGA实现中的跨时钟域问题在用Verilog写SPI从机时SCK是外部输入与FPGA内部的主时钟是异步的。如果你直接用内部时钟去采样SCK或由SCK产生的信号就会面临亚稳态风险。正确的做法是使用同步器两级或多级寄存器对SCK和CS信号进行同步化处理或者使用专门的双时钟FIFO来处理跨时钟域的数据流。网上一些简单的verilog spi从机代码可能忽略了这一点在高速或严苛环境下会不稳定。7. 进阶话题SPI的变体与相关生态7.1 Quad-SPI和QPI为了进一步提升速度尤其是针对Flash存储器出现了Quad-SPI。它将数据线从1根MOSIMISO增加到了4根IO0~IO3每个时钟周期可以传输4位数据吞吐量直接翻四倍。QPI模式则更进一步将命令和地址也通过这4根线发送完全告别了单线模式。你在vivado生成bit文件 spi速率时如果选择Quad SPI Flash作为配置存储器就是在利用这个高速接口。7.2 SPI与I2C、UART的对比这也是面试常考题。简单总结SPI高速MHz级全双工硬件简单但引脚多无应答机制主从通信。I2C中低速kHz到低MHz半双工硬件简单引脚少有应答支持多主多从有设备地址。UART低速通常到几百kbps全双工硬件简单点对点异步通信有起始位、停止位无时钟线。选择谁要速度和大数据量选SPI要省引脚和连接多个设备选I2C要简单、远距离、点对点选UART。7.3 在RTOS或Linux下的SPI驱动在复杂系统中SPI通常由驱动层管理。在FreeRTOS等RTOS中你可能会用信号量来保护SPI总线防止多个任务同时访问。在Linux下SPI是一个完整的子系统/dev/spidevX.X你可以通过ioctl调用配置模式、速度通过read/write进行传输。spi channel 与 spi job的概念在这里更清晰一个物理控制器可以虚拟出多个逻辑设备。SPI接口以其简洁高效的核心思想在嵌入式世界中占据了不可动摇的地位。从8位单片机到32位ARM再到可编程逻辑FPGA掌握SPI的每一处细节意味着你能驾驭从传感器、存储器到显示屏的绝大多数低速外设。理解协议本身只是第一步更重要的是在真实的电路板、真实的代码和真实的调试器中去感受时钟边沿的跳动去解决那些千奇百怪的时序问题。下次当你用STM32驱动一块SPI Flash或者用FPGA读取一个高速ADC时希望这篇文章能帮你少走些弯路更快地让数据流畅地跑起来。