1. SPI四种模式到底在解决什么问题SPI通信调不通十次里有八次栽在模式匹配上。我见过太多人对着示波器抓了半天波形最后发现从机手册上写的是Mode 3代码里配的是Mode 0。这种事不分新手老手因为SPI的四种模式本质上就是两个二进制位的组合但这两个位决定了数据在时钟的哪个边沿采样、空闲时时钟是高还是低一旦错位读回来的数据要么全FF要么全00要么就是看似有规律实则完全错乱的鬼数据。先把核心概念说清楚。SPI是同步串行通信主机产生时钟数据在时钟的驱动下一位一位地移入移出。这里有两个关键参数时钟极性CPOL和时钟相位CPHA。CPOL决定时钟空闲时是高电平还是低电平CPHA决定数据在第一个边沿还是第二个边沿被采样。两个参数各取0或1组合起来就是四种模式Mode 0CPOL0, CPHA0、Mode 1CPOL0, CPHA1、Mode 2CPOL1, CPHA0、Mode 3CPOL1, CPHA1。为什么要有四种模式因为不同厂商的芯片设计习惯不同。有的芯片在时钟上升沿输出数据、下降沿采样有的反过来。早期没有统一标准各家做各家的后来虽然大多数人用Mode 0和Mode 3但Mode 1和Mode 2依然存在于大量传感器、Flash、ADC芯片中。你没法要求所有芯片都统一只能让主机去适配从机。注意SPI模式没有“自动检测”机制。主机必须主动配置成从机要求的模式配错了通信必然失败。这一点和I2C不同I2C有起始条件和地址机制SPI什么都没有全靠事先约定。适合谁看这篇内容如果你正在用STM32、FPGA、或者任何MCU驱动SPI外设抓过波形但不确定采样边沿对不对或者手上有几个不同模式的SPI芯片要挂在同一条总线上那这篇内容就是给你写的。我会把四种模式的时序拆开讲配上实际配置代码和示波器抓波形的判断方法最后给一张速查表以后拿到任何SPI芯片三分钟内确定该用哪个模式。2. 四种模式时序的底层逻辑拆解2.1 CPOL和CPHA到底怎么影响数据采样先建立一个基本认知SPI通信的本质是移位寄存器。主机和从机各有一个8位或更多位的移位寄存器时钟每来一个边沿数据就移一位。问题是时钟有上升沿和下降沿两个边沿数据在哪个边沿“输出”、在哪个边沿“采样”必须提前约定好。CPOL0意味着时钟空闲时是低电平。当时钟开始工作时第一个边沿必然是上升沿。CPOL1则相反空闲时高电平第一个边沿是下降沿。CPHA0意味着数据在第一个边沿被采样。注意采样和输出是两回事。CPHA0时数据在第一个边沿之前就已经准备好输出第一个边沿到来时从机或主机读取数据。CPHA1则是在第二个边沿采样第一个边沿用来输出数据。把这两个参数组合起来四种模式的时序行为就清晰了模式CPOLCPHA空闲时钟第一个边沿采样边沿输出边沿Mode 000低上升上升下降Mode 101低上升下降上升Mode 210高下降下降上升Mode 311高下降上升下降这张表建议存下来。我调试SPI的时候经常对着它确认尤其是Mode 1和Mode 2这两个模式用得少容易记混。2.2 为什么Mode 0和Mode 3最常用实际项目中Mode 0和Mode 3的出现频率远高于Mode 1和Mode 2。原因不复杂这两种模式的采样边沿和输出边沿是“错开”的数据在半个时钟周期内保持稳定留给从机采样的窗口更大时序余量更充足。Mode 0的时序是这样的时钟空闲低电平第一个上升沿采样数据下降沿输出下一位。也就是说数据在上升沿被读走然后在下降沿切换。从机的数据建立时间是从上一个下降沿到当前上升沿有半个周期保持时间是从上升沿到下一个下降沿也有半个周期。这个对称性让Mode 0在高速通信时更稳定。Mode 3则是Mode 0的“镜像”空闲高电平第一个下降沿采样上升沿输出。逻辑一样只是极性反了。很多SPI Flash比如W25Q系列默认支持Mode 0和Mode 3因为这两种模式在高速下时序余量最好。Mode 1和Mode 2的问题在于采样边沿和输出边沿是同一个类型的边沿比如Mode 1都是上升沿输出、下降沿采样但第一个边沿是上升沿用于输出第二个边沿下降沿用于采样。这意味着数据在第一个边沿输出后只有半个周期就会被采样建立时间相对紧张。低速下没问题高速下容易出错。实操心得如果你在选型阶段有主动权尽量选支持Mode 0或Mode 3的芯片。如果不得不用Mode 1或Mode 2把SPI时钟先降到1MHz以下调通再逐步往上提观察波形是否出现采样错误。2.3 从机手册里怎么快速找到模式信息拿到一颗SPI芯片第一件事是翻手册找时序图。不同厂商的写法不一样但核心信息就那几个CPOL、CPHA、或者直接写“Mode 0/1/2/3”。有些手册不直接写模式而是画一张时序图标出“SCK”、“MOSI”、“MISO”、“CS”的波形你需要自己判断。判断方法看SCK空闲时是高还是低确定CPOL看数据在第一个边沿还是第二个边沿变化确定CPHA。注意有些手册画的是“数据在上升沿变化”那采样就是在下降沿对应CPHA1。这个逻辑要理清楚不然容易反。还有一种情况手册只写了“数据在SCK上升沿采样”没提CPOL。这时候你需要看SCK空闲电平。如果空闲低那就是Mode 0如果空闲高那就是Mode 2。因为采样边沿是上升沿CPOL0时第一个边沿是上升沿Mode 0CPOL1时第二个边沿是上升沿Mode 2。3. STM32平台上四种模式的配置与实测3.1 CubeMX中SPI参数配置的对应关系用STM32CubeMX配置SPI时界面上的参数和四种模式的对应关系很直接。在SPI配置页里有两个关键选项Clock Polarity和Clock Phase。Clock Polarity选Low就是CPOL0选High就是CPOL1Clock Phase选1 Edge就是CPHA0选2 Edge就是CPHA1。具体对应Mode 0Clock Polarity LowClock Phase 1 EdgeMode 1Clock Polarity LowClock Phase 2 EdgeMode 2Clock Polarity HighClock Phase 1 EdgeMode 3Clock Polarity HighClock Phase 2 Edge这里有个容易踩的坑CubeMX里Clock Phase的“1 Edge”和“2 Edge”指的是第一个边沿和第二个边沿不是上升沿和下降沿。很多人看到“1 Edge”以为是上升沿结果配错了。记住Edge是边沿的序号不是边沿的类型。另外NSS片选的设置也需要注意。CubeMX里可以选择硬件NSS或软件NSS。硬件NSS由SPI外设自动控制但STM32的硬件NSS在多主机场景下容易出问题一般建议用软件NSS手动控制GPIO。软件NSS的灵活性更高也更容易调试。// STM32 HAL库中SPI初始化结构体的关键字段 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; // CPOL0 hspi1.Init.CLKPhase SPI_PHASE_1EDGE; // CPHA0 hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_128; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE;上面这段代码配置的是Mode 0。如果要改成Mode 3只需要把CLKPolarity改成SPI_POLARITY_HIGHCLKPhase改成SPI_PHASE_2EDGE。其他字段不变。3.2 用示波器抓波形判断模式是否匹配配置完之后怎么确认模式对了最直接的方法是用示波器或逻辑分析仪抓SCK和MOSI的波形。以Mode 0为例你应该看到CS拉低后SCK从低电平开始第一个上升沿时MOSI上的数据是稳定的下降沿时数据切换。如果看到数据在上升沿切换那说明实际工作在Mode 1或Mode 3和配置不符。我一般会发一个固定的字节比如0xA5二进制10100101然后抓波形对照。0xA5的位模式是1-0-1-0-0-1-0-1在示波器上很容易辨认。如果抓到的位顺序反了可能是MSB/LSB设置错了如果位顺序对但采样位置不对就是模式配错了。还有一种情况波形看起来完全不对SCK没有输出或者MOSI一直是高电平。这通常不是模式问题而是SPI外设没使能、GPIO复用没配好、或者CS没拉低。排查顺序应该是先确认SCK有没有波形再确认MOSI有没有数据最后才看采样边沿。注意用示波器抓SPI波形时建议用四通道同时抓CS、SCK、MOSI、MISO。CS是判断通信起止的关键没有CS参考单看SCK和MOSI很难判断第一个边沿是哪个。3.3 DMA方式读取数据时的模式注意事项用DMA方式读SPI数据时模式配置本身不变但有几个额外的坑要注意。首先是DMA的传输方向SPI全双工模式下发送和接收是同时进行的所以DMA需要配置两个通道一个发一个收。如果只配了接收DMA没配发送DMASPI时钟不会产生因为主机没有发送数据就不会产生时钟。其次是数据对齐。STM32的SPI数据寄存器是16位的但实际数据可能是8位。用DMA时如果外设数据宽度和内存数据宽度设置不一致会出现数据错位。比如SPI配置为8位数据DMA外设地址宽度应该设为字节内存地址宽度也设为字节。如果设成了半字读回来的数据会多出无效字节。// SPI1 RX DMA配置示例 hdma_spi1_rx.Instance DMA1_Channel2; hdma_spi1_rx.Init.Direction DMA_PERIPH_TO_MEMORY; hdma_spi1_rx.Init.PeriphInc DMA_PINC_DISABLE; hdma_spi1_rx.Init.MemInc DMA_MINC_ENABLE; hdma_spi1_rx.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE; hdma_spi1_rx.Init.MemDataAlignment DMA_MDATAALIGN_BYTE; hdma_spi1_rx.Init.Mode DMA_NORMAL; hdma_spi1_rx.Init.Priority DMA_PRIORITY_HIGH;还有一个容易忽略的点DMA传输完成中断。用DMA读数据时CPU不参与每个字节的搬运但需要在DMA传输完成后处理数据。如果没开传输完成中断或者中断里没清标志位会导致下一次传输无法启动。我遇到过DMA只工作一次就卡死的情况最后发现是中断标志没清。另外SPI的DMA请求使能要在SPI使能之后、DMA配置完成之后。顺序错了会导致DMA请求丢失。正确的顺序是配置SPI - 配置DMA - 使能DMA - 使能SPI的DMA请求 - 拉低CS - 启动DMA传输。4. 不同模式在实际芯片上的对比测试4.1 用同一颗Flash芯片测试四种模式为了直观对比四种模式的差异我拿W25Q64 Flash做了一组测试。W25Q64支持Mode 0和Mode 3手册里明确写了这两种模式。我分别用Mode 0和Mode 3读取同一个地址的数据对比波形和读取结果。测试条件SPI时钟设为10MHz读取地址0x000000的3个字节。Mode 0下SCK空闲低第一个上升沿采样读取结果正常。Mode 3下SCK空闲高第一个下降沿采样读取结果也正常。两种模式读出的数据完全一致因为Flash内部会自动适配这两种模式。但把时钟提到30MHz后Mode 0的波形依然干净Mode 3的波形在下降沿附近出现了轻微振铃。这是因为Mode 3的空闲高电平在切换时更容易受到PCB走线寄生电容的影响。当然这跟板子设计有关不是绝对的。然后我故意把主机配成Mode 1去读W25Q64结果读回来的全是0xFF。用示波器看SCK和MOSI的波形都有但MISO一直是高阻态。原因是Flash在Mode 1下不响应它只认Mode 0和Mode 3。这个测试说明从机不支持的模式主机配了也没用从机根本不会驱动MISO。4.2 多从机不同模式挂同一条总线的处理方案实际项目中经常遇到这种情况一条SPI总线上挂了多个从机但它们的模式不一样。比如一个传感器用Mode 0一个ADC用Mode 3。这时候怎么办方案一分时复用动态切换模式。在访问不同从机之前重新配置SPI的CPOL和CPHA。STM32的SPI外设支持在运行时修改CPOL和CPHA但要注意修改这些位之前必须先禁用SPI清除SPE位修改完再重新使能。如果在传输过程中修改会导致时序错乱。方案二用软件SPI模拟。如果从机数量不多、速率要求不高可以用GPIO模拟SPI时序针对每个从机单独控制时钟极性和相位。软件SPI的灵活性最高但速率上不去一般不超过1MHz。方案三加SPI多路复用器。用硬件多路复用器切换SCK、MOSI、MISO到不同的从机每个从机可以独立配置模式。但这样增加了硬件成本而且多路复用器本身会引入延迟和寄生参数。我一般优先选方案一因为改动最小。但要注意动态切换模式时CS必须保持高电平确保从机处于空闲状态。切换完成后再拉低对应从机的CS开始通信。方案优点缺点适用场景动态切换模式无需额外硬件切换有开销高速下可能不稳从机数量少速率要求一般软件SPI灵活性最高速率低占CPU低速、少量从机硬件多路复用各从机独立成本高引入延迟高速、多从机4.3 模式不匹配时的典型波形特征模式配错时波形会有一些典型特征认识这些特征能帮你快速定位问题。最常见的是数据移位读回来的数据比预期多一位或少一位或者高低位颠倒。这是因为采样边沿错了导致数据在错误的时刻被读取。另一种典型特征是MISO一直为高或一直为低。如果MISO一直是高电平可能是从机没有驱动MISO模式不支持或CS没拉低如果一直是低电平可能是从机在错误的时间驱动了MISO导致总线冲突。还有一种情况是数据间歇性错误。低速下正常高速下出错。这通常是时序余量不足采样边沿太靠近数据切换边沿。解决办法是降低时钟频率或者换用Mode 0/Mode 3。实操心得调试SPI时先用手册确认从机支持的模式再用示波器确认主机输出的波形符合该模式最后检查CS、MISO的连接。三步走完90%的问题都能定位。5. 常见问题排查与避坑指南5.1 SPI通信完全不生效的排查顺序SPI调不通按这个顺序排查基本不会漏确认CS是否拉低。CS没拉低从机根本不会响应。用万用表或示波器测CS引脚通信期间应该是低电平。确认SCK是否有波形。SCK没波形说明SPI外设没使能、GPIO复用没配好、或者DMA没启动。先解决时钟问题。确认MOSI是否有数据。MOSI没数据说明发送寄存器没写入或者DMA发送通道没配置。确认模式是否匹配。对照从机手册检查CPOL和CPHA设置。确认MISO是否被从机驱动。如果MISO一直高阻说明从机没响应可能是模式不对、CS不对、或者从机供电有问题。确认时钟频率是否过高。降低到1MHz以下再试排除时序余量问题。这个顺序是从“有没有”到“对不对”的递进。先确认最基本的信号有没有再确认参数对不对。很多人一上来就怀疑模式结果发现是CS没接好。5.2 模式配错导致的数据错位案例分析我之前调一颗压力传感器手册上写的是Mode 1我配成了Mode 0。读出来的压力值一直是量程的一半而且不随压力变化。用示波器抓波形发现MISO上的数据在SCK上升沿变化但我在上升沿采样采到的是变化中的值导致数据错位。改成Mode 1后数据在下降沿采样上升沿变化采样点落在数据稳定区读数立刻正常了。这个案例说明模式配错不一定导致通信完全失败但会导致数据错误。如果读回来的数据“看起来有规律但不对”优先怀疑模式。还有一种更隐蔽的情况数据在低速下正常高速下错位。这是因为模式虽然配对了但采样边沿太靠近数据切换边沿高速下建立时间不够。解决办法是换用Mode 0或Mode 3这两种模式的采样窗口更大。5.3 硬件片选与软件片选的模式影响硬件片选NSS由SPI外设自动控制通信开始时自动拉低结束时自动拉高。软件片选则是手动控制GPIO。从模式配置的角度看硬件片选和软件片选不影响CPOL和CPHA但会影响通信的起止时序。硬件片选的问题是STM32的NSS在主机模式下如果配置为硬件模式当NSS引脚被外部拉低时SPI会自动进入从机模式导致通信混乱。所以主机模式下一般建议用软件片选把NSS配置为SPI_NSS_SOFT然后用普通GPIO控制CS。软件片选的好处是灵活可以在CS拉低之前先配置好模式CS拉高之后再做其他操作。而且多个从机可以共用SCK、MOSI、MISO只用不同的GPIO做CS节省引脚。注意用软件片选时CS的拉低和拉高之间要留足够的延时。CS拉低后不要立刻发数据等几个微秒让从机准备好数据发完后也不要立刻拉高CS等最后一个时钟边沿完成后再拉高。这个延时在高速通信时尤其重要。5.4 常见问题速查表现象可能原因排查方法解决方案读回全0xFFMISO未驱动、模式不支持测MISO电平确认从机支持的模式检查CS读回全0x00总线冲突、从机未响应测MISO波形检查CS和模式配置数据错位模式配错、采样边沿不对抓SCK和MOSI波形对照手册改CPOL/CPHA低速正常高速出错时序余量不足降低时钟测试换Mode 0/3或降频只工作一次DMA中断未清、CS未拉高查中断标志清标志确保CS拉高数据高低位颠倒MSB/LSB设置错误查FirstBit配置改SPI_FIRSTBIT_MSB/LSB6. 模式选择与代码复用的实战建议6.1 拿到新芯片后三分钟确定模式的流程我总结了一个快速确定SPI模式的流程拿到任何新芯片都能用第一步翻手册找“SPI Timing”或“Serial Interface”章节找CPOL、CPHA、或者Mode字样。如果直接写了Mode 0/1/2/3直接对应配置。第二步如果手册只给了时序图看SCK空闲电平定CPOL看数据在第一个还是第二个边沿变化定CPHA。注意数据变化的边沿和采样的边沿是相反的。第三步如果手册什么都没写清楚用排除法先试Mode 0不行试Mode 3再不行试Mode 1和Mode 2。每次只改模式其他不变。一般试到第二个就能通。第四步通了之后用示波器确认波形确保采样点落在数据稳定区。如果波形看起来勉强降低时钟频率再试。这个流程我用了很多次基本三分钟内能确定模式。关键是不要瞎试要有逻辑地排除。6.2 代码中如何优雅地支持四种模式切换如果项目需要支持多种模式的从机代码结构上建议把SPI配置封装成函数传入模式参数。这样切换模式只需要改一个参数不用改一堆寄存器。typedef enum { SPI_MODE_0 0, SPI_MODE_1, SPI_MODE_2, SPI_MODE_3 } spi_mode_t; void SPI_SetMode(SPI_HandleTypeDef *hspi, spi_mode_t mode) { // 先禁用SPI __HAL_SPI_DISABLE(hspi); switch (mode) { case SPI_MODE_0: hspi-Init.CLKPolarity SPI_POLARITY_LOW; hspi-Init.CLKPhase SPI_PHASE_1EDGE; break; case SPI_MODE_1: hspi-Init.CLKPolarity SPI_POLARITY_LOW; hspi-Init.CLKPhase SPI_PHASE_2EDGE; break; case SPI_MODE_2: hspi-Init.CLKPolarity SPI_POLARITY_HIGH; hspi-Init.CLKPhase SPI_PHASE_1EDGE; break; case SPI_MODE_3: hspi-Init.CLKPolarity SPI_POLARITY_HIGH; hspi-Init.CLKPhase SPI_PHASE_2EDGE; break; } HAL_SPI_Init(hspi); __HAL_SPI_ENABLE(hspi); }这个函数的核心是先禁用SPI改配置重新初始化再使能。注意HAL_SPI_Init会重新配置所有SPI寄存器所以调用前要确保其他参数波特率、数据宽度等已经设置好。实操心得动态切换模式时如果SPI正在传输必须先等传输完成再禁用。否则会导致当前传输的数据丢失从机可能进入不确定状态。我一般会在切换前检查SPI的BSY标志位确保空闲后再操作。6.3 高速通信下模式选择的经验法则速率超过10MHz时模式选择就变得很关键。我的经验法则是优先选Mode 0或Mode 3这两种模式的采样窗口最大时序余量最充足。如果从机只支持Mode 1或Mode 2尽量把时钟降到5MHz以下或者缩短PCB走线减少寄生电容。用DMA方式传输时模式本身不影响DMA但高速下DMA的响应延迟可能成为瓶颈。建议用双缓冲DMA减少CPU干预。如果波形出现振铃或过冲加串联电阻一般22Ω到100Ω做阻抗匹配能明显改善信号质量。最后再分享一个小技巧调试SPI时如果手头没有示波器可以用逻辑分析仪的低速采样模式先抓个大概。逻辑分析仪虽然采样率不如示波器但能解码SPI协议直接告诉你每个字节是什么比看波形更直观。我常用的一款是Saleae Logic配合它的SPI解码器能快速定位是模式问题还是数据问题。
阅读完成 · 觉得有帮助?