1. 项目概述
如果你在嵌入式系统开发中用过传统的SPI(Serial Peripheral Interface),大概率会对它“一问一答”的阻塞式通信模式又爱又恨。爱的是协议简单,接线方便;恨的是每次传输都需要CPU深度介入,从配置寄存器到搬运数据,一个都跑不了,严重拖累了系统效率。尤其是在需要连续、批量与多个SPI设备(比如多个传感器、多片闪存)打交道的场景里,CPU的时间几乎都耗在了等待和搬运上。今天要聊的MC68377微控制器里的QSPI(Queued Serial Peripheral Interface)模块,就是飞思卡尔(Freescale,现恩智浦NXP)为彻底解决这个问题而设计的“神器”。它通过在SPI控制器内部集成一个80字节的双端口静态RAM(QSPI RAM)和一套精巧的命令队列机制,让SPI通信实现了从“手动挡”到“自动挡”的飞跃。
简单来说,QSPI允许CPU提前把一系列SPI传输命令(包括选哪个芯片、传多少位、时钟怎么配、数据是啥)像写剧本一样,预先编排好并存入那块共享的RAM里。然后CPU只需一声令下启动QSPI,自己就可以去处理其他任务了。QSPI模块会像一个尽职的“执行导演”,自动、连续地按剧本(命令队列)执行所有SPI传输,收发数据也直接和RAM交互,全部完成后才通过中断通知CPU“戏拍完了,来验收吧”。这种设计将CPU从繁琐的、重复的SPI通信事务中解放出来,特别适合汽车电子、工业控制等对实时性和效率要求苛刻的领域。接下来,我们就深入这块80字节的RAM和它的两种操作模式,看看这套自动化流水线是如何高效运转的。
2. QSPI RAM:共享内存区的精妙设计
QSPI的核心创新,就在于这块80字节的“双访问静态RAM”(Dual-Access Static RAM)。理解它的结构和访问特性,是掌握QSPI编程的关键。
2.1 双端口访问与访问一致性
这块RAM之所以称为“双访问”,是因为它同时被两个“主人”共享:CPU和QSPI模块本身。CPU通过系统总线读写它,以配置命令和存取数据;QSPI模块则在其内部逻辑控制下,从中读取命令和待发送数据,并将接收到的数据写回。
这种共享带来了一个核心问题:访问冲突与仲裁。当两个“主人”同时想访问RAM时,谁先谁后?MC68377的硬件设计确保了QSPI模块拥有更高的优先级。这意味着,当QSPI正在访问RAM(例如,正在读取下一条命令或写入接收数据)时,如果CPU也试图访问,CPU的访问会被插入等待状态(Wait States)。手册明确指出,根据访问类型,可能会插入1到4个等待状态。这相当于告诉CPU:“稍等一下,串口模块正忙着呢。”
这里引出一个非常重要的概念:访问一致性(Coherent Access)。手册特别强调,只有CPU进行的字(Word,16位)访问才是“一致性访问”。所谓一致性访问,是指该访问是一个“不可分割”的操作。当CPU发起一次字访问时,硬件会保证在这次访问完成前,QSPI模块无法介入,从而确保CPU读取或写入的是一个完整的、不被中间过程打断的数据。
注意:长字与非对齐字访问的风险与之相对,CPU的长字(Long-Word,32位)访问或非对齐的字访问则不是一致性的。因为MC68377的16位数据总线处理32位访问时,实际上会拆分成两次16位的传输。同样,非对齐的字访问也可能需要多次总线操作。在这拆分操作的间隙,QSPI模块有可能获得RAM的访问权并进行修改。这就可能导致CPU读到的数据前半部分是旧值,后半部分是被QSPI更新后的新值,造成数据错乱。因此,在编程时,强烈建议对QSPI RAM的访问一律使用对齐的字(16位)操作,以确保数据的完整性。
2.2 RAM的三段式结构
这80字节的RAM被清晰地划分为三个功能段,其组织结构如下图所示(根据手册图7-5):
QSPI RAM 组织结构 地址偏移 (Hex) | 段名称 | 大小 | 访问单位 | 用途 --------------|-------------|-------|----------|------ 0x00 - 0x1F | 接收数据 RAM | 32字节 | 字 (Word) | 存储从外部设备接收到的数据 0x20 - 0x3F | 发送数据 RAM | 32字节 | 字 (Word) | 存储CPU准备的待发送数据 0x40 - 0x4F | 命令控制 RAM | 16字节 | 字节 (Byte) | 存储每条SPI传输命令的控制信息表:QSPI RAM 内存布局
2.2.1 接收数据RAM(Receive Data RAM)这个段用于存放QSPI从外部SPI设备(从设备或其他主设备)接收到的数据。QSPI模块在完成一次传输后,会自动将移位寄存器中收到的数据,存入当前队列指针所指向的接收RAM地址中。数据在RAM中是右对齐存放的,即无论本次传输定义了8位还是16位,数据的LSB(最低有效位)总是放在存储字的bit 0位置,未使用的高位会被硬件自动清零。
CPU通过读取这个段来获取数据。关键在于,CPU需要知道哪些位置的数据是新鲜有效的。这就要借助状态寄存器SPSR中的CPTQP(Completed Queue Pointer)字段。CPTQP指向最后一条已完成的命令在队列中的位置。假设队列从0开始执行,当CPTQP的值为5时,就意味着队列条目0、1、2、3、4、5对应的接收数据RAM位置(RR0到RR5)都已经存放了本次执行周期接收到的有效数据。CPU可以安全地读取这些位置。而CPTQP+1之后的位置,要么是旧数据,要么数据还未就绪。
2.2.2 发送数据RAM(Transmit Data RAM)这个段由CPU写入,用于存放将要通过QSPI发送出去的数据。同样需要右对齐存放。对于纯输入的外设(如某些ADC),此段可以不初始化或写入任意值。QSPI模块在执行命令时,会从当前指针指向的发送RAM位置读取数据,加载到移位寄存器中发送出去。发送RAM中的数据一旦被写入,除非CPU再次改写,否则会一直保持不变,可供QSPI在循环模式下重复使用。
2.2.3 命令控制RAM(Command Control RAM)这是QSPI的“大脑”,决定了每一次SPI传输的具体行为。每个队列条目(对应一次SPI传输)占用这里的一个字节。这个字节又被分为两个字段:
- 高4位(Bit7-4):命令控制字段。定义了本次传输的特定参数,如是否连续、传输位数、是否使能传输后延时等。
- 低4位(Bit3-0):外设片选字段。用于在本次传输中激活哪一根(或哪几根)PCS(Peripheral Chip Select)线,以选中目标外设。
命令RAM仅当QSPI工作在主模式时被使用。在从模式下,传输由外部主设备发起,QSPI只是被动响应,因此无需(也无法)使用内部的命令来控制传输参数。
2.3 命令控制字节详解
命令控制RAM中的每个字节都精细地控制着一次传输的方方面面。理解每一位的含义至关重要。
| 位 | 名称 | 描述与功能解析 |
|---|---|---|
| 7 | CONT (Continue) | 连续模式位。这是处理多段传输的关键。 •0:本次传输结束后,PCS线将恢复为PORTQS寄存器中设定的默认状态(通常为高电平,即取消选中)。 •1:本次传输结束后,如果下一条命令的片选模式(PCS[3:0])与当前命令相同,则PCS线将保持有效(低电平)状态,不释放。这允许连续向同一个外设发送多条命令而无需反复选通,对于需要连续写入寄存器或存储器的设备非常有用。若下一条命令片选模式不同,则PCS线会切换为新模式。 |
| 6 | BITSE (Bits per Transfer Enable) | 传输位数使能位。 •0:固定传输8位数据。 •1:传输位数由SPCR0寄存器中的BITS字段定义,范围是8-16位。这提供了传输长度的灵活性。 |
| 5 | DT (Delay after Transfer) | 传输后延时使能位。用于与某些慢速外设(如串行ADC)接口,给其留出转换或处理时间。 •0:使用标准延时(系统时钟16.78MHz下约为1µs)。 •1:启用由SPCR1寄存器中DTL字段定义的可编程延时,最长可达约489µs(@16.78MHz)。 |
| 4 | DSCK (PCS to SCK Delay) | 片选到时钟延时使能位。用于满足外设对片选有效到第一个时钟沿到来的建立时间要求。 •0:PCS有效到第一个SCK边沿的延时为1/2个SCK周期。 •1:延时长度由SPCR1寄存器中的DSCKL字段定义。 |
| 3:0 | PCS[3:0] | 外设片选字段。这4位直接对应到MCU的PCS3~PCS0/SS引脚。可以同时设置多位为1,以在一次传输中选中多个外设(广播模式)。硬件上,这些位为0时,对应引脚输出高电平(假设低电平有效);为1时输出低电平。 |
表:命令控制字节位定义详解
实操心得:CONT位的巧妙运用在实际项目中,CONT位能极大优化通信效率。例如,你需要向一片SPI Flash的连续地址写入数据。如果不使用CONT,流程将是:拉低片选 -> 发送写使能命令 -> 拉高片选 -> 拉低片选 -> 发送页编程命令 -> 发送地址 -> 发送数据 -> 拉高片选。片选频繁翻转,产生大量无用时间。 使用CONT位,你可以将“写使能命令”、“页编程命令”、“地址字节”、“数据字节”这四条传输命令的CONT位都设为1(最后一条除外),并将它们的PCS字段设为相同的值。这样,QSPI会在一次片选有效期内,连续完成这四次传输,中间片选不会释放,速度显著提升。需要注意:必须确保你的外设支持在片选持续有效期间接收连续命令流。
3. QSPI操作模式与工作流程
QSPI支持两种基本操作模式:主模式(Master)和从模式(Slave)。模式的选择通过设置SPCR0寄存器中的MSTR位来实现。两种模式共享同一块RAM,但使用方式和工作流程有显著差异。
3.1 核心指针:队列执行的指挥棒
在深入模式之前,必须理解控制队列执行的三个核心指针,它们全部位于QSPI的寄存器中:
- NEWQP (New Queue Pointer):位于SPCR2寄存器。指向即将被QSPI执行的第一条命令在队列中的地址。CPU通过修改它来改变QSPI的起始执行点。复位后为0。
- CPTQP (Completed Queue Pointer):位于SPSR状态寄存器。这是一个只读(由QSPI硬件更新)状态字段,指向最后一条已完成的命令的地址。CPU通过读取它来知晓传输进度和确定哪些接收数据是有效的。
- ENDQP (End Queue Pointer):位于SPCR2寄存器。指向队列中最后一条需要执行的命令的地址。当QSPI执行完该地址对应的命令后,会认为队列结束(除非使能了循环模式)。
队列的执行范围就是从NEWQP到ENDQP(包含两端)。例如,设置NEWQP=2, ENDQP=5,则QSPI将执行队列中第2、3、4、5条命令。
3.2 主模式(Master Mode)详解
在主模式下,QSPI作为SPI总线的主设备,主动发起通信,控制时钟SCK,并驱动片选线PCS[3:0]以选择从设备。
3.2.1 初始化配置流程在主模式下启用QSPI,需要一套标准的初始化流程,手册中的流程图(图7-6)清晰地展示了这一点:
- 初始化QSM全局寄存器:这包括配置整个队列串行模块(QSM)的工作模式、中断等基础设置。
- 初始化QSPI控制寄存器:设置SPCR0(波特率、时钟相位极性CPHA/CPOL、位数BITS等)、SPCR1(延时参数DTL, DSCKL)、SPCR2(NEWQP, ENDQP, 循环/中断使能WREN, SPIFIE等)、SPCR3(HMIE等)。
- 初始化QSM引脚寄存器:
- PQSPAR:将需要用到的引脚(MOSI, MISO, SCK, PCSx)功能分配给QSPI模块。
- DDRQS:将SCK、MOSI和用到的PCSx引脚方向设置为输出,MISO设置为输入。
- PORTQS:设置PCSx引脚在非传输期间的默认输出电平(通常为高,即无效)。
- 初始化QSPI RAM:
- 根据需求,向命令控制RAM(0x40~0x4F)的相应位置写入命令字节。
- 向发送数据RAM(0x20~0x3F)的相应位置写入待发送的数据。
- 接收数据RAM通常无需初始化,但良好的习惯是将其清零,便于调试。
- 启用QSPI:最后,通过设置SPCR1寄存器中的SPE (Serial Peripheral Enable)位为1,启动QSPI。一旦SPE置位,QSPI立即开始从NEWQP指向的地址执行命令队列。
3.2.2 主模式单次执行流程手册的流程图(图7-7, 7-8, 7-9)详细描绘了主模式下QSPI的自动执行过程,我们可以将其提炼为以下步骤:
- 取指与取数:QSPI根据内部工作队列指针(初始等于NEWQP),从命令RAM和发送数据RAM的对应地址读取控制命令和待发送数据。
- 配置与启动:根据命令字节配置本次传输参数(如片选、位数、延时等),然后拉低对应的PCS线(激活外设)。
- 执行传输:根据DSCK位插入可选延时后,启动内部波特率发生器产生SCK时钟,同时在MOSI上移出数据,在MISO上采样数据。传输位数由BITSE和BITS决定。
- 后处理:
- 将接收到的数据存入接收数据RAM的对应位置。
- 将内部工作队列指针的值更新到CPTQP状态位。
- 根据CONT位决定是保持PCS有效还是恢复PORTQS状态。
- 根据DT位决定是否插入可编程延时。
- 指针递增与循环判断:内部工作队列指针加1,指向下一个队列条目。然后判断:
- 如果HALT或FREEZE信号有效,则暂停QSPI,置位HALTA标志,并可产生中断。
- 否则,判断当前指针是否超过了ENDQP(即完成了最后一条命令)。
- 如果未超过,则跳回第1步,执行下一条命令。
- 如果已超过(队列完成),则置位SPIF (SPI Finished)标志。如果SPIFIE中断使能,则向CPU发出中断请求。
- 队列结束处理:此时,如果WREN (Wrap Enable)位为0(非循环模式),则QSPI会自动清除SPE位,停止工作。如果WREN为1(循环模式),则内部工作队列指针会被重置为NEWQP(或0),然后跳回第1步,开始新一轮的循环执行。
3.2.3 循环模式(Wraparound Mode)循环模式是QSPI提升效率的利器。在此模式下,当QSPI执行完ENDQP指向的命令后,不会停止,而是将指针重置并重新开始执行队列。每次循环到ENDQP时,SPIF标志都会被置位,可用于触发周期性中断,通知CPU进行数据搬运或更新发送缓冲区。
重要提示:SPIF标志的清除在循环模式下,即使QSPI不停运行,SPIF标志在每次循环结束时仍会被置位。如果SPIFIE使能,将导致持续的中断。清除SPIF标志有固定步骤:CPU必须先读取SPSR寄存器(此时SPIF=1),然后再向SPSR写入一个SPIF位为0的值。这个“读-写”操作是清除SPIF和MODF等标志的标准方法,缺一不可。简单地只写0是无效的。
3.3 从模式(Slave Mode)详解
在从模式下,QSPI作为SPI总线的从设备,其工作完全由外部主设备驱动。它不产生SCK时钟,也不主动控制任何PCS线(PCS0/SS此时作为从机选择输入SS)。
3.3.1 从模式配置要点
- 引脚配置:设置MSTR=0。在PQSPAR中分配MISO、MOSI、SCK和SS引脚给QSPI。在DDRQS中,将MISO设为输出,MOSI、SCK、SS设为输入。
- RAM使用:命令控制RAM在从模式下完全无效,可以被CPU当作普通内存使用。只需要关注发送和接收数据RAM。
- 传输控制:传输的位数由SPCR0中的BITS字段统一设定,所有传输都遵循此长度。命令字节中的BITSE、CONT、DT、DSCK、PCS等位均不起作用。
3.3.2 从模式工作流程从模式的工作流程(图7-10, 7-11)相对简单:
- 等待选择:QSPI使能(SPE=1)且处于从模式时,持续监测SS引脚。
- 响应传输:当外部主设备拉低SS引脚选中本机时,QSPI开始一次传输。它从当前NEWQP指向的发送数据RAM中读取数据,通过MISO引脚移出;同时从MOSI引脚采样数据,存入当前NEWQP指向的接收数据RAM。
- 指针更新与连续传输:一次传输(BITS定义的长度)完成后,QSPI更新CPTQP,并将内部工作指针加1。
- 如果SS引脚始终保持低电平,QSPI会继续使用下一个队列条目进行传输,直到达到队列末尾(ENDQP)。这允许从设备接收超过16位(一个队列条目容量)的长数据流。
- 如果SS引脚变高,本次通信结束。下次SS再次变低时,QSPI将从NEWQP(或当前指针,取决于配置)重新开始。
- 队列结束与中断:当内部指针达到ENDQP且SS仍为低时,行为取决于WREN位:
- WREN=0:停止,置位SPIF。
- WREN=1:指针绕回(至NEWQP或0),继续传输,同时置位SPIF。
3.3.3 从模式下的特殊考量:长数据流接收从模式有一个强大功能:在SS持续有效期间,可以接收远超单个队列条目容量的数据流。QSPI会自动在收满一个条目(BITS定义的位数)后,将指针递增,将后续数据存到下一个接收RAM位置。这要求CPU必须及时读取数据,避免被覆盖。
避坑指南:从模式下的时序要求手册特别强调,在从模式下连续接收时,QSPI需要大约1µs(在16.78MHz系统时钟下)的时间来预取(Prefetch)下一个发送RAM条目,为下一次传输做准备。因此,外部主设备产生的SCK时钟间隔(即两次传输之间的空闲时间)必须大于这个预取时间。如果SCK连续不断,可能导致QSPI来不及准备下一个数据,造成发送错误。设计时需要根据系统时钟频率计算并确保满足此时序要求。
4. 关键寄存器与标志位实战解析
理解了架构和流程,我们还需要熟悉与之交互的“控制面板”——寄存器。这里重点解析几个最核心的寄存器和标志位。
4.1 状态寄存器(SPSR)与关键标志位
SPSR是CPU了解QSPI状态的主要窗口。除了之前提到的CPTQP,还有几个至关重要的标志位:
| 位 | 名称 | 功能与操作解析 |
|---|---|---|
| 7 | SPIF (SPI Finished) | 队列完成标志。当QSPI执行完ENDQP指向的命令时置1。在循环模式下,每完成一轮队列置1一次。清除方法:先读SPSR,再写SPSR(SPIF位写0)。 |
| 6 | MODF (Mode Fault) | 模式错误标志。仅主模式有效。当QSPI作为主设备(MSTR=1)时,如果其SS引脚(PCS0/SS)被外部拉低,则置1。这通常表示总线冲突(另一个主设备试图接管总线)或硬件错误。发生此错误时,QSPI会自动清除SPE位以禁用自身,防止总线竞争。清除方法:同SPIF(先读后写)。问题解决后,需重新设置SPE=1来启用QSPI。 |
| 5 | HALTA (Halt Acknowledge) | 暂停应答标志。当CPU通过设置HALT位请求QSPI暂停,且QSPI已完成当前传输并安全暂停后,此位置1。清除方法:同SPIF(先读后写)。 |
实操心得:利用CPTQP实现“乒乓”缓冲在循环模式下,为了高效处理连续数据流,可以设计“乒乓缓冲”策略。将接收RAM分成两部分(例如,条目0-7为Buffer A,条目8-15为Buffer B)。设置ENDQP=15,使能循环。 CPU可以定期检查CPTQP的值。当发现CPTQP从7变为8时,说明Buffer A已满,QSPI开始向Buffer B写入。此时CPU可以安全地读取和处理Buffer A(RR0-RR7)中的数据。同理,当CPTQP从15绕回0时,Buffer B已满,CPU转而处理Buffer B。这样,数据生产和消费并行不悖,实现了高效的无锁缓冲。
4.2 控制寄存器关键位配置
SPCR0:配置通信基础参数。
- MSTR:主/从模式选择。
- CPOL, CPHA:时钟极性与相位,必须与外设匹配。
- BITS:当命令字节中BITSE=1时,定义传输位数(8-16)。
- SPBR:波特率分频器,决定SCK频率。
SPCR1:配置使能和延时。
- SPE:QSPI总使能位,相当于开关。
- DTL:传输后延时长度(当命令字节DT=1时生效)。
- DSCKL:PCS有效到SCK开始的延时长度(当命令字节DSCK=1时生效)。
SPCR2:队列与中断控制。
- NEWQP, ENDQP:队列起始与结束指针。
- WREN:循环模式使能。
- WRTO:循环回绕目标地址(0或NEWQP)。
- SPIFIE:SPIF中断使能。
SPCR3:其他中断控制。
- HMIE:HALTA/MODF中断使能。
5. 实战编程指南与常见问题排查
理论最终要服务于实践。下面以一个典型的主模式、多命令、循环传输场景为例,梳理编程步骤和可能遇到的坑。
5.1 典型应用场景与编程步骤
场景:MC68377作为主设备,需要循环查询三个SPI温度传感器(Sensor A, B, C),每个传感器读取一个16位数据。
步骤:
- 硬件连接:将三个传感器的片选分别接到PCS0, PCS1, PCS2。MOSI, MISO, SCK并联。
- 规划队列:我们需要3条命令,分别选中三个传感器并读取数据。假设传感器读取命令是发送一个8位命令字(如0xAA),然后接收16位数据。我们可以这样规划队列条目:
- 条目0:命令= {CONT=1, BITSE=1, DT=0, DSCK=0, PCS=0b0001 (选中A)}。发送数据=0xAA00(右对齐,高8位是命令)。
- 条目1:命令= {CONT=1, BITSE=1, DT=0, DSCK=0, PCS=0b0010 (选中B)}。发送数据=0xAA00。
- 条目2:命令= {CONT=0, BITSE=1, DT=0, DSCK=0, PCS=0b0100 (选中C)}。发送数据=0xAA00。CONT=0表示读完C后释放片选。
- 初始化代码框架(C语言风格伪代码):
// 1. 配置引脚功能与方向 PQSPAR |= (MOSI_MASK | MISO_MASK | SCK_MASK | PCS0_MASK | PCS1_MASK | PCS2_MASK); // 分配引脚给QSPI DDRQS |= (MOSI_MASK | SCK_MASK | PCS0_MASK | PCS1_MASK | PCS2_MASK); // 设为输出 DDRQS &= ~(MISO_MASK); // MISO设为输入 PORTQS |= (PCS0_MASK | PCS1_MASK | PCS2_MASK); // 片选默认高电平 // 2. 配置QSPI控制寄存器 SPCR0 = (MSTR_MASK | CPOL_0 | CPHA_0 | BITS_16); // 主模式,时钟模式0,传输16位 SPCR1 = 0; // 不使用可编程延时,或根据需要设置DTL/DSCKL SPCR2 = (WREN_MASK | SPIFIE_MASK | (0x0 << NEWQP_SHIFT) | (0x2 << ENDQP_SHIFT)); // 使能循环和中断,队列从0到2 SPCR3 = 0; // 或使能HMIE // 3. 初始化QSPI RAM // 命令RAM (地址 0x40 - 0x4F) *((volatile uint8_t*)(QSPI_RAM_BASE + 0x40)) = 0x51; // 条目0命令: CONT=1, BITSE=1, PCS0=1 *((volatile uint8_t*)(QSPI_RAM_BASE + 0x41)) = 0x52; // 条目1命令: CONT=1, BITSE=1, PCS1=1 *((volatile uint8_t*)(QSPI_RAM_BASE + 0x42)) = 0x14; // 条目2命令: CONT=0, BITSE=1, PCS2=1 // 发送数据RAM (地址 0x20 - 0x3F),按字访问 *((volatile uint16_t*)(QSPI_RAM_BASE + 0x20)) = 0xAA00; // 条目0发送数据 *((volatile uint16_t*)(QSPI_RAM_BASE + 0x22)) = 0xAA00; // 条目1发送数据 *((volatile uint16_t*)(QSPI_RAM_BASE + 0x24)) = 0xAA00; // 条目2发送数据 // 4. 清除可能存在的旧状态标志 uint16_t temp = SPSR; // 读SPSR SPSR = 0x0000; // 写SPSR清除SPIF, MODF, HALTA // 5. 使能QSPI SPCR1 |= SPE_MASK; - 中断服务程序(ISR):
void QSPI_ISR(void) { uint16_t status = SPSR; if (status & SPIF_MASK) { // 1. 清除SPIF标志 SPSR = 0x0000; // 先读已在入参时完成 // 2. 读取三个传感器的数据(从接收RAM) sensorA_data = *((volatile uint16_t*)(QSPI_RAM_BASE + 0x00)); // RR0 sensorB_data = *((volatile uint16_t*)(QSPI_RAM_BASE + 0x02)); // RR1 sensorC_data = *((volatile uint16_t*)(QSPI_RAM_BASE + 0x04)); // RR2 // 3. 进行数据处理... process_data(sensorA_data, sensorB_data, sensorC_data); // 注意:在循环模式下,QSPI会自动开始下一轮,无需重新使能。 // 如果需要更新发送数据(例如发送不同命令),可以在此处写入发送RAM。 // 由于QSPI可能在读取,为了安全,最好在它执行到其他条目时更新(利用CPTQP判断)。 } // 处理MODF或HALTA中断... }
5.2 常见问题与排查技巧
在实际调试中,你可能会遇到以下问题:
问题1:QSPI启动后没有任何动静(SCK无波形,片选不拉低)。
- 检查SPE位:确认SPCR1的SPE是否已设置为1。
- 检查MSTR位:主模式必须设置SPCR0的MSTR=1。
- 检查NEWQP和ENDQP:确保ENDQP >= NEWQP,否则队列长度为负,QSPI不会执行。
- 检查引脚分配:确认PQSPAR寄存器已正确将SCK、MOSI、PCSx引脚分配给QSPI功能,而不是通用IO。
- 检查HALT/FREEZE:确认开发板的调试器没有意外触发CPU的HALT或FREEZE信号,这会使QSPI暂停。
问题2:数据传输错误,收到全是0xFF或乱码。
- 检查CPOL和CPHA:这是SPI通信中最常见的错误。必须确保主从设备的时钟极性和相位完全匹配。用逻辑分析仪抓取SCK和MOSI/MISO的波形,对照设备数据手册检查。
- 检查波特率:SPBR设置是否过高,超过了外设支持的最高SCK频率。
- 检查数据对齐:确保写入发送RAM的数据是右对齐的。例如要发送0xAB,对于8位传输就写0xAB,对于16位传输应写0x00AB。
- 检查片选极性:确认外设片选是低电平有效还是高电平有效。QSPI的PCS输出低电平表示选中。如果外设是高有效,需要外部反相器或在命令中设置PCS位为0(通过PORTQS默认高电平来选中),但这通常不符合常理,需仔细设计。
问题3:在循环模式下,CPU读取的数据似乎不是最新的。
- 检查数据同步:在CPU读取接收RAM时,QSPI可能正在写入同一位置。务必利用CPTQP指针。只有CPTQP指向的及之前的条目才是稳定、已完成的。读取的数据地址应小于等于当前的CPTQP值。
- 检查内存访问类型:确保使用对齐的字(16位)访问来读取接收RAM,避免非一致性访问导致的数据撕裂问题。
问题4:使能循环模式后,SPIF中断频繁发生,甚至丢失数据。
- 中断处理速度:循环模式下,每完成一轮队列就产生一次中断。如果队列很短或SCK很快,中断频率会非常高。确保你的中断服务程序足够快,能在下一轮数据被覆盖前完成读取。如果处理不过来,可以考虑:
- 降低SCK波特率。
- 增加队列长度(插入空操作),降低中断频率。
- 使用查询模式而非中断模式,在主循环中定期检查CPTQP。
- 正确清除SPIF:确认中断程序中严格遵循了“先读SPSR,再写SPSR清零SPIF”的步骤。
问题5:多主系统中出现通信冲突,MODF标志被置位。
- 硬件仲裁:QSPI本身不提供硬件仲裁。MODF只是一个冲突指示标志。需要软件实现仲裁协议。一种简单的方法是检测到MODF后,延迟一个随机时间再重试。
- SS引脚配置:在多主系统中,所有主设备的MISO引脚应配置为开漏输出,并加上拉电阻。同时,确保当一个设备不是主设备时,其SS引脚配置为输入,以检测总线冲突。
深入理解MC68377的QSPI模块,尤其是其双端口RAM和队列化的工作机制,能够让你在涉及SPI通信的嵌入式项目中游刃有余。它将CPU从繁重的通信管理中解脱出来,通过精心设计的命令队列和自动执行逻辑,实现了高效、并发的数据交换。掌握它,意味着你掌握了在资源受限的嵌入式环境中进行高效外设管理的一把利器。