1. UNICOMM SPI模块概览与核心设计思路
在嵌入式开发中,SPI(Serial Peripheral Interface)几乎是工程师的“瑞士军刀”,从读取温湿度传感器数据到驱动TFT屏幕,再到与Flash存储器通信,无处不在。但很多时候,我们只是调用库函数,对底层寄存器如何协同工作一知半楚。最近在基于TI的MSPM33 C3系列微控制器开发一个高实时性的数据采集板时,我不得不深入其UNICOMM SPI模块的寄存器手册,进行了一番“庖丁解牛”式的梳理。我发现,真正吃透这些寄存器,不仅能解决棘手的通信问题,更能将SPI的性能压榨到极致。这篇文章,我就结合实战经验,带你从寄存器层面,彻底搞懂MSPM33的UNICOMM SPI模块,让你从“会用”进阶到“精通”。
MSPM33的UNICOMM SPI模块远不止一个简单的串行移位寄存器。它是一个高度集成、可配置性极强的通信引擎。其核心设计思路围绕着效率和灵活性展开。一方面,它通过深度可编程的时钟系统、多模式帧格式和FIFO缓冲,来适应从低速传感器到高速ADC的各种外设;另一方面,它提供了两套独立且完整的中断与DMA事件发布系统(CPU_INT和DMA_TRIG),让CPU可以从繁琐的数据搬运中解放出来,专注于业务逻辑。理解这个模块,关键在于建立起“时钟配置 -> 通信参数设定 -> 数据流控制 -> 事件/中断响应”这条主线。我们配置每一个寄存器,实际上都是在为这条数据流水线设置“阀门”和“触发器”。
2. 时钟系统配置:通信速率与稳定性的基石
SPI通信的稳定性和速率上限,首先取决于时钟。UNICOMM SPI的时钟配置是一个两步走的过程:先选源,再分频。这比许多MCU上固定时钟源的SPI模块要灵活得多。
2.1 时钟源选择寄存器(CLKSEL)
CLKSEL寄存器(偏移地址 0x1008)是时钟配置的“总开关”。它允许你为SPI模块选择系统内可用的多个时钟源。这个设计在低功耗应用中尤其有用。
寄存器位域详解:
- 位[11:8] ASYNC_*_SEL: 这四个位用于选择异步时钟源,包括PLL、HFCLK(高频时钟)、SYSCLK(系统时钟)和LFCLK(低频时钟)。你可以同时使能多个源,模块内部会进行“或”操作。一个关键技巧:在需要SPI在低功耗模式下(如睡眠模式)仍能响应主机通信的场景下,必须使能
ASYNC_LFCLK_SEL,因为主系统时钟可能已关闭,而低频时钟依然运行。 - *位[3:1]_SEL: 这三个位用于选择同步时钟源,包括BUSCLK(总线时钟)、MFCLK(模块功能时钟)和LFCLK。这里有一个重要的优先级逻辑:当同步时钟源和异步时钟源同时使能时,同步时钟源通常拥有更高的优先级或用于不同的操作模式。在大多数主动通信场景下,我们选择
MFCLK_SEL或BUSCLK_SEL以获得更高的性能。
配置心得:我通常的配置策略是,在初始化阶段,同时使能一个高频同步时钟(如MFCLK_SEL)和一个低频异步时钟(ASYNC_LFCLK_SEL)。这样,在CPU进入低功耗模式前,只需在软件上将SPI的时钟源切换为异步低频时钟,就能保证SPI作为从设备时,依然能监听总线活动,并在收到数据时通过中断唤醒CPU,实现极低功耗的待机监听功能。配置代码如下(假设使用TI的DriverLib风格,下同):
// 假设 SPI_BASE 为 UNICOMM SPI 模块的基地址 HWREG(SPI_BASE + CLKSEL) = 0x0; // 先清零 // 使能模块功能时钟(MFCLK)作为主同步源,同时使能异步低频时钟以备低功耗模式 HWREG(SPI_BASE + CLKSEL) = (1 << 2) | (1 << 8);2.2 模块时钟分频寄存器(CLKDIV)与串行时钟分频器(CLKCTL.SCR)
选好时钟源后,接下来要进行两级分频,以得到最终驱动SCLK的位时钟。
CLKDIV寄存器(偏移 0x1000):这是一个粗调分频器,位于时钟路径的前端。它的
RATIO域(位[2:0]可以将输入的模块时钟进行2到8分频。它的主要作用是:当你的系统主频很高(如MSPM33的160MHz),而SPI外设支持的最高SCLK频率较低时,先用CLKDIV将频率降到合理范围,避免后续SCR分频系数过大导致精度损失。例如,系统时钟160MHz,目标SPI波特率1MHz。如果直接用SCR分频,需要设置SCR = (160M / (1M*2)) -1 = 79。但如果先经过CLKDIV8分频,得到20MHz,再设置SCR = (20M / (1M*2)) -1 = 9。后者SCR值更小,在某些对时钟抖动敏感的应用中可能更有优势。CLKCTL.SCR域(位[9:0]):这是精确设置SPI波特率的核心寄存器。计算公式非常明确:
SPI Bit Rate = Functional Clock Frequency / ((SCR + 1) * 2)。这里的“Functional Clock Frequency”是已经经过CLKDIV分频后的时钟。计算时务必注意:SCR的取值范围是0-1023。当SCR=0时,波特率等于功能时钟频率的一半,这是最高速率。
实战配置示例:假设我们使用MFCLK,频率为80MHz,目标SPI波特率为5MHz。
- 方案一:不使用
CLKDIV。计算SCR = (80M / (5M * 2)) - 1 = 7。直接设置SCR=7。 - 方案二:使用
CLKDIV先2分频,得到40MHz。计算SCR = (40M / (5M * 2)) - 1 = 3。设置CLKDIV.RATIO=1(代表2分频),SCR=3。
如何选择?如果目标波特率与时钟源频率成简单的分频关系,且SCR值适中(比如在10-200之间),直接用SCR即可,逻辑简单。如果系统时钟频率远高于目标波特率,导致SCR值过大(接近1023上限),或者你需要在一个很宽的频率范围内进行动态调整,那么结合CLKDIV进行“粗调+微调”会更灵活。在我的项目中,需要SPI在不同工作模式下在100kHz到10MHz之间切换,我就采用了固定CLKDIV为2分频,然后动态修改SCR的方案,代码控制起来非常清晰。
3. 通信协议与控制寄存器深度解析
时钟配好了,接下来就要定义通信的“语言规则”,也就是协议格式。这部分主要由CTL0和CTL1两个控制寄存器掌控。
3.1 CTL0寄存器:帧格式与数据帧控制
CTL0(偏移 0x1100)是协议配置的核心,它定义了SPI通信的基本范式。
关键位域与配置策略:
DSS (Data Size Select, 位[4:0]): 选择数据帧长度,从4位到16位。这里有一个极易踩坑的细节:数据帧长度并不直接等于你写入
TXDATA的数据位数。例如,设置DSS=7代表8位数据。当你写入一个16位的值到TXDATA时,只有低8位会被发送。务必在发送前,将数据右对齐到有效的低位。接收时,数据会自动右对齐在RXDATA的低位中,高位补零。FRF (Frame Format, 位[6:5]): 选择帧格式。
00: Motorola SPI (3线模式)。即标准的SPI,使用SCLK, MOSI (PICO), MISO (POCI)三线。01: Motorola SPI (4线模式)。多了一根使能线,具体行为需参考芯片数据手册对额外引脚的定义。10: TI同步串行帧格式。这种模式下,每个数据帧前会有一个低电平脉冲作为帧同步信号,与标准SPI的持续片选不同。11: 保留。选择建议:绝大多数SPI外设使用00标准模式。只有明确支持TI同步格式或需要4线控制的外设,才选择其他模式。
SPO与SPH (位[8]和位[9]): 时钟极性(CPOL)与相位(CPHA)。这是SPI通信的“灵魂”,必须与从设备严格匹配。
SPO=0: 时钟空闲时为低电平。SPO=1: 时钟空闲时为高电平。SPH=0: 数据在第一个时钟边沿采样。SPH=1: 数据在第二个时钟边沿采样。 常见的模式有:Mode 0 (SPO=0, SPH=0) 和 Mode 3 (SPO=1, SPH=1)。一个快速判断方法:观察从设备数据手册的时序图,看数据线在时钟的哪个边沿(上升沿或下降沿)需要保持稳定(即采样时刻),哪个边沿可以变化(即输出时刻)。采样时刻对应的那个边沿,就是第一个边沿(如果采样发生在第一个边沿,则SPH=0)。
CSSEL (Chip Select Select, 位[13:12]): 选择使用哪个硬件片选线(CS0-CS3)。注意:这个选择适用于控制器(主机)模式。在从机模式下,片选由外部主机控制。
CSCLR (位[14]): 这个位非常有用。当设置为1时,在片选信号变为无效(拉高)的瞬间,硬件会自动清空内部的移位寄存器。这能有效避免跨帧数据污染。例如,上一帧发送了0xAA,下一帧只想发送0x55。如果移位寄存器未清空,且下一帧长度更短,就可能残留旧数据位,导致通信错误。在通信可靠性要求高的场合,建议启用此功能。
3.2 CTL1寄存器:工作模式与高级功能
CTL1(偏移 0x114C)控制着SPI的使能、模式选择和一些高级特性。
核心位域配置指南:
ENABLE (位[0]): SPI模块总开关。一个黄金法则:任何对
CTL0、CTL1(除ENABLE位本身)、CLKCTL等关键配置寄存器的修改,都必须在ENABLE=0(模块禁用)的情况下进行。修改完成后,再置位ENABLE启动模块。否则可能导致不可预知的通信错误。CP (Controller/Peripheral, 位[2]): 主从模式选择。0为从机,1为主机。同样,模式切换也必须在模块禁用时进行。
MSB (位[4]): 数据传输位序。0为LSB(最低有效位)先发,1为MSB(最高有效位)先发。这必须与外设保持一致。大部分器件是MSB先发。
LBM (Loopback Mode, 位[1]): 回环模式。置1后,模块内部将发送数据线直接连接到接收数据线。这是硬件自检和调试的利器。在系统初始化后、连接真实外设前,可以开启回环模式,发送一组已知数据并接收,验证SPI控制器本身的硬件和底层配置是否正确,能快速隔离问题是出在MCU端还是外设端。
RXTIMEOUT (位[29:24]): 接收超时计数器(仅从机模式有效)。当SPI作为从机,主机发起传输后突然停止(如片选意外拉高),从机的接收FIFO中可能残留未读数据。这个超时功能可以在总线空闲超过设定周期后,触发超时中断(
RTOUT),通知CPU进行异常处理,防止软件死等。
配置流程示例(主机模式,8位数据,Mode 0, MSB先发):
void SPI_Master_Init(void) { // 1. 确保SPI模块禁用 HWREG(SPI_BASE + CTL1) &= ~(1 << 0); // 清除ENABLE位 // 2. 配置CTL0 uint32_t ctl0_value = 0; ctl0_value |= (7 << 0); // DSS=7, 即8位数据 ctl0_value |= (0 << 5); // FRF=0, Motorola 3线模式 ctl0_value |= (0 << 8); // SPO=0, 时钟空闲低 ctl0_value |= (0 << 9); // SPH=0, 第一个边沿采样 ctl0_value |= (1 << 14); // CSCLR=1, 片选无效时清空移位寄存器 ctl0_value |= (0 << 12); // CSSEL=0, 使用CS0线 HWREG(SPI_BASE + CTL0) = ctl0_value; // 3. 配置CTL1 uint32_t ctl1_value = 0; ctl1_value |= (1 << 2); // CP=1, 控制器模式 ctl1_value |= (1 << 4); // MSB=1, 高位先发 ctl1_value |= (0 << 1); // LBM=0, 关闭回环 // ... 其他位保持默认 HWREG(SPI_BASE + CTL1) = ctl1_value; // 4. 配置时钟(假设已按前文配置好CLKSEL和CLKCTL) // HWREG(SPI_BASE + CLKCTL) = ... ; // 5. 最后,使能SPI模块 HWREG(SPI_BASE + CTL1) |= (1 << 0); // 设置ENABLE位 }4. 数据缓冲区与FIFO管理实战
UNICOMM SPI模块内置了收发FIFO(先入先出缓冲区),这是提升通信效率、减轻CPU负担的关键。对FIFO的管理主要通过TXDATA、RXDATA、STAT和IFLS寄存器完成。
4.1 数据收发寄存器:TXDATA与RXDATA
- TXDATA (偏移 0x1120): 写入寄存器即向发送FIFO压入数据。这里有PACKEN(CTL0.7)的妙用。当
PACKEN=0时,每次32位写入,只有低16位有效,占用一个FIFO条目。当PACKEN=1时,一次32位写入,高16位和低16位会被当作两个独立的数据,依次压入发送FIFO。这在需要连续发送大量16位数据时,能减少一半的写操作次数,提升总线效率。 - RXDATA (偏移 0x1124): 读取寄存器即从接收FIFO弹出数据。
PACKEN位同样影响读取行为,PACKEN=1时一次读取返回两个16位数据组成的32位值。
操作注意事项:
- 写TXDATA前,一定要检查
STAT.TXFF(发送FIFO满)标志,避免数据覆盖丢失。 - 读RXDATA前,一定要检查
STAT.RXFE(接收FIFO空)标志,避免读取无效数据。 - 对于非16位的数据长度(由
DSS设定),软件必须负责数据的对齐。发送时右对齐写入TXDATA,接收时从RXDATA的低位提取有效数据。
4.2 状态寄存器(STAT)与FIFO水平控制寄存器(IFLS)
STAT寄存器(偏移 0x1108)提供了FIFO和模块的实时状态。
BUSY位:指示SPI是否正在通信。在启动一次传输或查询传输是否完成时非常有用。TXFE/TXFF: 发送FIFO空/满。RXFE/RXFF: 接收FIFO空/满。
IFLS寄存器(偏移 0x110C)是中断/DMA触发策略的核心。它不控制FIFO大小,而是控制FIFO达到何种水平时触发事件。
- TXIFLSEL (位[2:0]): 发送FIFO中断水平选择。它定义的是“FIFO空到什么程度”触发中断。例如,默认值
2(010b)代表“TX FIFO <= 1/2 empty”,即当发送FIFO中的数据少于或等于一半容量时,就会触发发送中断(如果中断已使能),提示CPU可以填充更多数据了。这对于流式数据传输非常高效。 - RXIFLSEL (位[6:4]): 接收FIFO中断水平选择。它定义的是“FIFO满到什么程度”触发中断。默认值
2代表“RX FIFO >= 1/2 full”,即接收FIFO中的数据达到或超过一半容量时,触发接收中断,通知CPU来批量取走数据。 - TXCLR/RXCLR (位[3]和位[7]): 写1可分别清空发送和接收FIFO。在通信协议切换或错误恢复时,记得清空FIFO,避免残留数据干扰。
IFLS配置策略:
- 低延迟、小数据包场景:将
RXIFLSEL设置为1(>=1/4满)甚至4(非空即触发),TXIFLSEL设置为5(空即触发)。这样任何数据的到达或发送完成都会立刻通知CPU,响应最快,但中断频率也最高。 - 大数据块、高吞吐量场景(配合DMA):将
RXIFLSEL设置为3(>=3/4满)或5(全满),TXIFLSEL设置为1(<=3/4空)。这样DMA会在FIFO几乎满或几乎空时才搬运数据,减少DMA传输次数,提升总线效率,同时降低CPU中断负载。
5. 中断系统详解与编程模型
UNICOMM SPI的中断系统设计精巧,分为CPU中断和DMA触发事件两套独立的逻辑,分别对应CPU_INT和DMA_TRIG两组寄存器。理解这两套系统如何协同,是实现高效SPI驱动的关键。
5.1 CPU中断处理流程与寄存器组
CPU中断路径用于让CPU亲自处理SPI事件。其相关的寄存器组位于偏移0x1020至0x1048,以及0x10E4。
中断状态流与核心寄存器:
- 原始中断状态 (RIS, 0x1030): 这是“事实”寄存器。任何中断事件(��发送完成、接收满、溢出错误)发生,无论你是否关心,对应的位都会在
RIS中被置1。你可以通过轮询这个寄存器来实现无中断的软件查询。 - 中断掩码 (IMASK, 0x1028): 这是“关注”寄存器。只有
IMASK中对应位被置1(即解除屏蔽)的中断事件,才能继续向下传递。 - 已屏蔽中断状态 (MIS, 0x1038): 这是“有效”寄存器。
MIS = RIS & IMASK。它反映了那些既发生又被你关心的中断。通常,CPU中断服务程序(ISR)会读取MIS来判断具体是哪个中断源触发了本次ISR调用。 - 中断索引 (IIDX, 0x1020): 这是一个“快速通道”寄存器。硬件会自动将当前已屏蔽中断中优先级最高的那个中断的编号(见其
STAT域定义)放在这里。一个关键特性:读取IIDX寄存器,硬件会自动清除该中断在RIS和MIS中的标志位,以及IIDX本身。这简化了ISR的清理工作。中断优先级是固定的,1最高(奇偶校验错),然后是2(RX溢出)... 具体见手册。 - 中断清除 (ICLR, 0x1048) 与 中断设置 (ISET, 0x1040):
ICLR用于手动清除RIS中的标志位。ISET则允许软件模拟一个中断事件,用于自测试。 - 中断控制 (INTCTL, 0x10E4): 仅有一位
INTEVAL。向该位写1会强制硬件重新评估所有中断源并更新IIDX。在复杂的中断屏蔽动态切换后,可以使用此操作确保IIDX立即反映最新状态。
标准CPU中断处理流程(以接收中断为例):
// 1. 初始化时,使能接收中断(假设RX FIFO半满时触发) HWREG(SPI_BASE + IFLS) = (2 << 4); // RXIFLSEL = 2 (1/2 full) HWREG(SPI_BASE + IMASK) |= (1 << 2); // 使能 IMASK.RX (接收事件) // 2. 在中断服务程序(ISR)中 void SPI_ISR(void) { uint32_t iidx = HWREG(SPI_BASE + IIDX); // 读取并自动清除最高优先级中断 uint32_t mis = HWREG(SPI_BASE + MIS); // 读取所有已发生且使能的中断 if (iidx == 2 || (mis & (1 << 2))) { // 判断是否为接收中断 // 处理接收数据 while (!(HWREG(SPI_BASE + STAT) & (1 << 2))) { // 当RXFE不为1(即FIFO不空) uint16_t data = HWREG(SPI_BASE + RXDATA) & 0xFFFF; // 读取数据 // ... 处理data } } // 检查并处理其他中断源,如错误中断 if (mis & (1 << 0)) { // 奇偶校验错 // 错误处理 HWREG(SPI_BASE + ICLR) = (1 << 0); // 手动清除PER中断标志 } // ... 其他中断处理 }5.2 DMA触发事件机制
DMA触发路径是专为解放CPU设计的。其寄存器组有三套:DMA_TRIG_RX(偏移0x1058起)用于接收触发,DMA_TRIG_TX(偏移0x1088起)用于发送触发,还有一套CPU_INT我们已介绍。DMA_TRIG的寄存器(IMASK,RIS,MIS,ISET)功能与CPU_INT的完全类似,但中断源更少,专用于触发DMA传输。
DMA工作流程:
- 配置DMA通道:设置好源地址(例如SPI的
RXDATA寄存器)、目标地址(内存缓冲区)、传输数据量等。 - 配置SPI的DMA触发:在
DMA_TRIG_RX的IMASK寄存器中,使能RX事件(位2)。在IFLS中设置好RXIFLSEL水平(例如半满)。 - 启动传输:当接收FIFO中的数据达到设定的水平(如半满)时,
RIS.RX置位。由于IMASK.RX已使能,MIS.RX也置位。这个MIS.RX信号会直接连接到DMA控制器,作为DMA传输请求(Trigger)。 - DMA响应:DMA控制器收到请求,自动执行一次或一批数据传输,将
RXDATA中的数据搬移到内存。搬移完成后,可以配置DMA产生完成中断通知CPU。 - 发送DMA同理,使能
DMA_TRIG_TX的TX事件,当发送FIFO空到一定水平(由TXIFLSEL决定)时,触发DMA从内存向TXDATA填充数据。
优势:整个过程无需CPU介入数据搬运,CPU只在DMA传输全部完成后得到一个中断进行后续处理,极大提高了系统效率,尤其适合高速、大批量数据交换。
6. 常见问题排查与调试技巧实录
即使寄存器配置烂熟于心,实际调试中依然会遇到各种问题。下面是我在项目中踩过的一些坑和总结的排查技巧。
6.1 通信无响应或数据全错
这是最常见的问题,排查可以遵循以下路径:
- 检查物理连接与电源:确保SCLK, PICO, POCI, CS四条线连接正确、牢固,没有短路到地或电源。确保从设备已上电且电平匹配。
- 确认时钟极性与相位(SPO/SPH):这是导致数据错位的头号原因。使用示波器或逻辑分析仪,抓取SCLK和PICO/MOSI的波形。对照从设备数据手册的时序图,检查:
- 时钟空闲电平是否正确(
SPO)? - 数据是在SCLK的哪个边沿稳定的?这个稳定的边沿是第一个还是第二个边沿(
SPH)? - 数据变化是否发生在采样边沿的对立面?
- 时钟空闲电平是否正确(
- 检查片选(CS)信号:确认
CSSEL选择的是你硬件连接对应的CS线。用示波器看CS信号是否在每帧数据开始前有效(拉低),结束后无效(拉高)。检查CTL0.CSCLR是否配置合理。 - 验证时钟频率:根据
CLKSEL、CLKDIV、CLKCTL.SCR的设置,重新计算实际的SCLK频率,看是否超出从设备支持的最大速率。 - 启用回环模式(LBM)自检:将
CTL1.LBM置1,MCU自发自收。编写测试代码,发送一个固定模式(如0xAA, 0x55),然后读取RXDATA。如果回环模式下数据都收不对,问题肯定出在MCU的SPI配置或软件驱动上,排除外设问题。
6.2 FIFO操作异常与中断不触发
- 发送数据丢失:写入
TXDATA前未检查TXFF标志。在高速连续发送时,必须等待TXFE或根据TXIFLSEL触发中断/DMA来补充数据,否则会覆盖FIFO导致数据丢失。 - 接收数据重复或旧数据:读取
RXDATA前未检查RXFE标志,读空了还读,就会读到旧值。或者,在通信开始前/协议切换时,没有使用IFLS.TXCLR/RXCLR或复位操作来清空FIFO。 - 中断不触发:
- 检查总中断开关:确认CPU的全局中断已开启,并且SPI模块的中断线已在NVIC(嵌套向量中断控制器)中使能。
- 检查中断屏蔽链:对于CPU中断,确认
IMASK对应位已置1。对于DMA触发,确认DMA_TRIG_XX的IMASK对应位已置1。 - 检查中断标志清除:在ISR中,是否正确地清除了中断标志?如果使用
IIDX,读操作已自动清除。如果使用MIS判断,则需要手动写ICLR清除对应的RIS位。标志未清除是导致中断只触发一次的最常见原因。 - 检查IFLS水平设置:中断触发依赖于FIFO水平。如果你设置
RXIFLSEL为“全满”(5),但每次只接收一个字节,那永远达不到触发条件。
6.3 DMA传输配置陷阱
- 数据宽度不匹配:DMA传输的数据宽度(字节、半字、字)需要与SPI的
TXDATA/RXDATA寄存器访问宽度以及PACKEN设置匹配。如果SPI是8位数据(DSS=7),PACKEN=0,那么每次访问TXDATA是16位低有效。此时DMA应配置为半字(16位)传输,但每次传输的有效数据是8位。你需要确保你的数据缓冲区在内存中是按此格式组织的,或者让DMA配合进行数据打包/解包。 - DMA触发源与使能顺序:正确的顺序是:先配置并使能DMA通道(但将其触发源设为SPI事件,且初始状态可设为暂停或手动触发),然后配置SPI的
DMA_TRIG相关寄存器并启动SPI通信。错误的顺序可能导致第一个触发事件被丢失。 - DMA传输完成中断与SPI空闲:一次DMA传输完成,只代表数据搬运完毕,不代表SPI线上的通信已经结束。SPI可能还在发送FIFO中最后一个数据。在DMA完成中断服务函数中,需要查询
STAT.BUSY位或等待IDLE中���,确保SPI通信完全结束,才能进行下一步操作(如切换片选、改变通信参数等)。
6.4 低功耗模式下的SPI操作
MSPM33的UNICOMM SPI支持在低功耗模式下作为从机唤醒主机,这依赖于异步时钟(ASYNC_*_SEL)的配置。
- 进入低功耗模式前,确保SPI时钟源包含了
ASYNC_LFCLK_SEL。 - 在芯片进入深度睡眠前,通过修改
CLKSEL寄存器,将SPI的时钟源切换到异步低频时钟。 - 配置好从机模式(
CP=0)和接收中断。 - 当主机发起通信时,SPI模块在异步时钟下仍能工作,接收数据并产生中断,从而将MCU从低功耗模式唤醒。
- 唤醒后,在中断服务程序中,需要将SPI时钟源切回高速时钟(如
MFCLK),以保证后续正常通信的速率。这个过程需要仔细处理时钟切换的稳定时间。
最后,寄存器编程虽然直接高效,但在复杂项目中,建议在理解寄存器的基础上,封装成层次清晰的驱动函数,例如SPI_Init(),SPI_Transmit(),SPI_TransmitReceive_DMA()等。这样既能保证对硬件的精准控制,又能提高代码的可读性和可维护性。调试时,善用STAT寄存器的各个状态位,并配合ISET寄存器进行软件触发自测试,可以极大提升问题定位的效率。把UNICOMM SPI的这些寄存器玩明白了,你会发现面对其他MCU的SPI模块,也能很快触类旁通,因为其核心思想和功能划分都是相通的。