
1. 项目概述与核心价值在嵌入式系统开发尤其是电机控制、数字电源、工业自动化这些对实时性要求极高的领域SPISerial Peripheral Interface通信是连接微控制器与各类传感器、存储芯片、ADC/DAC转换器的核心桥梁。然而当数据吞吐量增大传统的CPU轮询或中断处理SPI数据的方式就会成为系统性能的瓶颈频繁的中断响应会严重挤占CPU资源影响核心控制算法的执行。这时DMADirect Memory Access技术就成了解放CPU、实现高效数据搬运的“神器”。它能让数据在SPI外设和内存之间自动传输CPU只需在传输开始和结束时介入中间过程完全由硬件接管。而SPI的3线模式则是在引脚资源紧张时的“瘦身”方案通过复用数据线用三根线完成原本四根线的任务特别适合小型化、高集成度的设计。本文将以TI的C2000系列明星产品TMS320F2838x为例深入剖析如何将SPI、DMA和3线模式这三项技术结合起来构建一个高效、可靠的通信链路。我不会只停留在手册的翻译层面而是结合我多年在电机驱动和电源项目中的实战经验带你一步步拆解配置逻辑厘清那些容易让人栽跟头的参数计算细节并分享从调试中总结出的避坑指南。无论你是正在评估通信方案还是已经卡在数据传输的稳定性问题上这篇文章都能提供直接的、可落地的参考。2. SPI DMA传输的核心原理与配置精解2.1 为什么需要SPI DMA一个生动的场景比喻想象一下你CPU正在厨房执行核心控制算法精心烹饪一道大餐比如FOC电机控制。突然门铃响了SPI接收中断快递员SPI外设送来了一箱食材128个字的传感器数据。如果你每次都亲自跑去开门、搬箱子、清点、签字中断服务程序那你的菜很可能就糊了。DMA就像你雇了一个智能管家。你只需要告诉管家“去门口把接下来到的10箱食材DMA传输大小搬到储藏室内存的A区目标地址搬完了叫我。” 然后你就可以专心炒菜了。管家DMA控制器会自己监听门铃SPI FIFO触发信号自动搬运只在所有箱子搬完或出现异常时才通知你。这样CPU的负担大大减轻系统实时性得到保障。在TMS320F2838x上SPI模块内置了16级深度的FIFO先入先出缓冲区这是DMA高效协作的关键。FIFO充当了“门口临时堆放区”的角色DMA的搬运不是以单个字为单位而是以“突发Burst”为单位一次搬运多个字这极大地减少了DMA传输请求的次数提升了总线利用效率。2.2 关键参数计算避免数据丢失的数学基础手册里给出的公式看似简单但每个参数背后的设计意图至关重要。理解透了配置才能得心应手。1. 传输数据总量 (NUM_WORDS)这是你计划要发送或接收的总数据量单位是“字”Word对于C28x通常是16位。例如从SPI Flash读取一个4KB的扇区NUM_WORDS就是 4096 / 2 2048。2. FIFO触发水平 (TXFFIL / RXFFIL)这是FIFO的“水位线”。当FIFO中的数据量达到或超过对于接收或低于对于发送这个水平时就会向DMA控制器发出传输请求。发送FIFO触发水平 (TXFFIL)表示当发送FIFO中的剩余空间大于等于此值时DMA可以写入数据。例如TXFFIL8意味着当发送FIFO中空闲位置大于等于8个时DMA才会被触发去填充数据。接收FIFO触发水平 (RXFFIL)表示当接收FIFO中累积的数据量大于等于此值时DMA可以读取数据。例如RXFFIL4意味着当接收FIFO中有4个或更多数据时DMA才会被触发去搬走数据。关键经验TXFFIL和RXFFIL的设定是性能和稳定性的平衡艺术。设置得过低如TXFFIL1RXFFIL1DMA会被频繁触发增加总线开销可能影响其他高优先级外设的DMA。设置得过高则可能增加数据传输的延迟对于实时性要求高的场景不利。通常对于大数据流连续传输我会将TXFFIL设为8-12RXFFIL设为4-8这是一个在多个项目中验证过的经验值。3. DMA传输次数 (DMA_TRANSFER_SIZE)这个参数告诉DMA控制器需要完成多少次“突发传输”才能搬完所有数据。计算公式为DMA_TRANSFER_SIZE (NUM_WORDS / FIFO_LEVEL) - 1这里的FIFO_LEVEL对应TXFFIL发送或RXFFIL接收。为什么要减1因为DMA的传输计数寄存器通常是从0开始计数的。如果计算结果是15意味着DMA需要执行16次突发传输0到15。4. DMA突发大小 (DMA_BURST_SIZE)这是单次DMA请求中连续传输的数据字数。它必须与FIFO的物理特性严格匹配否则会导致数据丢失。发送时DMA_BURST_SIZE ≤ (16 - TXFFIL) - 1原理假设发送FIFO深度为16当前已有数据量为CURRENT_TX则空闲空间为16 - CURRENT_TX。当CURRENT_TX TXFFIL时DMA请求触发。在最坏情况下触发瞬间CURRENT_TX TXFFIL此时空闲空间为16 - TXFFIL。DMA突发大小必须小于等于这个空闲空间否则DMA试图写入的数据量会超过FIFO剩余容量造成溢出。公式中的-1是出于安全余量的考虑防止边界情况。接收时DMA_BURST_SIZE ≤ RXFFIL - 1原理当接收FIFO中数据量CURRENT_RX RXFFIL时DMA请求触发。在最坏情况下触发瞬间CURRENT_RX RXFFIL。DMA突发大小必须小于等于此时FIFO中可读的数据量否则DMA会试图从“空”的FIFO位置读取数据。同样-1是安全余量。额外要求手册还提到DMA_BURST_SIZE最好是RXFFIL的整数除数这是为了确保DMA能干净利落地搬空所有数据不会在FIFO里留下“尾巴”。例如RXFFIL6那么DMA_BURST_SIZE设为1、2、3或6都是理想的。2.3 实战配置示例与代码分析我们以手册中的例子进行扩展分析使用DMA发送128个字的数据TXFFIL设置为8。计算DMA_TRANSFER_SIZE:(128 / 8) - 1 15。这意味着DMA需要发起16次传输来完成全部128个字的搬运。计算DMA_BURST_SIZE:(16 - 8) - 1 7。这意味着每次DMA被触发时会连续搬运8个字因为从0计数7代表8次传输。配置流程与代码示意// 假设使用SPI-A和DMA通道1 void SPIA_DMA_TX_Config(uint16_t *srcBuffer, uint16_t numWords) { // 步骤1: 停止SPI进入安全配置状态 SpiaRegs.SPICCR.bit.SPISWRESET 0; // 步骤2: 配置SPI基本参数模式、波特率、字符长度等 SpiaRegs.SPICCR.bit.SPICHAR 7; // 8-bit字符长度 (71) SpiaRegs.SPICTL.bit.MASTER_SLAVE 1; // 主模式 SpiaRegs.SPIBRR 9; // 波特率 LSPCLK / (91) // ... 其他配置 // 步骤3: 使能并配置SPI FIFO SpiaRegs.SPIFFTX.bit.SPIFFENA 1; // 使能FIFO增强功能 SpiaRegs.SPIFFTX.bit.TXFIFO 1; // 释放发送FIFO复位 SpiaRegs.SPIFFTX.bit.TXFFIL 8; // 设置发送FIFO中断/触发水平为8 SpiaRegs.SPIFFTX.bit.TXFFIENA 0; // 本例用DMA禁用FIFO中断使用DMA触发 // 步骤4: 配置DMA // 假设使用DMA通道1触发源为SPI-A的发送FIFOSPITXINT DmaRegs.CH1.BURST_SIZE 7; // 突发大小 8 words DmaRegs.CH1.TRANSFER_SIZE 15; // 传输次数 16 bursts DmaRegs.CH1.SRC_BEG_ADDR (uint32_t)srcBuffer; // 源地址内存 DmaRegs.CH1.DST_BEG_ADDR (uint32_t)SpiaRegs.SPITXBUF; // 目标地址SPI发送缓冲 DmaRegs.CH1.SRC_ADDR_MODE DMA_ADDR_INC; // 源地址递增 DmaRegs.CH1.DST_ADDR_MODE DMA_ADDR_FIX; // 目标地址固定总是写到SPITXBUF DmaRegs.CH1.INT_ENABLE 1; // 使能传输完成中断 // 配置触发源和启动方式... // 步骤5: 重新使能SPI SpiaRegs.SPICCR.bit.SPISWRESET 1; // 步骤6: 启动DMA传输 DmaRegs.CH1.CONTROL.bit.RUN 1; }避坑指南配置顺序至关重要一定要遵循“先复位SPISPISWRESET0 - 配置所有参数 - 再使能SPISPISWRESET1”的顺序。在通信进行中修改配置寄存器是绝对要避免的这会导致不可预测的通信错误。DMA的配置也应在SPI使能前完成。3. SPI 3线模式深度解析与实战代码3.1 3线模式工作原理从4线到3线的精简标准SPI需要4根线SCLK时钟、MOSI主出从入、MISO主入从出、CS片选。3线模式的核心思想是双向数据线。在主机端MOSI引脚被复用为双向的SPIMOMISPI Master Out, Master In在从机端MISO引脚被复用为双向的SPISISOSPI Slave In, Slave Out。CS和SCLK功能不变。引脚功能变化对照表引脚名称4线模式3线模式 (主机)3线模式 (从机)SPICLK时钟时钟时钟SPISTE片选片选片选SPISIMO主机输出SPIMOMI (双向)未连接SPISPISOMI主机输入未连接SPISPISISO (双向)内部连接带来的关键影响在3线模式下由于发送和接收路径在SPI模块内部连接在了一起主机发送的数据也会被自己接收回来。这意味着每次你执行一次写入发送操作后SPI的接收缓冲区SPIRXBUF里会有一份你自己发出的数据的“回声”。应用程序必须执行一次虚读Dummy Read来清除这部分无用数据否则会干扰后续对真实接收数据的判断。3.2 核心控制位TALK与TRIWIRETRIWIRE (SPIPRI.0)这是3线模式的总开关。置1使能3线模式。TALK (SPICTL.1)在3线模式下这个位的作用被放大了。它不仅仅控制发送使能还直接决定了双向数据线的方向。TALK 1打开发送路径数据可以输出到双向数据线上。此时该引脚处于“输出”状态。TALK 0关闭发送路径数据不能输出。此时该引脚处于“高阻输入”状态准备接收从机发来的数据。3.3 3线模式下的数据收发流程与代码实现理解3线模式下的收发关键在于把握TALK位的切换时机和虚读操作的必要性。3.3.1 3线主机模式发送数据主机要发送数据给从机流程是标准的使能发送写入数据等待完成。但多了一步清理“回声”。void SPI_3Wire_Master_Transmit(uint16_t data) { Uint16 dummy; // 1. 确保处于3线模式 SpiaRegs.SPIPRI.bit.TRIWIRE 1; // 2. 打开主机发送路径SPIMOMI引脚变为输出 SpiaRegs.SPICTL.bit.TALK 1; // 3. 写入要发送的数据启动传输 SpiaRegs.SPITXBUF data; // 4. 等待传输完成查询中断标志位 while(SpiaRegs.SPISTS.bit.INT_FLAG ! 1) {} // 5. !!! 关键步骤执行虚读清除接收缓冲区中的“回声”数据 !!! dummy SpiaRegs.SPIRXBUF; // 读出的dummy值就是刚刚发送的data必须丢弃 }3.3.2 3线主机模式接收数据主机要从从机读取数据流程就有些“迂回”了。因为主机需要先产生时钟而从机只有在主机发送数据时才会在时钟驱动下输出数据。所以主机需要发起一次“虚假”的发送来产生时钟同时关闭自己的输出专心接收。uint16_t SPI_3Wire_Master_Receive(void) { uint16_t receivedData; uint16_t dummyData 0x0000; // 发送的虚拟数据内容任意 // 1. 确保处于3线模式 SpiaRegs.SPIPRI.bit.TRIWIRE 1; // 2. !!! 关键步骤关闭主机发送路径SPIMOMI引脚变为高阻输入!!! SpiaRegs.SPICTL.bit.TALK 0; // 3. 写入虚拟数据到发送缓冲区。由于TALK0这个数据不会出现在SPIMOMI引脚上。 // 但这个写入动作会启动SPI时钟生成从机将在时钟驱动下把数据放到双向数据线上。 SpiaRegs.SPITXBUF dummyData; // 4. 等待传输完成 while(SpiaRegs.SPISTS.bit.INT_FLAG ! 1) {} // 5. 此时接收缓冲区里的数据就是从机发送过来的真实数据。 receivedData SpiaRegs.SPIRXBUF; // 6. 读取完成后如果需要继续发送记得将TALK重新置1。 // SpiaRegs.SPICTL.bit.TALK 1; return receivedData; }核心要点主机接收时TALK0是精髓。它让主机的数据线变为输入状态同时一次虚拟的写入操作启动了时钟为从机提供了输出数据的“舞台”。此时主机接收到的就是从机“表演”的内容。3.3.3 3线从机模式从机模式的逻辑相对简单因为数据方向主要由主机的TALK位和片选信号控制。从机发送和主机发送类似需要使能TALK1并在发送后虚读SPIRXBUF清理自己的回声。从机接收和标准4线模式几乎一样TALK位应保持为0关闭发送等待主机时钟然后直接读取SPIRXBUF即可。因为主机在接收模式下TALK0不会驱动数据线从机发送的数据可以安全传输到主机。4. 高速模式High-Speed Mode配置要点TMS320F2838x的SPI支持高速模式最高通信速率可达LSPCLK/4。假设系统时钟SYSCLK200MHzLSPCLK默认与其相等则最高SPI时钟可达50MHz。这对于需要高速数据流的应用如高分辨率ADC、高速DAC、图像传感器非常有用。启用高速模式的两个关键步骤GPIO引脚复用配置高速模式对GPIO的压摆率Slew Rate和驱动强度有要求。必须通过GPIO控制寄存器GPxGMUX/GPxMUX选择支持高速模式的特定引脚复用功能。这一步经常被忽略导致通信失败或波形畸变。具体哪个引脚支持高速模式需要查阅芯片的DataSheet或Technical Reference Manual中关于Pin Multiplexing的章节。SPI寄存器配置在SPI配置流程中设置HS_MODE位。// 假设LSPCLK SYSCLK 100MHz ClkCfgRegs.LOSPCP.bit.LSPCLKDIV 0; // 设置低速外设时钟预分频为1即LSPCLKSYSCLK // 在SPI配置序列中SPISWRESET0之后 SpiaRegs.SPICCR.bit.HS_MODE 1; // 使能高速模式 SpiaRegs.SPIBRR 3; // 设置波特率。SPICLK LSPCLK/(31) 25MHz注意SPIBRR的计算公式在高速模式下依然适用。但为了达到最高速率LSPCLK/4需要设置SPIBRR 3。同时必须确保通信的另一端从设备也能支持如此高的时钟速率。高速模式限制通常仅支持单主单从的点对点配置。复杂的多从机网络在高速下可能因总线负载和信号完整性问题无法稳定工作。对PCB布局布线要求更高需要尽可能缩短走线做好阻抗控制和旁路滤波防止信号反射和振铃。5. 常见问题排查与试心得在实际项目中SPI通信尤其是结合了DMA和特殊模式后遇到的问题往往五花八门。下面是我总结的一些典型问题及其排查思路。5.1 DMA传输不启动或数据不完整症状程序启动了但SPI线上没有波形或者只传输了一部分数据就停止了。排查清单SPI软件复位位确认配置顺序正确在修改关键配置如模式、波特率前SPISWRESET是否已清零配置完成后是否重新置1这是最常见的疏忽。DMA触发源配置DMA的触发源Trigger Source是否正确设置为SPI的发送空SPITXINT或接收就绪SPIRXINT事件在CCS的寄存器视图或代码中仔细核对。FIFO使能与复位SPIFFENA是否置1TXFIFO和RXFIFORESET是否在配置完成后被释放置1它们默认是保持复位状态的。DMA传输计数与地址检查TRANSFER_SIZE和BURST_SIZE计算是否正确。检查源地址和目标地址是否有效地址自增模式是否符合预期。一个常见错误是目标地址设置错误比如该设为SpiaRegs.SPITXBUF却设成了SpiaRegs.SPIRXBUF。内存一致性如果DMA的源/目标地址涉及Cache如C28x的L1 SARAM确保在DMA操作前执行了CACHE_FLUSH对于CPU写后DMA读或在DMA操作后执行了CACHE_INVALIDATE对于DMA写后CPU读。忽略Cache一致性会导致CPU读到陈旧数据。5.2 3线模式通信失败症状切换到3线模式后主机收不到从机数据或数据全为0/0xFF。排查清单TALK位状态这是重中之重主机在接收前必须将TALK清零。用调试器实时查看SPICTL.1的值。很多同学在发送和接收流程交替时忘记切换TALK位。虚读操作主机发送数据后是否执行了dummy SpiaRegs.SPIRXBUF;如果没有接收缓冲区会被自己的回声数据占据导致后续读取错误。引脚复用配置3线模式使用了不同的引脚功能SPIMOMI/SPISISO。检查GPIO的MUX配置是否正确是否将对应的SIMO或SOMI引脚配置为了SPI功能而不是普通的GPIO。从机配置确认从机设备本身是否支持3线SPI模式有些SPI从设备只支持标准的4线模式。5.3 通信速率达不到预期或波形畸变症状实际测量的SCLK频率远低于配置值或者波形不是方波边沿有严重振铃。排查清单时钟源与分频确认LSPCLK的频率是否正确。检查系统时钟配置和LOSPCP分频寄存器。SPIBRR的值是否计算正确公式是SPIBRR LSPCLK / Desired_SPICLK - 1。高速模式如果目标是高速10MHz是否使能了HS_MODEGPIO是否配置为高速引脚PCB与负载用示波器直接测量SPI引脚波形。检查是否有过冲、振铃或边沿过于缓慢。这通常是由于走线过长、容性负载过重或没有端接匹配造成的。高速SPI对PCB布局非常敏感尽量使走线短而直远离噪声源并在驱动端串联一个小电阻如22-33欧姆以改善信号完整性。从机速度确认从设备支持的最大SCLK频率。很多SPI Flash或传感器在较高电压下才能达到最高速率或者需要特定的模式如“Fast Read”。5.4 中断与DMA冲突症状同时使能了SPI FIFO中断和DMA系统行为异常可能进入错误的中断服务程序。解决方案明确分工避免同时使能。如果使用DMA处理大数据块传输就禁用对应的FIFO中断TXFFIENA或RXFFIENA设为0。DMA和中断是两种处理FIFO事件的机制同时使能会导致重复响应引发不可预知的问题。通常DMA用于后台大数据搬运而中断用于处理特定事件如传输完成、错误的通知。调试SPIDMA系统示波器或逻辑分析仪是必不可少的工具。通过抓取SCLK、MOSI/MISO、CS的波形可以直观地看到数据传输的时序、内容是否正确是定位硬件连接问题、软件配置问题最直接的手段。在逻辑分析仪上设置好SPI协议解码能直接将二进制数据流翻译出来极大提升调试效率。