1. 通用定时器(GPT)在嵌入式系统中的核心地位
在嵌入式开发领域,尤其是涉及电机控制、电源转换、传感器接口或通信协议生成时,精确的时序控制是项目成败的关键。无论是驱动一个步进电机的微步进,还是生成一个特定频率的PWM信号来调节LED亮度,亦或是精确捕获一个外部脉冲的宽度,其底层都离不开一个核心硬件模块:通用定时器。对于使用TI CC35xx这类集成Wi-Fi 6和蓝牙低功耗的无线MCU开发者而言,深入理解其内置的通用定时器模块,是解锁高级实时控制能力、优化系统性能和可靠性的必经之路。
通用定时器,常简称为GPT,绝非一个简单的“计数器”。它是一个高度集成、功能丰富的数字外设,其本质是一个可编程的时钟计数器,配合一系列比较、捕获和输出控制逻辑,能够以硬件方式独立完成复杂的时序任务,极大减轻CPU的负担。在CC35xx这类资源受限但对实时性要求高的物联网设备中,GPT的价值尤为突出。它允许开发者在后台进行精确的PWM生成、输入信号测量,甚至多轴运动的同步控制,而主CPU可以专注于处理网络协议栈或应用程序逻辑,从而实现高效的系统分工。
2. CC35xx GPT模块架构与核心功能解析
CC35xx设备通常包含多个GPT实例,每个GPT都是一个功能完整的定时器单元。理解其整体架构是进行有效编程的第一步。
2.1 模块整体框图与数据通路
从逻辑上看,一个GPT模块可以抽象为几个核心部分:时钟源与预分频器、核心计数器、多个独立的捕获/比较通道以及输出控制逻辑。这些部分通过内部总线紧密协作。
- 时钟源:GPT的“心跳”可以来自系统主时钟(如80MHz),也可以来自事件管理器(Event Manager)提供的
TICKEN信号。后者为跨外设的精确同步提供了可能。 - 预分频器:这是一个8位向下计数器,通过对时钟源进行分频,产生最终的“定时器时钟”,用于驱动核心计数器。通过配置
PRECFG.TICKDIV寄存器,我们可以灵活调整计数器的基准频率,以适应从微秒到秒级的不同定时需求。 - 核心计数器:一个32位的向上/向下计数器,是GPT的“发动机”。其计数模式(单次、周期、上下周期等)决定了定时行为的基础波形。
- 捕获/比较通道:这是GPT的“多面手”。每个GPT通常有多个这样的通道(例如4个)。在“捕获”模式下,通道可以在外部信号发生特定边沿时,瞬间锁存当前计数器的值,用于测量脉冲宽度或周期。在“比较”模式下,通道会持续将计数器值与一个预设值进行比较,当匹配时触发预先定义的动作,如翻转一个输出引脚、产生中断或启动DMA传输。
- 输出控制与死区插入:比较事件产生的动作最终会作用到GPIO引脚上。GPT支持复杂的输出控制,包括直接设置、清除、翻转或产生一个固定宽度的脉冲。更高级的是,对于电机驱动等需要防止上下桥臂直通的场景,GPT支持互补PWM输出和可编程死区插入。死区时间是指在互补信号切换时,插入一个两者都为低电平的短暂延时,确保一个开关管完全关断后,另一个才开启。
注意:在阅读数据手册时,务必区分“定时器时钟”(Timer Clock)和“预分频器时钟”(Prescaler Clock)。定时器时钟是计数器
CNTR递增/递减的速率,而预分频器时钟是驱动预分频器本身的速率。通道滤波器的采样时钟可以独立选择为两者之一,这为抗噪声设计提供了灵活性。
2.2 核心寄存器组概览
与GPT交互主要通过内存映射的寄存器。以下是一些最关键的寄存器类别,理解它们的功能是编程的基础:
- 控制寄存器:如
CTL,用于设置计数器模式(MODE)、使能定时器等。 - 预分频配置寄存器:
PRECFG,用于选择时钟源和设置分频系数。 - 目标值寄存器:
TGT和PTGT。TGT是当前周期值,PTGT是“流水线”目标值。在PWM应用中,更新PTGT可以在下一个周期开始时无缝切换到新周期值,避免周期抖动。 - 通道配置寄存器:
CnCFG(n为通道号)。这是通道的“大脑”,配置其工作模式(捕获还是比较?具体哪种动作?)、输入源、输出映射、边沿检测类型等。 - 通道比较/捕获值寄存器:
CnCC和PCnCC。在比较模式下,CnCC存放比较值;在捕获模式下,捕获到的计数器值会存入CnCC。PCnCC是其对应的流水线寄存器,用于无抖动更新。 - 中断与DMA寄存器:
IMASK用于使能特定事件的中断,RIS用于查看挂起的中断状态。DMA相关寄存器则用于配置DMA传输触发。
3. 核心工作模式深度剖析与配置指南
GPT的强大源于其多样化的可配置模式。下面我们深入几个最常用和关键的模式。
3.1 计数器模式:定时行为的基石
计数器模式由CTL.MODE字段控制,它定义了计数器CNTR如何运行:
UP_ONCE:单次向上计数。从0开始计数到TGT值,然后停止。适用于需要单次延时或触发的场景。UP_PER:周期性向上计数。从0计数到TGT,然后自动重置为0并重新开始。这是生成边沿对齐PWM最常用的模式。UPDWN_PER:周期性向上向下计数。从0计数到TGT,然后递减回0,如此循环。这是生成中心对齐PWM的基础。QDEC:正交解码模式。在此模式下,计数器会根据两个相位差90度的编码器信号(PHA, PHB)自动进行加减计数,用于读取旋转编码器或线性尺的位置。SYNC_*模式:同步模式。允许一个GPT的计数器与另一个GPT(通常是GPT0)同步启动,实现多个定时器的精确对齐,在多轴协同控制中至关重要。
实操心得:在
UP_PER和UPDWN_PER模式下,TGT寄存器的值实际对应的是“周期-1”。例如,若定时器时钟为1MHz,需要产生1kHz的PWM,则周期应为1000个计数。在UP_PER模式下,TGT应设置为999;在UPDWN_PER模式下,计数器会从0到TGT再到0,一个完整周期是2 * TGT个计数,因此TGT应设置为499。
3.2 通道动作:GPT的灵魂操作
每个通道的CnCFG.CCACT字段定义了该通道的具体行为,这是GPT功能多样化的核心。动作可分为单次和连续两大类。这里重点解析几个工程中最关键的动作:
3.2.1 周期与脉宽测量此模式(CCACT = PER_PULSE_WIDTH_MEAS)是测量外部信号频率和占空比的利器。通道会连续测量输入信号的周期和脉冲宽度。其巧妙之处在于,它会用输入信号的边沿来同步复位计数器。这意味着,即使被测信号与定时器时钟不同步,也能获得精确测量。如图13-5所示,两个通道可以交替测量两个不同信号的参数,实现时间复用。
配置要点:
- 将通道
CCACT设置为PER_PULSE_WIDTH_MEAS。 - 通过
CnCFG.INPUT选择输入源(GPIO或事件管理器信号)。 - 通过
CnCFG.EDGE选择捕获边沿(上升沿、下降沿或双边沿)。测量周期通常使用相同的边沿,测量高电平脉宽则使用上升沿捕获、下降沿捕获相结合的逻辑(通常由硬件自动处理,CnCC存周期,PCnCC存脉宽)。 - 使能该通道对应的输出事件(
CnCFG.OUTn),以便在测量完成时触发中断或DMA。
3.2.2 重复比较动作:PWM生成的引擎这是生成PWM的核心。两种最常用的动作是:
SET_ON_0_TGL_ON_CMP:在CNTR=0时置位输出,在CNTR=CnCC时翻转输出。结合UP_PER模式,可生成边沿对齐PWM。PWM周期由TGT决定,高电平时间由CnCC决定。CLR_ON_0_TGL_ON_CMP:在CNTR=0时清零输出,在CNTR=CnCC时翻转输出。结合UPDWN_PER模式,可生成中心对齐PWM。此时,输出在CNTR=CnCC和CNTR=TGT-CnCC时都会翻转,形成对称的PWM波形。中心对齐PWM在电机驱动中能有效降低谐波噪声。
无抖动更新技巧:在PWM运行时,直接修改CnCC或TGT可能会导致当前周期输出出现毛刺或宽度异常。正确的做法是更新其对应的流水线寄存器PCnCC或PTGT。硬件会在下一个计数器周期开始时(CNTR=0)自动将流水线寄存器的值加载到工作寄存器中,实现平滑过渡。
3.3 输入滤波与边沿检测
外部信号往往带有噪声,GPT的通道输入滤波器是抗干扰的第一道防线。如图13-3所示,信号在进入边沿检测器之前,会经过一个可配置的数字滤波器。
- 工作原理:滤波器计数器从
CHFILT.LOAD值开始递减,仅当连续LOAD+1个采样时钟周期内输入信号都稳定不变时,该变化才会被传递。任何抖动都会重置计数器。 - 时钟选择:滤波器采样时钟可以独立选择为系统时钟、预分频器时钟或定时器时钟。对于慢速信号,使用定时器时钟滤波即可;对于需要抑制高频毛刺的快速信号,则应使用更快的系统时钟进行采样滤波。
- 配置公式:必须确保滤波器延时不会错过有效信号。即:
滤波器时钟周期 * (CHFILT.LOAD + 1) < 待测信号的最小脉宽。如果此条件不满足,可能会丢失捕获事件。
4. 高级应用场景实现与避坑指南
掌握了基础原理和配置后,我们可以将这些功能组合起来,解决复杂的工程问题。
4.1 生成高精度PWM信号
假设我们需要用GPT0的通道0生成一个38kHz、占空比50%的PWM方波(例如用于红外载波)。
计算参数:系统时钟80MHz,目标频率38kHz。
- 周期计数 = 80MHz / 38kHz ≈ 2105。
- 在
UP_PER模式下,TGT= 周期 - 1 = 2104。 - 对于50%占空比,
C0CC=TGT/ 2 = 1052。 - 但为了更精确,我们可以利用预分频器。设
PRECFG.TICKDIV = 4,则预分频后时钟 = 80MHz / (4+1) = 16MHz。 - 新周期计数 = 16MHz / 38kHz ≈ 421。
TGT= 420,C0CC= 210。这样量化误差更小。
配置步骤:
// 1. 配置预分频器 GPT0.PRECFG.TICKSRC = 0; // 使用系统时钟 GPT0.PRECFG.TICKDIV = 4; // 分频系数5 // 2. 配置周期和比较值 GPT0.TGT = 420; GPT0.C0CC = 210; // 3. 配置通道0动作和输出 GPT0.C0CFG.CCACT = SET_ON_0_TGL_ON_CMP; // 边沿对齐PWM GPT0.C0CFG.OUT0 = 1; // 动作输出到Output 0 // 注意:还需通过IO复用配置,将GPT0的输出0连接到具体的GPIO引脚。 // 4. 启动定时器 GPT0.CTL.MODE = UP_PER;
4.2 实现正交编码器接口
将GPT配置为QDEC模式,可以轻松接口旋转编码器。
- 硬件连接:将编码器的A相、B相接至GPT通道0和通道1对应的GPIO引脚。零位索引信号(如果存在)接通道2。
- 软件配置:
// 1. 配置输入源 GPT0.C0CFG.INPUT = 1; // PHA 来自IO GPT0.C1CFG.INPUT = 1; // PHB 来自IO GPT0.C2CFG.INPUT = 1; // IDX 来自IO (如果使用) GPT0.C2CFG.EDGE = RISING; // 配置IDX信号的边沿 // 2. 配置采样率(需高于编码器信号最高频率的2倍) GPT0.PRECFG.TICKDIV = 0; // 使用80MHz直接采样(适用于高速编码器) // 或使用预分频降低采样率 // 3. 设置计数器上限(可选,用于溢出处理) GPT0.TGT = 0xFFFF; // 例如设置一个上限 // 4. (可选)使能计数器变化中断,用于实时读取位置 GPT0.IMASK.CNTRCHNG = 1; // 5. 启动正交解码模式 GPT0.CTL.MODE = QDEC; - 读取位置:直接读取
CNTR寄存器即可得到累积的位置计数。方向信息隐含在计数的增减中。
4.3 多定时器同步与联动
在复杂的控制系统中(如多电机协同),需要多个PWM输出严格同步。CC35xx的GPT支持硬件同步。
- 主从配置:通常将GPT0设为主定时器,GPT1设为从定时器。
- 配置步骤:
// 配置从定时器(GPT1)为同步模式 GPT1.CTL.MODE = SYNC_UP_PER; // 同步周期向上模式 // 在事件管理器中,配置一个同步事件源,例如GPT0的计数器归零事件 // 假设我们将GPT0的ZERO事件映射到事件管理器的某个同步信号SYNC_EVT_X EVENT_MANAGER.CHANNEL[n].SRC = GPT0_ZERO_EVENT; // 配置GPT1订阅该同步事件 GPT1.EVTCTL1.SYNC = SYNC_EVT_X; - 效果:当GPT0的计数器归零时,会发出同步事件。GPT1在收到该事件后,会在下一个定时器时钟周期将自己的计数器重置为0并开始计数,从而实现两个定时器的周期完全对齐。这对于需要多个相位关联的PWM输出至关重要。
4.4 故障输入与安全停车
在电机驱动等安全关键应用中,GPT的故障(Fault)和停车(Park)功能是硬件安全屏障。
- 故障输入:可以将一个GPIO(通常通过通道0复用)配置为故障输入。当该引脚出现有效电平(可配置高或低有效)时,GPT会根据
FAULT.CTL的配置采取行动。IMMEDIATE:立即停止计数器,PWM输出立即进入安全状态。ZEROCOND:在当前PWM周期结束后(CNTR=0)再停止,实现“软关断”,避免因突然关断引起的大电压电流冲击。
- 停车状态:当故障触发停车后,
PARK寄存器可以控制每个输出引脚被驱动到一个确定的安全状态(高、低或高阻),而不是保持悬空。
配置示例:
// 1. 配置故障输入引脚和极性 GPT0.C0CFG.INPUT = 1; // 通道0输入来自IO GPT0.C0CFG.EDGE = RISING; // 上升沿(高电平)为故障有效 // 配置对应GPIO为输入,并上拉/下拉以确保默认无效 // 2. 配置故障模式 GPT0.FAULT.CTL = ZEROCOND; // 选择周期结束后停车 // 3. 配置停车状态 GPT0.PARK.CTL = FAULT; // 故障时进入停车模式 GPT0.PARK.IOPS0 = DRIVE_LOW; // 故障时,输出0被驱动为低电平(安全状态) // 4. 使能故障中断,以便CPU响应 GPT0.IMASK.FAULT = 1; // 5. 启动定时器 GPT0.CTL.MODE = UP_PER;5. 实战调试与常见问题排查
即使配置正确,在实际调试中也可能遇到各种问题。以下是一些常见坑点及排查思路。
5.1 PWM输出无信号或频率不对
- 检查时钟树:确认GPT的时钟源是否使能。检查
PRECFG.TICKSRC和PRECFG.TICKDIV配置是否正确。一个简单的验证方法是:在调试器中单步运行,启动定时器后,观察CNTR寄存器的值是否在递增。 - 检查GPIO复用:这是最容易被忽略的一步!GPT的输出信号需要正确映射到具体的GPIO引脚。必须配置该引脚的复用功能为对应的GPT输出,并且将引脚方向设置为输出。
- 检查输出使能:确认
CnCFG.OUTn字段已使能,将通道动作绑定到了具体的输出上。一个通道可以同时驱动多个输出。 - 计算错误:重新计算
TGT和CnCC的值。记住UP_PER模式下周期=TGT+1,UPDWN_PER模式下周期=2*(TGT+1)。使用逻辑分析仪或示波器测量实际输出,与计算值对比。
5.2 捕获功能不稳定或数据错误
- 输入信号质量:首先用示波器观察输入信号的边沿是否有振铃或毛刺。硬件上的RC滤波可能是必要的。
- 滤波器配置不当:如果
CHFILT.LOAD设置过大,可能会滤掉正常的窄脉冲;如果设置过小,则无法滤除噪声。根据信号最小脉宽和滤波器时钟重新计算LOAD值。可以尝试暂时旁路滤波器(CHFILT.MODE = BYPASS)来排查是否是滤波器引起的问题。 - 边沿检测配置:确认
CnCFG.EDGE设置与期望捕获的边沿一致。 - 中断服务程序:在捕获中断中,读取
CnCC寄存器后,该中断标志会被自动清除。确保你的ISR没有过于耗时,导致错过后续的捕获事件。对于高频信号,考虑使用DMA将捕获值直接搬运到内存中。
5.3 中断无法触发或触发异常
- 中断使能嵌套:需要使能三层中断:
- GPT模块级:设置
IMASK寄存器中对应事件位(如CMP,ZERO)。 - 中断控制器级:在MCU的NVIC中使能GPT对应的中断线。
- 全局中断:确保执行了
__enable_irq()或类似指令。
- GPT模块级:设置
- 中断标志清除:某些中断标志在读取相关寄存器(如
CnCC,TGT)时会自动清除。如果你的ISR中需要读取这些寄存器,又需要手动清除标志,请使用RIS.CLR子寄存器,或者使用对应的“无清除”寄存器(如TGTNC)进行访问,以避免意外清除中断标志。 - 短周期下的中断丢失:在PWM频率很高(周期很短)时,如果同时使能了
ZERO和TGT中断,且ISR中通过读写TGT来清除中断,可能会因为CPU响应速度跟不上,导致刚产生的ZERO中断在还没来得及处理时,就被对TGT的写操作连带清除了。解决方案是使用PTGTNC/TGTNC寄存器进行更新,并分别清除RIS.ZERO.CLR和RIS.TGT.CLR。
5.4 同步功能失效
- 事件管理器配置:同步依赖于事件管理器。确保主定时器(如GPT0)的同步输出事件(如
ZERO)已正确配置到事件管理器的一个通道。 - 从定时器订阅:确认从定时器(如GPT1)的
EVTCTL1.SYNC寄存器正确订阅了事件管理器产生的那个同步事件信号。 - 模式匹配:确保从定时器的
CTL.MODE设置为SYNC_UP_PER等同步模式,而不是普通的UP_PER。 - 启动顺序:通常应先启动从定时器(使其进入等待同步状态),再启动主定时器。如果先启动主定时器,从定时器可能会错过第一个同步事件。
GPT模块是CC35xx这类现代MCU的瑞士军刀,其功能深度足以应对绝大多数嵌入式时序挑战。从简单的延时到复杂的多轴同步运动控制,理解其工作原理并掌握配置技巧,能让你在嵌入式开发中游刃有余。最好的学习方式永远是动手实践:从一个简单的LED呼吸灯(PWM)开始,再到测量一个按键的抖动时间(输入捕获),最后尝试用两个GPT生成一组互补带死区的电机驱动信号。过程中遇到的每一个问题,都会让你对数据手册上的那些位字段有更深刻的理解。