CC13x2/CC26x2 SSI模块深度解析:从SPI协议到DMA高效数据传输
1. SSI模块核心架构与设计思路
在嵌入式系统开发中,设备间的通信是构建复杂应用的基石。同步串行接口(SSI)作为一种高效、可靠的短距离通信方案,其核心价值在于通过一根时钟线同步主从设备的数据收发,从而避免了异步通信中复杂的波特率匹配问题。CC13x2/CC26x2系列无线MCU集成了两个高度灵活的SSI模块,这不仅仅是增加了一个外设,更是为开发者提供了应对多种传感器、存储器和无线模组通信协议的“瑞士军刀”。
我接触过不少项目,从简单的温湿度传感器数据读取,到复杂的多通道ADC采集系统,再到与外部Flash或SRAM的高速数据交换,SSI都扮演着关键角色。它的设计思路非常清晰:以硬件FIFO缓冲数据流,以可编程时钟控制时序,以灵活的帧格式适配不同协议,最终通过中断或DMA解放CPU。理解这个思路,比死记硬背寄存器位要有用得多。例如,当你需要驱动一个SPI接口的OLED屏时,数据吞吐量不大但要求实时更新,你会优先考虑使用FIFO中断;而当你需要从一块SPI Flash中连续读取数MB的固件时,DMA就成了不二之选,它能将CPU从繁琐的数据搬运中彻底解放出来。
CC13x2/CC26x2的SSI模块在设计上考虑得非常周全。其双缓冲FIFO(深度为8,宽度为16位)是一个亮点。这意味着你可以在TX FIFO中预先填充最多8个数据帧,然后SSI硬件会自动依次发送,同时接收到的数据也会被存入RX FIFO,等待CPU或DMA取走。这种“预存后发,先收后处”的机制,极大地平滑了数据流,避免了因CPU处理延迟而导致的数据丢失或通信停顿。在实际调试中,我经常通过监控FIFO的状态标志位来判断通信瓶颈所在。如果TX FIFO经常为空,说明CPU填充数据的速度跟不上发送速度;如果RX FIFO经常满,则说明CPU或DMA读取数据的速度太慢。这时就需要调整你的数据处理策略,或者启用DMA的突发传输模式。
1.1 模块框图与信号流解析
要驾驭SSI模块,不能只看软件API,必须对其硬件信号流有清晰的认知。模块框图(对应手册中的Figure 22-1)是理解其工作原理的路线图。整个数据通路的核心是TX FIFO、RX FIFO以及连接它们的收发逻辑单元。
数据发送的旅程始于CPU或μDMA控制器对SSI数据寄存器(SSI_DR)的写入。这个写入操作实际上是将数据压入了TX FIFO队列。当SSI被启用且时钟条件满足时,收发逻辑会从TX FIFO的底部取出一个数据帧,装入发送移位寄存器,然后在时钟SSIn_CLK的节拍下,将数据一位一位地从SSIn_TX引脚移出。与此同时,SSIn_RX引脚上来自从设备的数据,也在同样的时钟节拍下被采样,并移入接收移位寄存器。当一个完整的数据帧接收完毕后,该帧数据会被从移位寄存器推入RX FIFO的顶部,等待被读取。
这里需要特别注意SSIn_FSS(帧同步信号)引脚的角色,它是区分三种协议模式的关键。在Motorola SPI模式下,它通常作为低有效的片选信号(nSS);在TI SSI模式下,它是在每个数据帧前产生一个时钟周期的高脉冲;在MICROWIRE模式下,它则在整帧传输期间保持低电平。SSIn_CLK时钟信号则由片内的可编程分频器链生成,其频率由系统时钟PERDMACLK、预分频器CPSDVSR和时钟速率因子SCR共同决定,公式为SSIn_CLK = PERDMACLK / [CPSDVSR × (1 + SCR)]。这个公式是配置通信速率的根本,我后面会用一个具体的计算例子来演示如何“配平”它。
中断和DMA控制器像两个高效的“秘书”。中断系统监控着FIFO的空满状态、超时和溢出,一旦触发条件满足,就会向CPU“汇报”,请求处理。而DMA控制器则更“自动化”,它可以直接与FIFO对接,在满足特定条件(如FIFO非空或非满)时,自动发起数据搬运,完全不需要CPU介入。在资源紧张的实时系统中,合理分配中断和DMA任务,是优化系统性能的关键。
1.2 三种协议模式的选择与考量
CC13x2/CC26x2的SSI模块支持Motorola SPI、TI SSI和National MICROWIRE三种协议,这不是简单的兼容,而是给了我们根据实际外设灵活选择的权利。
Motorola SPI是目前最通用、支持器件最多的模式,它是一个全双工、四线制(CLK,MOSI,MISO,nSS)的同步接口。其最大的特点是通过SPO(时钟极性)和SPH(时钟相位)两个位的组合,可以产生四种不同的时钟时序模式(Mode 0-3)。绝大多数SPI器件都会在其数据手册中明确要求工作在哪种模式。例如,很多Flash存储器使用Mode 0(SPO=0,SPH=0),而一些ADC则可能使用Mode 3(SPO=1,SPH=1)。在配置时,必须严格与从设备匹配,否则通信必然失败。我的经验是,拿到一个新器件,第一件事就是翻数据手册的时序图,确认其CPOL和CPHA(对应SPO和SPH)。
TI SSI模式是德州仪器自己的同步串行格式。它与SPI的主要区别在于帧同步信号SSIn_FSS的行为:它在每个数据帧传输前产生一个时钟周期的高脉冲,而不是在整个传输期间保持有效。这种模式在某些TI专属的音频编解码器或旧式器件中可能会用到。如果你的项目中没有这类器件,通常可以忽略此模式。
MICROWIRE模式是一种半双工协议,采用类似“命令-响应”的机制。主机先发送一个8位的控制字,从机解析后,再回送4-16位的数据。这非常适用于那些需要先发送指令、再读取数据的简单传感器。它的优势是连线简单(同样四根线),协议本身包含了寻址或命令功能。但需要注意的是,它是半双工的,同一时刻只能有一个方向的数据流,因此吞吐量通常低于全双工的SPI。
选择建议:对于全新的设计,优先选择Motorola SPI,因其生态最广。如果外设是TI的老产品,检查是否要求TI SSI模式。如果通信逻辑是典型的“先发命令,后读数据”,且速率要求不高,可以考虑MICROWIRE以简化协议层设计。
2. 关键功能深度解析与配置要点
2.1 比特率生成:计算与误差控制
比特率的配置是SSI通信稳定的第一步。手册中给出的公式SSIn_CLK = PERDMACLK / [CPSDVSR × (1 + SCR)]看似简单,但其中有两个关键约束常被忽略:1.CPSDVSR必须是2到254之间的偶数;2.SCR的范围是0到255。最终计算出的SSIn_CLK必须满足主从模式下的最低核心时钟倍数要求。
假设我们的系统时钟PERDMACLK为48MHz,目标比特率是4 Mbps。我们首先需要确定分频系数N =PERDMACLK / SSIn_CLK= 48MHz / 4MHz = 12。接下来,我们需要将这个N分解为CPSDVSR × (1+SCR)的形式,且CPSDVSR为偶数。
一种直观的尝试是让CPSDVSR=2,则(1+SCR)=6,所以SCR=5。校验:48MHz / (2 * 6) = 4MHz,完美匹配。但如果我们想要一个更精确的波特率,比如3.125 Mbps(常用于某些特定音频采样率),计算N=48MHz / 3.125MHz = 15.36,这不是一个整数。我们必须寻找最接近的整数分频比。取N=15,则SSIn_CLK=3.2MHz,误差为2.4%;取N=16,则SSIn_CLK=3.0MHz,误差为4%。我们需要根据外设的容忍度来选择。分解N=15,15的因数有1,3,5,15。为了满足CPSDVSR为偶数,我们可以令CPSDVSR=2,则(1+SCR)=7.5,SCR必须为整数,此路不通。令CPSDVSR=6,则(1+SCR)=2.5,也不行。实际上,15无法分解为一个偶数和一个整数的乘积。因此,最接近的可行配置是N=16 (CPSDVSR=2,SCR=7) 或 N=14 (CPSDVSR=2,SCR=6)。这时就需要通过示波器测量实际时钟,并评估通信误码率。
实操心得:在计算波特率时,不要只追求理论值的完美匹配。首先应使用
PRCMPeripheralClockGet()函数确认当前运行模式下准确的PERDMACLK值(它可能在休眠模式下被降低)。其次,对于高速通信(>1MHz),建议优先将CPSDVSR设置为较小的偶数(如2或4),这样可以获得更精细的SCR调整粒度,从而得到更接近目标值的时钟。最后,务必用逻辑分析仪抓取SSIn_CLK的实际波形,测量其频率和占空比,这是排查通信故障最直接的手段。
2.2 FIFO操作机制与中断策略
TX和RX FIFO是SSI模块的“心脏”,深度为8,宽度为16位。这意味着它们可以缓存最多8个数据帧(每帧4-16位)。这种缓冲机制带来了巨大的灵活性,但也需要精心管理。
TX FIFO的工作逻辑是“写即缓冲”。当你向SSI_DR寄存器写入数据时,数据实际上进入了TX FIFO队列。只有当SSI发送逻辑空闲且时钟就绪时,才会从FIFO底部取出数据发送。你可以通过查询SSI_SR寄存器中的TFE(发送FIFO空)和TNF(发送FIFO未满)位,或者利用发送FIFO服务中断来管理数据写入。当中断被使能,且TX FIFO中的数据条目数少于或等于4个(即半空或更空)时,就会触发中断,提示你可以安全地写入更多数据而不会溢出。
RX FIFO的工作逻辑是“收即缓冲”。从SSIn_RX引脚接收到的完整数据帧会被自动推入RX FIFO。你可以通过查询SSI_SR寄存器中的RNE(接收FIFO非空)位,或者利用接收FIFO服务中断来读取数据。当中断被使能,且RX FIFO中的数据条目数达到或超过4个(即半满或更满)时,就会触发中断,提示你可以批量读取数据,提高效率。
除了服务中断,还有两个重要的中断源:接收超时中断和接收溢出中断。接收超时中断在RX FIFO非空状态持续超过32个SSIn_CLK周期时触发。这非常有用!想象一下,主机发送了3个字节的数据,触发了RX FIFO半满中断(阈值4),但你因为阈值未到而没有进入中断服务程序。如果没有超时机制,这3个数据将永远留在FIFO里。超时中断确保了即使数据量不足半满,也能被及时取出。而接收溢出中断则是在RX FIFO已满时,又有新数据到来触发的,这通常意味着你的数据读取速度严重跟不上接收速度,是一个需要紧急处理的错误状态。
避坑指南:在中断服务程序(ISR)中读取
SSI_DR以清空RX FIFO时,务必使用一个while(SSIBusy())或检查RNE位的循环,确保取出所有有效数据。否则,残留的数据会立即再次触发中断,导致ISR重入,造成系统锁死。同样,在清除中断标志时,特别是超时中断(通过写SSI_ICR寄存器的RTIC位),这个操作必须在ISR返回前完成。我曾遇到过因为清除中断标志的代码放在了ISR末尾,且前面有较长的处理逻辑,导致中断标志被清除前系统又检测到超时条件,引发了虚假的连续中断。
2.3 DMA配置与高效数据传输
当需要传输大量数据时,使用DMA是必须的。SSI模块为发送和接收提供了独立的DMA通道,并与FIFO深度智能联动。对于接收通道,当RX FIFO中有任何数据时,会产生一个单次传输请求;当RX FIFO中数据达到或超过4个时,会产生一个突发传输请求。对于发送通道,当TX FIFO中至少有一个空位时,产生单次请求;当TX FIFO中空位达到或超过4个时,产生突发请求。
这种设计非常精妙。单次请求适合零散、非连续的数据传输,而突发请求则能最大化总线利用率,一次性搬运多个数据,特别适合连续读写场景。在μDMA控制器中,你需要为SSI的TX和RX通道分别配置一个通道描述符(Control Table Entry),设置好源地址、目的地址、传输数据量(ARB_SIZE)和传输模式。
一个典型的SPI Flash读取DMA配置流程如下:
- 使能SSI模块和对应引脚的时钟与功能映射。
- 配置SSI为SPI主模式,并设置好时钟速率、帧格式。
- 初始化μDMA控制器,为SSI RX通道配置描述符:源地址为
SSI_DR(外设地址),目的地址为内存中的缓冲区,传输模式为基本模式(Basic),ARB_SIZE设置为8(与FIFO深度匹配)。 - 使能SSI的接收DMA(设置
SSI_DMACR.RXDMAE)。 - 使能SSI本身(设置
SSI_CR1.SSE)。 - 向Flash发送读命令和地址(通常通过CPU或另一个DMA通道写TX)。
- 一旦SSI开始接收到数据,RX FIFO非空即会触发DMA请求,μDMA自动将数据从
SSI_DR搬运到指定内存。 - 传输完成后,DMA会触发完成中断,你在中断中处理数据或启动下一次传输。
注意事项:DMA的开启和关闭时机需要仔细控制。在SSI使能(
SSE=1)前,最好不要开启DMA,否则可能产生不可预料的传输请求。在传输结束后,应先关闭DMA通道,再考虑关闭SSI。另外,DMA的传输数据量单位是“次”,一次传输的大小取决于你在SSI控制寄存器中设置的数据帧长度(DSS)。如果你设置的是8位数据帧,那么DMA的一次传输就是搬运一个字节。
3. 三种帧格式的时序实现与代码配置
理解协议时序图是正确配置和调试的基石。手册中的波形图看起来复杂,但抓住几个关键点就能化繁为简:时钟极性(SPO)、时钟相位(SPH)、帧同步信号(FSS)的行为,以及数据在哪个边沿采样和输出。
3.1 Motorola SPI模式详解与配置实例
SPI的四种模式由SPO和SPH组合而成。我习惯用“空闲电平”和“采样边沿”来记忆:
SPO=0:时钟空闲时为低电平。SPO=1:时钟空闲时为高电平。SPH=0:数据在第一个时钟边沿采样(对于SPO=0是上升沿,对于SPO=1是下降沿)。SPH=1:数据在第二个时钟边沿采样。
以最常见的Mode 0 (SPO=0,SPH=0)为例:时钟线空闲为低。传输开始时,片选SSIn_FSS拉低。在第一个时钟上升沿之前,主设备的数据(SSIn_TX)就已经建立好了。在时钟上升沿时刻,从设备采样主设备的数据(MOSI),同时主设备采样从设备的数据(MISO)。数据在时钟下降沿时发生改变。对于单次传输,传输结束后片选拉高。
在CC13x2/CC26x2的驱动库中,配置一个SPI主机的典型代码如下(以TI的DriverLib为例):
#include <ti/drivers/SPI.h> #include <ti/drivers/spi/SPICC26X2DMA.h> #include <ti/drivers/dma/UDMACC26XX.h> SPI_Handle spiHandle; SPI_Params spiParams; SPICC26X2DMA_HWAttrsV1 spiHWAttrs; SPICC26X2DMA_Object spiObject; // 1. 初始化SPI参数结构体为默认值 SPI_Params_init(&spiParams); // 2. 配置用户参数 spiParams.bitRate = 4000000; // 4 Mbps spiParams.frameFormat = SPI_POL0_PHA0; // Mode 0 spiParams.mode = SPI_MASTER; // 主模式 spiParams.dataSize = 8; // 8位数据帧 // 3. 配置硬件属性(引脚、DMA通道等) spiHWAttrs.baseAddr = SSI0_BASE; // 使用SSI0模块 spiHWAttrs.intNum = INT_SSI0_COMB; // 中断号 spiHWAttrs.intPriority = ~0; // 默认优先级 spiHWAttrs.swiPriority = 0; spiHWAttrs.powerMngrId = PERIPH_SSI0; // 电源管理ID spiHWAttrs.rxChannelBitMask = UDMA_CHAN_SSI0_RX; // RX DMA通道掩码 spiHWAttrs.txChannelBitMask = UDMA_CHAN_SSI0_TX; // TX DMA通道掩码 // 引脚配置:根据你的电路板原理图定义 spiHWAttrs.pinMosi = IOID_10; // MOSI spiHWAttrs.pinMiso = IOID_9; // MISO spiHWAttrs.pinClk = IOID_8; // SCLK spiHWAttrs.pinCs = IOID_11; // CS (可选,也可用GPIO手动控制) // 4. 打开SPI实例 spiHandle = SPI_open(Board_SPI0, &spiParams); if (spiHandle == NULL) { // 打开失败处理 } // 5. 进行数据传输 (阻塞式) uint8_t txBuffer[4] = {0x01, 0x02, 0x03, 0x04}; uint8_t rxBuffer[4] = {0}; SPI_Transaction transaction; transaction.count = 4; transaction.txBuf = txBuffer; transaction.rxBuf = rxBuffer; bool transferOK = SPI_transfer(spiHandle, &transaction); if (!transferOK) { // 传输失败处理 }这段代码隐藏了底层对SSI_CR0、SSI_CPSR等寄存器的复杂操作。驱动库会根据bitRate和系统时钟自动计算CPSDVSR和SCR,并设置FRF字段为SPI模式。对于需要精细控制或使用TI SSI、MICROWIRE模式的情况,你可能需要直接操作寄存器,或者修改驱动库的底层实现。
3.2 TI SSI与MICROWIRE模式的关键差异
TI SSI模式的独特之处在于SSIn_FSS信号。它不是一个持续的片选,而是在每个数据帧开始前,产生一个SSIn_CLK周期的高脉冲。这意味着在连续传输多个帧时,SSIn_FSS信号会在每帧之间产生一个高脉冲。这种模式要求从设备能识别这种脉冲式的帧起始信号。在配置上,只需将SSI_CR0寄存器中的FRF(帧格式)字段设置为0x01即可。
MICROWIRE模式的配置则更为特殊。首先,需要将FRF字段设置为0x02。其次,由于它是半双工且分控制字/数据两阶段,在软件处理上需要分两步。第一步,将8位控制字写入TX FIFO,SSI会自动发送。第二步,在控制字发送结束后,SSI会切换为接收状态,等待从设备回送数据。你需要确保在从设备回送数据期间,TX FIFO没有新的数据写入,否则会破坏通信。在代码实现上,通常采用查询或中断的方式,等待TX FIFO空且一次传输完成,然后再去读取RX FIFO中的数据。
调试技巧:当通信不正常时,逻辑分析仪是你的最佳伙伴。同时抓取
CLK、FSS、MOSI、MISO四路信号。首先看FSS信号的行为是否符合预期模式(持续低、脉冲高)。其次看CLK的极性和相位。最后对照数据手册的时序图,看数据在哪个边沿变化,在哪个边沿稳定(采样点)。90%的SPI通信问题都可以通过这时序“四线图”定位。
4. 初始化流程、常见问题与实战排查
4.1 完整的SSI模块初始化步骤
手册22.6节给出了初始化的步骤,但那是寄存器级别的“骨骼”。结合TI的DriverLib,一个健壮的初始化流程应该包含以下“血肉”:
电源与时钟使能:这是所有外设操作的前提。确保SSI模块所在的电源域已上电,并通过PRCM模块使能其运行态、休眠态和深度休眠态的时钟。使用
PRCMPeripheralRunEnable(),PRCMPeripheralSleepEnable(),PRCMPeripheralDeepSleepEnable()函数,最后调用PRCMLoadSet()生效。PRCMPeripheralRunEnable(PRCM_PERIPH_SSI0); PRCMPeripheralSleepEnable(PRCM_PERIPH_SSI0); PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_SSI0); PRCMLoadSet(); while(!PRCMLoadGet()); // 等待时钟配置加载完成GPIO引脚复用配置:将具体的物理IO引脚映射到SSI功能。通过IOC模块的
IOCFGn寄存器配置,或者使用PIN_init()和PIN_setMux()函数。务必确认引脚功能与硬件原理图一致。PIN_Handle pinHandle; PIN_State pinState; PIN_Config pinConfig[] = { Board_SPI0_MOSI | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, // MOSI Board_SPI0_MISO | PIN_INPUT_EN, // MISO Board_SPI0_CLK | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, // SCLK Board_SPI0_CS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, // CS (手动控制) PIN_TERMINATE }; pinHandle = PIN_open(&pinState, pinConfig); PIN_setMux(pinHandle, Board_SPI0_MOSI, IOC_PORT_MCU_SSI0_TX); PIN_setMux(pinHandle, Board_SPI0_MISO, IOC_PORT_MCU_SSI0_RX); PIN_setMux(pinHandle, Board_SPI0_CLK, IOC_PORT_MCU_SSI0_CLK); // CS引脚通常作为GPIO手动控制,不映射到SSI_FSSSSI模块基础配置: a. 确保SSI已禁用(
SSI_CR1.SSE = 0)。 b. 配置主从模式(SSI_CR1.MS位)。 c. 配置时钟预分频器SSI_CPSR。 d. 配置SSI_CR0寄存器:设置SCR(时钟速率)、SPO/SPH(SPI模式)、FRF(协议模式)、DSS(数据位宽)。 e. (可选)配置DMA,并设置SSI_DMACR寄存器使能TX/RX DMA。 f. 最后,置位SSI_CR1.SSE使能SSI模块。
4.2 典型问题排查速查表
在实际开发中,你会遇到各种各样的问题。下面这个表格是我多年调试经验的总结,可以帮助你快速定位问题根源。
| 现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 完全无时钟输出 | 1. SSI模块时钟未使能。 2. GPIO引脚功能映射错误。 3. SSI未使能( SSE=0)。4. 配置为主模式,但TX FIFO为空。 | 1. 检查PRCM配置代码,确认PRCMPeripheralRunEnable已调用且PRCMLoadSet()已执行。2. 用万用表或示波器检查引脚电压,或用寄存器查看工具确认 IOCFGn.PORTID值正确。3. 单步调试,检查 SSI_CR1寄存器的SSE位是否为1。4. 主模式只在有数据要发送时才产生时钟。向TX FIFO写一个数据再观察。 |
| 时钟频率不对 | 1.PERDMACLK系统时钟频率与预期不符。2. CPSDVSR或SCR计算错误。3. 寄存器值未成功写入。 | 1. 确认MCU运行在何种频率下(例如48MHz DCO),使用PRCMPeripheralClockGet()验证。2. 重新计算分频系数,确保 CPSDVSR为偶数,(1+SCR)为整数。3. 在写配置寄存器前,确保 SSE=0。写完后读取寄存器回读确认。 |
| 能发不能收,或收的数据全为0/FF | 1. SPI模式(SPO/SPH)不匹配。2. 从设备未就绪或损坏。 3. 主从设备间电平不兼容。 4. 接收FIFO溢出或未及时读取。 | 1.这是最常见原因!用逻辑分析仪抓取时序,与从设备数据手册对比CLK极性和相位。2. 检查从设备电源、复位引脚、片选信号。尝试与已知正常的设备通信。 3. 检查双方IO电压是否一致(如3.3V vs 5V),可能需要电平转换。 4. 检查 SSI_SR寄存器的RFF(接收FIFO满)和ROR(接收溢出)位。确保中断或DMA能及时取走数据。 |
| 通信不稳定,偶发错误 | 1. 时钟速率过高,信号质量差。 2. 布线过长,引入干扰。 3. 电源噪声大。 4. 未正确处理多从设备片选。 | 1. 降低比特率测试。检查PCB布线,CLK和MOSI/MISO线应等长、靠近,远离高频噪声源。2. 增加串联匹配电阻(如22-33欧姆)。 3. 检查电源纹波,在MCU和从设备电源引脚就近加退耦电容(0.1uF)。 4. 确保在切换不同从设备时,有足够的片选建立和保持时间,通常用 delay_us()短暂延时。 |
| DMA传输不启动或数据不全 | 1. DMA通道未正确配置或使能。 2. DMA源/目的地址或传输量错误。 3. SSI的DMA使能位未设置。 4. DMA传输完成中断未清除。 | 1. 检查μDMA通道控制字、源地址(SSI_DR)、目的地址、ARB_SIZE设置。2. 确认 SSI_DMACR中的TXDMAE或RXDMAE位已置位。3. 在DMA完成中断ISR中,必须读取 UDMA_REQDONE寄存器以清除中断标志,并重新配置描述符以备下次传输。 |
4.3 低功耗设计中的SSI使用要点
CC13x2/CC26x2是无线MCU,低功耗是核心考量。SSI模块本身在空闲时功耗很低,但需要注意以下几点:
- 动态时钟管理:在不需要通信时,可以通过PRCM模块关闭SSI的外设时钟(
PRCMPeripheralRunDisable()),以节省功耗。再次使用前重新使能并初始化。 - IO引脚配置:在SSI禁用期间,将相关的
CLK、MOSI、CS引脚配置为输出低或高阻输入,避免引脚浮空产生漏电流。对于MISO输入引脚,使能内部上拉或下拉电阻,将其固定在一个确定电平。 - DMA与唤醒:可以配置DMA在传输完成后产生中断,并结合MCU的休眠模式。让MCU进入深度睡眠,由DMA完成大量数据搬运,搬运完成后触发中断唤醒MCU进行处理,这能极大降低平均功耗。
- 从设备功耗:别忘了控制从设备的电源或片选。通信间隙,拉高从设备的片选引脚(如果它是低有效),使其进入休眠模式。对于完全断电的从设备,重新上电后要留足复位稳定时间。
最后,再分享一个调试复杂SPI总线(多从设备)的心得:逐个击破。先将所有从设备的片选线置为无效(通常拉高),然后单独使能一个设备进行通信测试。确认一个正常后,再加入第二个。同时,仔细规划每个从设备的片选GPIO,确保软件操作片选时不会通过总线串扰影响到其他设备。有时,一个设备初始化时发出的垃圾数据可能会被另一个设备误接收,因此严格的时序隔离非常重要。