1. 从移位寄存器到复杂总线:SPI接口的本质与演进
搞嵌入式开发这些年,SPI(Serial Peripheral Interface)是我打交道最多的外设接口之一。它不像I2C那样有复杂的地址机制和开漏输出,也不像UART那样需要精确的波特率匹配。SPI给我的感觉就像是一个勤恳的“搬运工”——简单、直接、高效。但就是这个看似简单的四线制接口,在实际项目中却藏着不少门道,尤其是在像TI OMAP-L138这样的异构处理器上,它的灵活性远超很多人的想象。
很多人对SPI的理解停留在“四根线:SCK、MOSI、MISO、CS”这个层面,这没错,但不够。SPI本质上是一个同步的、全双工的、基于主从架构的串行通信协议。它的核心是一个移位寄存器。想象一下,主设备和从设备各有一个16位的移位寄存器,通过MOSI(Master Out Slave In)和MISO(Master In Slave Out)两根数据线首尾相连,形成一个巨大的“环形移位寄存器”。当时钟信号(SCK)的边沿到来时,主从双方的数据就同时沿着这个环移动一位。一个时钟周期,就完成了一位数据的交换。这种机制决定了SPI是全双工的,发送和接收可以同时进行,这是它相对于半双工I2C的一个显著优势。
OMAP-L138的SPI模块框图清晰地展示了这一点:核心就是一个16位移位寄存器和一个16位缓冲器(Buffer)。数据在传输前被写入移位寄存器(SPIDAT0/1),传输结束后再从缓冲器(SPIBUF)中读取。这种双缓冲机制允许软件在本次传输进行时,就准备下一帧要发送的数据,或者处理上一帧接收到的数据,从而实现连续的数据流传输,这对高吞吐量应用至关重要。
然而,SPI的“简单”也带来了一个经典问题:标准缺失。它没有统一的协议规范,通信的细节——比如数据位宽(8位、16位)、时钟极性(CPOL)、时钟相位(CPHA)、比特序(MSB/LSB First)——都需要主从双方事先约定好。这就引出了SPI的四种工作模式,由CPOL和CPHA的组合决定。CPOL决定时钟空闲时的电平(0为低,1为高),CPHA决定数据在时钟的哪个边沿采样(0为第一个边沿,1为第二个边沿)。这四种模式覆盖了绝大多数外设的需求,但也是驱动开发中第一个容易踩坑的地方:主从设备的模式必须严格匹配。
OMAP-L138的SPI模块在基础3线模式(SCK, SIMO, SOMI)之上,提供了更强大的扩展能力,即支持4引脚和5引脚操作。这不仅仅是多了一两根线那么简单,而是引入了硬件流控和更精确的从设备管理机制,直接影响到系统设计的可靠性和效率。接下来,我们就深入OMAP-L138的内部,看看它是如何实现这些高级特性的。
2. OMAP-L138 SPI模块架构深度拆解
OMAP-L138芯片内部集成了两个独立的SPI模块:SPI0和SPI1。从寄存器地址映射来看(SPI0基址0x01C4 1000,SPI1基址0x01F0 E000),它们是完全相同的,可以独立配置和使用,这为需要连接多个SPI总线设备的系统提供了便利。每个模块的架构可以看作由几个关键部分组成:控制逻辑、数据通路、引脚管理和中断/DMA系统。
控制逻辑的核心是状态机和一系列配置寄存器。状态机负责指挥整个数据传输的流程:从空闲状态,到启动传输,控制时钟生成和数据移位的节奏,再到传输结束返回空闲状态或准备下一次传输。我们程序员通过配置寄存器来“指导”这个状态机工作。最重要的几个寄存器包括:
- SPIGCR0/1(全局控制寄存器):这是SPI模块的总开关。SPIGCR1的
CLKMOD位决定模块是主模式(产生SCK)还是从模式(接收SCK),ENA位则是整个模块的使能位。这里有个细节:上电后或复位后,必须先配置好其他参数,最后再置位ENA来激活模块,顺序错了可能导致不可预料的行为。 - SPIFMTx(格式寄存器):这是定义通信“方言”的地方。你可以设置数据字长(
CHARLEN,1到16位)、时钟极性相位(POL和PHA)、移位方向(SHIFTDIR,决定MSB还是LSB先发送)。OMAP-L138支持多达4个不同的格式设置,并可以通过SPIDAT1寄存器中的DFSEL位在传输中动态选择,这在与多个不同格式要求的从设备通信时非常有用,无需频繁重配置。 - SPIDAT0/1(数据寄存器):这是数据进出移位寄存器的入口。写入SPIDATx会触发传输(在主模式下)。需要注意的是,SPIDAT1寄存器还包含
CSHOLD位,当使能时,在一次数据传输结束后片选信号(如果使用)会保持有效,这对于需要连续发送多帧数据给同一从设备的场景非常高效,避免了片选信号的反复跳变。
引脚管理则由SPIPC0-SPIPC5这一组引脚控制寄存器负责。它们的功能非常明确:
SPIPC0:配置引脚功能。每个SPI引脚(CLK, SIMO, SOMI, SCS[3:0], ENA)都可以被配置为SPI功能或通用的GPIO功能。这是硬件设计验证时常用的功能,你可以先把引脚设为GPIO,用软件模拟SPI时序来验证硬件连接,然后再切换到硬件SPI模式,非常方便。SPIPC1:配置引脚方向(输入/输出)。SPIPC2:读取引脚输入状态。SPIPC3/4/5:分别用于直接输出、置位和清零引脚输出。
中断和DMA是提升CPU效率的关键。SPI模块可以产生多种中断,例如发送缓冲空、接收缓冲满、传输结束等,这些中断状态位在SPIFLG(标志寄存器)中,而中断使能和类型则在SPIINT0和SPILVL寄存器中配置。更强大的是,它支持通过**DMA(直接存储器访问)**来搬运数据。当使能DMA后,数据可以直接在SPI缓冲器和内存之间传输,无需CPU频繁介入,极大地解放了CPU,特别是在进行高速、大批量数据交换(如读写SPI Flash或传输图像传感器数据)时,性能提升是数量级的。
理解了这个架构,我们就能明白,配置一个SPI通信,本质上就是给这个精密的硬件状态机编写一份详细的“工作说明书”。而这份说明书的核心章节,就是接下来要重点讨论的3/4/5引脚配置模式。
3. 3/4/5引脚模式详解:从基础连接到高级流控
3.1 3引脚基础模式:点对点连接的基石
3引脚模式是SPI最精简的形式,只使用三根线:SPIx_CLK(时钟)、SPIx_SIMO(主出从入)、SPIx_SOMI(主入从出)。在这种模式下,通信完全由主设备发起和控制,从设备永远处于“听候差遣”的状态。它适用于单一从设备的简单场景,比如连接一个SPI接口的温湿度传感器。
在OMAP-L138中,即使使用3引脚模式,片选(CS)信号的管理依然存在,只是由软件通过通用GPIO来模拟。你需要在SPIPC0寄存器中将SCS引脚配置为GPIO,然后在数据传输前手动拉低对应的GPIO,传输后再拉高。这种方式的优点是灵活,缺点是需要CPU干预,并且时序上不如硬件自动控制精确。对于3引脚模式,驱动配置相对简单,主要关注SPIFMT寄存器中的时钟格式和数据长度即可。
3.2 4引脚模式(带片选):多从机系统的钥匙
当总线上需要挂载多个从设备时,3引脚模式就不够用了,因为所有从设备的MISO线会直接短路在一起。这时,4引脚模式登场,它引入了SPIx_SCS[3:0](Slave Chip Select)信号。OMAP-L138最多提供4个独立的硬件片选输出。
其工作逻辑非常清晰:主设备在发起与某个特定从设备的通信前,先将其对应的SCS引脚拉低(有效),总线上其他从设备的SCS保持高电平。被选中的从设备会“激活”其SOMI(MISO)输出驱动器,将数据线拉出高阻态,从而响应主设备。而未被选中的从设备则将其SOMI引脚置于高阻态,避免总线冲突。这就好比在一个会议室里,主持人(主设备)点名让某个人(从设备)发言,其他人保持安静。
在从设备端,SCS引脚还有一个关键作用:它作为从设备移位寄存器的使能门控。只有当SCS为低时,从设备内部的移位寄存器才会响应主设备发来的时钟信号进行数据移位。这确保了从设备不会在非选中状态下被总线上的杂散时钟干扰。
配置4引脚模式时,除了基础设置,还需要关注SPIDEF(默认片选)寄存器,它可以指定默认使用的片选线。更重要的是SPIDELAY(延时寄存器),它里面的C2TDELAY(CS有效到第一个时钟的延迟)和T2CDELAY(最后一个时钟到CS无效的延迟)字段,允许你精细地调整片选信号的时序,以满足某些特殊从设备对建立时间和保持时间的要求。例如,一些EEPROM芯片需要在片选有效后等待一段时间才能接收第一个时钟,这时就可以通过C2TDELAY来插入延迟。
3.3 4引脚模式(带使能)与5引脚模式:硬件流控的艺术
这是OMAP-L138 SPI模块更高级的特性,引入了SPIx_ENA(Enable)信号,实现了硬件级别的“握手”机制,我习惯称之为硬件流控。这个功能在标准SPI中不常见,但对于提高总线利用率和系统可靠性意义重大。
4引脚使能模式:在此模式下,SPIx_ENA作为从设备的“就绪”输出信号。从设备内部的传输缓冲器(SPIBUF)状态直接反映在ENA引脚上。当从设备的发送缓冲器满(即已准备好新数据供主设备读取)时,ENA被拉低(有效),告知主设备:“我准备好了,你可以发起下一次传输了”。主设备可以配置为在发起传输前检查ENA信号,如果ENA为高(未就绪),则自动等待。这样就避免了主设备盲目发起传输而从设备还未准备好的情况,特别适用于从设备处理速度较慢(例如需要时间进行模数转换)的场景。在从机端,需要配置SPIINT0.ENABLE_HIGHZ位来决定传输结束后ENA引脚是输出高电平还是进入高阻态(需外接上拉电阻)。
5引脚模式:这是功能最全的模式,同时使用了SCS和ENA两个信号。此时,ENA信号的有效(低电平)需要同时满足两个条件:1) 片选SCS有效(低电平);2) 从设备发送缓冲器满。这允许多个从设备共享同一条ENA线连接到主设备。主设备选中某个从设备(拉低其SCS)后,只需要监控这一条共享的ENA线。只有被选中的、且已就绪的从设备才会将ENA拉低。这种设计简化了多从机系统中的连线。
在主机配置上,需要设置SPIGCR1.ENABLE_HIGHZ位(注意,此位名与从机端的类似但作用不同)来控制主机对ENA信号的反应。同时,SPIDELAY寄存器中的相关延时参数对于确保握手信号同步至关重要。例如,td(ENA_SPC)M这个参数定义了从机ENA有效后,到主机发出第一个时钟之间的最小延迟,必须满足此时序,否则可能采样到错误的就绪状态。
从实际项目经验来看,是否使用ENA流控是一个需要权衡的决策。对于速度匹配、实时性要求高的主从设备对(如与高速ADC/DAC通信),启用ENA流控可以消除不必要的等待,提升平均吞吐率。但对于连接简单、速度不高的传感器,增加一根ENA线带来的硬件复杂性和软件配置成本可能并不划算。我的经验法则是:当从设备的数据准备时间不确定或可能长于几个SPI时钟周期时,考虑使用ENA流控;否则,使用简单的查询或中断方式足矣。
4. 时序参数实战解析:数据手册数字背后的电路逻辑
数据手册里密密麻麻的时序参数表常常让人望而生畏,但一旦理解其物理意义和测量方法,它们就成了调试硬件的“罗盘”。OMAP-L138的SPI时序表(Table 6-70至Table 6-85)虽然详尽,但我们可以抓住几个最关键的概念来解读。
4.1 通用时序参数:建立、保持与输出延迟
无论是主模式还是从模式,也无论是3、4、5引脚,一些基础的时序关系是共通的。我们结合**图6-38(主模式时序)和图6-39(从模式时序)**来看。
对于主设备发送(SIMO):
td(SIMO_SPC)M:这是第一个数据位的建立时间。它定义了在第一个时钟有效边沿(根据CPOL/CPHA确定是上升沿还是下降沿)到来之前,主设备SIMO引脚上的数据必须稳定多久。注意这个值可能是负数(例如表中-0.5M+5 ns),负的建立时间?这听起来违反直觉。其实这表示数据可以在时钟边沿之后才有效,这通常发生在CPHA=1的模式下,数据是在时钟的第二个边沿采样,因此第一个边沿时数据允许有稍许延迟。这体现了SPI时序的灵活性。td(SPC_SIMO)M:这是后续数据位的输出延迟。它定义了在时钟的有效传输边沿之后,主设备SIMO引脚上的下一位数据最多需要多长时间才能稳定。这个参数决定了主设备驱动器的速度。toh(SPC_SIMO)M:输出保持时间。在时钟的接收边沿(采样边沿)之后,数据还需要保持稳定的时间。确保这个时间能满足从设备的输入保持时间要求。
对于主设备接收(SOMI):
tsu(SOMI_SPC)M:输入建立时间。在主机采样数据的时钟边沿到来之前,从设备SOMI引脚上的数据必须稳定至少这个时间。tih(SPC_SOMI)M:输入保持时间。在主机采样数据的时钟边沿之后,从设备SOMI引脚上的数据还必须继续保持稳定至少这个时间。
从设备的时序参数(tsu(SOMI_SPC)S,td(SPC_SOMI)S等)与主设备是对称的,但数值不同,因为它们描述的是从设备内部逻辑对时钟和数据信号的响应速度。一个关键的设计原则是:主设备的输出时序必须满足从设备的输入时序要求;从设备的输出时序必须满足主设备的输入时序要求。这构成了一个完整的时序链。
4.2 工作频率的计算与选择
主模式下的最大SPI时钟频率受限于两个因素:主设备本身的输出能力和从设备的输入要求。数据手册给出了tc(SPC)M(SPI主时钟周期)的最小值,例如在1.3V/1.2V核心电压下为20ns,对应最大频率为50MHz。但这只是理论峰值。
实际最高安全频率需要通过时序分析来计算。以一个主设备(OMAP-L138)连接一个从设备(例如SPI Flash)为例:
- 主到从路径(写操作):主设备数据在时钟边沿后
td(SPC_SIMO)M时间有效,经过PCB走线延迟Tpcb,到达从设备输入端。从设备要求在时钟边沿前tsu(SOMI_SPC)S时间数据稳定。因此,需要满足:Tclk/2 - Tskew + td(SPC_SIMO)M + Tpcb >= tsu(SOMI_SPC)S。其中Tclk是时钟周期,Tskew是时钟与数据之间的偏斜。 - 从到主路径(读操作):从设备在时钟边沿后
td(SPC_SOMI)S时间输出数据,经PCB延迟到达主设备。主设备要求tsu(SOMI_SPC)M的建立时间。因此需满足:Tclk/2 - Tskew + td(SPC_SOMI)S + Tpcb >= tsu(SOMI_SPC)M。
取两个路径计算出的更严格的Tclk最小值,即为系统能稳定工作的最短时钟周期。在PCB布线良好(Tpcb和Tskew很小)的情况下,OMAP-L138驱动典型的SPI从设备(如NOR Flash,其tsu和td通常在几个ns)跑在几十MHz通常是没问题的。但如果从设备速度较慢(例如某些慢速传感器),就必须降低SPI时钟频率。在驱动初始化时,切勿一开始就设置最高频率,应从较低频率(如1MHz)开始测试,逐步提高,直到发现通信错误,然后留出20%-30%的余量作为工作频率。
4.3 4/5引脚模式下的特殊时序
当引入SCS和ENA信号后,时序关系变得更加复杂,图6-40和图6-41清晰地展示了这些关系。
对于4引脚片选模式,关键参数是td(SCS_SPC)M和td(SPC_SCS)M。前者是片选有效到第一个时钟边沿的延迟,这个延迟给了从设备一个准备时间,使其内部电路稳定下来并准备好监听时钟。后者是最后一个时钟边沿到片选无效的延迟,这个延迟确保了最后一个数据位被从设备完整地锁存。OMAP-L138允许通过SPIDELAY.T2CDELAY寄存器位来软件增加这个延迟,这对于某些需要较长CS保持时间的存储器芯片(如一些EEPROM)是救命稻草。
对于4引脚使能模式和5引脚模式,核心是td(ENA_SPC)M和td(SPC_ENA)M(或从机端的td(SPC_ENAH)S)。这些参数定义了“握手”信号的时序窗口。td(ENA_SPC)M(主机参数)指的是从机拉低ENA(表示就绪)后,主机需要等待至少这个时间才能发出第一个时钟。如果主机过早发出时钟,可能采样到的是从机未就绪前的状态。td(SPC_ENA)M指的是最后一个时钟边沿后,从机必须在多长时间内释放ENA(拉高),否则主机可能会误认为从机已为下一次传输准备好,从而发起冲突的传输。
调试技巧:当使用带ENA的流控模式出现数据丢失时,第一个要怀疑的就是这些握手时序。用示波器同时抓取SCK、CS、ENA和一根数据线的波形,对照数据手册的时序图,测量关键的延迟时间是否满足要求。如果不满足,尝试调整SPIDELAY寄存器中的可编程延迟字段,或者降低SPI时钟频率。很多时候,问题就出在这里。
5. 寄存器配置实战:从零构建一个SPI驱动
理解了原理和时序,最终都要落到寄存器的配置上。下面我以一个具体的场景为例,展示如何配置OMAP-L138的SPI0模块作为主机,以4引脚片选模式(使用SCS0)与一个SPI Flash通信,模式为CPOL=0, CPHA=0,数据位宽8位,MSB先传。
5.1 初始化步骤与代码示例
首先,需要启用SPI0模块的时钟(这部分涉及系统时钟配置,此处略过)。假设引脚复用已配置好(通过PINMUX寄存器将相关引脚功能设置为SPI0)。
// 宏定义寄存器地址 (以SPI0为例) #define SPI0_BASE 0x01C41000 #define SPIGCR1 (*(volatile unsigned int *)(SPI0_BASE + 0x04)) #define SPIFMT0 (*(volatile unsigned int *)(SPI0_BASE + 0x50)) #define SPIDEF (*(volatile unsigned int *)(SPI0_BASE + 0x4C)) #define SPIDELAY (*(volatile unsigned int *)(SPI0_BASE + 0x48)) #define SPIPC0 (*(volatile unsigned int *)(SPI0_BASE + 0x14)) #define SPIDAT1 (*(volatile unsigned int *)(SPI0_BASE + 0x3C)) #define SPIBUF (*(volatile unsigned int *)(SPI0_BASE + 0x40)) #define SPIFLG (*(volatile unsigned int *)(SPI0_BASE + 0x10)) void SPI0_Master_Init(void) { // 1. 软件复位并关闭模块 SPIGCR1 = 0x00000000; // 确保模块禁用 // 2. 配置引脚功能:将SCS0, CLK, SIMO, SOMI 设置为SPI功能 // 假设这些引脚在SPIPC0寄存器中的位是已知的,例如BIT0: CLK, BIT1: SIMO, BIT2: SOMI, BIT3: SCS0 SPIPC0 |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3); // 3. 配置格式寄存器0 (SPIFMT0) // CHARLEN = 7 (表示8位数据,因为值=字长-1), POL=0, PHA=0, SHIFTDIR=0 (MSB first) // 假设预分频器设置为产生约10MHz时钟(系统时钟SYSCLK2假设为100MHz) // 计算公式: SPI时钟 = SYSCLK2 / (2 * (PRESCALE + 1)) // 若需10MHz, PRESCALE = (100 / (2*10)) -1 = 4 unsigned int prescale = 4; unsigned char charlen = 7; // 8 bits SPIFMT0 = (prescale << 24) | (0 << 23) | // SHIFTDIR=0, MSB first (0 << 22) | // POL=0 (0 << 21) | // PHA=0 (charlen << 16); // 4. 配置默认片选和延时 (使用SCS0) SPIDEF = 0x00000001; // 默认使用CS0线,其他CS线无效时为高 // 设置片选有效到第一个时钟的延迟(C2TDELAY)和最后一个时钟到片选无效的延迟(T2CDELAY) // 这里各插入约2个SYSCLK2周期延迟(假设P=10ns)。寄存器位域宽度为5位。 unsigned int c2tdelay = 2; unsigned int t2cdelay = 2; SPIDELAY = (t2cdelay << 8) | (c2tdelay << 0); // 5. 清除所有状态标志 // 向SPIFLG写入1可清除对应的标志位(根据数据手册) // 通常读取SPIFLG后,向需要清除的位写1。为简单起见,这里假设全清。 // 注意:有些标志是只读的,需要查看具体手册。这里仅为示例流程。 // SPIFLG = 0xFFFF; // 具体清除方式需参考手册,可能不是直接写 // 6. 使能主模式,并最终启动SPI模块 // 设置CLKMOD=1 (主模式), ENA=1 (使能模块) SPIGCR1 = (1 << 24) | (1 << 0); // Bit24: CLKMOD=1, Bit0: ENA=1 }5.2 数据传输函数实现
初始化完成后,就可以进行数据传输了。一个基本的阻塞式单字节收发函数如下:
unsigned char SPI0_TransferByte(unsigned char data) { // 等待发送缓冲区为空(TXBUF标志位为1表示空) // 这里需要根据SPIFLG寄存器的具体定义来查询,假设Bit8为TXBUF标志位 while (!(SPIFLG & (1 << 8))) { // 超时处理应在这里添加 } // 将要发送的数据写入SPIDAT1寄存器,同时选择格式0(DFSEL=00),并保持CS(CSHOLD=1) // CSHOLD=1意味着本次传输结束后CS线保持有效,适用于连续传输。 // 对于单次传输,也可以设置为0,让硬件自动释放CS。 SPIDAT1 = (0x00 << 22) | // DFSEL = 0, 使用SPIFMT0格式 (1 << 20) | // CSHOLD = 1, 传输后保持CS有效(根据后续操作决定) (data << 0); // 写入要发送的数据 // 等待接收缓冲区满(RXBUF标志位为1表示有数据) // 假设SPIFLG的Bit0为RXBUF标志位 while (!(SPIFLG & (1 << 0))) { // 超时处理 } // 从缓冲寄存器读取接收到的数据 unsigned char received_data = (unsigned char)(SPIBUF & 0xFF); return received_data; }对于多字节连续传输(例如读取Flash的ID或数据),更好的做法是使用DMA或者中断。配置DMA需要设置源地址(内存)、目标地址(SPIBUF/SPIDAT)、传输数量,并启用SPI模块的DMA事件。这能极大提升效率,但配置也更为复杂,需要仔细处理数据对齐和传输结束中断。
5.3 关键配置陷阱与避坑指南
- 使能顺序陷阱:绝对不要在配置完成前使能SPI模块(设置SPIGCR1.ENA=1)。正确的顺序是:引脚复用 -> 配置格式、延时等寄存器 -> 最后使能模块。错误的顺序可能导致引脚输出异常时钟或数据,损坏外设。
- 时钟极性相位不匹配:这是最常见的问题。务必确认主从设备的CPOL和CPHA设置完全一致。一个快速验证方法是:用示波器看时钟线和数据线。在CPOL=0,CPHA=0模式下,数据应在时钟上升沿稳定,在下降沿变化。如果不匹配,看到的采样点数据会是错误的。
- 片选管理混乱:在4引脚模式下,硬件自动管理CS线很方便,但要注意
SPIDAT1.CSHOLD位的使用。如果设置为1,在一次SPIDAT写入触发的传输结束后,CS会保持有效。你必须再写入一次SPIDAT(即使数据是哑元),并在这次传输前或后清除CSHOLD位,或者通过操作SPIDEF寄存器,才能释放CS。否则CS会一直拉低,独占总线。 - 使能(ENA)信号上拉:在4引脚使能或5引脚模式下,如果从设备配置为传输结束后将ENA引脚置为高阻态(
ENABLE_HIGHZ=1),那么必须在主设备的ENA引脚外部连接一个上拉电阻(通常4.7kΩ-10kΩ),以确保在从设备未驱动时,主设备能检测到确定的高电平(未就绪)状态。漏接这个上拉电阻是导致流控模式失效的典型硬件原因。 - 时序裕量不足:在接近最高频率运行时,或者PCB走线较长时,务必进行时序裕量计算。如果发现间歇性数据错误,首先尝试降低时钟频率。使用示波器测量关键时序点,特别是数据相对于时钟的建立和保持时间,确保它们满足从设备手册要求并留有足够余量(建议>20%)。
6. 高级应用与调试技巧
6.1 多从机系统设计
OMAP-L138的每个SPI模块支持最多4个硬件片选(SCS[3:0]),可以轻松连接4个独立的从设备。设计时,每个从设备的CS线独立,但SCK、SIMO、SOMI三根线是共享的。必须确保任何时刻只有一个从设备的MISO(SOMI)输出是有效的,其他必须处于高阻态。这通常由从设备的CS引脚控制其MISO输出使能来实现。在软件上,每次传输前正确设置SPIDEF或操作对应的SPIPC(如果CS用GPIO模拟)来选择目标从设备。
如果需要连接超过4个从设备,可以采用“菊花链”方式(如果从设备支持),或者使用外部译码器(如74HC138)来扩展片选信号。另一种方法是利用GPIO模拟额外的片选,但要注意软件控制CS的时序,确保在SPI传输开始前拉低,结束后拉高。
6.2 使用DMA进行高效数据传输
对于大数据量传输(如读写SPI Flash的多个扇区、传输图像数据),使用CPU轮询或中断来搬运每一个字节/字是巨大的浪费。OMAP-L138的SPI模块与EDMA(增强型DMA)控制器紧密集成。
配置SPI DMA的大致步骤:
- 配置SPI中断:使能传输结束(
INT0)或接收缓冲满(RXBUF)等中断,但中断服务程序(ISR)中不处理数据,仅用于通知DMA传输完成或处理错误。 - 配置EDMA通道:设置源地址(例如内存数组)、目标地址(
SPIDAT1寄存器用于发送,SPIBUF寄存器用于接收)、传输数量、地址递增模式等。关键是要将地址设置为恒定地址(因为SPI数据寄存器是固定的),并链接到正确的SPI事件(如SPI0_TX_EVT和SPI0_RX_EVT)。 - 启动传输:先启动EDMA通道,然后通过向
SPIDAT1写入数据(或直接由DMA写入)来触发第一次SPI传输。之后,SPI每完成一次收发,会自动触发DMA搬运下一个数据,形成流水线。 - 循环缓冲与链式DMA:对于连续流数据,可以设置DMA为乒乓(Ping-Pong)缓冲模式或链式(Chaining)模式,实现数据的不间断传输。
使用DMA后,CPU开销几乎为零,SPI总线的实际吞吐量可以非常接近理论时钟速率。在调试DMA+SPI时,建议先用简单的内存模式(Memory-to-Memory)测试DMA通道是否工作正常,然后再接入SPI。
6.3 示波器调试实战心法
当SPI通信出现问题时,示波器是你最好的朋友。以下是我的调试流程:
- 连接探头:至少连接SCK和一根数据线(通常是MOSI)。如果使用CS或ENA,也一并接上。确保探头接地良好,使用短接地弹簧而非长鳄鱼夹,以减少噪声。
- 触发设置:将触发源设置为CS信号(如果使用)的下降沿(有效沿)。如果没有CS,则用SCK的边沿触发。设置合适的触发电平。
- 观察静态波形:
- 检查空闲电平:看SCK在空闲时的电平是否与CPOL设置一致(CPOL=0应为低,CPOL=1应为高)。
- 检查数据对齐:放大波形,找到第一个数据位(通常是MSB)。检查数据是在SCK的哪个边沿(第一个还是第二个)保持稳定,哪个边沿发生变化。这应与CPHA设置一致。
- 测量建立/保持时间:使用示波器的测量功能或光标,测量数据信号在SCK采样边沿前后的稳定时间。对比从设备手册要求的最小值。
- 解码SPI协议:现代数字示波器大多有SPI解码功能。打开它,设置正确的极性、相位、位序和帧大小(如8位)。示波器会自动将波形解析为十六进制或二进制数据。这是验证你发送的数据是否正确、以及从设备是否响应的最快方法。如果解码出来的数据与你预期不符,检查配置、连线或从设备电源。
- 检查流控信号(如使用):如果使用了ENA,观察其与CS、SCK的时序关系。它是否在从设备准备好后才变低?主设备是否在检测到ENA低电平后才发出SCK?时序是否符合数据手册中
td(ENA_SPC)M等参数的要求?
记住,很多SPI问题源于简单的配置错误或硬件连接问题(如虚焊、线接反)。系统性的测量和逻辑分析能帮你快速定位问题根源。