ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SPI通信协议详解:从CPOL/CPHA时序到Flash与屏幕实战

SPI通信协议详解:从CPOL/CPHA时序到Flash与屏幕实战 有次我调一块SPI接口的存储芯片示波器挂上去看了半天SCLK有波形、MOSI有数据、CS拉低时间也对但从设备读回来的数据全是0xFF。代码翻来覆去改不动最后发现是CPOL和CPHA的配置和芯片手册的时序图差了一个边沿。那次之后我把SPI通信协议的细节完整过了一遍也整理出这篇东西——从四根线的基本分工、时序模式、片选策略到实际项目中屏幕和SD卡共享总线、CubeMX配置、外置Flash下载算法这些实操场景。适合刚开始接触SPI的开发者也适合那些“能跑通但说不清为什么”的工程师对照着查漏补缺。1. 为什么板子上这么多器件都选SPI1.1 先认识四根线的分工SPI是Motorola最早提出的一种同步串行通信接口全称Serial Peripheral Interface中文叫串行外设接口。它最常见的形态是四根线SCLK、MOSI、MISO、CS。很多人第一次看原理图时容易把这几个信号搞混其实从名字就能拆出它们各自的职责。SCLK是时钟线由主设备产生并输出相当于整条总线的节拍器。通信双方都按照这个时钟边沿来采样或切换数据所以叫“同步”通信。MOSI全称Master Out Slave In主设备出、从设备进数据方向是从主到从。MISO则是Master In Slave Out从设备出、主设备进方向反过来。CS是片选信号大部分从设备都是低电平有效也就是CS被拉低时这个从设备才“醒过来”参与通信CS拉高后就进入高阻或待机状态。我用一个生活化类比帮你理解CS相当于电话接通的动作SCLK是通话时的节拍节奏MOSI和MISO则是两个人轮流说话的两条独立通道。没有CS所有从设备都会同时抢着应答总线直接乱套没有SCLK双方不知道什么时候该采样数据收到的全是噪声。四根线缺一不可但理解了每根线的角色后续再看时序图就不会觉得头大。1.2 全双工与速度边界SPI之所以在嵌入式里被广泛使用最重要的是两个特性全双工和高速度。全双工意味着主设备在发送一个字节的同时可以接收从设备返回的一个字节。STM32这类MCU的SPI硬件外设里发送寄存器和接收寄存器是独立工作的你往发送寄存器写一个字节硬件同时把接收移位寄存器里的内容搬进接收寄存器。所以SPI的一次字节交换是“一发一收”同时完成的这个特性在读写寄存器类设备时尤其好用——主机发送命令字节时从机可以同时把状态寄存器或数据寄存器里的旧值吐出来。速度方面SPI的SCLK最高可以跑到几十MHz甚至更高。比如STM32F4系列的SPI外设在APB2总线上配合合适的分频跑个42MHz很常见W25Q128这类SPI Flash更是支持最高133MHz的时钟通常需要开启QPI或Dual/Quad模式。相比之下UART受限于波特率I2C标准模式只有100kHz快速模式400kHz高速模式也就3.4MHz。所以需要吞吐量的场景比如LCD屏幕刷新、Flash读写、ADC高速采样SPI几乎是板上钉钉的选择。当然SPI也不是没有短板。它没有像I2C那样的地址机制也没有UART那样的帧起始检测所以不太适合远距离传输和设备热插拔。SPI的本质是板级通信协议几厘米到十几厘米的PCB走线内非常可靠一旦拉到半米以上信号完整性问题就要开始考虑了。2. 读懂SPI时序图CPOL和CPHA决定了成败2.1 时钟极性和时钟相位的真实含义SPI时序图里最常见的两个参数就是CPOLClock Polarity时钟极性和CPHAClock Phase时钟相位。这两个词看着吓人实际理解起来就两句话CPOL决定SCLK在空闲状态时是高电平还是低电平。CPOL等于0时SCLK空闲为低发送数据时先拉高再在边沿采样CPOL等于1时SCLK空闲为高发送数据时先拉低再在边沿采样。CPHA决定数据是在SCLK的上升沿采样还是下降沿采样。CPHA等于0表示第一个边沿采样CPHA等于1表示第二个边沿采样。注意这里的“第一个边沿”“第二个边沿”要和一个完整时钟周期对应起来一个时钟周期有上升沿和下降沿两个边沿CPHA选的是数据采样用哪个沿而数据切换则在另一个沿发生。展开说就是CPHA0时数据在SCLK的第一个边沿被采样。如果CPOL0SCLK空闲低第一个边沿是上升沿所以上升沿采样数据在下降沿变化如果CPOL1SCLK空闲高第一个边沿是下降沿所以下降沿采样数据在上升沿变化。CPHA1时数据在SCLK的第二个边沿被采样。如果CPOL0第一个边沿是上升沿但采样在下降沿如果CPOL1第一个边沿是下降沿但采样在上升沿。很多人看这段文字容易绕晕我的经验是直接记结论你要找的是“SCLK空闲电平”和“采样边沿”这两个组合。实际项目中打开从设备的数据手册时序图里通常直接画出数据和时钟的关系你只要对照手册推荐的模式配置即可不需要每次都从CPOL/CPHA推导一遍。2.2 四种模式一张表看懂SPI外设通常支持四种模式由CPOL和CPHA组合而来模式CPOLCPHASCLK空闲电平数据采样边沿典型用途Mode 000低电平上升沿绝大多数SPI Flash、SD卡、传感器Mode 101低电平下降沿部分音频编解码器、特定ADCMode 210高电平下降沿某些LCD控制器、EEPROMMode 311高电平上升沿部分射频芯片、特定FlashMode 0是最常用的默认模式。大部分开源的SPI驱动、Arduino库、STM32例程默认都跑在Mode 0。但千万别因此在所有设备上都硬套Mode 0我见过不少硬件上没问题、软件死活调不通的情况最后发现是某颗芯片手册规定要用Mode 3。顺带提一句判断一个从设备支持哪种模式最快的方法是看数据手册里时序图。手册通常会给出类似“Data is sampled on the rising edge of SCLK”的描述然后你在时序图上确认SCLK空闲电平高低两个信息一凑就能确定模式。2.3 模式配错后的两种典型故障现象模式配错不会把硬件烧掉但调试时会非常痛苦因为现象经常看起来“像是软件逻辑错了”。第一种故障读回的数据整体错位或全错。比如你发0x9F读Flash ID正常应该返回0xEF 0x40 0x19W25Q256但实际返回的每个字节像是被移位了或者是0xFF、0x00这种固定值。原因是采样时刻不对主设备采到的根本不是从设备稳定输出的数据位。第二种故障偶尔正常、偶尔错误。这种最折磨人因为看起来像是接触不良或者信号干扰实际上可能是时钟边沿正好落在数据翻转的中间区域导致采样值处于亚稳态边界时好时坏。降低SPI速率后故障消失但一旦恢复原速率又出现这种“速率敏感”的问题大概率也要怀疑模式配置或者时序裕量。排查方法其实很简单用逻辑分析仪抓一次完整的字节交换把SCLK和MOSI/MISO的波形放大看数一数主设备采样时数据线是否已经稳定。如果采样边沿和数据翻转边沿离得太近那基本就是模式选错了或者时钟相位配置反了。3. 硬件片选与软件片选别小看这根CS线3.1 硬件片选的工作机制现在的MCU SPI外设基本都支持硬件NSSNot Slave Select管理也就是把片选信号交给外设自动控制。你在CubeMX里把NSS配置为硬件模式配置好GPIO复用功能然后调用HAL_SPI_Transmit时硬件就会在开始传输前自动拉低CS传输完成后自动拉高CS。硬件片选最大的好处是省心、效率高。CPU不需要手动控制CS引脚尤其在DMA传输大块数据时硬件自动管理CS能保证时序的连续性和一致性避免软件在多次GPIO操作之间被中断打断。但硬件片选也有局限。很多MCU的硬件NSS引脚是固定的不能随便映射到任意GPIO这会给PCB布局带来限制。更关键的是不同从设备对片选时序的要求不一样有的要求CS拉低后要等几十纳秒再发时钟有的要求最后一个时钟边沿结束后CS还要保持低电平一段时间。硬件NSS的自动时序不一定满足所有这些要求遇到这种极端情况就只能改用软件片选。3.2 软件片选灵活但要用对方法软件片选就是用一个普通GPIO手动控制CS的高低。传输之前先拉低传输结束后拉高全程由CPU参与。这样做的好处很明显CS引脚可以随便挑不受外设复用限制CS的拉低和拉高时机可以由软件精确控制你想在拉低后加上几个空延时都行灵活度极高。代价是每次传输都要多几次GPIO写操作。如果一次只传一两个字节问题不大但高频小包传输时软件片选的开销会比较明显。另外有个不太容易被注意到的坑如果软件片选过程中发生中断而中断里又去操作同一个SPI外设就可能出现CS状态和外设状态不匹配的混乱。解决办法是传输期间相关临界区做好保护或者干脆把片选操作放在关中断的代码段里。实际项目中我看到最多的是“半自动”方案SPI外设的数据收发用硬件片选用软件GPIO控制。这个方案兼具灵活性和效率是大多数STM32项目的常见做法。3.3 选型建议和实测体会维度硬件片选软件片选引脚灵活性受限通常固定引脚任意GPIO时序控制能力不强依赖外设自动行为强可随意插入延时CPU开销低适合DMA大块传输高每次传输需操作GPIO配置复杂度低CubeMX勾选即可略高需自行管理CS电平适用场景大吞吐量、固定引脚、无极端时序要求引脚受限、芯片时序特殊、试验阶段我的个人习惯是能用一个普通GPIO搞定的绝不用硬件NSS。原因是调试阶段的灵活性太重要了——怀疑时序问题时可以直接在CS拉低和拉高之间加延时不用改PCB或者重新配置外设。只有到了性能调优阶段发现软件片选成了瓶颈再考虑切换到硬件NSS。4. 一条SPI总线挂多个设备屏幕与SD卡共存的接法对比4.1 分时共用的接线方式SPI天然支持一主多从只要把多个从设备的SCLK、MOSI、MISO并在一起每个从设备单独一根CS就能组成共享总线。关键前提是同一时刻只能有一个CS有效而且不工作的从设备必须让出MISO也就是输出高阻态。不同从设备对“让出MISO”的处理不一样。规范的芯片会在CS无效时自动把MISO置为高阻但有些芯片在CS没拉低前MISO会一直输出某个固定电平这种芯片直接并联MISO会互相打架。解决方法是串电阻隔离或者用三态缓冲门芯片但会增加成本和复杂度。所以多设备共享总线前一定要先确认所有从设备的MISO行为。软件层面要格外小心CS的切换时序。我见过有人写完第一个设备后立即拉高CS、然后马上拉低第二个设备的CS中间没留任何间隔结果第一个设备的内部状态还没完全复位第二个设备就抢上了总线导致数据错乱。稳妥的做法是在两个片选切换之间加一点延时至少保证一个完整SCLK周期的间隔。4.2 ESP32场景下两种典型方案对比“ESP32屏幕和SD卡怎么共享SPI”这个问题在社区里被问过无数次很多开发板把ST7789或ILI9341屏幕和TF卡槽都接到了同一个SPI总线上。方案A屏幕和SD卡共用同一组SPI外设分时复用。接线是屏幕CS和SD卡CS各自独立MOSI/MISO/SCLK并联。优点是省引脚、布线简单缺点是屏幕刷新和SD卡读写不能同时进行。如果SD卡频繁写入日志屏幕刷新会明显卡顿反过来屏幕高帧率刷新时SD卡的写入时间片被压缩。方案B屏幕用硬件SPISD卡用另一个SPI或者SDIO。ESP32的SDMMC和SPI外设都比较丰富完全可以各管各的。优点是互不干扰屏幕刷新和SD卡写入可以并行只要CPU顶得住缺点是引脚占用翻倍某些封装上引脚不够用。对比项方案A共用SPI方案B独立接口引脚占用少约6个多约10个以上并发能力差串行执行好可并行软件复杂度需要协调驱动库两个驱动相互独立典型场景简单GUI、低频记录数据采集GUI同时跑4.3 我最终采用的方案和理由我做过一个温湿度OLEDTF卡的数据记录器屏幕刷新率要求不高但SD卡要周期性写入日志。一开始用方案A结果每次写SD卡时屏幕闪烁得厉害后来改成方案BOLED用SPI2SD卡用SPI3ESP32上这两个外设都能用问题立刻消失。如果你的屏幕刷新率要求很高比如要跑流畅的动画或者视频播放我的建议是屏幕单独占一条SPI不要和任何需要频繁读写的存储设备共享总线。屏幕刷新是一帧一帧连续往外推数据的中间插入SD卡操作很容易出现撕裂或者掉帧。反过来如果屏幕只是显示静态数据SD卡写入频率也不高共用一条SPI完全够用没必要浪费引脚。很多开发板出厂设计就是方案A用得好好的说明这个方案在低负载场景下完全成立。5. CubeMX配置SPI从界面勾选到代码落地5.1 每个参数到底在配什么CubeMX里打开SPI外设配置页第一眼会看到一堆下拉框。我用STM32F4系列举例从上到下顺一遍Mode里常见的四个选项是Transmit Only、Receive Only、Transmit Receive、Half-Duplex。Transmit Receive是全双工收发同时进行绝大多数从设备都选这个Transmit Only只发不收适合OLED这类无需回传数据的屏幕Half-Duplex是半双工只有一根数据线适合三线制设备。硬件NSS配置选项有Disable、Hardware NSS Output、Hardware NSS Input等。如果你用软件片选这里选Disable如果用硬件片选选Hardware NSS Output普通输出或者Hardware NSS Input多主机仲裁场景。参数设置区域里Prescaler是时钟分频直接决定SCLK频率。举个例子如果APB2时钟是84MHzPrescaler设为2SCLK就是42MHz设为4就是21MHz。选分频时参考从设备手册的最大SCLK频率同时留出一点余量。CPOL和CPHA前面章节已经讲过按从设备手册选。Data Size选8位还是16位取决于设备寄存器宽度大部分传感器和Flash是8位。MSB First还是LSB First也值得注意——SD卡协议明确要求先发高位W25Q系列Flash也是MSB First但有些音频芯片可能是LSB First。5.2 容易被忽略的三个低调选项第一个是CRC计算。SPI硬件可以自动对数据流计算CRC检测传输错误。普通板级通信基本用不上但如果你做的是工业控制或者长距离总线开启CRC很有价值。CubeMX里只要启用CRC硬件会在发送数据后自动附着CRC值接收端自动校验。第二个是NSSPNSS Pulse。配置为Enabled时帧和帧之间CS会有一个脉冲翻转主要用于需要“帧同步信号”的多字节设备。普通单字节传输不需要开启某些音频芯片和特定传感器可能会有要求。第三个是TI Mode。TITexas Instruments模式是另一种帧格式数据和时钟的关系与Motorola SPI完全不一样典型特征是每个字节前有一个延时时钟空闲为高。绝大多数项目根本用不到TI模式但如果你用了TI的某些模拟前端芯片别忘记检查这里。5.3 生成代码之后必须补的几件事CubeMX生成代码后SPI外设初始化函数已经在MX_SPIx_Init里写好了但别忘了软件片选的GPIO初始化需要自己加。很多人第一步只配置了SPI引脚忘记把CS引脚配成GPIO Output然后调用HAL_SPI_Transmit时灯不亮、数据不出查半天才发现CS引脚根本没初始化为输出。另一个容易忽略的地方是完成回调。使用HAL_SPI_Transmit_DMA或HAL_SPI_Transmit_IT时传输完成事件通过回调函数通知但CubeMX生成的回调是弱定义weak默认是空函数需要自己在用户代码区域重写。很多人传输完数据后直接接着往Flash发送写使能指令结果前一次还没传完数据乱套。还要记住SPI外设把数据“发出去”不代表从设备已经“处理完”。尤其读写Flash这类设备主机把页编程指令和地址数据全部发完后从设备还需要内部擦写时间这时候你必须通过读状态寄存器轮询Busy位。我看到很多新手在这栽跟头——发完编程指令立即读数据读回来的全是0xFF以为SPI有问题。6. 实操SPI Flash从W25Q256到下载算法6.1 标准读写流程拆解以W25Q256这颗常见的SPI Flash为例它的操作流程高度典型理解了它基本上就理解了绝大多数SPI Flash芯片。读ID0x9F是调试时最该先跑通的第一个命令。主机拉低CS发出0x9F然后连续读取三个字节正常情况下会返回厂商ID 0xEF、类型ID 0x40、容量ID 0x19。返回不出这几个值先别急着往下写说明SPI通信链路本身就没通。读数据的操作很简单CS拉低发出0x03Read Data跟着24位地址然后连续读取任意长度的数据。注意W25Q256已经超过16MB有些型号用24位地址访问不了全部空间需要切换到4字节地址模式指令0xB7这个细节在大于128Mbit的Flash上尤其容易踩坑。写入流程就复杂多了。Flash写入前必须先擦除擦除分成扇区擦除0x20、块擦除0x52/0xD8和整片擦除0xC7。擦除之前还要发0x06写使能命令把状态寄存器里的WEL位置1。擦除完成后检查状态寄存器1的Bit0BUSY位变成0表示内部操作完成然后才能发页编程指令0x02。页编程一次最多写256字节写之前主机把0x02、24位地址、最多256字节数据依次发出CS拉高后从设备开始内部编程。编程完成前同样要轮询BUSY位。这里有个人尽皆知但反复出现的坑如果一页数据跨越了256字节边界W25Q256会认为地址有误编程失败或数据写入错误位置正确做法是在跨越边界前截断数据分两次页编程。6.2 下载算法到底在做什么STM32F429这类MCU如果要从外部SPI Flash启动或者用Keil/IAR直接烧写外部Flash就需要一个“编程下载算法”。很多人在网上找“STM32F429的W25Q256下载算法”其实就是一小段被加载进RAM里执行的代码作用是实现外部Flash的擦除、编程和校验。为什么下载算法要放在RAM里跑因为编程Flash时代码本身可能就在这颗Flash里如果直接从Flash取指令执行边擦除边取指令指令都被擦没了程序早飞了。所以Keil/IAR会把下载算法加载到RAM比如STM32F4的CCM或普通SRAM然后从RAM里执行烧写代码。下载算法文件通常包含一个初始化函数、一个擦除函数、一个编程函数和一个校验函数函数接口由调试器规定。调试器启动烧写流程时会先调用擦除函数擦掉目标区域然后逐块调入编程函数写入数据最后校验函数读回数据验证。自己写下载算法时最需要注意的是时钟配置——初始化函数里要把SPI外设和时钟分频正确初始化否则烧写过程中Flash操作不稳定时好时坏。6.3 实测中容易翻车的三个环节翻车点一是忘记清写保护。很多SPI Flash出厂时状态寄存器里的WP写保护位是置位的直接发写命令会被拒绝操作状态寄存器2时还要先发写使能。调试时如果擦除指令发出后状态寄存器一直不忙大概率就是写保护没关。翻车点二是页编程跨页。前面提过一次页编程最多256字节地址跨越页边界就会出问题。我自己的代码里专门加了一段判断如果剩余数据超过当前页剩余空间就先只写当前页的部分剩下的下一轮再写这样才稳定。翻车点三是状态寄存器轮询的时序。SPI速度拉高后主机轮询0x05指令获取状态寄存器时有些Flash在BUSY位刚清零后还不能立即接受下一轮编程需要一点额外的恢复时间。我遇到过高速轮询时偶尔返回0xFF的情况把分频调低一档或者轮询之间加几个空周期就好了。7. IO口模拟SPI和三线SPI低配方案也有大用7.1 软件模拟SPI的实现要点很多MCU的SPI外设数量有限但实际需要挂的设备一多硬件SPI就不够用了。这个时候用普通GPIO模拟SPI是最直接的解法。模拟SPI的核心就是按时序图翻转GPIO。以Mode 0为例一个完整字节发送的逻辑是CS拉低然后循环8次每次先把数据位写到MOSI引脚然后SCLK拉高再SCLK拉低。接收时则在SCLK拉高后立刻读取MISO引脚电平。顺序千万不能错先设数据线稳定再给时钟边沿给完边沿再读数据。模拟SPI最常见的坑是时序间隔不均匀。如果代码里漏了延时或者延时函数被中断影响就会出现某些位被读得特别早、某些位被读得特别晚的情况从设备就能收到错数据。稳妥的做法是最低限度在两个字节之间加一个稳定的延时必要时在操作GPIO前后关闭中断。性能上模拟SPI确实拼不过硬件外设但很多场景够用。GPIO翻转一个周期大约几十纳秒一次8位传输加上各种开销大概几微秒也就是有效速率在几百kHz到1MHz量级和硬件SPI动辄几十MHz确实没法比。但对于温湿度传感器、EEPROM、低速OLED这类设备模拟SPI完全能跑。7.2 三线SPI省掉一根线要付出什么三线SPI通常指SCLK、CS、外加一根双向数据线DIO也有叫1-Wire-like SPI的。相比标准SPI少了MOSI和MISO的分工主机发送数据时DIO作为输出口从机返回数据时DIO作为输入口通过方向切换实现半双工通信。软件模拟三线SPI时最关键的就是方向切换时机。我见过很多人在这翻车主机发完命令后马上想读数据结果DIO还是输出状态读回来的电平全是自己驱动出来的根本没收到从机数据。正确做法是在发送完最后一个位后先把DIO引脚切换到输入模式然后拉高SCLK产生几个时钟周期再采样电平。三线SPI的性能一般比标准SPI差一些因为每次方向切换都要额外时间而且半双工模式不能同时收发。但它能省一个引脚在某些引脚极其紧张的项目里这是非常现实的优势。很多屏幕驱动和单线传感器都采用这种方案比如某些ST7789的串口模式就是三线制。7.3 硬件外设足够时不要模拟我的态度一向明确如果MCU还有空闲的硬件SPI外设就尽量不要用模拟。硬件SPI除了速度快还有几个软件模拟很难替代的优点DMA免CPU参与、硬件自动处理帧格式和CRC、中断唤醒等。但要提醒的是这里说的“够用”不只是看有没有SPI外设还要看引脚是否足够。有些MCU虽然有SPI外设但复用引脚全被屏幕、按键、LED占满了这时候软件模拟反而是更合理的解法。另外模拟SPI有个隐藏的好处代码可移植性极好同一个模拟SPI驱动文件可以随便搬到任何带GPIO的MCU上不需要改任何外设配置。8. 调试中踩过的SPI坑完整排查链路8.1 波形正常但数据全错的排查过程这是我在文章开头提到的那个问题SCLK有连续时钟MOSI有串行数据CS拉低时间也完全符合预期但读回的数据全错。排查时我先用逻辑分析仪抓了完整波形然后把波形逐位展开数了一遍MOSI在采样时刻对应的电平发现主设备发出的数据本身是对的但读回来的MISO数据在每一个位上都像是被移了一位。这个特征基本锁定是时钟相位问题——MISO上的数据是在SCLK下降沿才刚翻转完成的而主设备却在上升沿采样采到的是上一次翻转后的旧值。把CPHA从0改成1后问题立刻消失。这一类问题的本质是主设备和从设备对“采样沿”的约定不一致。排查时不要上来就改软件逻辑先在逻辑分析仪上确认数据线和时钟边的相对位置再决定改CPOL还是改CPHA。8.2 两个从设备共总线后第二个设备无法读写一个项目里把串行Flash和加速度传感器挂在同一条SPI总线上单独访问Flash一切正常单独访问传感器也一切正常但只要先访问过Flash再访问传感器传感器就完全不响应。排查时我先怀疑CS切换太仓促在两个CS之间加了好几个微秒延时没用。后来用逻辑分析仪盯着传感器CS拉低期间的MISO波形发现MISO上一直有其他设备的输出电平在干扰。真相是Flash芯片在CS无效时MISO并没有完全变成高阻而是维持了上一次输出的电平和传感器的MISO输出直接“打架”了。解决方式是在Flash的CS无效状态下让主设备额外发几个SCLK时钟周期先把MISO上的残留电平清掉再切换设备。更彻底的做法是给MISO信号串一个几百欧的隔离电阻从物理上削弱冲突。8.3 SPI速率一调高就随机出错某个屏幕项目在SCLK为5MHz时显示正常调到10MHz后偶发花屏。这不是偶发的软件bug而是典型的信号完整性问题。排查时我用示波器看了SCLK和MOSI的上升沿发现沿上有明显振铃过冲电压甚至超过了IO耐压范围。原因是走线过长加上分布电容过大信号反射严重。缓解手段有好几个降低SCLK分频在MOSI/SCLK上串22到33欧姆的电阻抑制反射调整PCB走线缩短距离平衡各信号线长度。工程上最务实的建议是如果调高速率只是为了“觉得应该更快”而实际应用并不缺这点性能那就老老实实降回低速率。SPI的吞吐量瓶颈往往不只在通信协议这一层后面还有CPU处理数据的时间和显示驱动芯片的刷新能力。8.4 从设备要求的建立时间比主设备默认值更苛刻有一块射频芯片的手册规定CS拉低后SCLK首个边沿至少需要等待200ns而主设备默认配置下CS拉低到SCLK首个边沿可能只有几十纳秒。芯片收不到正确的命令起始位通信时好时坏。解决思路其实很简单改用软件片选在CS拉低之后插入一个延时再启动SPI硬件传输。软硬件结合的方式可以解决大部分“主设备时序和从设备手册不匹配”的问题。如果从设备的要求极其苛刻比如CS拉高后还要保持低电平一段时间那就需要在传输结束后延时再拉高CS。9. 从MCU走向FPGA自己写SPI控制器的几个注意点这部分是给往FPGA方向走的开发者一个扩展思路。很多人用过MCU的SPI外设但到了FPGA平台上所有外设都要自己写状态机的设计思路和MCU完全不同。FPGA上实现SPI主机通常分三层时钟分频逻辑、发送移位逻辑、接收采样逻辑。最核心的是一个状态机按字节划分阶段IDLE - CS拉低 - 按位移位发送/采样 - CS拉高 - 回到IDLE。比特率由时钟分频计数器控制发送和接收共用同一个移位时钟只是采样点要落在时钟边沿之后。在FPGA上写SPI有一个和MCU很不一样的地方你需要手动处理跨时钟域问题。如果SPI的采样时钟来自分频后的慢时钟而状态机跑在系统快时钟里那么快慢时钟域之间的同步寄存器必不可少否则会出现亚稳态导致随机错误。很多初学FPGA的人写SPI Controller第一次上板跑数据全错排查半天往往就是跨时钟域没处理干净。Verilog实现时还有个容易忽略的细节收发的最后一位结束后SCLK要保持一个稳定电平再拉高CS给从设备足够的内部锁存时间。这不是硬性规范但很多芯片确实需要这么一点余量。代码里通常用一个简单的计数器产生“尾部等待”状态来解决。FPGA侧的优势是时序完全可控、模式可以按手册任意定制缺点是每个新功能都要自己造轮子。如果只是验证逻辑用IP核是最快的但如果要做高速、多路、定制时序的控制器手写状态机虽然费时间但能让你对SPI协议的理解到达另一个层次。
返回列表