深入解析MibSPI的TG7CTRL与DMAxCTRL寄存器:硬件自动化SPI通信设计
1. MibSPI核心机制与寄存器设计哲学
在嵌入式系统,尤其是汽车电子和工业控制这类对实时性和可靠性要求极高的领域,SPI通信的效率直接决定了系统的整体性能。传统的SPI驱动方式,无论是轮询还是中断,都不可避免地会消耗大量CPU资源,在高速、多通道、多设备的数据交换场景下显得力不从心。德州仪器(TI)在其多款高性能微控制器中集成的多缓冲SPI模块,其设计哲学正是为了解决这一核心矛盾:将复杂的通信序列管理和数据搬运工作,从软件和CPU手中解放出来,交由硬件自动、并发地执行。
MibSPI的精髓在于“多缓冲”和“传输组”这两个概念。你可以把它想象成一个高度自动化的物流分拣中心。普通的SPI就像一个快递员,每次只能处理一个包裹(数据帧),取件、派送、再取下一个,全程需要你(CPU)不停地打电话(中断)或盯着(轮询)指挥。而MibSPI则建立了一个拥有多个智能货架(缓冲区)和预设路线图(传输组)的自动化分拣系统。
缓冲区是这个系统的静态存储单元,每个缓冲区都预先配置好了通信参数(如时钟极性、相位、字长、片选引脚等),以及它对应的数据内容。这就像为每个包裹贴上了详细的派送标签和目的地信息。
传输组则是动态的执行逻辑。它将一系列缓冲区按顺序组织成一个“传输任务列表”。更重要的是,它为这个任务列表定义了触发条件(比如某个外部引脚的电平变化、定时器事件,甚至是另一个传输组的完成信号)和执行策略(是执行一次就停止,还是循环执行;被高优先级任务打断后如何处理)。TG7CTRL寄存器,就是用来配置第七号传输组(TG7)所有这些行为的“控制面板”。
而DMA通道,则是连接这个自动化分拣中心与仓库(系统内存)之间的高速传送带。DMAxCTRL寄存器的作用,就是配置这条传送带的工作模式:它服务于哪个货架(缓冲区)、一次搬运多少货物(ICOUNT)、是连续搬运直到搬完(NOBRK模式)还是允许穿插其他搬运任务、以及搬运完成后是否自动停止(ONESHOT模式)。
理解TG7CTRL和DMAxCTRL,本质上是在学习如何为这个硬件自动化系统编写“工作流程脚本”。你的代码不再需要关心每一个时钟沿上的数据位,而是转变为更高层次的策略制定者:定义任务、设置规则、建立关联,然后启动系统,让硬件自己去忙。这种从“过程式驱动”到“声明式配置”的转变,是驾驭现代复杂嵌入式外设的关键。
2. TG7CTRL寄存器:传输组的行为控制器
TG7CTRL寄存器是控制第七传输组所有行为的核心。它的每一个位域都直接决定了该传输组如何响应事件、如何执行以及如何与其他任务协调。我们逐位深入剖析,并结合实际场景解释其配置逻辑。
2.1 核心使能与执行模式位域
TGENA (Bit 31): 传输组使能这是传输组的“总开关”。只有将此位置1,对应的传输组才进入就绪状态,等待其设定的触发条件到来。这里有一个至关重要的优先级调度机制需要理解:MibSPI内部有一个硬件调度器。当多个传输组同时满足触发条件时,编号小的传输组(如TG0)拥有更高的优先级。此外,如果一个高优先级的传输组正在传输(Active Transfer Mode),低优先级的传输组即使被触发,也必须等待其完成。但是,如果高优先级传输组处于“挂起等待”模式(例如,在等待DMA填充数据),那么调度器可以转而执行一个已被触发的低优先级传输组。这种设计实现了硬件级的任务抢占与协作,是构建复杂、确定性强实时通信序列的基础。
ONESHOT (Bit 30): 单次传输模式此模式用于需要精确控制执行次数的场景。当ONESHOT=1时,传输组在响应一次有效触发事件并完成整个组传输(即遍历完组内所有缓冲区)后,硬件会自动将TGENA位清零。这意味着该传输组在执行一次后便自动禁用,直到你再次手动置位TGENA。这个功能非常实用,例如,在系统初始化时需要发送一段特定的配置序列给外设,发送一次即可,之后便不再需要。它确保了关键配置命令不会被意外重复发送,同时也节省了你在软件中手动禁用的步骤。
PRST (Bit 29): 指针复位模式这是理解MibSPI传输流控制的一个难点,它专门针对电平触发的传输组。对于边沿触发的传输组,每次触发事件都是离散的,PRST位不起作用。但对于电平触发(TRIGEVT配置为高有效或低有效)的传输组,触发信号是一个持续的状态。PRST位决定了,当传输组正在执行传输的过程中,如果触发信号依然有效(对于电平触发而言,这意味着“要求再次启动”),硬件该如何处理。
- PRST = 0 (默认): 传输优先。一旦传输开始,在本次组传输完成之前,任何新的触发事件(对于电平触发,即电平持续有效)都会被忽略。这保证了当前传输任务的完整性,避免被打断。
- PRST = 1: 触发优先。只要触发条件有效,它就会强制将传输组的当前指针
PCURRENT重置为起始地址PSTART。这意味着,如果一次传输还没完成,新的触发事件会强制它从头开始。这在某些需要“最新数据覆盖”或“紧急命令中断”的场景下有用。例如,一个用于周期性发送传感器数据的电平触发传输组,如果PRST=1,那么当新的传感器数据准备好(触发信号有效)时,无论上一次发送是否完成,都会立即中止并开始发送最新的数据包,确保接收端总是拿到最新的信息。
TGTD (Bit 28): 传输组已触发状态位这是一个只读状态位。当TGTD=1时,表示该传输组已经被某个触发事件“标记”了,它可能正在服务中,也可能正在排队等待服务(因为更高优先级的传输组正在运行)。要精确知道哪个传输组正在实际占用SPI总线进行传输,需要查询LTGPEND寄存器中的“TG IN SERVICE”字段。TGTD位更像是一个“任务已提交”的标志,而“TG IN SERVICE”则是“任务正在执行”的标志。在调试时,可以通过监控TGTD位来判断你的触发逻辑是否按预期工作。
2.2 触发源与事件类型配置
TRIGSRC[3:0] (Bits 19-16): 触发源选择这4个位定义了“谁来按下传输组的启动按钮”。MibSPI提供了丰富的触发源,从完全由软件控制到各种硬件事件。
- 0000b: Disabled: 无触发源。通常需要与
TRIGEVT=0111b (ALWAYS)和ONESHOT=1配合,实现纯软件触发:先配置好这些位,然后写TGENA=1,传输组立即启动。 - 0001b - 1110b: EXT0 - EXT13: 外部触发源。具体对应哪个物理引脚或内部模块(如HET的高分辨率定时器通道、GPIO事件),需要查阅具体芯片的数据手册。这允许SPI传输由外部传感器信号、其他定时器模块或通信接口的事件来启动,实现系统级联动。
- 1111b: TICK: MibSPI内部周期性事件。这通常关联到一个可编程的定时器(Tick Counter),可以产生固定周期的触发脉冲,用于实现无需CPU干预的定时数据采集或发送,是构建后台自动通信任务的利器。
TRIGEVT[3:0] (Bits 23-20): 触发事件类型这决定了触发源信号需要呈现何种变化,才能被认为是一个有效的“触发事件”。
- 0000b: Never: 永不触发。一种禁用状态。
- 0001b: Rising Edge: 上升沿触发。经典、干净的触发方式,适用于脉冲型启动信号。
- 0010b: Falling Edge: 下降沿触发。同样常见。
- 0011b: Both Edges: 双边沿触发。任何跳变都会启动一次传输。适用于需要快速响应信号变化的场景。
- 0101b: High-active: 高电平有效。这是一种电平触发模式。只要触发源信号为高电平,传输组就会连续、循环地执行其缓冲区序列(除非ONESHOT=1,则只执行一次)。如果在传输过程中电平变低,则传输会立即停止。这常用于“使能”型控制,比如一个片选信号为高时,持续向某个设备发送数据流。
- 0110b: Low-active: 低电平有效。与高电平有效逻辑相反。
- 0111b: Always: 总是触发。这是一种特殊模式,当选择此模式时,传输组会忽略TRIGSRC的信号,只要
TGENA=1,它就会根据ONESHOT的设置立即开始连续执行或单次执行。这通常用于实现纯软件触发或与其他条件组合成复杂逻辑。
注意:电平触发(High-active/Low-active)与PRST位的配合是高级应用的关键。如果你希望一个电平触发传输组在触发信号持续期间只完整执行一遍序列就停止,即使信号还在有效,也需要设置
ONESHOT=1。如果你希望信号一有效就立即启动,并且信号持续期间不断重复,则设置ONESHOT=0。PRST则进一步决定了在重复执行时,如果上一次循环还没结束,新循环是等待还是强行重启。
2.3 缓冲区指针管理
PSTART[15:8] (Bits 15-8): 传输组起始地址这个8位字段定义了该传输组所管理的缓冲区序列的起始缓冲区编号(0-255)。MibSPI的缓冲区在内存中是线性排列的。PSTART指向这个序列的第一个缓冲区。序列的结束地址是隐式定义的:它是下一个传输组的PSTART减1。例如,如果TG7的PSTART=0x20,TG8的PSTART=0x30,那么TG7管理的缓冲区范围就是0x20到0x2F(共16个缓冲区)。这种设计使得缓冲区序列可以无缝衔接,便于管理。
PCURRENT[7:0] (Bits 7-0): 当前缓冲区指针这是一个只读指针,指示传输组当前正在操作(或下一个将要操作)的缓冲区编号。在以下三种情况下,PCURRENT会被硬件自动加载为PSTART的值:
- 传输组被使能(
TGENA从0变为1)。 - 传输组完成了整个序列的传输(到达了隐式的结束地址)。
- 传输组处于
PRST=1模式,且在传输过程中收到了新的触发事件。
当传输组因为等待条件(如等待DMA)而进入“挂起等待”模式时,PCURRENT会保持指向当前被挂起的缓冲区。当条件满足、传输组恢复执行时,它会从这个缓冲区继续,而不会重复或跳过任何缓冲区,保证了数据传输的完整性。
3. DMAxCTRL寄存器:打通SPI与内存的高速通道
DMAxCTRL寄存器(x为0-3)用于配置MibSPI的四个DMA通道。每个通道可以独立地将一个指定的SPI缓冲区与系统内存通过DMA控制器连接起来,实现数据自动搬入(发送)和搬出(接收)。
3.1 DMA通道的使能与映射
RXDMAENA (Bit 15) / TXDMAENA (Bit 14): 接收/发送DMA通道使能这两个位分别独立控制接收路径和发送路径的DMA请求生成。一个常见的误区是认为使能DMA后,SPI和DMA就会自动同步。实际上,这里有一个关键的同步时序问题需要处理。
- 发送使能 (TXDMAENA): 当此位置1后,MibSPI会立即产生一个DMA发送请求,要求DMA控制器填充第一个待发送数据到指定的缓冲区(
BUFID)。因此,在使能TXDMAENA之前,你必须确保DMA控制器已经正确配置好源地址(通常是内存中的发送数组),并且目标地址就是MibSPI的这个缓冲区。否则,DMA可能会传输错误的数据。 - 接收使能 (RXDMAENA): 当此位置1后,MibSPI不会立即产生请求。它会在完成一次针对
BUFID缓冲区的SPI传输后,才产生第一个DMA接收请求,要求DMA控制器将接收到的数据从缓冲区搬走。因此,你需要确保在SPI传输开始前使能接收DMA。
为了实现可靠的同步,数据手册强烈建议将BUFID指向的缓冲区配置为“挂起等待直到...”模式。例如,对于发送,配置为“挂起等待直到TXFULL被清除”(即等待DMA填满数据);对于接收,配置为“挂起等待直到RXEMPTY被设置”(即等待DMA取走数据)。这样,SPI序列器会在该缓冲区处自动暂停,直到DMA完成相应操作,完美解决了数据竞争问题。
RXDMA_MAP[3:0] / TXDMA_MAP[3:0] (Bits 23-20 / 19-16): DMA请求线映射这是连接MibSPI模块与芯片内部DMA控制器的“物理接线”定义。每个MibSPI DMA通道需要两根请求线:一根用于发送请求,一根用于接收请求。这两个字段分别指定它们连接到DMA控制器的哪一根物理请求线上(编号通常为0~31,取决于具体芯片)。
重要警告:你必须确保在同一MibSPI模块内,以及在整个芯片范围内,任何两条同时使用的DMA请求线编号都不能相同。如果
RXDMA_MAP和TXDMA_MAP设成了相同值,或者与其他外设(如另一个SPI、ADC、ePWM)的DMA请求线冲突,会导致DMA控制器无法区分请求来源,引发不可预知的数据传输错误,这种错误通常很难调试。在系统设计初期,就需要规划好所有外设的DMA请求线分配。
3.2 传输模式与长度控制
BUFID[7:0] (Bits 30-24, 其中Bit 7为BUFID7): DMA目标缓冲区这个字段(7位或8位,取决于是否启用扩展缓冲区功能)指定了当前DMA通道服务于哪个SPI缓冲区。所有通过此DMA通道搬运的数据(无论是发送还是接收),其目的地或来源都是这个特定的缓冲区。这再次强调了MibSPI的架构:DMA通道是与缓冲区绑定的,而不是直接与传输组绑定。一个传输组可以包含多个缓冲区,每个缓冲区可以被不同的DMA通道服务,从而实现复杂的流水线操作。
NOBRK (Bit 13): 非交错块传输模式这是DMA操作中一个提升性能的关键位,但仅在主模式下有效。默认情况下(NOBRK=0),MibSPI序列器会在各个活跃的缓冲区和传输组之间进行“时间片轮转”。假设你有一个DMA缓冲区正在传输,同时还有一个高优先级的传输组被触发,序列器可能会在传输完DMA缓冲区的一个数据帧后,立刻跳去执行高优先级传输组的一个帧,然后再回来。这种“交错”传输保证了高优先级任务的实时性,但破坏了DMA缓冲区数据传输的连续性。
当NOBRK=1时,情况完全不同。一旦序列器开始服务这个DMA缓冲区,它就会锁定在此,连续传输ICOUNT+1个数据帧,期间不会被任何其他更高优先级的传输组或缓冲区打断。这会产生一个连续的、无间隔的SPI数据流。这对于需要维持片选信号(CS)持续有效的设备(如某些Flash存储器、ADC)进行突发读写操作至关重要。你只需要将对应缓冲区的CSHOLD位设为1,在NOBRK传输期间,片选信号就会一直保持有效,从而实现高效的数据块传输。
ICOUNT[4:0] (Bits 12-8): 初始传输计数这个5位字段定义了块传输的长度。实际传输的数据帧数量是ICOUNT + 1。例如,ICOUNT = 4,则实际传输5帧数据。这个计数器被加载到只读的COUNT[5:0]字段中,每完成一帧传输,COUNT减1。当COUNT减到0时,如果ONESHOT=0,它会自动重载ICOUNT的值,继续下一轮传输;如果ONESHOT=1,则会在本次块传输完成后停止。
ONESHOT (Bit 31): DMA通道单次传输此位与传输组的ONESHOT功能类似,但作用在DMA通道层面。当ONESHOT=1时,DMA通道在完成ICOUNT+1次传输后,硬件会自动将RXDMAENA和TXDMAENA清零,从而停止产生DMA请求。这个模式特别适合与NOBRK模式结合使用,用于发起一次确定长度的、不可被打断的突发传输。传输结束后,通道自动关闭,无需软件干预。
COUNT[5:0] (Bits 5-0): 剩余传输计数这是一个只读状态字段,实时显示当前DMA块传输还剩余多少帧数据需要传输。在调试时,监控这个字段可以清楚地了解DMA传输的进度。
4. 实战配置:构建一个自动化的传感器数据采集系统
理论需要结合实践。让我们设计一个常见的应用场景:使用MibSPI的TG7和DMA1通道,实现对一个SPI接口的工业温度传感器进行定时采集,并通过DMA将数据自动存入内存数组。
系统目标:
- 每100ms采集一次传感器数据(共16位,即2字节)。
- 使用DMA自动将采集到的数据存入一个长度为100的循环缓冲区(即存储最近5秒的数据)。
- 整个过程无需CPU频繁中断,仅在DMA完成100次传输(即缓冲区满)后通知CPU一次。
4.1 硬件与缓冲区配置
假设我们使用芯片的SPI3接口,传感器连接在SPI3的片选0上。
- 配置SPI3基础参数:主模式,时钟极性=0,相位=0,比特率设为1 Mbps。
- 配置缓冲区:我们至少需要两个缓冲区。
- 缓冲区 100 (0x64): 用于命令发送。配置为:
CSHOLD=0(发送完命令即释放片选),数据长度为8位,内容为传感器的读命令字节(例如0xAA)。 - 缓冲区 101 (0x65): 用于数据接收。配置为:
CSHOLD=1(保持片选有效以连续读取),数据长度为16位,工作模式设置为“挂起等待直到 RXEMPTY 被设置”。这是关键!这会让序列器在发送完命令后,停在这个缓冲区,等待DMA将上一次接收的数据取走(RXEMPTY被置1),然后才进行下一次16位数据的读取,完美同步DMA。
- 缓冲区 100 (0x64): 用于命令发送。配置为:
4.2 传输组TG7配置
我们的目标是让TG7每100ms自动执行一次“发送命令+读取数据”的序列。
- 设置缓冲区序列:
TG7PSTART = 0x64。这意味着TG7管理从缓冲区100开始的序列。由于我们只用了100和101两个缓冲区,且没有定义TG8,所以TG7的序列默认到最后一个缓冲区(255)结束,但我们的传输会在缓冲区101完成后,因为下一个缓冲区未定义或未使能而自然停止。 - 配置触发:
TRIGSRC = 1111b(TICK): 使用MibSPI内部定时器触发。TRIGEVT = 0001b(Rising Edge): 定时器溢出产生上升沿触发。- 需要配置MibSPI的TICK计数器分频,使其产生100ms的周期信号。
- 配置执行模式:
TGENA = 1: 使能传输组。ONESHOT = 0: 我们需要周期性采集,所以不设为单次。PRST = 0: 电平触发不适用,设为0。
这样,每隔100ms,硬件定时器触发TG7,TG7自动执行缓冲区100(发送命令)和缓冲区101(读取16位数据)的操作。
4.3 DMA通道1配置
我们需要用DMA1来自动搬运缓冲区101中接收到的数据。
- 关联缓冲区:
DMA1BUFID = 0x65。告诉DMA1,你的服务对象是缓冲区101。 - 配置DMA请求映射:查阅芯片手册,找到两个未被使用的DMA请求线,例如请求线8和9。设置
RXDMA_MAP = 0x8,TXDMA_MAP = 0x9。我们只接收数据,所以实际上只用了接收映射。 - 配置DMA控制器(这部分是芯片DMA控制器的寄存器,非MibSPI):
- 源地址:MibSPI接收数据寄存器(SPI3BUF)。
- 目的地址:内存中的循环数组
SensorData[100]。 - 传输宽度:16位(与缓冲区配置匹配)。
- 传输模式:连续模式,目的地址自动递增,源地址固定。
- 传输计数:100(我们的数组大小)。
- 配置DMA通道,使其响应MibSPI通道1的接收请求(对应我们映射的请求线8)。
- 配置MibSPI DMA控制:
RXDMAENA = 1: 使能接收DMA。注意,应在TG7使能之前就配置好此位,因为一旦TG7开始传输,缓冲区101收到数据后就会立即产生DMA请求。TXDMAENA = 0: 本例不需要发送DMA。NOBRK = 0: 我们不需要连续突发传输,允许交错。ONESHOT = 0: 我们希望DMA持续工作。实际上,DMA控制器的传输计数(100)控制了总传输量。当DMA控制器完成100次传输后,它会自动停止并产生中断通知CPU。MibSPI侧的ONESHOT保持为0即可。ICOUNT = 0: 此模式下,传输长度由DMA控制器控制,ICOUNT可忽略或设为0。
4.4 系统启动与流程
- 按上述步骤配置好所有缓冲区、TG7和DMA1。
- 启动DMA控制器通道。
- 最后,置位
TG7CTRL.TGENA。 - 系统开始自动运行:每100ms,TICK触发TG7 -> TG7发送命令并读取数据 -> 数据存入缓冲区101 -> 缓冲区101产生DMA请求 -> DMA将数据搬入
SensorData数组 -> 缓冲区101状态更新,序列器继续。 - 当DMA完成100次搬运,触发中断,CPU在中断服务程序中处理这100个数据(如求平均、判断告警),并重置DMA目的地址和计数,开始下一轮循环采集。
5. 深度调试技巧与常见陷阱排查
即使理解了所有位域,实际调试中依然会遇到各种问题。以下是我在多个项目中总结出的核心排查思路和常见陷阱。
5.1 传输组不触发的排查清单
- 检查TGENA:这是最容易被忽略的。确认
TGENA位确实已置1。有时在配置其他位时意外将其清零。 - 确认触发源和事件:用示波器或逻辑分析仪检查
TRIGSRC选择的物理引脚或内部信号,是否确实产生了符合TRIGEVT设定的边沿或电平。例如,如果你配置为上升沿触发,但信号一直是高电平,则不会触发。 - 优先级阻塞:使用调试器读取
LTGPEND寄存器。检查是否有更高优先级的传输组(TG0-TG6)正处于“服务中”(IN SERVICE)状态。你的TG7必须等待它们完成。考虑调整传输组优先级分配,或检查高优先级传输组是否因等待条件(如DMA)而卡住。 - 缓冲区状态:传输组执行的基础是缓冲区。检查TG7对应的起始缓冲区
PSTART及其后续缓冲区是否已正确配置(例如,CSNR片选号、数据长度、时钟模式等)。一个未正确配置的缓冲区可能导致序列器提前停止。
5.2 DMA数据错乱或丢失的排查清单
- 请求线冲突:这是最隐蔽的bug之一。反复核对
RXDMA_MAP/TXDMA_MAP的值,确保在整个芯片范围内唯一。检查其他所有使用DMA的外设(UART, ADC, ePWM等)的配置。 - 缓冲区同步模式错误:这是导致数据丢失最常见的原因。对于DMA操作的缓冲区,务必将其配置为“挂起等待”模式(
SUSPEND = 1, 并设置合适的SUSPSRC,如RXEMPTY或TXFULL)。如果没有这个配置,SPI序列器可能在该缓冲区数据未被DMA处理完之前就再次使用它,造成数据被覆盖。 - DMA使能时序错误:
- 发送DMA:必须在SPI序列器开始使用该缓冲区之前,就使能
TXDMAENA并确保DMA控制器已就绪。因为使能瞬间就会产生请求。 - 接收DMA:必须在SPI传输开始前使能
RXDMAENA。可以在TG使能前配置好。
- 发送DMA:必须在SPI序列器开始使用该缓冲区之前,就使能
- 数据长度不匹配:确保MibSPI缓冲区的数据长度(如16位)与DMA控制器配置的传输宽度(如字节、半字、字)匹配。不匹配会导致地址计算错误和内存对齐问题。
- NOBRK模式下的中断延迟:在
NOBRK=1模式下,高优先级传输组也无法打断。如果你的高优先级任务对实时性要求极高,需要评估其最大允许延迟是否会被NOBRK块传输时间所超越。块传输时间 =(ICOUNT+1) * 每帧数据时间。
5.3 高级调试手段:利用状态寄存器
不要只盯着控制寄存器,状态寄存器能告诉你硬件“正在做什么”和“发生了什么”。
- TGSTAT寄存器:查看各个传输组的当前状态(禁用、就绪、运行、挂起等)。
- BUFx寄存器:每个缓冲区都有状态位,如
TXFULL,RXEMPTY,可以帮你判断数据流是否堵塞。 - DMAXCOUNT寄存器:实时查看
COUNT值,了解DMA块传输的剩余帧数。 - 全局中断标志:MibSPI有丰富的中断源,如传输完成中断、DMA计数为零中断等。在调试初期,可以使能相关中断,在中断服务程序中设置标志位或打印信息,能非常清晰地追踪程序的执行流。
个人心得:配置MibSPI这类复杂外设,最有效的方法是“分步验证”。不要试图一次性配置好所有功能并期望它工作。我的习惯是:首先,仅配置一个缓冲区和一个传输组,不使用DMA,用查询或中断方式,让最基本的SPI通信跑通。然后,加入DMA,但先只做发送或只做接收,验证数据链路。最后,再组合成复杂的多传输组、多缓冲区、带触发的完整应用。每一步都通过读取状态寄存器和实际数据来验证,这样能将问题隔离,极大提高调试效率。记住,硬件不会说谎,状态寄存器就是它对你说的“真话”。