
1. 项目概述如果你在嵌入式领域摸爬滚打过几年肯定对SPISerial Peripheral Interface不陌生。它就像电子设备间的高速“方言”简单几根线就能让主控芯片和外设“聊”起来速度快、协议简单是连接Flash、传感器、显示屏的常客。但当你真正上手调一个复杂的项目尤其是数据吞吐量一大或者时序要求苛刻时就会发现标准SPI那套“一问一答”的轮询方式有点力不从心了。主控CPU频繁被中断去处理收发数据效率低下不说时序上稍有不慎就可能丢数据。这时候像Freescale现NXPPXS20这类微控制器里的DSPI模块就显得格外“香”了。DSPI全称Deserial Serial Peripheral Interface你可以把它理解为SPI的“Pro Max”版本。它绝不仅仅是换个名字其核心在于引入了硬件FIFOFirst-In First-Out缓冲区和一套更精细的内部状态机FSM。这意味着什么意味着CPU可以一次性往发送FIFO里塞好几帧数据然后去忙别的DSPI的硬件会自己按顺序、按设定好的时序把数据发出去接收时也一样数据会自动存入接收FIFO等攒到一定数量CPU再来批量读取。这大大解放了CPU也使得实现高吞吐量、低延迟的通信成为可能。我最近在调试一个基于PXS20的高速数据采集板传感器通过SPI以10Mbps的速率吐数据传统SPI接口根本处理不过来CPU占用率飙升。正是深入折腾了DSPI的FIFO机制和各种时序参数才让系统稳定跑了起来。这篇文章我就结合PXS20的参考手册和实际踩坑经验带你彻底搞懂DSPI特别是它的FIFO工作机制和那些让人眼花缭乱的传输格式与延时配置。无论你是刚接触SPI的新手还是想优化现有通信协议的老鸟相信这些“干货”都能让你少走弯路。2. DSPI核心架构与运行状态解析要驾驭DSPI首先得理解它的“大脑”和“工作状态”。和简单的GPIO模拟SPI不同DSPI是一个相当复杂的片上外设其行为由内部状态机精密控制。2.1 模块的两种基本状态STOPPED与RUNNINGDSPI模块在任何时候都处于两种基本状态之一STOPPED停止或RUNNING运行。这个设计非常关键它区分了配置阶段和通信阶段。当模块处于STOPPED状态时它就像进入了“休眠”或“配置模式”。此时如果DSPI被配置为主机它不会主动发起任何传输如果被配置为从机它也不会响应外部主机的任何通信请求。这个状态是一个“安全区”你可以放心地读写DSPI所有的配置寄存器比如DSPI_MCR,DSPI_CTARx等而不用担心正在进行的传输被打断或产生不可预知的结果。刚上电或复位后DSPI默认就处于STOPPED状态。当模块切换到RUNNING状态DSPI就进入了“战斗模式”。主机会根据命令主动产生SCK时钟和PCS片选信号来发起传输从机则时刻准备着响应外部主机的呼叫。所有的数据传输、FIFO操作都发生在这个状态。那么状态如何切换呢模块的状态由DSPI_SR状态寄存器中的TXRXS位直观反映该位为1表示RUNNING为0表示STOPPED。从STOPPED进入RUNNING启动传输必须同时满足以下三个条件缺一不可DSPI_SR[EOQF]传输队列结束标志必须为0即未结束。系统芯片SoC未处于调试模式或者DSPI_MCR[FRZ]冻结位被清零。这意味着在调试时你可以通过设置FRZ位来冻结DSPI方便观察状态。DSPI_MCR[HALT]暂停位必须为0。从RUNNING退出到STOPPED停止传输则简单得多只要满足以下任一条件即可DSPI_SR[EOQF]位被置1通常由软件设置表示主动结束队列。SoC进入调试模式并且DSPI_MCR[FRZ]位被置1。DSPI_MCR[HALT]位被置1。这里有个重要的时序细节当有传输正在进行时状态转换RUNNING - STOPPED会发生在下一帧的边界如果没有传输在进行则会立即转换。这保证了当前帧的完整性不会在半途被强行终止。实操心得在初始化DSPI时务必先确保模块在STOPPED状态检查TXRXS再配置寄存器。配置完成后再清除HALT或满足其他条件使其进入RUNNING。在需要动态修改关键参数如波特率前一个稳妥的做法是先设置HALT位让其暂停修改后再清除。直接“热修改”正在使用的寄存器是嵌入式开发的大忌极易导致通信错乱。2.2 SPI配置模式与主从模式DSPI模块功能强大支持SPI、DSI、CSI等多种配置。我们最关心的是其SPI配置模式此时DSPI_MCR寄存器中的DCONF字段应为0b00。在这个模式下数据通过移位寄存器串行传输且帧长度可在4到16位之间灵活编程。SPI配置下数据流的核心路径是主机CPU或DMA控制器将待发送的数据从外部内存搬运到DSPI内部的发送FIFOTX FIFO发送时数据从TX FIFO加载到移位寄存器再一位一位地从SOUT引脚移出。接收时从SIN引脚移入的数据先存到移位寄存器在一帧接收完成后再被转存到接收FIFORX FIFO最后由CPU或DMA读走。这个双FIFO结构是DSPI高性能的基石。SPI配置支持两种角色主模式Master和从模式Slave。两种模式下的FIFO操作逻辑基本相似核心区别在于传输的发起与控制权。在主模式下DSPI是“老板”它完全掌控通信。它负责生成串行通信时钟SCK和外围设备片选PCS信号。最关键的是TX FIFO中的每一个条目不仅包含要发送的数据Data Field还包含一个SPI命令字段Command Field。这个命令字段是个“指挥棒”它决定了本次传输使用哪一组时钟与传输属性寄存器CTAR0-CTAR3来设定波特率、时钟极性与相位等以及具体断言拉低哪个PCS信号。这实现了“帧级”的精细控制每一帧都可以有不同的通信参数。而在从模式下DSPI是“员工”它只响应外部SPI主机的呼叫。它不主动发起传输SCK和片选信号都由外部主机提供。因此从机模式下的TX FIFO命令字段空间被“挪用”了——它存储的是待发送数据的高16位假设数据是32位。从机的通信参数如时钟极性CPOL、相位CPHA、帧长必须通过DSPI_CTAR0寄存器预先设定好并且必须与主机严格匹配否则通信无法进行。注意事项很多工程师在调试主从通信时只关注主机的配置忽略了从机也需要正确配置CPOL和CPHA。即使从机不产生时钟这两个参数也决定了从机在哪个时钟边沿采样数据和更新输出数据必须与主机一致。此外从机的TX FIFO如果没有及时填入数据当主机发起读操作时从机会发送命令字段中的内容此时是数据高16位这可能不是你期望的数据需要特别注意。3. FIFO缓冲机制深度剖析FIFO是DSPI的灵魂理解了它就掌握了高效使用DSPI的钥匙。它本质上是一个硬件队列解决了CPU与串行通信速度不匹配的问题。3.1 TX FIFO发送数据的蓄水池与调度中心发送FIFOTX FIFO是一个深度为1到5个条目的缓冲区具体深度取决于具体的SoC型号。每个条目都是一个32位的“数据包”其中包含16位的命令字段和16位的数据字段对于8位帧数据在低8位。你可以通过写DSPI_PUSHR寄存器来向TX FIFO“压入Push”数据。DSPI_SR寄存器中的TXCTR字段就像一个水位计实时显示TX FIFO中有效条目的数量。每次写入PUSHR或从TX FIFO移出一个条目到移位寄存器时这个计数器都会更新。TXNXTPTR则像是一个“读指针”指向下一个将要被传输的FIFO条目。填充TX FIFO当TX FIFO未满时状态寄存器中的TX FIFO Fill Flag (TFFF)会被置位。这个标志位非常有用它可以触发DMA请求或中断通知CPU或DMA控制器“FIFO还有空位可以继续送数据了”当你向PUSHR写入数据填满FIFO后TFFF会自动清零。如果试图向一个已满的TX FIFO写入数据DSPI会直接忽略这次操作且不会报错。因此在编写发送程序时必须通过查询TFFF标志或利用其中断/DMA来管理数据流避免数据丢失。排空TX FIFOTX FIFO的条目是通过“移位发送”的方式被移除的。只要FIFO里有数据DSPI的状态机就会自动将其加载到移位寄存器并发送出去。每发送一帧TXCTR就减1。当一帧数据完全移出后TCF传输完成标志位会被置位。你也可以通过设置DSPI_MCR[CLR_TXF]位来手动清空整个TX FIFO。这里有一个从机模式下的特殊场景需要注意发送下溢Transmit FIFO Underflow。如果外部主机向一个DSPI从机发起读操作而此时从机的TX FIFO是空的没有数据可以发送那么从机的TFUF标志位就会被置位并可能产生中断。这通常意味着从机端的软件没有及时准备待发送的数据。3.2 RX FIFO接收数据的收纳箱接收FIFORX FIFO的深度更大通常是1到16个条目同样SoC相关用于缓冲接收到的数据。当一帧数据在移位寄存器中接收完毕它会被自动转存到RX FIFO中。CPU或DMA通过读取DSPI_POPR寄存器来从RX FIFO中“弹出Pop”数据。与TX FIFO类似RXCTR指示RX FIFO中有效数据的数量POPNXTPTR是弹出指针。当RX FIFO非空时RFDFRX FIFO Drain Flag标志置位可用于触发读取中断或DMA请求。填充RX FIFO的过程是自动的。但如果RX FIFO和移位寄存器都满了此时又有新的传输完成就会发生溢出Overflow。RFOF标志位会被置位。此时的行为由DSPI_MCR[ROOE]位控制若ROOE1新数据会覆盖移位寄存器中的旧数据可能造成数据丢失但维持通信若ROOE0则新数据直接被丢弃。在高速连续接收场景下必须确保及时读取RX FIFO或使用DMA避免溢出发生。排空RX FIFO就是读取POPR的过程。尝试从一个空的RX FIFO读取数据会被忽略读出的值是不确定的。RFDF标志在FIFO变空后会在DMA读操作完成或软件写1清除时复位。3.3 FIFO禁用模式双缓冲简化操作DSPI也提供了禁用FIFO的选项通过设置DSPI_MCR[DIS_TXF]和DIS_RXF位实现。当FIFO被禁用时DSPI会退化为一个双缓冲的简易SPI接口。这对软件来说是透明的你依然读写PUSHR和POPR寄存器但此时它们直接对接移位寄存器。在这种模式下状态寄存器中的TFFF、TFUF、TXCTR等字段的行为就像FIFO只有1个深度条目一样。这对于那些不需要大数据缓冲、或者希望用最简逻辑控制SPI的场景非常有用可以减少状态管理的复杂度。避坑指南关于FIFO指针和计数器手册里提到当FIFO禁用时TXNXTPTR和POPNXTPTR以及DSPI_TXFR/RXFR寄存器的内容是未定义的undefined。这意味着在禁用FIFO的模式下不要试图去读取或依赖这些寄存器的值来判断状态而应该只关心TFFF、TCF、RFDF这些核心标志位。我曾遇到过在禁用FIFO后还去查TXCTR导致程序逻辑错误的问题排查了很久。4. 波特率与时钟延时生成机制SPI通信的时序是其稳定性的核心。DSPI提供了非常灵活的时钟和延时控制这也是它比基础SPI强大的地方。所有这些时序参数都是通过DSPI_CTAR0~DSPI_CTAR3这组时钟与传输属性寄存器来配置的。4.1 波特率Baud Rate计算波特率就是SCK的频率。DSPI通过两级分频来从系统时钟fsys得到SCK一个预分频器PBR和一个分频器BR并且可以通过双波特率位DBR选择是否将分频系数减半。计算公式如下SCK频率 fsys / [(1DBR) * PBR * BR]其中PBR和BR的值由CTAR寄存器中的PBR和BR字段选择它们对应着固定的分频系数如PBR0b00对应分频系数2BR0b0000对应分频系数2。DBR为0时分母中的(1DBR)为1DBR为1时则为2相当于波特率翻倍。例如手册中的例子fsys100MHz,PBR0b00(2),BR0b0000(2),DBR0则SCK 100MHz / (2*2) 25MHz。这是一个非常直观的计算过程在编程时我们通常是根据需要的SCK频率反向推算出合适的PBR、BR和DBR组合。4.2 关键时序延时详解除了波特率三个关键的延时参数对匹配不同外设的时序要求至关重要PCS到SCK延时tCSC从片选信号PCS有效通常拉低到第一个SCK时钟边沿出现之间的时间。这个延时给了从设备一个准备时间确保在时钟开始前片选已稳定建立。其计算方式与波特率类似tCSC (PCSSCK * CSSCK) / fsys。PCSSCK和CSSCK是CTAR中的配置字段。SCK后延时tASC从最后一个SCK时钟边沿到片选信号PCS无效通常拉高之间的时间。这个延时保证了在时钟结束后数据有足够的保持时间Hold Time被从设备采样。计算公式tASC (PASC * ASC) / fsys。传输后延时tDT前一帧的PCS无效到下一帧的PCS有效之间的最小空闲时间。它用于分隔两次独立的传输。计算公式tDT (PDT * DT) / fsys。特别注意在连续时钟模式CONT_SCKE1下此延时固定为1个SCK周期。这些延时参数的单位都是系统时钟周期。通过灵活配置它们你可以精确地满足各种SPI外设数据手册中规定的tCSS片选建立时间、tCSH片选保持时间等参数要求。4.3 外设片选选通使能PCSS这是一个高级功能用于生成一个额外的PCSS信号。当DSPI_MCR[PCSSE]置位时PCSS信号可以作为外部多路选择器的控制信号将有限的几个PCS引脚如PCS[0:4], [6:7]解码成多达128个无毛刺的片选信号。PCSS的断言和置位延时分别由tPCSSCK和tPASC控制其计算直接依赖于PCSSCK和PASC字段的值tPCSSCK PCSSCK / fsys。这在对片选切换时序有严苛要求或者需要扩展大量片选时非常有用。参数配置经验在配置这些延时参数时我习惯先根据外设数据手册的要求计算出所需的最小时间单位纳秒然后根据系统时钟频率换算成需要的时钟周期数。接着对照CTAR寄存器中PCSSCK、CSSCK等字段的取值表这些字段通常是几位二进制数对应一个预设的分频系数乘数选择能够提供大于等于所需周期数的组合。宁大勿小多留一点余量有利于系统在极端温度或电压下稳定工作。例如某Flash芯片要求tCSH 20ns我的fsys100MHz周期10ns那么tASC至少需要配置为2个系统时钟周期20ns。5. SPI传输格式与连续模式实战SPI通信有四种基本“方言”由时钟极性CPOL、时钟相位CPHA和修改传输格式使能MTFE共同决定。理解这四种格式的波形是调试SPI通信的必修课。5.1 经典SPI格式CPHA0 与 CPHA1CPHA0数据在第一个时钟边沿被采样。具体来说主机在片选有效后会先将第一位数据放到MOSI主出从入线上然后等待tCSC延时再产生第一个SCK边沿对于CPOL0是上升沿CPOL1是下降沿。主从设备都在这个奇数边沿采样数据在随后的偶数边沿更新输出数据。这种格式下数据在时钟边沿到来之前就已经稳定适合对数据建立时间要求宽松的从设备。CPHA1数据在第二个时钟边沿被采样。主机在片选有效并等待tCSC后先产生第一个SCK边沿同时将第一位数据放到MOSI线上。从设备则在第一个边沿时将它的第一位数据放到MISO主入从出线上。主从设备都在偶数边沿采样数据在奇数边沿更新输出数据。这种格式下数据在时钟边沿到来时才变化给了从设备更多的准备时间。CPOL则决定了SCK时钟空闲时的电平CPOL0为低电平CPOL1为高电平。CPOL和CPHA的组合构成了SPI的四种模式Mode 0-3。主从设备的模式必须完全一致。5.2 修改的SPI传输格式MTFE1当通信速率非常高时PCB走线延时、器件输入输出延时等会成为不可忽视的因素。经典SPI格式假设主从设备在时钟边沿的中心点采样但在高速下这个假设可能不成立导致采样错误。DSPI的修改传输格式MTFE1就是为了解决这个问题。它允许主设备推迟采样点。通过配置DSPI_MCR[SMPL_PT]字段可以将主设备对SIN即MISO的采样点从时钟边沿延迟1个或2个系统时钟周期。这相当于给了从设备发送的数据更长的“旅行时间”到达主设备提高了高速通信的容错性。手册中的时序图清晰地展示了这一点。在MTFE1且CPHA0的格式下从设备依然在奇数SCK边沿采样主机数据但DSPI作为主机其采样点SMPL_PT可以延后。当系统时钟与SCK频率比fsys/fsck小于4时建议将SMPL_PT设为0。5.3 连续选择与连续时钟模式连续选择格式由TX FIFO命令字段中的CONT位控制。当CONT0时每传输完一帧PCS片选信号都会恢复到空闲状态由PCSISn位定义并在下一帧开始前插入tDT延时。这是最常见的方式。当CONT1时PCS信号在连续的多个帧传输期间保持有效帧与帧之间没有tDT延时。这对于那些需要在一个片选有效周期内传输多字节命令和数据的设备如很多SPI Flash的页编程命令非常有用。重要警告使用连续选择格式时必须确保在TX FIFO变空之前将所有需要在同一个PCS有效期内连续发送的帧全部填入FIFO。并且最后一个使PCS保持有效的帧即TX FIFO中最后一个CONT1的帧之后必须紧跟一个CONT0的帧来结束本次连续传输。否则如果FIFO空了而PCS还保持有效从设备可能会误解后续的通信或者导致DSPI从机模式下发生TX FIFO下溢错误。连续时钟模式通过设置DSPI_MCR[CONT_SCKE]来启用。在此模式下SCK时钟在帧与帧之间不会停止而是持续运行。这仅支持CPHA1的模式。启用连续时钟后tCSC和tDT延时被简化或固定tDT固定为1个SCK周期。这种模式适用于那些需要持续时钟的从设备。避坑指南手册中特别用NOTE强调在连续时钟模式下进行SPI传输必须使用CTAR0并且在启动传输前必须使用MCR.CLR_TXF位清空TX FIFO。我曾在启用连续时钟后未清空FIFO就发起传输结果出现了难以解释的时序错乱。另外在连续时钟且连续选择CONT1模式下如果TX FIFO空了但PCS还保持有效SCK会继续空跑这可能导致从设备采样到错误数据。因此必须严格管理好CONT位的设置和FIFO的数据流。6. 常见问题排查与调试技巧实录调通DSPI尤其是用到FIFO和复杂时序时总会遇到各种“坑”。下面是我在实际项目中总结的一些典型问题及排查思路。6.1 通信完全无反应或数据全错检查基本配置这是最基础的。确认主从设备的CPOL、CPHA、数据位顺序LSBFE、帧长度FMSZ是否完全一致。一个字节位序设反整个数据就面目全非。确认模块状态使用调试器或通过软件读取DSPI_SR[TXRXS]位确认DSPI模块是否已从STOPPED进入了RUNNING状态。如果HALT或FRZ位被意外置位模块是不会工作的。检查时钟和引脚用示波器或逻辑分析仪测量SCK、PCS、SIN、SOUT引脚。首先看SCK有没有波形频率是否正确计算波特率。如果SCK都没有检查DSPI_MCR的模块使能位、对应引脚的复用功能是否已正确设置为DSPI。验证FIFO操作如果SCK有但没数据。检查TX FIFO是否已填入数据查TXCTR或TFFF。对于从机检查主机发起传输时从机的TX FIFO里是否有待发送数据避免下溢。6.2 FIFO数据卡住或传输不连续中断/DMA未正确处理如果依赖TFFF中断来填充TX FIFO或RFDF中断来清空RX FIFO必须确保中断服务程序ISR正确清除中断标志位通常通过写1清除。标志位没清后续中断就无法触发。FIFO指针溢出虽然不常见但如果你手动操作PUSHR/POPR的速率和硬件移位速率不匹配理论上可能导致TXNXTPTR或POPNXTPTR指针溢出超过DSPI_HCR[TXFR/RXFR]定义的最大值。通常让硬件自动管理即可但极端测试时需留意。连续模式下的CONT位错误在连续选择CONT1模式下传输一串数据后通信突然停止或出错。务必检查你是否在TX FIFO的最后一个条目中将CONT位设为了0以正确结束本次连续传输并释放PCS线。6.3 时序不稳定高速时出错延时参数不足这是高速SPI的常见病。用逻辑分析仪抓取波形重点测量tCSCPCS有效到第一个SCK边沿、tASC最后一个SCK到PCS无效。与外设数据手册要求的最小建立时间tSU和保持时间tHOLD对比。如果不足增大PCSSCK/CSSCK或PASC/ASC的配置值。启用修改传输格式MTFE在SCK频率接近或超过10MHz时强烈建议尝试启用MTFE1并调整SMPL_PT。这能有效补偿板级延时。可以先从SMPL_PT2延迟2个系统时钟开始测试。检查PCB布局高速SPI对走线非常敏感。确保SCK线尽可能短并远离其他高速或噪声源。MISO和MOSI线最好等长并做好阻抗控制。如果可能在信号线上串联一个小电阻如22欧姆有助于减少过冲和振铃。6.4 从机模式下的特殊问题从机无响应确保从机的DSPI_CTAR0中的CPOL、CPHA、FMSZ与主机完全匹配。同时检查从机模块是否已使能并进入RUNNING状态。从机发送错误数据在从机模式下当TX FIFO为空时发送的数据是TX FIFO命令字段的内容此时是数据高16位这可能是一个固定值或上次残留值。确保在主机发起读操作前从机已将要发送的数据写入TX FIFO写入PUSHR的低16位数据字段部分高16位会被忽略/用作命令这里需注意从机模式下写入PUSHR的32位字中高16位会被当作待发送数据的高位低16位是数据低位。手册原文是“the SPI command field space is used for 16 most significant bit of the transmit data.”。调试DSPI一个好的逻辑分析仪是必不可少的。我习惯设置触发条件为PCS下降沿然后同时捕获SCK、MOSI、MISO四路信号。通过分析波形可以直观地看到数据是否对齐、延时是否满足、CONT位是否生效比单纯看代码和寄存器值高效得多。最后耐心阅读芯片参考手册的时序图结合波形反复比对是解决复杂时序问题的终极法宝。