深入解析CC27xx LGPT3定时器:从寄存器原理到PWM与捕获实战

发布时间:2026/7/26 23:06:51
深入解析CC27xx LGPT3定时器:从寄存器原理到PWM与捕获实战 1. LGPT3定时器从寄存器手册到实战应用的全景解析如果你正在使用德州仪器TI的CC27xx系列无线MCU开发低功耗物联网设备那么低功耗通用定时器LGPT模块绝对是你绕不开的核心外设。手册里那几十页密密麻麻的寄存器描述是不是让你看得头大别担心今天我就以一个在嵌入式领域摸爬滚打多年的老工程师视角带你彻底拆解LGPT3这个定时器。我们不止看手册更要讲清楚每个寄存器背后“为什么”这么设计以及在实际项目中“怎么用”才能避免踩坑。无论是生成精准的PWM控制电机还是捕获传感器脉冲测量转速或是用最省电的方式产生周期性唤醒中断LGPT3都能胜任。关键在于你是否真正理解了它的运作机制。很多人拿到技术手册习惯性地从第一个寄存器开始逐字阅读结果看到后面忘了前面配置时依然一头雾水。我的经验是先建立整体框架理解定时器作为一个“系统”是如何协同工作的再去啃每个寄存器的细节效率会高得多。LGPT3不是一个简单的计数器它是一个集成了时钟管理、计数逻辑、三个独立可配置通道、中断系统、DMA触发以及输出控制于一体的复杂外设。它的设计目标很明确在满足复杂时序需求的同时最大限度降低CPU干预和系统功耗。接下来我们就按照这个思路层层深入。2. 架构总览与核心设计思想拆解在动手配置任何一个寄存器之前我们必须先在心里画出一张LGPT3的“功能框图”。这不是手册上那种冰冷的方块图而是理解数据流和控制流的逻辑地图。2.1 核心工作流程与时钟树LGPT3的“心脏”是一个24位的向上/向下计数器CNTR。它的跳动节奏计数时钟由预分频器PRECFG决定。你可以把预分频器想象成一个水龙头系统时钟或外部引脚TICKEN来的水流时钟脉冲经过这个水龙头的调节分频变成一滴一滴有规律的水滴定时器时钟再去驱动计数器这个水车转动。公式很简单定时器时钟频率 时钟源频率 / (TICKDIV 1)。这里TICKDIV是一个8位寄存器意味着分频系数可以从1到256。选择合适的分频系数是平衡精度和计数范围的第一步。比如你的系统时钟是48MHz想要一个1MHz的定时器时钟来产生微秒级精度那么TICKDIV就应该设置为(48/1) - 1 47。计数器怎么转由控制寄存器CTL的MODE字段决定。它支持多种模式单次向上计数UP_ONCE从0数到目标值TGT后停止。适合做精确的延时。周期向上计数UP_PER从0数到TGT然后自动回到0重新开始如此循环。这是产生边沿对齐PWM的经典模式。周期向上向下计数UPDWN_PER从0数到TGT再倒着数回0循环往复。用于产生中心对齐的PWM可以有效降低开关电源的电磁干扰。正交解码模式QDEC这个模式非常实用可以直接连接光电编码器通过两路相位差90度的脉冲PHA, PHB自动判断转动方向和计数常用于电机位置反馈。一个关键细节在同步模式SYNC_UP_ONCE等下定时器的启动由另一个LGPT实例通过STARTCFG寄存器控制。这在需要多个定时器严格同步启动的场景下非常有用比如多相电机控制。2.2 通道、输出与事件系统的联动LGPT3有三个独立的通道C0, C1, C2这是它的“双手”。每个通道都能被独立配置为两种基本工作模式比较模式和捕获模式。在比较模式下通道寄存器CxCC里存放着一个你预设的值。计数器CNTR不断变化当它的值等于CxCC的值时就触发一个“比较匹配”事件。这个事件可以用来做什么这就由通道配置寄存器CxCFG中的CCACT捕获-比较动作字段来定义。动作非常丰富可以设置SET、清除CLEAR、翻转TOGGLE对应的输出引脚也可以产生一个固定宽度的脉冲PULSE。例如配置为“SET_ON_ZERO, TOGGLE_ON_COMPARE”CCACT0xB并启用周期向上计数模式就能生成一个标准的PWM波计数器为0时输出置高计数器等于比较值时输出翻转为低。占空比就是CxCC / (TGT 1)。在捕获模式下角色反了过来。通道会监视一个外部输入引脚通过IO控制器或事件结构输入当检测到指定的边沿上升沿、下降沿或双边沿时就把计数器当前的值“抓拍”下来存到CxCC寄存器中。这常用于测量脉冲宽度或信号周期。手册中特别强调的“周期与脉宽测量”模式CCACT0x8就是基于此它能连续自动测量输入信号的周期和占空比结果分别存于CxCC和PCxCC寄存器大大简化了代码。输出控制是一个需要理清的层次。首先每个通道可以控制三个物理输出OUT0, OUT1, OUT2中的任意几个通过CxCFG.OUTx位使能。然后这些输出信号会送到输出控制单元OUTCTL这里可以进行手动置位/清零优先级高于通道的自动控制。最后信号经过IO控制器IOCTL在这里你可以选择直接输出、强制拉高/拉低甚至取反输出。这种分层设计提供了极大的灵活性比如你可以在故障状态下用IOCTL强制将PWM输出拉低实现硬件保护。2.3 中断与DMA解放CPU的关键LGPT3拥有一个完备的中断系统这是实现高效、低功耗程序的关键。它包含几个层次清晰的寄存器RIS原始中断状态这是最底层的中断标志位。只要事件发生比如计数器到目标值TGT、到零ZERO、或通道比较匹配CxCC对应的RIS位就会被硬件置1不管你是否想处理它。IMASK中断掩码你可以把它看作一个开关板。只有IMASK中对应位被置1的中断源才能从RIS传递到下一级。MIS被屏蔽的中断状态这是最终提交给CPU中断控制器的状态。MIS RIS IMASK。你的中断服务程序ISR应该查询MIS寄存器或者直接由MIS触发中断向量。ISET/ICLR, IMSET/IMCLR这两组“置位/清零”寄存器提供了更便捷和安全的中断管理方式。想象一下你想在软件中模拟一个比较匹配事件来测试代码直接写RIS寄存器是危险的可能影响硬件状态而向ISET寄存器的对应位写1就能安全地设置RIS标志。同样用ICLR来清除中断标志比直接写RIS更规范。更高级的玩法是结合DMA。DMA寄存器允许你将特定的中断事件如每次计数器到零映射到DMA请求。一旦事件发生DMA控制器就会自动将PTGT或PCxCC等“流水线”寄存器中的新值搬运到TGT或CxCC寄存器中完全无需CPU介入。这对于需要实时、无抖动地更新PWM频率或占空比的应用如音频合成、数字电源是至关重要的。ADC触发寄存器ADCTRG也是类似原理可以在定时器事件的精确时刻自动启动ADC采样实现时域上的严格同步。3. 关键寄存器深度解析与配置要点理解了架构我们再来深入几个最核心、最容易出错的寄存器看看配置时有哪些门道。3.1 控制寄存器CTL模式选择与通道复位CTL寄存器是定时器的大脑。除了前面提到的MODE字段还有几个关键位CxRST通道复位这是软件复位位。向C0RST写1会复位C0CC、PC0CC和C0CFG寄存器到它们的默认值。注意这个复位是“瞬态”的你写1之后硬件会自动将其清除。它不会影响计数器的运行。当你发现某个通道行为异常又不想停止整个定时器时可以用这个功能单独复位该通道。INTP中断相位这个位决定了RIS.TGT和RIS.ZERO这两个中断在何时被置位。0表示在CNTR TGT/ZERO后的一个系统时钟周期置位1表示在一个定时器时钟周期后置位。有什么区别系统时钟通常比定时器时钟快得多。如果你需要非常精确地在计数器到达目标值的那个时刻处理中断例如用于超精确的延时补偿应该选择INTP1让中断与定时器时钟同步避免因时钟域不同步带来的一个系统时钟周期的抖动。CMPDIR比较方向这个位仅在通道配置为比较动作时有效。它定义了在哪种计数方向上才触发比较事件。0上下计数都触发1仅向上计数触发2仅向下计数触发。在中心对齐PWMUPDWN_PER模式中如果你希望PWM的跳变沿只发生在向上计数或向下计数的过程中以简化控制逻辑或实现特定波形这个配置就派上用场了。配置顺序的黄金法则手册在CTL.MODE的描述里用加粗字体强调——“When configuring the timer, MODE should be the last thing to configure.” 意思是MODE寄存器应该是你配置定时器的最后一步。为什么因为一旦你设置了MODE为非DISABLE的值计数器可能就开始跑了此时再去改动其他配置如预分频器PRECFG、目标值TGT可能会导致不可预知的行为比如产生毛刺脉冲。正确的顺序是先配置PRECFG、TGT、CxCFG、CxCC等所有参数最后再写CTL.MODE来启动定时器。3.2 通道配置寄存器CxCFG功能定义的核心CxCFG寄存器是每个通道的“行为手册”其中最重要的两个字段是EDGE和CCACT。EDGE字段决定了通道在捕获模式时对什么边沿敏感。它位于输入滤波之后。这里有一个巨大的坑手册在CxCFG的说明里用了大段文字警告。每个通道都有一个边沿检测电路。当你从DISABLE模式切换到其他模式时如果CCACT选择的是捕获功能这个电路会被“刷新”Flush以防止旧的信号值造成误触发。但是如果你在定时器已经运行MODE ! DIS时去动态重配CCACT或者没有正确执行刷新流程就可能导致错误的捕获事件。手册给出的安全配置流程是先将EDGE设置为NONE关闭输入。配置你想要的CCACT动作。等待至少三个系统时钟周期。最后再将EDGE设置为所需的边沿Rising, Falling, Both。这个“等待三个周期”是确保边沿检测电路内部状态稳定的关键。我在早期项目中就因为忽略这一步导致捕获值偶尔出现巨大的错误调试了很久。CCACT字段这个4位字段定义了通道的15种不同功能0为禁用。它巧妙地将捕获、比较、零事件组合起来。我们重点看几个最常用的0xB (SET_ON_ZERO, TOGGLE_ON_CMP_REP)这是生成边沿对齐PWM的标准配置。计数器从0开始向上计数UP_PER为0时输出置位SET等于比较值CxCC时输出翻转TOGGLE。占空比 CxCC / (TGT 1)。输出在CxCC0时CNTR0的时刻被清除。0xA (CLEAR_ON_ZERO, TOGGLE_ON_CMP_REP)这是生成中心对齐PWM的标准配置。计数器上下计数UPDWN_PER为0时输出清零CLEAR等于比较值时输出翻转TOGGLE。占空比计算稍复杂当CxCC TGT时占空比 1 - (CxCC / TGT)当CxCC TGT时占空比为0。输出在CxCC0时CNTR0的时刻被置位。0x8 (PER_PULSE_WIDTH_MEAS)周期与脉宽测量模式。这是LGPT3一个非常强大的功能。它需要配合EDGE设置如果EDGE是上升沿则测量连续上升沿之间的时间周期存于CxCC高电平脉宽存于PCxCC如果是下降沿则测量周期和低电平脉宽。使用时必须注意必须先配置好INPUT和CCACT0x8然后再设置CTL.MODE为UP_ONCE或UP_PER启动定时器。同时输入信号的周期和脉宽必须满足定时器时钟分辨率的要求手册给出了明确公式。3.3 流水线寄存器PTGT, PCxCC与无抖动更新这是LGPT3设计上的一个亮点用于解决PWM或周期更新时的“抖动”问题。什么是抖动假设你正在用定时器生成PWM并在中断服务程序中更新比较值CxCC。如果中断响应稍有延迟或者你更新CxCC的时刻正好发生在计数器经过比较值之后、但在下一个周期开始之前那么新的比较值在本周期就不会生效必须等到下个周期。这会导致实际输出的PWM脉宽突然变化跳变一个周期也就是抖动。PTGT流水线目标值和PCxCC流水线通道比较值寄存器就是为了消除这种抖动而生的。它们的机制是你写入的新值PTGT.VAL或PCxCC.VAL不会立即生效而是被缓存起来。只有在下一个计数器归零CNTR.VAL 0的瞬间缓存的值才会被自动加载到实际工作的TGT或CxCC寄存器中。这样无论你在周期内的哪个时刻写入新值所有更改都会在同一个周期边界同步生效输出波形是绝对平滑的。对应的“No Clear”版本寄存器PTGTNC, PCxCCNC功能完全一样唯一的区别是读写它们不会自动清除对应的中断标志RIS.TGT, RIS.CxCC。这有什么用想象一个场景你使用DMA在后台自动更新这些寄存器值同时CPU也需要轮询或处理中断。如果你使用普通版本DMA的读写操作会意外地清除中断标志导致CPU错过事件。而使用“No Clear”版本DMA可以安心搬运数据中断标志的状态由CPU通过ICLR寄存器来专门管理互不干扰。这种设计体现了对复杂应用场景的周到考虑。4. 实战应用从PWM生成到脉冲捕获理论说得再多不如一行代码。下面我以CC27xx SDK的驱动库风格为例展示几个关键场景的配置流程和代码片段。请注意以下代码侧重于逻辑演示具体寄存器地址和位域定义请参考你的设备头文件。4.1 生成一路1kHz占空比50%的边沿对齐PWM假设系统时钟48MHz我们需要1kHz的PWM即周期1ms。计算预分频和计数值我们选择定时器时钟为1MHz这样每个计数代表1us方便计算。则TICKDIV (48MHz / 1MHz) - 1 47。PWM周期为1ms即1000us所以目标值TGT 1000 - 1 999因为从0开始计数。占空比50%则比较值C0CC 500。配置步骤// 1. 配置预分频器定时器时钟 48MHz / (471) 1MHz LGPT3-PRECFG.TICKDIV 47; LGPT3-PRECFG.TICKSRC 0; // 使用系统时钟源 // 2. 配置目标值周期 LGPT3-TGT.VAL 999; // 周期 1000个计数 1ms // 3. 配置通道0为比较模式控制OUT0输出 LGPT3-C0CFG.OUT0 1; // 使能通道0控制OUT0 LGPT3-C0CFG.CCACT 0xB; // SET_ON_ZERO, TOGGLE_ON_CMP_REP (边沿对齐PWM) LGPT3-C0CC.VAL 500; // 比较值高电平时间500us占空比50% // 4. 可选配置IO控制器将定时器输出映射到具体GPIO引脚 // 这部分依赖于具体的IO MUX配置此处省略 // 5. 最后启动定时器为周期向上计数模式 LGPT3-CTL.MODE 2; // UP_PER关键点一定要遵循“MODE最后配置”的原则。占空比调整只需在运行时修改C0CC.VAL即可。若要实现无抖动更新则应写入PC0CC.VAL。4.2 测量外部脉冲的高电平宽度我们使用通道1的捕获功能测量施加在对应输入引脚上的脉冲高电平宽度。思路配置通道为捕获模式在上升沿捕获一次计数器值在下降沿再捕获一次两次值之差乘以定时器时钟周期即为脉宽。更简单的方法是使用双边沿捕获但需要记录上一次的捕获值。配置步骤volatile uint32_t first_capture 0; volatile uint32_t pulse_width 0; // 1. 配置预分频器获得合适的定时器时钟和测量范围 LGPT3-PRECFG.TICKDIV 0; // 不分频假设用48MHz时钟每个计数约20.8ns LGPT3-TGT.VAL 0xFFFFFF; // 设置最大计数值避免溢出 // 2. 配置通道1为捕获模式在上升沿触发 // 首先严格按照安全流程配置CCACT LGPT3-C1CFG.EDGE 0; // 先关闭边沿检测 LGPT3-C1CFG.CCACT 1; // SET_ON_CAPTURE (捕获后禁用通道单次捕获) // 等待至少3个系统时钟周期。通常插入一个空操作循环或使用__nop()。 for(int i0; i10; i) { __nop(); } LGPT3-C1CFG.EDGE 1; // 设置为上升沿触发 LGPT3-C1CFG.INPUT 1; // 输入源选择IO控制器连接外部引脚 // 3. 使能通道1的捕获完成中断 LGPT3-IMASK.C1CC 1; // 屏蔽寄存器中使能C1CC中断 // 4. 启动定时器为单次向上计数模式作为自由运行的计时器 LGPT3-CTL.MODE 1; // UP_ONCE但我们会手动在中断里重启 // 5. 中断服务程序示例 void LGPT3_IRQHandler(void) { if(LGPT3-MIS.C1CC) { // 检查是否是通道1捕获中断 if(first_capture 0) { // 第一次捕获上升沿 first_capture LGPT3-C1CC.VAL; // 读取捕获值 // 重新配置为下降沿捕获准备抓下降沿 LGPT3-C1CFG.EDGE 0; LGPT3-C1CFG.CCACT 1; for(int i0; i10; i) { __nop(); } LGPT3-C1CFG.EDGE 2; // 下降沿 // 清除中断标志并重启定时器如果需要连续测量 LGPT3-ICLR.C1CC 1; LGPT3-CNTR.VAL 0; // 计数器清零 LGPT3-CTL.MODE 1; // 重新启动单次计数 } else { // 第二次捕获下降沿 uint32_t second_capture LGPT3-C1CC.VAL; // 计算脉宽考虑计数器可能溢出一次使用24位掩码 pulse_width (second_capture - first_capture) 0xFFFFFF; first_capture 0; // 重置准备下一次测量 // 重新配置为上升沿捕获开始下一轮 LGPT3-C1CFG.EDGE 0; LGPT3-C1CFG.CCACT 1; for(int i0; i10; i) { __nop(); } LGPT3-C1CFG.EDGE 1; LGPT3-ICLR.C1CC 1; LGPT3-CNTR.VAL 0; LGPT3-CTL.MODE 1; } } }关键点捕获模式下的安全配置流程至关重要。测量长脉宽时要注意计数器溢出问题可能需要使用周期计数模式并结合溢出中断。对于高频信号预分频器可以降低定时器时钟以提高测量范围。4.3 使用DMA实现PWM占空比的无CPU干预更新这是一个高级应用用于需要实时、连续更新PWM波形的场景如LED呼吸灯渐变或电机软启动。思路将DMA的源地址设置为存有占空比序列的内存数组目标地址设置为LGPT3的PC0CCNC流水线通道比较值无清除寄存器。触发源配置为定时器的ZERO或TGT中断。这样每次计数器归零时DMA自动将下一个占空比值从内存搬运到PC0CCNC在下一个PWM周期自动生效实现无抖动、无CPU干预的平滑更新。配置步骤概要// 1. 配置PWM定时器同4.1但使用PC0CCNC作为比较值寄存器 // 初始比较值仍通过C0CC设置 LGPT3-C0CC.VAL initial_duty; // 2. 配置DMA通道 // 假设使用uDMA通道0 // 设置源地址占空比数组的地址 // 设置目标地址(LGPT3-PC0CCNC.VAL) // 设置传输数据大小数组长度 // 设置传输模式Ping-Pong模式或基本模式根据需求 // 3. 配置LGPT3的DMA寄存器将ZERO事件映射到DMA请求 LGPT3-DMA.REQ 2; // 2 对应 RIS.ZERO 触发DMA请求 LGPT3-DMA.RWADDR ((uint32_t)LGPT3-PC0CCNC) 2; // 计算基地址偏移 LGPT3-DMA.RWCNTR 0; // 每次请求传输1个数据项RWCNTR1 // 4. 启动DMA和定时器 // 使能DMA通道 LGPT3-CTL.MODE 2; // 启动周期计数模式关键点使用PC0CCNC而不是PC0CC可以避免DMA写操作意外清除中断标志。确保DMA配置的地址对齐和传输数据宽度应为32位正确。通过这种方式CPU只需要在初始化时设置好DMA和波形数据之后就可以进入低功耗睡眠由DMA和定时器硬件协同完成复杂的波形生成。5. 调试技巧与常见问题排查即使理解了所有寄存器实际调试中还是会遇到各种问题。这里分享几个我踩过的坑和解决方法。5.1 没有输出或输出异常检查IO映射这是最常见的问题。LGPT3的内部输出信号OUT0/1/2需要正确映射到具体的设备引脚上。这通常通过IO控制器IOC或类似的外设来完成并不在LGPT模块本身配置。务必查阅CC27xx的引脚复用Pin Mux表格和IOC寄存器确保LGPT的输出已经路由到了你想要的GPIO引脚并且该引脚被配置为外设功能而非通用IO。检查输出使能确认CxCFG寄存器中的OUT0/OUT1/OUT2位是否已使能。即使配置了CCACT如果对应的OUTx位为0通道也不会控制物理输出。检查IOCTL覆盖IOCTL寄存器可以强制覆盖输出值。确保IOCTL.OUTx和COUTx字段是0正常输出而不是被意外设置为强制高、低或取反。验证时钟和模式用调试器读取CNTR寄存器的值看它是否在按预期递增/递减。如果CNTR不动首先检查CTL.MODE是否已设置为非DISABLE然后检查PRECFG配置的时钟源和分频是否有效。5.2 中断无法触发或触发异常频繁中断路径检查遵循事件 - RIS - IMASK - MIS - CPU中断控制器 - 你的ISR这个路径逐一排查。读RIS寄存器看预期的事件标志位是否被置1。如果没有说明硬件比较/捕获事件根本没发生回头检查定时器配置和输入信号。读IMASK寄存器确认对应中断源已使能。读MIS寄存器确认中断已传递至此。如果RIS有而MIS没有就是IMASK没开。检查CPU层面的中断控制器如NVIC是否使能了LGPT3的中断向量并设置了合适的优先级。清除中断标志在ISR中必须通过写ICLR寄存器对应位写1来清除中断标志。常见的错误是忘了清除标志导致中断连续触发。错误地通过读RIS或MIS来清除标志对于LGPT3只有写ICLR/ISET或读写某些特定寄存器如CxCC才能清除。在清除标志前MIS状态因为新事件发生又发生了变化导致标志“清除不掉”。安全的做法是在ISR开头读取MIS的值保存到局部变量然后根据这个变量值去清除对应的标志。中断服务程序耗时过长如果中断处理太慢可能错过下一个中断事件或者导致系统响应迟缓。对于高频定时器中断ISR里只做最必要的操作如设置标志、读写寄存器复杂的处理放到主循环中。5.3 捕获值不准或出现错误值输入滤波配置如果输入信号有毛刺可能导致误触发。CHFILT寄存器提供了输入滤波功能。例如设置MODE为系统时钟滤波LOAD3则表示需要连续4个LOAD1相同的采样值输入信号才被确认。这能有效滤除窄毛刺但会引入几个时钟周期的延迟。根据信号质量调整滤波参数。安全配置流程再次强调在定时器运行中切换通道的捕获功能必须遵循手册所述的流程EDGE置NONE - 配置CCACT - 等待 - 配置EDGE。忽略等待步骤是导致捕获到陈旧边沿值的最主要原因。计数器溢出在单次捕获模式下如果信号间隔超过计数器最大值对于24位计数器在1MHz时钟下约16.7秒计数器会归零。你的捕获值计算逻辑必须考虑这种回绕情况。使用(current_capture - last_capture) 0xFFFFFF可以安全地计算差值。时钟同步问题如果输入信号是异步的来自另一个时钟域即使经过滤波在亚稳态窗口附近采样仍可能出错。对于要求极高的应用可能需要使用外部同步电路或者使用芯片提供的同步输入功能如果支持。5.4 功耗高于预期LGPT3本身是低功耗设计但配置不当仍会增加功耗。未使用的模块要关闭如果没用到某个通道将其CCACT设为0禁用。如果没用到输出确保CxCFG中的OUTx位为0。如果没用到中断将IMASK中所有位清零。选择合适的时钟源PRECFG.TICKSRC可以选择系统时钟或外部TICKEN引脚。如果使用低频的外部时钟定时器本身功耗会更低。在深度睡眠模式下系统时钟可能关闭此时如果需要定时器工作必须选择能在此模式下运行的时钟源。调试后关闭调试功能EMU寄存器的HALT位用于调试时冻结定时器。在产品代码中确保将其设为0否则当CPU因调试器暂停时定时器也会停止可能影响功能。最后善用芯片的仿真功能或示波器。直接观察引脚波形结合调试器查看寄存器实时值是定位硬件时序问题最直接有效的方法。把LGPT3当成一个精密的乐高积木理解了每个寄存器的“榫卯结构”你就能搭建出满足各种复杂时序需求的稳定系统。