MC68HC908GP32 SPI模块深度解析:寄存器配置、低功耗管理与实战避坑指南
1. 项目概述与SPI核心价值
在嵌入式系统开发中,与外设进行高效、可靠的数据交换是基本功。无论是读取传感器数据、配置无线模块,还是驱动显示屏幕,都需要一个稳定且灵活的通信接口。SPI(Serial Peripheral Interface,串行外设接口)协议,以其简单、高速、全双工的特性,成为了众多场景下的首选方案。它不像I2C那样需要复杂的地址寻址和应答机制,也不像UART那样对时钟同步有严格要求,SPI的核心就是“主从同步”,一切由主设备说了算,数据传输干脆利落。
今天,我们就以Freescale(现NXP)经典的8位微控制器MC68HC908GP32为例,深入它的SPI模块内部,把寄存器配置、低功耗模式下的行为以及那些数据手册里一笔带过但实际开发中坑死人的细节,一次性讲透。MC68HC908GP32的SPI模块麻雀虽小,五脏俱全,从基本的收发控制到复杂的错误处理、低功耗管理,都通过几个关键的寄存器来实现。理解并驾驭这些寄存器,你就能让这颗老当益壮的MCU在现代嵌入式项目中继续发挥余热,无论是做个小巧的数据采集器,还是复杂的多机通信网络,都能得心应手。
2. SPI模块架构与寄存器全景解析
MC68HC908GP32的SPI模块是一个相对独立的外设,它通过五根信号线与外界通信,并通过三个核心寄存器与CPU交互。在开始配置之前,我们必须先建立起一个清晰的全局视图,知道每一部分是如何协作的。
2.1 SPI模块的物理接口与信号定义
SPI通信至少需要四根线,MC68HC908GP32则提供了五根,多出来的一根是时钟地(CGND),用于提高时钟信号的完整性。我们先明确每一根线的角色:
- MOSI (Master Out Slave In): 主设备数据输出,从设备数据输入。当SPI被配置为主机时,这根线是输出;配置为从机时,是输入。数据方向由SPI模块硬件自动管理,与对应的并行I/O端口方向寄存器状态无关。
- MISO (Master In Slave Out): 主设备数据输入,从设备数据输出。这是数据流反向的一根线。这里有个关键细节:只有当SPI被配置为从机,且其SS引脚为低电平时,MISO引脚才会被使能输出。如果SS引脚被拉高,MISO会进入高阻态,这在多从机系统中用于避免总线冲突。
- SPSCK (Serial Clock): 串行时钟。由主设备产生,用于同步主从设备之间的数据移位。时钟极性和相位可配置,这是SPI通信模式的核心。
- SS (Slave Select): 从设备选择。这是一个多功能引脚,其行为取决于SPI是主模式还是从模式,以及MODFEN位的配置。
- 在从机模式下:SS是纯粹的输入引脚,用于被主机选中。它的电平变化(特别是CPHA=0时)还定义了数据传输的起始边界。
- 在主机模式下:SS引脚的角色由MODFEN位决定。如果MODFEN=0,该引脚可作为通用I/O使用,SPI模块忽略它。如果MODFEN=1,SS引脚被强制为输入,用于检测“模式故障”(Mode Fault),即防止有另一个主机意外驱动总线。
- CGND: 时钟地。内部连接到VSS,为SPSCK信号提供干净的参考地,在高速或长距离通信时有助于减少噪声。
这五根线中,MISO、MOSI、SPSCK和SS是与并行I/O端口复用的。这意味着在初始化SPI前,你需要通过SPI控制寄存器(SPCR)中的SPE位来“接管”这些引脚的控制权,否则它们就是普通的GPIO。
2.2 核心寄存器地图与功能总览
MC68HC908GP32的SPI模块由三个内存映射寄存器控制,地址分别为$0010、$0011和$0012。它们分工明确:
- SPI控制寄存器 (SPCR - $0010): 这是SPI的“总开关”和“模式选择器”。它负责开启/关闭SPI模块、选择主从模式、配置时钟极性与相位、设置中断使能,以及决定输出引脚是推挽还是开漏模式。
- SPI状态与控制寄存器 (SPSCR - $0011): 这是SPI的“状态监视器”和“精细调谐器”。它包含了数据收发完成、溢出错误、模式故障等状态标志位,同时也提供了错误中断使能、模式故障检测使能以及最重要的——主机模式下的波特率选择位。
- SPI数据寄存器 (SPDR - $0012): 这是SPI的“数据收发站”。它是一个特殊的寄存器,写操作访问的是发送数据缓冲区,读操作访问的是接收数据缓冲区。这里有一个至关重要的陷阱:绝对不能对这个寄存器使用“读-修改-写”指令(如BSET、BCLR),因为你读到的和写到的根本不是同一个物理寄存器。
理解这三个寄存器的每一位是精准控制SPI的基础。接下来,我们将逐一拆解,并附上实际配置代码和避坑指南。
3. 寄存器深度配置与实战指南
仅仅知道寄存器有哪些位是不够的,关键是要理解每一位在具体通信场景下的影响,以及如何组合它们来实现目标功能。下面我们以实战为导向,深入每个寄存器的配置细节。
3.1 SPI控制寄存器 (SPCR) 配置详解
SPCR寄存器是配置的起点。它的位定义如下:
| 位 | 名称 | 功能描述 | 复位值 | 配置要点与实战技巧 |
|---|---|---|---|---|
| 7 | SPRIE | 接收中断使能。1=允许SPRF标志产生CPU中断。 | 0 | 技巧:在查询方式下保持为0;在中断驱动接收时设为1。注意,即使SPI被禁用(SPE=0),已置位的SPRF标志仍可产生中断,方便你在关闭SPI后处理残留数据。 |
| 6 | SPMSTR | 主从模式选择。1=主机模式,0=从机模式。 | 1 | 关键:复位后默认为主机。如果你设计的是从机,上电后第一件事就是把它清0。多主机系统中,结合MODFEN使用可以检测总线冲突。 |
| 5 | CPOL | 时钟极性。0=空闲时SCK为低,1=空闲时SCK为高。 | 0 | 核心:必须与通信对端设备严格一致。通常传感器、存储器数据手册会标明支持的SPI模式(Mode 0,1,2,3),CPOL是模式定义的一部分。 |
| 4 | CPHA | 时钟相位。0=数据在SCK第一个边沿采样,1=数据在SCK第二个边沿采样。 | 1 | 核心:必须与对端一致。CPHA决定了数据采样的时刻。CPHA=0时,从机的SS引脚必须在每个字节传输间被拉高,这在实际布线时需特别注意。 |
| 3 | SPWOM | 线或模式。1=SPSCK, MOSI, MISO为开漏输出,0=推挽输出。 | 0 | 应用:当多个设备需要共享总线(非多从机标准SPI,而是类似总线竞争)时,设置为开漏,并外加上拉电阻。标准点对点或带片选的SPI用推挽即可,驱动能力强。 |
| 2 | SPE | SPI模块使能。1=启用SPI并控制相关引脚,0=禁用SPI,引脚恢复为GPIO。 | 0 | 重要:写0会导致SPI“部分复位”(见下文第4章)。在两次传输之间短暂关闭SPI以省电时,无需重新配置其他控制位。 |
| 1 | SPTIE | 发送中断使能。1=允许SPTE标志产生CPU中断。 | 0 | 技巧:在查询发送时保持为0;在中断驱动或DMA发送时设为1。确保在SPTE=1(发送缓冲区空)时才写入新数据。 |
一个典型的主机初始化代码示例(C语言风格,假设总线时钟BUSCLK为2MHz):
void SPI_Master_Init(void) { // 1. 首先禁用SPI,避免配置过程中产生意外操作 SPCR = 0x00; // 确保SPE=0,其他位也为默认值 // 2. 配置SPCR: 主机模式,模式0 (CPOL=0, CPHA=0),推挽输出,使能模块 // 位: 7(SPRIE)=0, 6(SPMSTR)=1, 5(CPOL)=0, 4(CPHA)=0, 3(SPWOM)=0, 2(SPE)=1, 1(SPTIE)=0 SPCR = 0x44; // 二进制 0100 0100 // 3. 配置SPSCR: 选择波特率,使能模式故障检测 // 假设我们选择波特率为 BUSCLK / 8 = 250kbps // SPR1:SPR0 = 01 (分频因子8), MODFEN=1 (使能模式故障检测) // 位: 7(SPRF)只读, 6(ERRIE)=0, 5(OVRF)只读, 4(MODF)只读, 3(SPTE)只读, 2(MODFEN)=1, 1(SPR1)=0, 0(SPR0)=1 SPSCR = 0x05; // 二进制 0000 0101 }注意:上述代码中,先禁用SPI再进行配置是一个好习惯。另外,SPTIE和SPRIE在初始化时通常先关闭,待系统其他部分(如中断控制器)准备好后再开启。
3.2 SPI状态与控制寄存器 (SPSCR) 与波特率计算
SPSCR寄存器混合了状态标志和控制位,需要仔细处理。
| 位 | 名称 | 功能描述 | 复位值 | 配置要点与实战技巧 |
|---|---|---|---|---|
| 7 | SPRF | 接收满标志。1=接收数据寄存器有数据。 | 0 | 清除方法:先读SPSCR(此时SPRF=1),再读SPDR。陷阱:必须在下一个字节完全移入前读取数据并清除此标志,否则会触发溢出(OVRF)。 |
| 6 | ERRIE | 错误中断使能。1=允许MODF和OVRF标志产生中断。 | 0 | 建议:在可靠性要求高的系统中使能,便于及时处理总线冲突或数据丢失。处理错误中断后,务必按正确序列清除MODF或OVRF标志。 |
| 5 | OVRF | 溢出标志。1=接收数据未及时读取,被新数据覆盖。 | 0 | 清除方法:先读SPSCR(此时OVRF=1),再读SPDR。溢出意味着丢失了一个字节的数据,需要应用层处理。 |
| 4 | MODF | 模式故障标志。主机模式下SS被拉低,或从机模式下SS在传输中被拉高(且MODFEN=1)。 | 0 | 清除方法:先读SPSCR(此时MODF=1),再写SPCR(任何值均可)。模式故障会使SPI自动禁用(SPE清零),清除故障后需重新使能SPI。 |
| 3 | SPTE | 发送空标志。1=发送数据寄存器空,可写入新数据。 | 1 | 黄金法则:写SPDR之前,必须确认SPTE=1。可以在循环中查询,或使用SPTIE中断。 |
| 2 | MODFEN | 模式故障检测使能。1=使能SS引脚的模式故障检测功能。 | 0 | 多主机系统必设:设为1,将SS配置为输入,用于检测总线冲突。单主机系统可选:设为0,则SS引脚可作为通用GPIO使用。 |
| 1-0 | SPR1, SPR0 | 波特率选择位(仅主机模式有效)。 | 00 | 计算公式:波特率 = BUSCLK / (2 × BD)。BD为分频因子,由SPR1:SPR0决定:00->BD=2, 01->BD=8, 10->BD=32, 11->BD=128。 |
波特率计算实例: 假设MCU总线时钟BUSCLK = 2MHz。
- 若
SPR1:SPR0 = 00,则 BD=2,波特率 = 2MHz / (2*2) = 500 kbps。 - 若
SPR1:SPR0 = 01,则 BD=8,波特率 = 2MHz / (2*8) = 125 kbps。 - 若
SPR1:SPR0 = 10,则 BD=32,波特率 = 2MHz / (2*32) = 31.25 kbps。 - 若
SPR1:SPR0 = 11,则 BD=128,波特率 = 2MHz / (2*128) = 7.8125 kbps。
选择依据:根据从设备支持的最高速率和总线长度选择。长线或高噪声环境应降低波特率。同时,也要考虑CPU处理数据的能力,过高的速率可能导致来不及处理接收中断。
3.3 SPI数据寄存器 (SPDR) 与数据传输流程
SPDR的访问看似简单,但隐藏着并发操作的陷阱。它是一个地址对应两个物理寄存器:发送寄存器(只写)和接收寄存器(只读)。
基本发送流程(查询方式):
- 等待
SPTE标志变为1(发送缓冲区空)。 - 将待发送数据写入
SPDR地址。写入操作会清零SPTE,并启动数据传输。 - 数据开始通过MOSI线移出,同时MISO线上的数据被移入接收移位寄存器。
- 一个字节传输完成后,接收移位寄存器的内容被自动载入接收数据寄存器,
SPRF标志置1,SPTE标志也重新置1(表示可以发送下一个字节)。 - 读取
SPDR获取接收到的数据,此操作会清除SPRF标志。
一个简单的阻塞式字节交换函数:
unsigned char SPI_ExchangeByte(unsigned char txData) { while(!(SPSCR & 0x08)) { ; // 等待 SPTE 位(第3位)为1,发送缓冲区空 } SPDR = txData; // 写入数据,启动传输 while(!(SPSCR & 0x80)) { ; // 等待 SPRF 位(第7位)为1,接收完成 } return SPDR; // 读取接收到的数据,同时清除SPRF }严重警告:如前所述,绝对禁止对SPDR使用像
BSET、BCLR、INC这类读-修改-写指令。例如,BSET 0, SPDR试图将SPDR的bit0置1,但CPU会先读取SPDR(得到的是接收寄存器的值),修改这个值,再写回SPDR(写到发送寄存器)。这会导致不可预知的数据被发送出去,并且破坏了接收到的数据。任何对SPDR的操作都必须是直接的写(SPDR = value;)或直接的读(value = SPDR;)。
4. SPI复位机制与状态机管理
理解SPI的复位行为对于编写健壮的初始化和错误恢复代码至关重要。MC68HC908GP32的SPI模块有两种复位:系统复位和部分复位。
4.1 系统复位与部分复位的区别
- 系统复位:指上电复位或看门狗复位等。这会重置SPI模块的一切,所有寄存器(SPCR, SPSCR)恢复为默认值,所有状态标志(SPRF, OVRF, MODF)被清除,SPI被禁用,相关引脚变为GPIO。
- 部分复位:当软件将SPE位(SPCR.2)写0时触发。这不会改变SPCR和SPSCR中的控制位(如SPMSTR, CPOL, CPHA, MODFEN, SPR1/0等),但会立即执行以下操作:
- 置位
SPTE标志(发送缓冲区变为“空”状态)。 - 中止任何正在进行的传输。
- 清空发送/接收移位寄存器。
- 复位SPI内部状态计数器,准备下一次传输。
- SPI逻辑停止控制引脚,引脚功能交还给GPIO。
- 置位
部分复位的设计意图:允许你在两次通信间隔中关闭SPI模块以节省功耗,而当下次需要通信时,只需将SPE重新置1,无需重新配置所有通信参数(如主从模式、时钟极性、波特率等)。这是一个非常实用的低功耗优化点。
4.2 关键状态标志的清除序列
SPRF、OVRF、MODF这些标志的清除需要特定的操作序列,而不是简单地写0。这是许多新手容易出错的地方。
清除 SPRF (接收满) 和 OVRF (溢出):
- 正确操作:先读取
SPSCR寄存器(此时标志位为1),紧接着读取SPDR寄存器。 - 硬件逻辑:读取SPSCR锁定了当前状态,随后读取SPDR完成了数据“消费”的动作,硬件据此清除标志。顺序不能颠倒。
- 正确操作:先读取
清除 MODF (模式故障):
- 正确操作:先读取
SPSCR寄存器(此时MODF=1),紧接着写入SPCR寄存器(写入任何值均可,通常写入当前值以保持其他配置不变)。 - 后续动作:发生MODF后,SPI模块会自动将SPE位清零(禁用)。因此,清除MODF标志后,必须重新将SPE位置1,才能恢复SPI功能。
- 正确操作:先读取
错误的清除方式示例:
// 错误!直接对标志位写0是无效的。 SPSCR &= ~(1<<7); // 试图清除SPRF,无效操作。正确的做法应封装成函数:
void SPI_ClearSPRF(void) { volatile unsigned char dummy; dummy = SPSCR; // 读SPSCR,锁定状态 dummy = SPDR; // 读SPDR,完成清除序列 (void)dummy; // 防止编译器警告 } void SPI_ClearMODF(void) { volatile unsigned char dummy; dummy = SPSCR; // 读SPSCR SPCR = SPCR; // 写SPCR(写入自身当前值),清除MODF (void)dummy; }5. 低功耗模式下的SPI行为与配置策略
在电池供电的设备中,低功耗设计是生命线。MC68HC908GP32提供了WAIT和STOP两种低功耗模式,SPI模块在这两种模式下的行为不同,需要针对性处理。
5.1 WAIT模式下的SPI操作
执行WAIT指令后,CPU进入休眠状态,但外设时钟(包括SPI的时钟)通常仍在运行(取决于具体电源模式配置)。
- SPI状态:SPI模块保持活动状态。这意味着SPI的移位时钟、数据传输逻辑仍在工作。
- 寄存器访问:CPU无法访问SPI寄存器(因为CPU停了)。
- 中断唤醒:这是关键!如果SPI的中断(SPRF接收中断、SPTE发送中断、或ERRIE使能下的错误中断)被使能,那么当这些中断事件发生时,可以唤醒CPU,使其退出WAIT模式。
- 功耗优化:如果你的应用在WAIT模式下完全不需要SPI功能(例如,设备处于深度睡眠,不监听任何外部SPI事件),那么在进入WAIT模式前,应该将SPE位清零,彻底关闭SPI模块,以消除其静态和动态功耗。唤醒后再重新使能。
WAIT模式下的配置流程建议:
void Enter_WaitMode(void) { // 场景1:需要SPI中断唤醒(如SPI从机等待主机命令) if (spi_wakeup_enabled) { // 确保SPI已正确配置,且所需中断(SPRIE/SPTIE/ERRIE)已使能 asm("WAIT"); // 执行WAIT指令 } // 场景2:不需要SPI功能 else { SPCR &= ~(1<<2); // 清除SPE位,关闭SPI以省电 asm("WAIT"); // 退出WAIT模式后... SPCR |= (1<<2); // 重新使能SPI } }5.2 STOP模式下的SPI操作
执行STOP指令后,MCU进入最深的低功耗模式,核心时钟和大多数外设时钟都会停止。
- SPI状态:SPI模块完全停止,不活动。没有时钟,没有数据传输。
- 寄存器状态:寄存器的值被保持(冻结)。
- 唤醒与恢复:只能通过外部中断或复位来退出STOP模式。如果通过外部中断唤醒,SPI模块会从停止的地方恢复运行。如果通过复位唤醒,则任何正在进行的传输都会被中止,且SPI模块经历系统复位。
- 特殊应用:数据手册提到,Timebase模块(TBM)如果配置了外部32.768kHz晶振并在STOP模式下保持运行,可以产生周期性中断来唤醒MCU。但SPI模块本身不具备在STOP模式下工作的能力。
STOP模式下的重要注意事项: 在进入STOP模式前,必须考虑SPI通信的上下文。如果主机MCU进入STOP,而从设备还在发送数据,会导致通信失败。通常的做法是:
- 确保所有SPI传输队列已完成。
- 如果是从机,应通知主机“即将休眠”,或依赖超时机制。
- 清除SPE位不是必须的,因为时钟已停,但关闭它可以减少一点漏电流。
- 唤醒后,需要检查SPI状态。如果是通过复位唤醒,必须重新初始化SPI。如果是外部中断唤醒,则需评估是否需要重新同步通信(例如,从机可能需要等待主机重新发起传输)。
6. 高级主题:模式故障、Break中断与多机通信考量
除了基本的数据收发,SPI模块还提供了一些高级功能,用于构建更可靠、更复杂的系统。
6.1 模式故障(MODF)检测与处理
模式故障是多主机SPI系统中防止总线冲突的机制。其触发条件如下表所示:
| SPI模式 | MODFEN位 | SS引脚状态 | 结果 |
|---|---|---|---|
| 主机 | 1 | 被外部拉低 | MODF标志置1,SPE自动清零,SPI禁用。 |
| 从机 | 1 | 在传输中被拉高 | MODF标志置1。 |
| 主机 | 0 | 任何状态 | 无影响,SS可作为GPIO。 |
| 从机 | 0 | 在传输中被拉高 | MODF标志不置位,但传输可能被破坏。 |
处理MODF的流程:
- 在中断服务程序或主循环中检测到MODF标志。
- 按照“先读SPSCR,再写SPCR”的序列清除MODF标志。
- 重要:检查SPE位,由于MODF会自动清除SPE,此时SPI已禁用。需要根据应用逻辑决定下一步:是重新竞争总线后再次使能SPI(SPE=1),还是切换为从机模式?
- 记录错误日志,必要时通知上层应用。
6.2 Break中断期间的SPI行为
MC68HC908GP32支持背景调试模式(BDM),其中断称为Break中断。在Break状态下,CPU暂停,但调试器可以访问内存和寄存器。
- SBFCR寄存器中的BCFE位:此位控制Break状态下状态位(如SPRF, OVRF, MODF, SPTE)是否可被清除。
BCFE = 1: 允许在Break状态下清除状态位。这对于调试时单步执行代码、观察状态变化很有用。BCFE = 0(默认): 保护状态位。在Break状态下对寄存器的读写不会影响这些状态位。特别注意:此时对SPDR的写操作是无效的,不会启动传输,也不会将数据载入移位寄存器。
- 对调试的影响:如果使用调试器单步调试SPI通信代码,且BCFE=0,你可能会发现写入SPDR后SPTE位不变化,传输不启动。这是正常现象,退出Break状态后才会恢复正常。调试SPI驱动时,需要注意这一点,或者临时设置BCFE=1。
6.3 构建多从机SPI系统
MC68HC908GP32作为主机时,构建多从机系统需要硬件和软件配合。
硬件连接:标准的SPI总线是MOSI、MISO、SPSCK三线共享,每个从机独占一根SS片选线。主机通过拉低对应从机的SS线来选中它。
软件操作:
- 将不用的从机的SS线置为高电平(通过GPIO控制),使其MISO输出高阻态。
- 拉低目标从机的SS线。
- 对于CPHA=0的模式:必须在每个字节传输开始前,先拉低SS,在字节传输间隔(或结束后)拉高SS。这意味着传输一串连续数据时,SS需要不断翻转,或者每个字节间插入空闲时间。这是CPHA=0模式的一个易忽略点。
- 对于CPHA=1的模式:SS可以在整个通信期间保持低电平。
- 通信完成后,将SS拉回高电平。
GP32作为从机的注意事项:当GP32作为从机时,其MISO输出仅在
SPMSTR=0且SS=0时有效。如果主机在通信间隙将SS拉高,从机的MISO会立即变为高阻态。这符合多从机总线规范,但意味着主机必须严格控制SS的时序。
7. 常见问题排查与调试心得
在实际项目中,SPI通信不出问题几乎是不可能的。下面是我在多年使用MC68HC908GP32的SPI功能中,总结的一些最常见的问题和排查思路。
7.1 通信完全无反应
- 检查清单:
- 电源与时钟:MCU和从设备是否都已上电?MCU的总线时钟(BUSCLK)是否正常?SPI波特率是否设置得过高?
- SPE位:是否已置1?这是最常被忘记的一步。用调试器查看SPCR寄存器的bit2。
- 引脚配置:SPI引脚是否与GPIO功能冲突?确保在使能SPE前,对应的端口方向寄存器(DDR)配置正确(对于SPI控制的方向,通常由SPI模块自动管理,但初始状态需留意)。实测技巧:可以用万用表或示波器测量MOSI和SPSCK引脚,在SPE使能后,即使不发送数据,这些引脚也可能会有固定的电平输出(取决于CPOL),与作为GPIO时不同。
- 主从模式:SPMSTR位配置是否正确?主机和从机不能配反。
- 硬件连接:MOSI是否接对了MOSI?MISO接MISO?线是否断了?用示波器同时看主机的MOSI和SPSCK,是最直接的诊断方法。
7.2 能发送,但接收数据全为0或全为0xFF
- 排查方向:
- MISO线路:从设备的MISO引脚是否有输出?从设备是否被正确选中(SS为低)?从设备本身是否需要特定命令才会输出数据?很多SPI存储器或传感器,需要先发送一个读命令,才会在后续时钟周期输出数据。
- CPOL与CPHA:这是SPI通信的头号杀手。99%的通信异常都与这两位的配置不匹配有关。务必仔细核对主机和从设备数据手册中关于SPI模式的描述(Mode 0, 1, 2, 3)。一个快速验证的方法是:用示波器捕获SPSCK和MOSI的波形,对照从设备时序图,看数据采样边沿是否对齐。
- SS引脚时序(CPHA=0时):如果模式是CPHA=0,检查主机是否在每个字节传输前正确拉低了SS,并在字节间正确翻转了SS?示波器看SS信号。
- 软件读取时机:是否在SPRF标志置位后才去读取SPDR?读取SPDR的操作是否意外放在了启动发送之前?
7.3 数据传输出现错位或随机错误
- 可能原因及解决:
- 波特率不匹配或过高:降低波特率试试。过高的波特率在长导线或面包板上容易受到干扰。
- 中断干扰:SPI通信过程中被高优先级中断长时间打断,导致没有及时读取SPRF造成溢出(OVRF),或者没有及时写入数据导致发送断流。检查中断服务程序的执行时间,或在关键SPI通信段暂时关闭全局中断。
- 电源噪声:在MCU和从设备的电源引脚附近增加去耦电容(如100nF)。
- 未使用引脚处理:不用的SPI从机,其SS引脚最好通过上拉电阻接到高电平,防止浮空引入噪声。
7.4 进入低功耗模式后无法唤醒或通信异常
- 诊断步骤:
- 确认唤醒源:在WAIT模式下,期望用SPI中断唤醒,那么SPRIE或SPTIE是否已使能?对应的中断向量表是否正确配置?
- 检查SPE位:在进入WAIT/STOP前,如果为了省电关闭了SPE(SPE=0),那么SPI模块根本不工作,自然无法产生中断。需要在唤醒后的代码中重新使能SPE。
- STOP模式恢复:如果从STOP模式被外部中断唤醒,SPI模块会从停止的状态恢复。但若总线上的主机在MCU休眠期间开始了传输,从机可能会丢失起始部分的数据。设计协议时应考虑加入同步字节或唤醒后重新初始化的机制。
调试SPI,示波器或逻辑分析仪是必不可少的工具。不要只依赖软件打印调试信息,亲眼看到时钟和数据线上的波形,能解决绝大部分疑难杂症。把SPSCK、MOSI、MISO、SS四路信号同时抓下来,对照数据手册的时序图一点点分析,没有解决不了的问题。
