1. 项目概述:从手册到实战,拆解DSPI核心寄存器
如果你正在使用飞思卡尔(现恩智浦)的PXS20系列微控制器,或者任何基于其内核的芯片,并且需要驱动SPI外设,那么你大概率绕不开一个模块:DSPI。手册里关于DSPI_HCR、DSPI_CTAR这些寄存器的描述,往往是一大堆位域定义和时序图,看得人头大。但说实话,把这些寄存器玩明白了,你的SPI通信就稳了一大半。今天,我就结合手册里那些“干巴巴”的寄存器描述,聊聊在实际项目中,我是怎么理解、配置并最终驯服DSPI的。这不仅仅是照着手册填几个十六进制数,更是理解硬件如何工作,以及如何让它高效、可靠地为你服务的过程。无论你是刚接触嵌入式的新手,还是想优化现有驱动性能的老手,希望这篇从寄存器出发的实战解析能给你带来一些启发。
2. DSPI硬件配置寄存器(DSPI_HCR):你的SPI模块“身份证”
手册里把DSPI_HCR定义为只读寄存器,提供模块的实现细节。这话听起来很官方,但翻译成工程师的语言就是:这个寄存器告诉你,你手里的这块芯片,它的DSPI模块“硬件能力”到底怎么样。在写驱动之前,先读一下这个寄存器,是避免后续很多坑的第一步。
2.1 核心字段解读与实战意义
DSPI_HCR里几个关键字段,直接决定了你驱动程序的编写策略。
CTAR、TXFR、RXFR:这三个字段直接告诉你硬件资源。CTAR表示实现了几个时钟与传输属性寄存器(通常是0-3,即最多4个)。TXFR和RXFR则表示发送和接收FIFO的最大深度。比如,如果RXFR读出来是4,意味着接收FIFO有4个条目。这个信息至关重要:
- 队列管理:知道了FIFO深度,你才能合理设计数据搬移策略。是等FIFO快满了再一次性读取,还是来一个读一个?深度决定了你中断或DMA触发的阈值。
- 性能预估:更深的FIFO意味着你可以一次性写入/读取更多数据,减少CPU频繁处理中断的开销,对于高波特率通信尤其重要。
DIS_RXF:禁用接收FIFO。这个位虽然可写,但通常我们不会主动去禁用它,除非在极其特殊的调试场景下。手册说设置后FIFO表现为单条目深度,这实际上是把FIFO旁路了,数据直接从移位寄存器到读数据寄存器。这样做会丧失FIFO的缓冲优势,任何微小的读取延迟都可能导致数据溢出。在实战中,我几乎从未主动设置过此位,保持FIFO启用是更稳妥的选择。
CLR_TXF / CLR_RXF:清除发送/接收FIFO计数器。这是两个非常关键的操作位。在哪些场景下你会用到它们?
- 通信初始化或重新开始:在启动一次新的SPI传输序列前,为了确保从一个干净的状态开始,我会先写1清除这两个FIFO。这能避免残留的旧数据干扰本次通信。
- 错误恢复:当通信出现异常(例如从设备无响应导致超时),在尝试恢复通信链路时,清除FIFO是标准操作流程的一部分,用于重置模块的内部状态。
注意:手册明确提到,这两个位“总是读为0”。这意味着你不能通过读取它们来确认清除操作是否完成。标准的做法是:写入1后,等待一个短暂的系统时钟周期(通常用
__NOP()或类似的空操作指令),或者通过查询DSPI_SR中的TXCTR和RXCTR(FIFO计数器)来确认它们已归零。
SMPL_PT:采样点控制。这个位仅在“修改的传输格式”下使用,它决定了主设备在SCK时钟沿之后多少个系统时钟周期去采样SIN(数据输入)线。这对于匹配不同从设备的数据建立/保持时间要求至关重要。例如,某些低速传感器可能在SCK边沿后需要较长时间才能稳定输出数据,这时将SMPL_PT设置为01或10,延迟1-2个系统时钟再采样,可以提高通信可靠性。在标准SPI模式下,这个位通常保持为00(在SCK驱动边沿采样)。
HALT:暂停位。这是控制传输流程的“总开关”。设置HALT=1会停止所有正在进行的和排队的传输。这在多种场景下有用:
- 动态参数更新:当你需要修改
DSPI_CTAR寄存器(如切换波特率)时,必须先停止DSPI(HALT=1),修改完成后再启动(HALT=0)。手册警告,在运行状态写入CTAR可能导致不可预知的行为。 - 低功耗管理:在进入低功耗模式前,停止DSPI模块。
- 紧急停止:当检测到通信总线冲突或其他严重错误时,立即暂停传输以保护硬件。
2.2 配置流程与避坑指南
基于以上理解,一个稳健的DSPI初始化流程应该是这样的:
- 读取DSPI_HCR:获取硬件FIFO深度和CTAR数量,并存储到你的驱动上下文结构体中。这为后续动态配置提供依据。
- 可选:清除FIFO:在初始化开始时,向
CLR_TXF和CLR_RXF写1,确保FIFO为空。 - 配置主控寄存器(DSPI_MCR):设置模块为主/从模式、是否使能DMA等全局配置(这部分在提供的手册片段之外,但通常先于HCR操作)。
- 配置DSPI_CTAR:根据从设备要求,设置波特率、时钟极性/相位等(下一章详述)。
- 启动传输:确保所有静态配置完成后,最后将
HALT位清零(如果之前被停止),并开始向DSPI_PUSHR写入数据。
避坑心得:
- 顺序很重要:一定要先
HALT,再改CTAR;先清FIFO,再开始新传输。顺序错乱是导致通信失败的常见原因。 - 状态查询:不要假设操作是瞬间完成的。在清除FIFO或改变
HALT状态后,通过查询DSPI_SR寄存器中的TXRXS(运行状态位)和TXCTR/RXCTR来确认状态变更,是编写健壮驱动的好习惯。 - 理解“只读”部分:像
CTAR数量、FIFO深度这些只读信息,不要在代码里写死。用DSPI_HCR读出的值作为你驱动程序的配置上限,这样代码在不同型号的芯片间移植性会更好。
3. 时钟与传输属性寄存器(DSPI_CTAR):精雕细琢通信时序
如果说DSPI_HCR是模块的“身份证”,那么DSPI_CTAR就是它的“行为准则手册”。你与每一个SPI从设备通信的时序细节,几乎都由它定义。DSPI最多支持4个CTAR寄存器(CTAR0-CTAR3),这意味着你可以预先定义最多4套不同的通信参数,并在每次传输时通过命令字快速切换,这对于需要与多个不同规格从设备通信的系统来说,是一个巨大的便利。
3.1 关键属性深度解析
FMSZ(帧大小):FMSZ的值加1就是每帧传输的比特数。手册规定最小有效值为3,即最小4位帧。这一点需要注意,常见的8位数据传输对应FMSZ=7。这个设置直接影响你写入DSPI_PUSHR和从DSPI_POPR读取的数据对齐方式。
CPOL与CPHA(时钟极性/相位):这是SPI通信的基石,决定了时钟空闲电平和数据采样的边沿。共有4种模式(模式0-3)。必须与从设备的数据手册要求严格匹配,否则通信必然失败。一个简单的记忆方法是:CPOL决定SCK空闲时的电平(0=低,1=高),CPHA决定数据在哪个边沿采样(0=第一个边沿,1=第二个边沿)。在“连续SCK”模式下,CPHA被忽略并按1处理。
LSBFE(LSB优先):设置数据传输是从最高位(MSB)还是最低位(LSB)开始。同样,这必须与从设备约定一致。大部分器件采用MSB优先。
波特率生成(DBR, PBR, BR):这是配置的难点和重点。波特率计算公式为:SCK波特率 = fSYS / [PBR * (1+DBR) * BR]
fSYS:系统时钟频率。PBR:预分��器(2, 3, 5, 7)。DBR:双倍波特率使能。置1时,公式中的(1+DBR)等于2,相当于波特率翻倍,但代价是SCK的占空比可能不再是50%(取决于PBR和CPHA,见手册表16-7)。BR:分频器(2到32768之间的特定值)。
实战计算示例:假设系统时钟fSYS = 50MHz,目标SCK波特率为1MHz。
- 先尝试简单分频:
50MHz / 1MHz = 50。我们需要在PBR、DBR、BR的组合中找到乘积接近50的解。 - 若设置
PBR=2,DBR=0, 则需要BR = 50 / 2 = 25。但查表16-9,BR的可选值中没有25,最接近的是16或32。 - 调整思路,设
PBR=5,DBR=1(此时1+DBR=2),则BR = 50 / (5*2) = 5。查表,BR值中也没有5。 - 再试,设
PBR=5,DBR=0, 则BR = 50 / 5 = 10。BR表中依然没有10。 - 最终找到一个可行解:
PBR=5,DBR=0,BR=8。计算实际波特率:50MHz / (5 * 1 * 8) = 1.25MHz。这个误差(25%)对于许多从设备可能不可接受。 - 更好的解:
PBR=2,DBR=0,BR=32。实际波特率:50MHz / (2*1*32) = 781.25kHz,误差约22%。 - 尝试使用
DBR:PBR=3,DBR=1,BR=8。实际波特率:50MHz / (3*2*8) ≈ 1.0417MHz,误差仅4.2%,这是一个相当好的结果。但需注意,此时SCK占空比可能不是50%,需核对表16-7确认从设备是否支持。
这个过程说明,SPI波特率的配置往往是一个寻找最接近目标值的组合过程,很难做到绝对精确。在高速通信时,还需考虑PBR和BR带来的时钟抖动。
延时控制(PCSSCK/CSSCK, PASC/ASC, PDT/DT):这三个延时(PCS到SCK延时、SCK后延时、传输后延时)是高级SPI主设备必须掌握的技能。它们用于满足从设备特定的时序要求,例如片选有效到第一个时钟沿的建立时间(tCSC)、最后一个时钟沿到片选无效的保持时间(tASC),以及连续传输间片选不活动的时间(tDT)。计算公式为:延时时间 = (1/fSYS) * PCSSCK * CSSCK(以tCSC为例)。PCSSCK是预分频(1,3,5,7),CSSCK是倍频器(2的幂次,2到65536)。通过灵活配置,可以产生从纳秒到微秒量级的精确延时。
3.2 主从模式配置差异
从提供的寄存器图可以看出,从模式下的CTAR配置项远少于主模式。在从模式下,你通常只能设置FMSZ、CPOL、CPHA,以及可选的奇偶校验(SLAVE_PE,SLAVE_PP)。波特率、各类延时均由外部主设备控制,从设备只需被动跟随。因此,在从设备初始化时,只需根据主设备将要使用的通信格式,配置好这几个有限的参数即可。DSPI_CTAR0和DSPI_CTAR1在从模式下可用,但通常只使用CTAR0。
3.3 多CTAR寄存器使用策略
拥有多个CTAR寄存器的优势在于可以实现“无缝”切换。例如:
CTAR0:配置为高速模式(10MHz, 8位数据),用于连接高速Flash。CTAR1:配置为低速模式(1MHz, 16位数据),用于连接精度较高的ADC。CTAR2:配置为极低速模式(100kHz, 模式3),用于连接一个老式的、时序要求特殊的传感器。
在向DSPI_PUSHR写入命令和数据时,可以通过命令字中的CTAS字段(2位)指定本次传输使用哪一个CTAR(0-3)。这样,在一次DMA传输或一个FIFO队列中,混合发送不同格式的数据帧成为可能,极大地提高了总线利用率和实时性。
配置步骤总结:
- 确定需求:仔细阅读从设备数据手册,列出所有时序参数:SCK模式(CPOL/CPHA)、最大SCK频率、数据位顺序、帧长度、以及
tCSC、tASC、tDT等时间要求。 - 计算参数:根据
fSYS和所需SCK频率,计算PBR、DBR、BR的最佳组合。根据时间要求计算延时参数。 - 编写配置函数:将计算好的值填入对应的CTAR寄存器位域。注意,在写入前确保DSPI已停止(
HALT=1)。 - 验证:如果条件允许,使用逻辑分析仪抓取SPI总线波形,实测SCK频率、占空比及各延时时间,与计算值进行对比,这是确保通信稳定的黄金标准。
4. 状态、中断与FIFO操作:构建高效数据流
配置好通信参数只是第一步,如何高效、可靠地收发数据才是驱动程序的灵魂。这离不开对状态寄存器(DSPI_SR)、中断请求寄存器(DSPI_RSER)以及FIFO推送/弹出寄存器(DSPI_PUSHR/DSPI_POPR)的协同操作。
4.1 状态寄存器(DSPI_SR):通信的“仪表盘”
DSPI_SR寄存器提供了模块运行的实时状态。理解每个标志位的含义和触发条件,是进行轮询或中断驱动编程的基础。
核心状态标志:
TCF(Transfer Complete Flag):单个SPI帧传输完成标志。注意:手册特别提醒,在传输正在进行时,不建议写入此位。因为串行链路侧的更新优先级高于寄存器访问。在实践中,我们通常只读取它,并通过写1来清除它。TXRXS:运行状态位。反映DSPI是处于运行(RUNNING)还是停止(STOPPED)状态。检查此位可以确认HALT位的操作是否生效。EOQF(End of Queue Flag):队列结束标志。当传输的命令字中EOQ位被置1,且该帧传输完成时,此标志置位。同时,TXRXS会自动清零,停止传输。这是实现精确控制传输帧数的关键机制。TFFF(Transmit FIFO Fill Flag) &RFDF(Receive FIFO Drain Flag):这是最常用的两个标志。TFFF=1表示发送FIFO未满,可以继续写入数据。RFDF=1表示接收FIFO非空,有数据可以读取。它们是驱动中断或DMA请求的核心。TFUF(Transmit FIFO Underflow) &RFOF(Receive FIFO Overflow):下溢和上溢标志,属于错误状态。在从模式下,如果TX FIFO为空时主设备发起传输,会发生TFUF。如果RX FIFO已满时又收到新数据,会发生RFOF。良好的驱动应能处理或避免这些情况。SPEF(SPI Parity Error Flag):奇偶校验错误标志(仅特定版本支持)。如果使能了奇偶校验,此标志在收到校验错误的帧时置位。
TXCTR, TXNXTPTR, RXCTR, POPNXTPTR:这些字段提供了FIFO内部的详细计数器和指针信息,在深度调试FIFO操作异常(如数据顺序错乱)时非常有用。
4.2 中断与DMA请求使能寄存器(DSPI_RSER):解放CPU
DSPI_RSER允许你将特定的状态标志(TFFF,RFDF,TCF,EOQF,TFUF,RFOF,SPEF)配置为触发中断或DMA请求。这是实现高效、非阻塞数据传输的关键。
配置策略:
- 轮询模式:不使能任何
*_RE位。程序循环查询TFFF和RFDF标志,进行读写。简单,但CPU占用率高。 - 中断模式:使能
TFFF_RE和RFDF_RE,并将TFFF_DIRS和RFDF_DIRS设为0(选择中断)。当FIFO可写或可读时,触发中断服务程序(ISR)进行数据搬移。适合中等数据量、低延迟的场景。 - DMA模式:使能
TFFF_RE和RFDF_RE,并将TFFF_DIRS和RFDF_DIRS设为1(选择DMA)。结合DMA控制器,可以在无需CPU干预的情况下,自动将内存中的数据搬移到DSPI_PUSHR,或将DSPI_POPR的数据搬移到内存。这是处理大批量、高速SPI通信(如读写SPI Flash、与高速ADC/DAC交互)的首选方案,能极大降低CPU负载。
典型的中断/DMA驱动流程:
- 初始化时,在
DSPI_RSER中使能TFFF_RE和RFDF_RE���并选择中断或DMA方向。 - 启动传输前,先向TX FIFO写入若干个数据(例如,填满一半深度),以启动传输引擎。
- 当TX FIFO有空位(
TFFF置位)时,触发中断/DMA,在ISR或DMA传输完成回调中补充数据。 - 当RX FIFO有数据(
RFDF置位)时,触发中断/DMA,在ISR或DMA传输完成回调中读取数据。 - 利用
EOQ机制,在最后一个数据帧的命令字中设置EOQ=1,传输完成后会触发EOQF中断,通知CPU/DMA本次传输序列全部结束。
4.3 数据推送与弹出寄存器(DSPI_PUSHR/POPR):数据交换的窗口
DSPI_PUSHR(Master Mode):在主机模式下,这是一个32位寄存器,高16位是命令(CONT,CTAS,EOQ,CTCNT,PCS[7:0]等),低16位是数据(TXDATA)。一次32位写入操作,会将命令和数据作为一个整体压入TX FIFO。命令字中的CTAS选择了本次传输使用的CTAR寄存器,PCS选择了激活哪个片选信号,EOQ标记队列结束,CONT控制片选是否在帧间保持有效。DSPI_PUSHR(Slave Mode):在从机模式下,整个32位寄存器都用作TXDATA,支持最大32位帧长。从设备在此准备好要发送的数据,等待主设备来读取。DSPI_POPR:读取此寄存器,会从RX FIFO中弹出一个接收到的数据。注意:读取操作本身就会更新FIFO的弹出指针和RXCTR。在中断或DMA处理中,通常需要连续读取RXCTR指示的多个数据。
避坑指南:
- 对齐问题:当帧大小不是16或32位时,需要关注数据在
TXDATA字段中的对齐方式(通常是右对齐或左对齐),以及在RXDATA中的位置。 - FIFO写满:在向
PUSHR写入前,务必检查TFFF标志或TXCTR计数器,避免写入已满的FIFO导致数据丢失。虽然有些硬件可能忽略此次写入,但这不是可靠的行为。 EOQ的使用:EOQ位需要设置在最后一个有效数据帧的命令字中。传输完该帧后,DSPI会自动停止(TXRXS清零),并置位EOQF。这对于控制多帧传输的精确结束点非常有用。- 从机模式下的TX FIFO管理:在从机模式下,务必确保在主机发起传输前,TX FIFO中已有待发送的数据,否则会发生
TFUF(下溢)。通常需要在初始化或每次应答后,提前将数据写入从机的TX FIFO。
5. 实战配置案例与调试技巧
让我们通过一个具体的场景,将上述所有知识点串联起来:使用DSPI主模式,以中断方式,循环读取一个SPI接口的温湿度传感器(假设为SHT3x,使用模式0,CPOL=0,CPHA=0,MSB优先,8位帧,最高时钟1MHz)。
5.1 初始化与配置代码框架(伪代码风格)
// 1. 使能DSPI模块时钟(此步骤依赖具体MCU的时钟控制系统) CLOCK_EnableClock(kCLOCK_Dspi0); // 2. 获取硬件配置信息 uint32_t hcr = DSPI0->HCR; uint8_t tx_fifo_depth = ((hcr >> 8) & 0x1F) + 1; // 提取TXFR字段 uint8_t rx_fifo_depth = ((hcr >> 16) & 0x1F) + 1; // 提取RXFR字段 uint8_t ctar_count = (hcr & 0x07) + 1; // 提取CTAR字段 // 存储这些信息到驱动上下文 // 3. 软件复位并停止DSPI DSPI0->MCR |= DSPI_MCR_MDIS_MASK | DSPI_MCR_HALT_MASK; // 先进入模块禁用模式可能需按手册顺序 DSPI0->MCR = DSPI_MCR_HALT_MASK | DSPI_MCR_MSTR_MASK; // 保持停止,设置为主模式 // 清除FIFO DSPI0->HCR |= DSPI_HCR_CLR_TXF_MASK | DSPI_HCR_CLR_RXF_MASK; // 短暂延时或等待SR中的计数器归零 while((DSPI0->SR & (DSPI_SR_TXCTR_MASK | DSPI_SR_RXCTR_MASK)) != 0); // 4. 配置CTAR0 // 假设系统时钟fSYS=48MHz,目标SCK=1MHz。 // 计算:48MHz / 1MHz = 48。 // 尝试组合:PBR=2, DBR=0, 则需要BR=24。查表无24,取BR=32,得0.75MHz。 // 尝试组合:PBR=3, DBR=1, 则分母为3*2=6,需要BR=8。查表有8。 // 实际波特率 = 48MHz / (3 * 2 * 8) = 1.0MHz。完美。 // CPOL=0, CPHA=0, FMSZ=7 (8 bits), LSBFE=0 (MSB first). // 延时参数根据传感器手册要求设置,假设使用默认最小值。 uint32_t ctar0_config = 0; ctar0_config |= DSPI_CTAR0_FMSZ(7); // 8-bit frame ctar0_config |= DSPI_CTAR0_CPOL(0) | DSPI_CTAR0_CPHA(0); // Mode 0 ctar0_config |= DSPI_CTAR0_LSBFE(0); // MSB first ctar0_config |= DSPI_CTAR0_PBR(1) | DSPI_CTAR0_BR(3); // PBR=3 (b01), BR=8 (b0011) ctar0_config |= DSPI_CTAR0_DBR(1); // Double baud rate enable // PCS to SCK delay, After SCK delay, Delay after transfer 使用默认或计算值 ctar0_config |= DSPI_CTAR0_CSSCK(0) | DSPI_CTAR0_ASC(0) | DSPI_CTAR0_DT(0); ctar0_config |= DSPI_CTAR0_PCSSCK(0) | DSPI_CTAR0_PASC(0) | DSPI_CTAR0_PDT(0); DSPI0->CTAR[0] = ctar0_config; // 5. 配置中断(使能TFFF和RFDF中断,选择中断非DMA) DSPI0->RSER = DSPI_RSER_TFFF_RE_MASK | DSPI_RSER_RFDF_RE_MASK; // 在NVIC中使能DSPI中断 EnableIRQ(DSPI0_IRQn); // 6. 准备发送数据(读取温度命令,例如0x2400) g_tx_buffer[0] = 0x2400; // 假设命令字,CTAS=0 (使用CTAR0), PCS=0 (选择传感器片选) // 将数据写入TX FIFO(首次手动写入启动传输) DSPI0->PUSHR = DSPI_PUSHR_CONT(0) | DSPI_PUSHR_CTAS(0) | DSPI_PUSHR_PCS(1) | DSPI_PUSHR_TXDATA(g_tx_buffer[0]); // 7. 启动传输(清除HALT位) DSPI0->MCR &= ~DSPI_MCR_HALT_MASK;5.2 中断服务程序(ISR)处理逻辑
void DSPI0_IRQHandler(void) { uint32_t sr = DSPI0->SR; // 读取状态寄存器 // 处理发送FIFO未满中断 if(sr & DSPI_SR_TFFF_MASK) { DSPI0->SR |= DSPI_SR_TFFF_MASK; // 写1清除标志 // 检查是否还有数据要发送 if(g_tx_index < g_tx_length) { uint32_t command = 0; if(g_tx_index == g_tx_length - 1) { command |= DSPI_PUSHR_EOQ_MASK; // 最后一帧设置EOQ } command |= DSPI_PUSHR_CTAS(0) | DSPI_PUSHR_PCS(1) | DSPI_PUSHR_TXDATA(g_tx_buffer[g_tx_index]); DSPI0->PUSHR = command; g_tx_index++; } } // 处理接收FIFO非空中断 if(sr & DSPI_SR_RFDF_MASK) { DSPI0->SR |= DSPI_SR_RFDF_MASK; // 写1清除标志 // 读取所有可用的接收数据 while(DSPI0->SR & DSPI_SR_RXCTR_MASK) { g_rx_buffer[g_rx_index++] = DSPI0->POPR & 0xFFFF; // 读取16位数据 } // 检查是否收到EOQF(传输结束) if(sr & DSPI_SR_EOQF_MASK) { DSPI0->SR |= DSPI_SR_EOQF_MASK; // 清除结束标志 // 本次传输序列完成,处理数据g_rx_buffer process_sensor_data(g_rx_buffer); // 准备下一次读取... g_tx_index = 0; g_rx_index = 0; // 重新填充第一个数据并启动(如果需要连续读取) } } // 处理错误中断(可选) if(sr & (DSPI_SR_TFUF_MASK | DSPI_SR_RFOF_MASK)) { // 记录错误,清除FIFO,可能需要进行错误恢复 DSPI0->HCR |= DSPI_HCR_CLR_TXF_MASK | DSPI_HCR_CLR_RXF_MASK; DSPI0->SR |= DSPI_SR_TFUF_MASK | DSPI_SR_RFOF_MASK; // ... 错误处理逻辑 } }5.3 常见问题排查实录
问题:完全没有SCK时钟输出。
- 检查1:确认
DSPI_MCR[MSTR]位已设置为1(主模式)。 - 检查2:确认
DSPI_MCR[HALT]位已清零。这是最容易被忽略的一点! - 检查3:确认对应SPI引脚(SCK, MOSI, MISO, PCS)的复用功能已正确配置,并且方向(输入/输出)设置正确。
- 检查4:TX FIFO是否为空?只有TX FIFO中有待发送的数据,DSPI才会启动传输并产生SCK。确保在启动(
HALT清零)前或之后,至少向PUSHR写入了一个数据。
- 检查1:确认
问题:有SCK时钟,但MOSI上没有数据,或数据全为0/1。
- 检查1:确认写入
PUSHR的TXDATA字段值是否正确。调试时可以先尝试写入一个固定的测试模式,如0xAA或0x55。 - 检查2:确认
CPOL和CPHA设置与逻辑分析仪捕获的波形是否匹配。错误的模式会导致数据采样错位。 - 检查3:检查
LSBFE位,确认数据传输顺序是否符合预期。
- 检查1:确认写入
问题:能发送数据,但接收到的数据总是0xFF或0x00。
- 检查1:确认从设备已正确上电,且片选信号
PCS已正确触发(低电平有效或高电平有效,取决于从设备)。 - 检查2:检查MISO引脚连接和配置。用逻辑分析仪同时抓取MOSI和MISO,看从设备是否在正确的时钟边沿回送了数据。
- 检查3:确认
DSPI_CTAR中的帧大小(FMSZ)设置与从设备返回的数据长度一致。 - 检查4:读取
DSPI_POPR的时机是否正确?是否在RFDF置位(或RXCTR>0)后才去读取?过早读取可能得到旧数据或无效数据。
- 检查1:确认从设备已正确上电,且片选信号
问题:高波特率下通信不稳定,出现偶发性错误。
- 检查1:检查SCK波特率计算是否准确,实际测量频率是否与预期相符。过高的波特率可能接近或超过从设备或PCB布线的极限。
- 检查2:检查
PCSSCK、PASC、PDT等延时设置是否满足从设备的最小时序要求。在高速下,建立/保持时间不足是常见问题。适当增加这些延时。 - 检查3:检查电源噪声和信号完整性。高速SPI信号对PCB走线敏感,确保时钟和数据线长度匹配,远离噪声源,并考虑是否需要串联端接电阻。
- 检查4:是否使能了FIFO?在高速传输中,使用FIFO并结合DMA是减少CPU中断延迟、避免数据溢出/下溢的关键。
问题:使用多个CTAR时,切换后通信异常。
- 检查:确保在修改
DSPI_CTARx寄存器(x=1,2,3)的内容之前,DSPI模块已处于停止状态(HALT=1)。这是手册明确强调的禁忌。修改完成后,再清除HALT位。
- 检查:确保在修改
调试SPI,一把逻辑分析仪是必不可少的。它能直观地展示SCK、MOSI、MISO、PCS的波形,让你清晰地看到时钟极性、相位、数据位、帧间隔是否完全符合预期。将抓取的波形与数据手册的时序图逐一比对,是定位硬件配置问题最快最直接的方法。寄存器配置是冰冷的数字,但逻辑分析仪上的波形,才是硬件真正“听懂”了你指令的证明。