TMS320F28003x SPI模块深度解析:从基础通信到FIFO/DMA高效驱动

发布时间:2026/7/19 10:41:09

TMS320F28003x SPI模块深度解析:从基础通信到FIFO/DMA高效驱动 1. SPI接口基础从四线制到通信哲学搞嵌入式开发这么多年SPISerial Peripheral Interface绝对是我打交道最多的通信接口之一。它不像I2C那样需要上拉电阻和复杂的地址协议也不像UART那样依赖精确的波特率匹配。SPI的魅力就在于它的“简单粗暴”——全双工、同步、主从架构用最少的信号线实现高速数据交换。对于TMS320F28003x这类面向实时控制的高性能微控制器来说SPI是连接外部ADC、DAC、传感器、Flash存储器甚至另一片MCU的血管。SPI通常被称为“四线制”接口这四根线各有其职SPICLK串行时钟由主机产生是整个通信的节拍器SPISIMO主机出从机入和SPISOMI主机入从机出构成全双工数据通道SPISTE从机使能则用于片选在多从机系统中指明当前与谁对话。这种设计使得SPI在硬件层面几乎无需额外的逻辑电路通信速率直接由主机的时钟频率决定轻松跑到几十兆赫兹远超常见的I2C和UART。但“简单”不代表“简陋”。以TMS320F28003x的SPI模块为例它远不止是一个简单的移位寄存器。其内置的16级FIFO、对DMA的完整支持、四种可编程的时钟方案以及灵活的中断机制共同构成了一个高效、可靠且节省CPU资源的通信引擎。理解这些高级特性并能在项目中熟练运用是区分嵌入式新手和老鸟的关键。本文将带你深入SPI的核心不仅看懂手册更能写出稳定、高效的驱动代码。2. TMS320F28003x SPI模块深度解析与设计思路拿到一款MCU我习惯先抛开具体寄存器从系统层面理解其外设的设计思路。TMS320F28003x的SPI模块是一个高度集成化的通信控制器它的设计目标很明确在保证极高实时性和确定性的同时最大限度减轻CPU负担。2.1 模块架构与信号管理模块的核心是一个16位的移位寄存器SPIDAT负责数据的并串/串并转换。但为了让CPU不必“盯”着每一次移位操作TI设计了双缓冲机制SPITXBUF发送缓冲和SPIRXBUF接收缓冲。当一次传输启动后CPU可以立即准备下一个要发送的数据到SPITXBUF同时从SPIRXBUF读取上次接收完成的数据从而实现流水线操作。SPISTE引脚的功能值得单独讨论。在标准应用中它作为低有效的从机片选信号。但手册里特别提到一个警告不建议将SPISTE永久拉低保持有效。这是因为如果从机意外失去与主机的同步例如受到噪声干扰持续的使能信号会导致从机无法区分新数据帧的开始从而造成永久性的通信错乱。正确的做法是主机在每帧数据传输前拉低SPISTE传输完成后立即拉高。如果发生同步丢失可以通过软件复位SPI模块操作SPISWRESET位来重置内部位计数器让通信重新对齐。注意在多从机系统中一个SPI主机模块通常只提供一个SPISTE信号。若要连接多个从设备需要利用普通GPIO来模拟额外的片选信号。配置SPI为主模式将SPISTE对应的GPIO引脚配置为普通输出口在数据传输前手动拉低对应的GPIO传输完成后拉高。此时硬件SPISTE引脚可以悬空或固定为高电平。2.2 时钟方案与数据格式的灵活性SPI通信的时序是其精髓也是新手最容易困惑的地方。时序由两个关键位控制CLKPOLARITY时钟极性和CLK_PHASE时钟相位。它们的四种组合产生了手册中提到的四种时钟模式模式0-3。CLKPOLARITY0时钟空闲时为低电平。CLKPOLARITY1时钟空闲时为高电平。CLK_PHASE0数据在第一个时钟边沿采样捕获。CLK_PHASE1数据在第二个时钟边沿采样数据输出提前半个周期。如何选择这完全取决于你的从设备如传感器、存储器的数据手册要求。例如很多SPI Flash芯片工作在模式0CPOL0 CPHA0或模式3CPOL1 CPHA1。一个务必遵守的黄金法则主设备的时钟模式必须严格与从设备匹配。我早期就曾因主从模式设置不一致导致读取的数据全是0xFF或0x00排查了半天。另一个关键点是数据字长SPICHAR寄存器配置1-16位。SPI并非只能传输8位字节。当你需要读取一个12位的ADC值时完全可以将字长设置为12位。这里有个重要细节写入SPIDAT或SPITXBUF的数据必须左对齐而从SPIRXBUF读出的数据是右对齐的。例如你要发送一个12位数据0xABC二进制 1010 1011 1100在16位的缓冲区中你需要写入0xABC0左移4位。接收时SPIRXBUF中得到的将是右对齐的0x0ABC。2.3 FIFO与DMA性能加速的双引擎这是TMS320F28003x SPI模块提升效率的核心设计。如果没有FIFO每传输一个字最多16位都可能产生一次中断CPU频繁陷入中断服务程序在高速或大数据量传输时将成为系统瓶颈。16级FIFO的引入彻底改变了局面。发送时CPU或DMA可以一次性向发送FIFO填入最多16个数据字接收时也能从接收FIFO中一次性读取最多16个数据字。模块的硬件逻辑会自动管理FIFO的进出。更强大的是可编程中断水位线TXFFIL, RXFFIL。你可以设置当发送FIFO中的数据量少于或等于某个值时才触发发送中断SPITXINT让CPU来补充数据同样设置当接收FIFO中的数据量大于或等于某个值时才触发接收中断SPIRXINT通知CPU来批量取走数据。这极大地减少了中断频率。DMA的集成则将CPU解放得更彻底。通过配置SPI的DMA触发事件SPITXDMA和SPIRXDMA可以将大批量数据的搬移工作完全交给DMA控制器。SPI模块的FIFO状态会直接触发DMA传输请求。例如设置发送FIFO水位线TXFFIL为8当FIFO中数据少于8个时自动触发DMA将源数据区的数据搬8个到FIFO中。整个过程无需CPU干预CPU可以专注于核心的控制算法运算。3. SPI模块配置与驱动实现实操要点理解了原理我们进入实战环节。配置TMS320F28003x的SPI模块需要遵循一个清晰的流程并注意诸多细节。3.1 初始化配置步骤详解时钟与引脚配置首先使能SPI模块所在的外设时钟通过PCLKCR寄存器。配置GPIO复用功能。将指定引脚设置为SPI功能SPICLK, SPISIMO, SPISOMI, SPISTE。这里有个关键顺序先写GPyGMUX寄存器再写GPyMUX寄存器可以避免引脚在切换功能时产生毛刺。对于输入引脚如主机的SPISOMI需要将对应的输入量化选择器GPxQSELn设置为异步模式11b避免采样延迟影响高速通信。复位与基本设置将SPICCR.2SPISWRESET位写0使模块处于复位状态。在此状态下配置寄存器是安全的。配置SPICCR寄存器设置SPICHAR字长选择是否使能高速模式HS_MODE设置时钟极性CLKPOLARITY。配置SPICTL寄存器设置时钟相位CLK_PHASE、主从模式MASTER_SLAVE、使能TALK允许发送等。波特率计算与设置计算SPIBRR值。公式分两种情况当3 ≤ SPIBRR ≤ 127时波特率 LSPCLK / (SPIBRR 1)当SPIBRR 0, 1, 2时波特率 LSPCLK / 4重要限制无论主从模式SPI时钟频率SPICLK不能超过LSPCLK / 4。例如若LSPCLK50MHz则SPICLK最大为12.5MHz。同时最终速率不能超过GPIO引脚的最大翻转频率需查芯片数据手册。示例LSPCLK50MHz目标波特率10Mbps。计算SPIBRR (50MHz / 10Mbps) - 1 4。将4写入SPIBRR寄存器。FIFO与中断配置如需要在SPIFFTX寄存器中使能FIFO模式SPIFFENA1。设置发送和接收FIFO的中断触发水位线TXFFIL, RXFFIL。例如设置TXFFIL8RXFFIL8。在SPIFFTX和SPIFFRX寄存器中清除FIFO复位位TXFIFO, RXFIFO使FIFO进入就绪状态。在SPICTL或SPIFFTX中使能相应的中断如SPIINTENA, RXFFIENA, TXFFIENA。最后在PIE模块中使能对应的PIE中断。启动SPI将SPICCR.2SPISWRESET位写1释放复位SPI模块开始运行。3.2 主从模式下的数据收发流程主机模式发送流程查询方式// 1. 检查发送缓冲是否就绪非FIFO模式看SPISTS.5 BUFFULL_FLAG FIFO模式看TXFFST while(SPI_isTxFifoFull(mySpiBase) true); // 等待有空位 // 2. 将数据写入发送缓冲SPITXBUF或FIFO SPI_writeDataNonFifo(mySpiBase, dataToSend); // 或 SPI_writeDataFifo // 3. 硬件会自动将数据移入SPIDAT并在SPICLK作用下移位输出。 // 4. 等待发送完成查询SPISTS.6 INT_FLAG或FIFO中断标志 while(SPI_getInterruptStatus(mySpiBase, SPI_INT_TX_EMPTY) false); // 5. 读取接收到的数据从SPIRXBUF或FIFO receivedData SPI_readDataNonFifo(mySpiBase);从机模式关键点从机的时钟SPICLK由外部主机提供其频率不能超过LSPCLK/4。从机若要发送数据必须在主机发起时钟之前将数据写入SPITXBUF。如果写晚了从机发送的将是无效数据或旧数据。TALK位的控制至关重要。当TALK0时从机的SPISOMI输出为高阻态。这在多从机共享总线时用于“沉默”未被选中的从机。即使TALK在传输中途被清零当前字符也会被完整发送完毕以保证通信的完整性。3.3 利用Driverlib库函数简化开发TI提供的Driverlib库将寄存器操作封装成了直观的函数极大提高了开发效率和代码可读性。以下是一些关键寄存器与库函数的对应关系及使用示例寄存器库文件关键函数示例功能描述SPICCRspi.hSPI_setConfig()综合配置时钟极性、字长、软件复位等SPICTLspi.hSPI_setConfig()配置时钟相位、主从模式、TALK、中断使能SPIBRRspi.hSPI_setBaudRate()设置波特率SPIFFTXspi.hSPI_enableFIFO(),SPI_setTxFifoIntLevel()使能FIFO设置发送中断水位线SPIFFRXspi.hSPI_setRxFifoIntLevel()设置接收中断水位线SPIDAT-通常通过SPITXBUF间接访问移位寄存器SPIRXBUFspi.hSPI_readDataNonFifo(),SPI_readDataFifo()读取接收到的数据SPITXBUFspi.hSPI_writeDataNonFifo(),SPI_writeDataFifo()写入要发送的数据初始化示例主机16位数据模式0使能FIFO#include driverlib.h void SPI_Master_Init(void) { // 1. 使能外设时钟 SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_SPIA); // 2. 配置GPIO引脚为SPI功能 GPIO_setPinConfig(GPIO_16_SPISIMOA); GPIO_setPinConfig(GPIO_17_SPISOMIA); GPIO_setPinConfig(GPIO_18_SPICLKA); GPIO_setPinConfig(GPIO_19_SPISTEA); // 作为GPIO手动控制片选时可不配 // 3. SPI模块复位期间进行配置 SPI_disableModule(SPIA_BASE); SPI_setConfig(SPIA_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA0, SPI_MODE_MASTER, 1000000, 16); // 1Mbps 16位数据 // 4. 配置FIFO SPI_enableFIFO(SPIA_BASE); SPI_setTxFifoIntLevel(SPIA_BASE, SPI_FIFO_TX8); // TX FIFO 8时中断 SPI_setRxFifoIntLevel(SPIA_BASE, SPI_FIFO_RX8); // RX FIFO 8时中断 SPI_enableInterrupt(SPIA_BASE, SPI_INT_RXFF); // 使能RX FIFO中断 // 5. 使能SPI模块 SPI_enableModule(SPIA_BASE); }4. 高级应用FIFO与DMA的协同编程当数据量较大或通信频率很高时合理运用FIFO和DMA是保证系统实时性的不二法门。这里面的配置有些“坑”。4.1 FIFO深度与中断水位线的权衡FIFO深度是16级但中断水位线TXFFIL/RXFFIL的设置需要仔细考量。设置得太激进如TXFFIL15中断虽然少但一旦触发CPU需要在极短的时间内填充15个字可能造成响应延迟。设置得太保守如TXFFIL1中断又会过于频繁。我的经验是对于发送将TXFFIL设置为FIFO深度的一半左右如8。这样给CPU留出了足够的时间响应中断并填充数据同时又不会让FIFO空置太久。对于接收将RXFFIL设置为1或2可以更快地响应数据到达避免FIFO溢出OVERRUN。当然最优值需要结合你的系统中断负载和SPI波特率来实测调整。4.2 DMA传输的精确计算手册里给出了DMA传输的配置公式但必须理解其背后的逻辑否则极易出错。发送DMA配置要点核心矛盾DMA的突发传输Burst不能向一个已满的FIFO写数据。公式推导DMA_BURST_SIZE ≤ 16 - TXFFIL假设TXFFIL设置为8意味着当TX FIFO中数据量≤8时会触发DMA请求。此时FIFO中最多有8个空位16-88。因此DMA单次突发传输的数据量BURST_SIZE最大只能是8。在DMA配置中BURST_SIZE寄存器通常填写的是传输次数-1。所以如果我们要每次传8个字则配置值应为8-1 7。传输次数计算DMA_TRANSFER_SIZE (总字数 / TXFFIL) - 1假设要发送128个字TXFFIL8则DMA需要被触发128/8 16次。DMA的传输次数配置寄存器通常填次数-1所以填16-115。接收DMA配置要点核心矛盾DMA不能从一个空的FIFO读数据。公式推导DMA_BURST_SIZE ≤ RXFFIL且最好是总字数的整数因子。假设RXFFIL设置为4意味着当RX FIFO中数据量≥4时会触发DMA请求。因此DMA单次突发读取的数据量BURST_SIZE不能超过4否则可能读空。设为4是最高效的。同样BURST_SIZE配置值为4-13。传输次数计算DMA_TRANSFER_SIZE (总字数 / RXFFIL) - 1接收200个字RXFFIL4则DMA需要被触发200/450次。配置值填50-149。重要避坑指南避免TXFFIL设置为0或16设置为0意味着FIFO空就触发但DMA可能来不及响应导致FIFO下溢设置为16则永远不会触发因为FIFO状态永远不会≤16。避免RXFFIL设置为0设置为0意味着永远不触发接收DMA请求因为FIFO状态永远不会≥0这里逻辑是FIFO非空即触发但可能过于频繁且与DMA配置公式冲突。通常从1开始设置。总字数必须是BURST_SIZE的整数倍否则最后一次DMA传输会尝试读取不完整的数据块可能导致错误。在程序设计中应确保发送/接收的数据缓冲区大小符合这个条件。4.3 延迟传输控制FFTXDLY的应用SPIFFCT寄存器的FFTXDLY位域0-255允许你在FIFO中的字被传输到发送移位存器之间插入延迟以SPICLK周期为单位。这个功能非常实用专门用于连接那些“慢速”的SPI设备比如EEPROM或某些ADC。设置为0连续模式。数据帧之间没有间隔一个接一个地发送速率最快。设置为非零值在每帧数据发送结束后插入N个SPICLK周期的空闲时间然后再开始下一帧。这给了从设备足够的处理时间例如EEPROM完成一页的写入操作。例如驱动一个需要几十微秒写周期的EEPROM时你可以通过计算设置一个合适的FFTXDLY值让硬件自动插入延迟而无需CPU用软件延时等待极大地提高了效率。5. 调试常见问题与排查技巧实录SPI调试说简单也简单说复杂也复杂。下面是我在多年项目中总结的一些典型问题及排查手段。5.1 通信完全无反应检查清单电源与时钟确认MCU和从设备均已上电MCU的LSPCLK时钟是否正确配置并运行。引脚配置用示波器或逻辑分析仪检查SPICLK、SPISTE引脚。确认GPIO复用功能是否配置正确这是最高发问题。主机的SPICLK是否有输出SPISTE在传输时是否拉低软件复位确认是否在配置前将SPISWRESET置0配置完成后置1。主从模式确认主机和从机的MASTER_SLAVE位设置是否正确且唯一。基本使能主机的TALK位是否置1从机的SPISTE引脚是否被正确拉低使能5.2 能发送但接收数据错误全0、全1、或固定错误值排查思路时钟相位/极性这是头号嫌疑犯用逻辑分析仪同时抓取SPICLK、SPISIMO、SPISOMI三路信号。仔细比对波形看数据采样边沿是否与从设备要求的一致。主机和从机的CLKPOLARITY和CLK_PHASE必须完全相同。数据字长对齐检查SPICHAR设置的数据位宽是否与从设备一致。检查软件中数据写入左对齐和读取右对齐的处理是否正确。硬件连接检查SPISIMO和SPISOMI线是否接反主机SIMO接从机SIMO主机SOMI接从机SOMI。检查线路是否有虚焊或短路。从设备就绪某些从设备如Flash在执行内部操作擦除、编程时会暂时不响应SPI命令。需要查询其状态寄存器。5.3 FIFO或DMA工作异常数据丢失、中断不触发问题诊断FIFO使能确认SPIFFENA位已置1进入了FIFO模式。中断水位线检查TXFFIL和RXFFIL的设置是否合理。例如如果你期望DMA搬运8个字但TXFFIL设置为12那么FIFO状态TXFFST永远达不到“≤12”的条件因为初始为空值为0DMA请求永远不会触发。DMA配置匹配严格按照4.2节的公式计算并配置DMA的BURST_SIZE和TRANSFER_SIZE。使用调试器查看DMA控制寄存器的当前计数值看传输是否如预期进行。中断标志清除在中断服务程序ISR中是否清除了相应的中断标志如RXFFINT, TXFFINT如果未清除将只会进入一次中断。FIFO复位状态配置完成后是否将SPIFFTX和SPIFFRX中的TXFIFO和RXFIFO复位位清零了这两个位为1时FIFO指针被强制清零无法工作。5.4 高波特率下的通信不稳定解决方案检查布线SPI属于高速信号应尽可能缩短走线避免过孔并远离噪声源。如果距离较长需考虑阻抗匹配或使用差分SPI某些器件支持。启用高速模式将SPICCR.HS_MODE位置1可以增强SPI引脚驱动器的压摆率改善信号完整性。但要注意这会增加功耗和EMI。调整时钟对称性当(SPIBRR 1)为奇数且大于3时SPICLK的占空比不是50%高低电平宽度会差一个LSPCLK周期。这在某些对时钟对称性敏感的从设备上可能引发问题。尝试调整SPIBRR值使(SPIBRR 1)为偶数。降低波特率这是最直接的验证方法。如果降低波特率后通信稳定问题很可能出在硬件信号质量或时序容限上。我个人在调试一个与高速ADC通信的项目时曾遇到间歇性数据错误。最终发现是SPICLK线过长且靠近一个开关电源引入了噪声。解决方案是缩短时钟线并在MCU输出端串联一个33欧姆的小电阻有效抑制了振铃。另一个项目中使用DMA传输大量数据时发现最后总少几个字节。经排查原因是DMA传输次数计算错误DMA_TRANSFER_SIZE少配置了1次。这些坑踩过之后就对手册里的公式和限制条件刻骨铭心了。调试SPI一把好的逻辑分析仪和耐心细致的波形对比比任何猜想都管用。

相关新闻