1. 项目概述与SPI核心价值
在嵌入式开发领域,微控制器与外设之间的高效、可靠通信是项目成败的关键。无论是读取传感器数据、配置无线模块,还是与外部存储器交换信息,都需要一个稳定且灵活的通信接口。串行外设接口(SPI)正是为此而生的经典方案。它不像I2C那样需要复杂的地址寻址和应答机制,也不像UART那样依赖精确的波特率匹配。SPI以其极简的四线制(有时三线)设计、全双工同步传输和极高的时钟速率,成为了连接ADC、DAC、Flash、显示屏驱动等高速外设的首选。
我接触过不少微控制器家族的SPI模块,从早期的8位机到现在的32位Arm Cortex-M系列,其核心思想一脉相承,但各家在实现细节和功能增强上各有千秋。德州仪器(TI)的MSPM0系列微控制器,作为其MSP430低功耗家族的现代化演进,其集成的SPI模块在保持经典架构的同时,加入了许多贴合现代开发需求的特性,比如深度可配置的FIFO、灵活的DMA触发机制以及对多种工业标准帧格式的支持。理解这些特性,不仅能让你“点亮”一个SPI外设,更能让你在设计系统时,从硬件和软件层面优化通信效率与可靠性,避免后期调试时那些令人头疼的时序和稳定性问题。这篇文章,我就结合MSPM0的官方技术手册和我的实际项目经验,带你从最基础的信号线认识开始,一步步拆解其内部工作原理、配置要点,并分享几个实战中总结出来的配置模板和避坑指南。
2. SPI基础原理与MSPM0模块架构
2.1 SPI通信的本质:一个主控的同步对话
要理解MSPM0的SPI模块,必须先吃透SPI协议本身。你可以把它想象成一场由主设备(Controller, 常称Master)严格控制的同步对话。这场对话至少需要三根线,理想情况下是四根:
- SCLK (Serial Clock): 时钟线,由主设备产生。这是对话的节拍器,每一个时钟脉冲,就标志着一次数据位的交换。所有数据位的采样和输出都严格对齐这个时钟边沿。
- PICO/MOSI (Peripheral In Controller Out): 主设备输出,从设备输入的数据线。主设备通过这根线向从设备发送指令或数据。
- POCI/MISO (Peripheral Out Controller In): 从设备输出,主设备输入的数据线。从设备通过这根线向主设备返回数据或状态。
- CS (Chip Select): 片选线,由主设备控制。这是对话的“邀请函”。在总线上挂载多个从设备时,主设备通过拉低对应从设备的CS线,来选中并激活它进行通信。未被选中的从设备必须将其数据线置于高阻态,以避免总线冲突。
SPI通信是全双工的,这意味着在SCLK的每个周期内,主设备和从设备同时在PICO和POCI线上发送一位数据。因此,一次完整的n位数据传输,会同时完成n位数据的发送和接收。这种“一换一”的机制效率很高,但也意味着主设备发送的数据(即使是无意义的填充字节)是启动接收过程的必要条件。
2.2 MSPM0 SPI模块的功能框图解析
MSPM0的SPI模块不是一个简单的移位寄存器,而是一个高度集成化的通信引擎。我们结合手册中的功能框图来理解其内部构造:
[SPI模块核心] —— 围绕“时钟”和“数据”两大路径展开。 | |-- 时钟路径 (Clock Path): | | | |-- 时钟源选择 (CLKSEL): 可以从系统总线时钟(BUSCLK)、主功能时钟(MFCLK)或低频时钟(LFCLK)中选择。 | | | |-- 时钟分频器 (CLKDIV): 对选中的时钟源进行初步分频(1~8分频),产生“功能时钟”(Functional Clock)。 | | | `-- 时钟预分频器 (Clock Prescaler): 通过`CLKCTL`寄存器中的`SCR`字段,对功能时钟进行二次分频,最终生成驱动SCLK引脚输出的串行时钟。计算公式为:SCLK = Functional Clock / ((1 + SCR) * 2)。这个设计提供了非常精细的波特率控制能力。 | |-- 数据路径 (Data Path): | | | |-- 发送FIFO (Tx FIFO): 一个4x16位的先进先出缓冲区。CPU或DMA将要发送的数据写入`TXDATA`寄存器,数据先存入此FIFO。这解决了CPU写入速度与串行移出速度不匹配的问题,允许CPU一次性写入多个数据后去处理其他任务。 | | | |-- 接收FIFO (Rx FIFO): 同样是一个4x16位的先进先出缓冲区。从POCI线移入的串行数据,在组成一个完整数据帧后,会被存入此FIFO,等待CPU或DMA通过`RXDATA`寄存器读取。 | | | `-- 发送/接收逻辑 (Transmit/Receive Logic): 核心的移位寄存器。它从Tx FIFO取出数据,在SCLK控制下逐位移出到PICO线;同时,从POCI线采样数据,逐位移入,凑满一帧后送入Rx FIFO。 | |-- 控制与状态逻辑 (Control/Status Logic): | |-- 模式配置 (CTL0, CTL1): 设置控制器/外设模式、数据帧长度(4-16位)、时钟极性/相位(SPO/SPH)、帧格式(Motorola/TI)等。 | |-- 中断控制 (Interrupt Control): 管理各种中断源,如Tx FIFO空、Rx FIFO满、接收超时、DMA完成等。通过`IFLS`寄存器可以设置FIFO触发中断的水位线。 | `-- DMA接口 (DMA Interface): 提供独立的TX和RX DMA触发信号。当Tx FIFO有空位或Rx FIFO有数据达到触发水位时,模块会向DMA控制器发出请求,实现数据块在内存和SPI FIFO之间的自动搬运,极大解放CPU。 | `-- 外部引脚接口: 连接到芯片GPIO复用功能的SCLK、PICO、POCI、CS0~CS3/CD引脚。这个架构的精妙之处在于“缓冲”和“解耦”。双FIFO缓冲了数据生产者和消费者之间的速度差,而DMA接口则将CPU从繁琐的字节搬运中解脱出来。理解这个框图,后续的所有配置操作就都有了清晰的归属。
2.3 关键特性与工程意义
MSPM0 SPI模块的特性并非纸上谈兵,每一项都对应着实际的工程需求:
- 可配置的控制器/外设模式:同一模块,既能当“主机”发号施令,也能当“从机”响应请求。这在多MCU协作或模块化设计中非常有用。
- 灵活的数据帧长度(4-16位):不再局限于8位字节。许多新型传感器或音频编解码器使用12位、16位甚至20位数据格式。MSPM0支持4-16位(控制器模式)或7-16位(外设模式)的帧长,让你可以直接匹配外设格式,无需在软件中进行位拼接和拆分,提高了效率和代码简洁性。
- Motorola与TI同步双格式支持:Motorola SPI是行业最广泛的标准,而TI同步格式则是德州仪器自家一些老型号外设使用的协议(主要区别在于CS和时钟的时序关系)。模块的兼容性减少了外设选型的限制。
- 深度可编程的FIFO与DMA:4级16位FIFO虽然不算深,但对于多数中低速通信场景已足够缓冲。结合可配置触发水位的DMA,你可以轻松实现“半满触发”或“单字节触发”等策略,在响应速度和中断频率之间取得平衡。
- 命令/数据(CD)模式:这是一个针对显示屏、存储器等设备的实用功能。通过
CS3/CD引脚,可以在不增加额外GPIO的情况下,区分发送的是命令(CD拉低)还是数据(CD拉高)。模块甚至支持自动计数器模式,在发送一条命令后,自动将后续指定数量的帧标记为数据,简化了驱动代码。
3. 核心配置详解与实操要点
了解了架构,我们进入实战环节。配置MSPM0的SPI模块,就像组装一台精密仪器,每一步的设置都影响着最终的通信质量。这里我以最常见的Motorola SPI格式、8位数据帧、控制器模式为例,拆解配置流程并分享关键细节。
3.1 引脚复用与基础时钟配置
任何外设使用的第一步,都是正确配置引脚。MSPM0的引脚功能通过IOMUX(输入输出复用器)模块控制。
// 假设使用SPI0, 引脚映射为:PA5 -> SCLK, PA6 -> PICO, PA7 -> POCI, PA4 -> CS0 // 1. 启用GPIO端口A的时钟(此处为示例,具体时钟使能寄存器请参考手册) SYSCTL->CLOCK_GPIOA_EN = 1; // 2. 配置引脚复用功能。每个引脚都有一个8位的IOMUX寄存器,需要查表找到SPI功能对应的编码。 // 例如,PA5的IOMUX寄存器地址偏移为0xXXX, 设置其值为SPI0_SCLK的功能编码(假设为0x02)。 IOMUX->PIN_CFG[GPIO_PA5] = 0x02; // 设置PA5为SPI0 SCLK IOMUX->PIN_CFG[GPIO_PA6] = 0x02; // 设置PA6为SPI0 PICO IOMUX->PIN_CFG[GPIO_PA7] = 0x02; // 设置PA7为SPI0 POCI IOMUX->PIN_CFG[GPIO_PA4] = 0x02; // 设置PA4为SPI0 CS0 // 3. (可选但推荐)配置CS引脚为推挽输出,并初始化为高电平(无效状态)。 GPIOA->DIR |= (1 << 4); // PA4设为输出 GPIOA->OUT |= (1 << 4); // PA4输出高电平 // SCLK, PICO, POCI的引脚方向通常由SPI模块自动控制,无需手动设置DIR。注意:手册中特别提到,在控制器模式下,为了满足严格的时序要求(尤其是在高频率下),建议将SCLK和CS信号通过GPIO环回(loopback)。这是因为信号从SPI模块产生,经过芯片内部的IOMUX和Pad时会有微小延迟。通过设置IOMUX中对应引脚的
INENA位,可以让模块同时监测该引脚的实际输出电平,进行内部补偿。这是一个高级优化点,在通信速率接近极限或对时序抖动要求极高时必须考虑。
接下来是时钟配置,它直接决定了SCLK的频率。
// 假设系统总线时钟BUSCLK = 32 MHz, 目标SCLK = 4 MHz。 // 1. 选择SPI功能时钟源。我们使用BUSCLK。 SPI0->CLKSEL = 0x0; // 0x0 通常代表选择BUSCLK,具体值查寄存器定义。 // 2. 配置时钟分频器CLKDIV。Functional Clock = BUSCLK / (1 + CLKDIV) // 我们希望Functional Clock是SCLK的偶数倍。设CLKDIV = 0, 则Functional Clock = 32MHz。 // 3. 配置时钟控制寄存器CLKCTL中的串行时钟速率分频器SCR。 // SCLK = Functional Clock / ((1 + SCR) * 2) // 目标SCLK = 4MHz = 32MHz / ((1+SCR)*2) => (1+SCR)*2 = 8 => 1+SCR=4 => SCR=3 SPI0->CLKDIV = 0; // 分频系数1 SPI0->CLKCTL = (3 << 0); // 假设SCR在bit[2:0], 写入3 // 计算验证:SCLK = 32MHz / ((1+3)*2) = 32MHz / 8 = 4MHz。实操心得:SCLK频率并非越高越好。需考虑外设支持的最高时钟频率、PCB走线长度带来的信号完整性限制。对于长导线或多个设备,适当降低SCLK可以提升通信稳定性。一个经验法则是,SCLK周期应大于信号在板卡上往返传播延迟的10倍以上。
3.2 工作模式、帧格式与数据控制
这是配置的核心,决定了通信的“语言规则”。
// 1. 在进行任何配置前,确保SPI模块未使能(CTL1.ENABLE=0)。 SPI0->CTL1 &= ~(1 << 0); // 清除ENABLE位,假设bit0为ENABLE // 2. 配置控制寄存器0 (CTL0) uint32_t ctl0_config = 0; ctl0_config |= (0x7 << 0); // DSS[3:0] = 0x7, 表示数据帧大小为8位 (因为0x7对应8位,需查表确认) ctl0_config |= (0x0 << 4); // FRF[1:0] = 0x0, 选择Motorola SPI帧格式 ctl0_config |= (0x0 << 6); // SPO = 0, 时钟空闲时为低电平 ctl0_config |= (0x0 << 7); // SPH = 0, 数据在第一个时钟边沿采样(上升沿采样,下降沿变化) // 对于4线模式,我们使用CS0, CSSEL位通常为0。 SPI0->CTL0 = ctl0_config; // 3. 配置控制寄存器1 (CTL1) uint32_t ctl1_config = 0; ctl1_config |= (1 << 1); // CP = 1, 设置为控制器模式 // MSB, PEN, PES等位根据需要设置。假设MSB先发送,无奇偶校验。 ctl1_config |= (1 << 2); // MSB = 1, 高位先传 // CTL1.ENABLE位稍后使能。 SPI0->CTL1 = ctl1_config; // 4. (可选)配置FIFO中断水位线。通过IFLS寄存器设置。 // 例如,设置TX FIFO中断在至少空出3个位置时触发,RX FIFO中断在至少有1个数据时触发。 SPI0->IFLS = (0x2 << 0) | (0x0 << 3); // TXIFLSEL=0x2(对应3/4空), RXIFLSEL=0x0(对应1/4满),具体映射查手册。时钟极性(SPO)与相位(SPH)的深入理解: 这是SPI配置中最容易混淆的地方,它定义了时钟空闲状态和数据采样的时刻。通常被称为SPI模式0,1,2,3:
- 模式0 (SPO=0, SPH=0): 时钟空闲低电平,数据在上升沿采样,下降沿变化。
- 模式1 (SPO=0, SPH=1): 时钟空闲低电平,数据在下降沿采样,上升沿变化。
- 模式2 (SPO=1, SPH=0): 时钟空闲高电平,数据在下降沿采样,上升沿变化。
- 模式3 (SPO=1, SPH=1): 时钟空闲高电平,数据在上升沿采样,下降沿变化。
关键细节:必须与外设数据手册的要求严格匹配。一个常见的错误是只关注模式编号,而忽略了不同厂商对模式0/1/2/3的定义可能略有差异。最可靠的方法是查看外设时序图,确认其在SCLK的哪个边沿(上升沿或下降沿)采样输入数据(对从设备而言是输出数据),以及在哪个边沿更新输出数据(对从设备而言是输入数据)。MSPM0作为控制器时,其
SPO/SPH配置决定了它产生时钟的波形和对输入数据的采样时刻。
3.3 FIFO操作与DMA配置实战
配置好通信参数后,数据的搬运方式决定了系统效率。
查询方式操作FIFO: 这是最简单直接的方式,适合低速或非实时场景。
// 发送单字节数据 void SPI_SendByte(uint8_t data) { // 等待TX FIFO有空位(检查STAT寄存器中的TNF位) while (!(SPI0->STAT & (1 << 1))) { // 超时处理可以加在这里 } SPI0->TXDATA = data; // 写入数据,触发传输 } // 接收数据(通常需要先发送一个虚拟字节来产生时钟) uint8_t SPI_ReceiveByte(void) { SPI_SendByte(0xFF); // 发送哑元数据以产生SCLK // 等待RX FIFO有数据(检查STAT寄存器中的RNE位) while (!(SPI0->STAT & (1 << 0))) { // 超时处理 } return (uint8_t)(SPI0->RXDATA); // 读取数据 }中断方式操作FIFO: 适合需要及时响应数据到达的场景,避免CPU空转等待。
// 首先使能所需中断(例如RX FIFO非空中断) SPI0->IMASK |= (1 << 0); // 使能RX中断(假设RIS[0]是RX中断标志) // 在全局中断使能后,当RX FIFO数据达到IFLS设置的水位线时,会触发中断。 void SPI0_IRQHandler(void) { uint32_t mis = SPI0->MIS; // 读取屏蔽后的中断状态 if (mis & (1 << 0)) { // RX中断 while (SPI0->STAT & (1 << 0)) { // 循环读取直到FIFO空 uint16_t received_data = SPI0->RXDATA; // 处理接收到的数据... } SPI0->ICLR = (1 << 0); // 清除RX中断标志 } // 处理其他中断源... }DMA方式搬运数据: 这是处理大批量数据(如读写Flash、传输图像数据)时的终极利器。MSPM0的SPI模块提供独立的TX和RX DMA触发信号。
// 假设使用DMA通道0用于SPI0 TX, 通道1用于SPI0 RX。 // 1. 配置DMA通道源/目标地址、传输数量等(此处略,参考DMA章节)。 // 2. 配置DMA触发源为SPI0的TX和RX事件。 DMA->CH0_TRIG_SEL = SPI0_TX_TRIGGER_ID; // 将DMA通道0触发源设为SPI0 TX DMA->CH1_TRIG_SEL = SPI0_RX_TRIGGER_ID; // 将DMA通道1触发源设为SPI0 RX // 3. 在SPI端,使能DMA请求。 SPI0->DMACTL |= (1 << 0) | (1 << 1); // 使能TX和RX DMA请求 // 4. 启动DMA传输。 // 5. 当需要发送数据时,只需将数据数组地址配置给DMA,并启动通道。SPI TX FIFO一旦有空位,就会自动触发DMA搬运数据填入。 // 6. 接收同理,SPI RX FIFO一旦有数据达到触发水位,就会自动触发DMA将数据搬走。避坑指南:使用DMA时,务必注意数据对齐和传输宽度。如果SPI配置为8位数据帧,但DMA设置为16位传输,那么一次DMA请求会搬运2个字节,但SPI硬件会将其视为两个独立的8位帧进行发送。你需要确保DMA的传输次数(Count)是SPI数据帧数量的整数倍,并且内存缓冲区地址对齐合理,否则可能导致数据错位或DMA错误。
4. 高级功能与工程实践技巧
4.1 延迟采样功能应对时序挑战
在高速或长距离SPI通信中,信号传输延迟(Propagation Delay)可能成为一个问题。POCI线上的数据到达控制器的时间可能晚于控制器内部采样时钟边沿,导致采样错误。MSPM0的CLKCTL.DSAMPLE功能就是为了解决这个问题。
原理:通过DSAMPLE寄存器,你可以将POCI数据的采样点,从默认的SCLK边沿(如上升沿)向后延迟若干个功能时钟周期。这相当于给接收数据一个“等待时间”,确保数据稳定后再采样。
配置示例:
// 假设在32MHz功能时钟下,测量发现数据延迟约30ns。 // 一个功能时钟周期 = 1 / 32MHz = 31.25ns。 // 30ns的延迟小于一个周期,但为了稳定,可以延迟一个周期采样。 SPI0->CLKCTL |= (1 << 8); // 设置DSAMPLE = 1, 延迟1个功能时钟周期采样。 // 注意:DSAMPLE的可用值和最大延迟取决于具体型号,需查阅数据手册。何时使用:当你发现通信在低速时正常,提高SCLK频率后出现随机错误,尤其是读取的数据位偶尔出现“位偏移”(例如总是读到前一位的值)时,就应该怀疑是建立/保持时间不足,可以尝试启用并调整延迟采样。
4.2 循环回环模式用于自检与调试
在硬件连接完成但通信异常时,首先要排除软件配置问题。MSPM0的SPI内部环回模式(Loopback Mode)是绝佳的调试工具。
操作方法:
// 1. 将SPI配置为控制器模式,并设置好时钟、帧格式等参数。 // 2. 在使能SPI模块前,设置环回模式。 SPI0->CTL1 |= (1 << 5); // 假设LBM位在CTL1寄存器的第5位 // 3. 正常使能SPI (CTL1.ENABLE = 1)。 // 4. 此时,从PICO发送出去的数据,不会真的送到引脚,而是直接在内部连接到POCI的接收路径。 // 5. 发送一个测试数据(例如0xAA),然后读取接收数据。如果读回0xAA,说明SPI模块内部的发送、接收、时钟生成逻辑工作正常。重要提示:环回模式仅验证芯片内部的SPI数字逻辑。它不能验证外部引脚连接、电平转换电路或对方设备。当环回测试通过但实际外设通信失败时,问题大概率出在PCB布线、电源、或外设本身的配置上。
4.3 低功耗模式下的SPI管理
MSPM0主打低功耗,SPI模块位于电源域1(PD1),这意味着在进入STOP或STANDBY等深度低功耗模式时,该模块的时钟可能会被关闭以省电。
操作流程:
- 进入低功耗前:必须确保所有SPI传输已完成。然后,通过清除
CTL1.ENABLE位来禁用SPI模块。最好再检查一下状态寄存器,确保模块完全停止。 - 唤醒后:系统从低功耗模式唤醒后,SPI模块处于禁用状态。你需要重新初始化SPI模块的时钟、引脚复用(如果需要)和所有控制寄存器,最后再使能
CTL1.ENABLE。
常见陷阱:在低功耗唤醒后,直接使用SPI发送数据而忘记重新初始化,会导致通信失败或总线锁死。一个稳健的做法是,将SPI的初始化代码封装成一个函数,在系统启动和每次从深度睡眠唤醒后都调用一次。
5. 典型问题排查与调试实录
即使按照手册配置,在实际项目中仍会遇到各种问题。下面是我总结的几个典型场景和排查思路。
5.1 问题一:通信完全无反应,SCLK无波形
- 现象:程序运行后,用示波器或逻辑分析仪测量SCLK、PICO、CS引脚,没有任何信号变化。
- 排查步骤:
- 检查电源和复位:确认MCU和外设供电正常,复位引脚已释放。
- 确认引脚复用:这是最常见的原因。使用调试器或添加代码,检查配置SPI功能的GPIO的IOMUX寄存器值是否正确。一个快速验证方法是,将该引脚临时配置为普通GPIO输出高低电平,看是否有变化。
- 检查SPI模块时钟:确认给SPI模块提供时钟的源(如BUSCLK)是否已使能并运行在预期频率。可以检查相关时钟树配置寄存器。
- 检查使能位:确认
CTL1.ENABLE位是否已置1。一个关键顺序:必须在所有配置(时钟、模式、帧格式等)完成之后,最后才置位ENABLE。手册明确警告,在使能状态下更改关键配置可能导致不可预测的行为。 - 检查CS引脚控制:在4线控制器模式下,SPI模块通常只在传输期间自动控制CS线。如果你在代码中手动控制CS(更常见),需要确保在调用发送函数前,手动将CS引脚拉低。模块的
CTL0.CSSEL等位应正确配置,但CS的GPIO输出控制权可能在你手上。
5.2 问题二:能发送数据,但接收全是0或0xFF
- 现象:主设备发送数据后,读取到的数据始终是固定的0x00或0xFF,而不是外设应有的回复。
- 排查步骤:
- 确认外设是否被选中:检查CS引脚电平在传输期间是否确实为低。用示波器双通道同时测量CS和SCLK,确保时序符合外设要求(如CS建立时间)。
- 检查SPI模式:这是最高频的错误源。用逻辑分析仪捕获SCLK、PICO、POCI波形。对照外设手册的时序图,检查:a) 时钟空闲电平(SPO)是否正确;b) 数据是在SCLK的哪个边沿采样(SPH)?确保主从设备模式完全一致。
- 检查POCI引脚连接与配置:确认POCI线已正确连接,并且外设在该线上有驱动能力。有些外设在未被选中时,其MISO引脚为高阻态,如果主设备端没有上拉电阻,该线会浮空,可能被误读为恒定高或低。尝试在主设备POCI引脚增加一个4.7k~10k的上拉电阻。
- 验证外设本身:发送该外设的“读状态寄存器”等简单且必有回应的命令,排除外设未初始化或损坏的可能。
5.3 问题三:高速通信时数据出错
- 现象:低速通信正常,提高SCLK频率后,出现偶发性数据错误。
- 排查步骤:
- 示波器观察信号质量:这是最直接的诊断方法。观察SCLK、PICO、POCI波形是否有过冲、振铃、边沿变缓(上升/下降时间过长)的情况。这通常是由PCB走线过长、阻抗不匹配或负载过重引起的。解决方案包括缩短走线、串联小电阻(如22欧姆)进行阻抗匹配。
- 检查电源噪声:高速切换的数字电路会产生电源噪声。确保MCU和外设的电源引脚有足够且靠近管脚的退耦电容(如100nF + 10uF)。
- 启用延迟采样:如前所述,尝试调整
CLKCTL.DSAMPLE值,延迟采样点以补偿信号延迟。 - 降低SCLK频率:这是最直接的验证方法。如果降低频率后错误消失,则基本确定是信号完整性问题或时序裕量不足。
5.4 问题四:使用DMA时数据丢失或错位
- 现象:启用DMA后,发送或接收的数据量不对,或者数据顺序混乱。
- 排查步骤:
- 检查DMA传输宽度与SPI数据宽度对齐:如果SPI是8位数据帧,DMA的源/目标数据宽度也应设置为8位(Byte)。如果DMA设置为16位(HalfWord),它一次会搬运2个字节,但SPI硬件会按顺序发送这两个字节。你需要确保你的数据缓冲区在内存中是按16位对齐的,并且传输总数是2的倍数。
- 检查DMA传输次数:DMA的传输次数(Count)应等于你期望传输的SPI数据帧数量(在8位模式下,一个帧就是一个字节)。
- 检查FIFO触发水位与DMA突发传输:如果DMA配置为突发传输(Burst),而SPI的FIFO触发水位设置得太低,可能导致DMA一次搬运过多数据,超过FIFO容量,造成溢出或欠载。调整
IFLS寄存器,或调整DMA的突发大小。 - 在DMA传输完成中断中检查:在DMA传输完成中断服务例程中,检查DMA通道的状态寄存器,确认传输是否真的完成,有无错误标志。
为了便于快速对照,我将上述常见问题及排查方向整理成下表:
| 问题现象 | 可能原因 | 排查工具/方法 | 解决方案 |
|---|---|---|---|
| 无任何信号 | 1. 引脚未正确复用 2. SPI模块未使能 3. 时钟未配置 | 1. 检查IOMUX寄存器 2. 检查CTL1.ENABLE位 3. 检查CLKSEL/CLKDIV | 1. 修正引脚配置 2. 最后使能模块 3. 配置正确时钟源与分频 |
| 发送正常,接收固定值 | 1. SPI模式(SPO/SPH)不匹配 2. 从设备未响应(CS问题) 3. POCI线浮空 | 1. 逻辑分析仪抓波形比对 2. 测量CS引脚时序 3. 测量POCI引脚电平 | 1. 严格按外设手册配置模式 2. 确保CS有效,满足建立时间 3. 在POCI加弱上拉电阻 |
| 低速正常,高速出错 | 1. 信号完整性差(过冲、振铃) 2. 建立/保持时间不足 | 1. 示波器观察波形 2. 计算时序裕量 | 1. 优化PCB布局,加匹配电阻 2. 降低SCLK频率,启用延迟采样(DSAMPLE) |
| DMA数据错乱 | 1. DMA与SPI数据宽度不对齐 2. DMA传输次数计算错误 3. FIFO溢出/欠载 | 1. 检查DMA配置寄存器 2. 核对传输数量 3. 调试器查看FIFO状态 | 1. 统一数据宽度(如均为8位) 2. 确保Count等于帧数 3. 调整FIFO触发水位(IFLS) |
调试SPI,逻辑分析仪是比示波器更高效的利器。它能同时解码多条总线上的数据,直观地显示出发送和接收的字节,并标注出CS、时钟和数据的时序关系,让你能快速定位是配置错误、时序问题还是物理层故障。