CC35xx系统定时器SYSTIM:高精度计时与无线协议栈的时序基石

📅 2026/7/18 12:11:03
CC35xx系统定时器SYSTIM:高精度计时与无线协议栈的时序基石
1. 系统定时器SYSTIM在CC35xx中的核心定位与设计思路在嵌入式无线MCU的开发中时间就是一切。无论是Wi-Fi信标帧的精确间隔、蓝牙连接事件的时序窗口还是低功耗模式下唤醒的毫秒级精度都离不开一个稳定、可靠且高精度的定时器。TI CC35xx系列作为集成Wi-Fi 6和蓝牙低功耗的无线MCU其系统定时器SYSTIM模块的设计直接反映了芯片对复杂无线协议栈和实时任务调度的深度支持。SYSTIM不是一个简单的计数器。它是一个34位宽、基础分辨率高达250纳秒的精密计时引擎。这个精度是什么概念对比一下常见的微秒级定时器一次计数是1微秒而SYSTIM的“心跳”快了整整4倍。这意味着在测量短脉冲、生成精确PWM或者为高速通信协议提供时间戳时它能提供更精细的粒度。但高精度往往伴随着计数范围的妥协——34位250ns的满量程大约是1.2小时计算方式2^34 * 250 ns ≈ 4294967296 * 250 ns ≈ 1073.74秒 ≈ 17.9分钟。实际上其高32位由TIME1U寄存器以1微秒分辨率提供组合起来才能达到约1.2小时的范围。因此TI采用了SYSTIM与实时时钟RTC协同工作的架构RTC提供长达数十年的“日历时间”但精度较低微秒到毫秒级而SYSTIM则在设备活跃ACTIVE状态下从RTC同步获取“粗”时间基准并在此基础上提供“细”粒度的精密计时。这种设计巧妙地平衡了长时运行和瞬时精度的双重需求。SYSTIM提供了两个独立的通道Channel 0和Channel 1。Channel 0是留给应用开发者的“自留地”它非常灵活可以在250ns或1μs两种分辨率间切换支持捕获和比较两种核心模式。Channel 1则被标记为“TI软件保留”通常预留给芯片内部的无线协议栈或实时操作系统内核使用固定为1μs分辨率。这种硬件资源划分保证了系统关键时序不被应用层意外干扰提升了系统的可靠性。理解这个模块不仅仅是知道如何配置寄存器更是理解CC35xx如何为复杂的无线应用构建其时间基石。2. SYSTIM核心工作机制深度解析要驾驭SYSTIM必须深入其内部运作机制。它的工作可以概括为同步、计数、比较/捕获、触发。2.1 与RTC的同步机制时间基准的传递SYSTIM本身在芯片上电或从休眠/关断模式唤醒进入活跃状态时其计数器并非从零开始。硬件会自动将当前RTC的时间值加载到SYSTIM中并以250ns的分辨率进行初始化。更重要的是在设备处于ACTIVE状态期间SYSTIM会持续与RTC保持同步。这个同步过程并非简单的数值拷贝而是通过一个PI比例-积分滤波器来实现的对应的就是KP和KI这两个增益系数寄存器。你可以把RTC看作一个走得稳但步幅大精度相对较低的时钟SYSTIM则是一个步幅小精度高但容易自己产生累积误差的时钟。PI滤波器的作用就是不断微调SYSTIM的“步伐”使其长期平均值严格对齐RTC的“步伐”。KP比例增益负责对当前的同步误差做出快速反应KI积分增益则负责消除历史累积的稳态误差。STA寄存器中的SYNCUP位就是这一过程的“状态指示灯”上电复位后为1表示正在进行首次同步首次同步完成后清零任何对STA寄存器的写操作都会重新触发一次同步流程。2.2 双分辨率模式与时间读取SYSTIM的34位时间值被拆分到两个只读寄存器中以适应不同的精度需求TIME250N(Offset 0x100)读取计数器低32位值直接反映250ns分辨率下的计数值。每个计数单位代表250ns。TIME1U(Offset 0x104)读取计数器高32位值实际上是bit[33:2]每个计数单位代表1微秒。这为你提供了一个更“人类友好”的粗粒度时间视图。注意这两个寄存器在SYSTIM计数器使能时是只读的。试图在计数器运行时写入它们会被忽略。这意味着你不能直接“设置”当前时间时间基准完全来源于与RTC的同步。2.3 通道的两种核心工作模式每个通道都可以独立配置为两种模式这是SYSTIM功能性的核心。比较模式这是最常用的定时功能。你向通道的CHnCC寄存器写入一个目标时间值比较值一旦SYSTIM的当前时间达到或超过这个值就会立即触发一个比较事件。这个事件可以产生中断也可以输出到事件管理器去触发其他外设比如启动一个ADC转换。这里有个关键细节如果你写入的比较值是一个“过去的时间”即当前时间已经超过了这个值SYSTIM并不会傻等。对于配置为1μs分辨率的通道如果写入的比较值在当前时间之前的4秒以内对于配置为250ns分辨率的通道仅Channel 0支持如果写入的比较值在当前时间之前的1秒以内硬件会立即触发比较事件。这个特性对于需要确保事件一定被触发的场景如超时检测非常有用。捕获模式用于精确测量外部事件发生的时间点。你需要先将通道配置为捕获模式设置CHnCFG.MODE 1并配置好捕获触发的边沿CHnCFG.INP字段可选上升沿、下降沿或双边沿。当指定的外部事件输入引脚需要通过事件管理器配置发生符合条件的边沿跳变时SYSTIM会瞬间将当前的34位时间值锁存到该通道的CHnCC寄存器中。这样你通过读取CHnCC就能知道事件发生的精确时刻。默认情况下一次捕获发生后通道会自动解除武装MODE位清零变为空闲状态。如果你需要连续捕获可以设置CHnCFG.REARM 1使通道在捕获后自动重新武装等待下一个事件。2.4 事件的产生、路由与中断管理SYSTIM的事件和中断逻辑稍显复杂但非常强大。每个通道0和1都有自己的事件输出此外还有一个将所有通道事件以及定时器溢出事件合并的“组合事件”。这些事件都被路由到芯片的事件管理器。事件管理器是CC35xx中的一个交叉开关网络允许不同外设的事件直接触发其他外设的动作无需CPU干预这对于实现高效、低延迟的响应至关重要。中断方面主要关注三个寄存器组RIS(Raw Interrupt Status)原始中断状态寄存器。只要事件发生无论是否被屏蔽对应的位就会置1。它反映了最底层的事件状态。IBM/IMSET/IMCLR(Interrupt Mask)中断屏蔽寄存器。通过IBM直接读写或通过IMSET置1使能/IMCLR置1禁用来操作。只有当RIS中的事件位为1且对应的IBM屏蔽位也为1即中断被使能时才会产生CPU可感知的中断。MIS(Masked Interrupt Status)被屏蔽后的中断状态寄存器。它是RIS IBM的结果。CPU通常查询这个寄存器来判断是否有有效的中断需要处理。ICLR(Interrupt Clear)中断清除寄存器。向某位写1可以清除RIS中对应的标志位。这里有一个非常重要的硬件特性除了写ICLR读取捕获模式的CHnCC寄存器值或者向比较模式的CHnCC寄存器写入新值也会自动清除该通道对应的RIS标志位。这个设计简化了编程模型但如果你不留意可能会导致中断标志被意外清除。3. 关键寄存器配置详解与实战步骤理解了原理我们来看如何动手配置。下面以Channel 0为例演示几种典型场景的配置流程。假设你已经完成了基本的时钟和电源初始化SYSTIM模块的时钟已使能CLKCFG.EN 1和SYSTIM_CTRL.MEM_SYSTIM_ENCLK 1。3.1 场景一配置Channel 0为1微秒分辨率比较模式实现单次定时中断假设我们需要在100毫秒后触发一个动作。步骤1计算比较值首先读取当前时间作为基准。由于使用1μs分辨率我们主要关心TIME1U寄存器。uint32_t current_time_1us HWREG(SYSTIM_BASE SYSTIM_O_TIME1U);然后计算目标时间。100ms 100,000 μs。uint32_t compare_value current_time_1us 100000;注意TIME1U是34位计数器的高32位bit[33:2]其累加是线性的。直接进行32位加法是安全的因为100ms远小于1.2小时的总范围不会溢出到第34位。但在编写通用代码时必须考虑34位溢出问题。步骤2配置通道并写入比较值Channel 1是TI保留的我们只用Channel 0。向CH0CC寄存器写入比较值这个动作本身就会将通道武装为比较模式。// 写入比较值此操作会自动武装通道为比较模式 HWREG(SYSTIM_BASE SYSTIM_O_CH0CC) compare_value;此时无需显式设置CH0CFG.MODE比较模式下应为0也无需设置CH0CFG.RES我们使用默认的1μs分辨率即RES0。步骤3使能中断我们需要在比较事件发生时产生中断。// 1. 清除可能存在的旧中断标志可选但推荐 HWREG(SYSTIM_BASE SYSTIM_O_ICLR) SYSTIM_ICLR_EVT0; // 2. 使能Channel 0事件中断设置中断掩码 HWREG(SYSTIM_BASE SYSTIM_O_IMSET) SYSTIM_IMSET_EVT0; // 3. 在CPU的NVIC中使能SYSTIM的中断此处为伪代码依赖具体驱动库 // NVIC_EnableIRQ(SYSTIM_IRQn);步骤4编写中断服务程序ISR在中断服务程序中你需要检查中断源读取MIS寄存器。处理事件执行你的100ms后要做的任务。清除中断标志。可以通过写ICLR寄存器完成但如前所述向CH0CC写入一个新的比较值也会清除标志。如果你是一次性定时可以在ISR中写ICLR如果你需要周期性定时更常见的做法是在ISR中重新计算并写入下一个比较值这个写入动作会自动清除中断标志。void SysTimer_ISR(void) { uint32_t mis_status HWREG(SYSTIM_BASE SYSTIM_O_MIS); if (mis_status SYSTIM_MIS_EVT0) { // 处理Channel 0比较事件 // ... 你的代码 ... // 方法A直接清除中断标志 HWREG(SYSTIM_BASE SYSTIM_O_ICLR) SYSTIM_ICLR_EVT0; // 方法B如果需周期性定时重新写入下一个比较值此操作也会清除标志 // uint32_t next_compare HWREG(SYSTIM_BASE SYSTIM_O_CH0CC) 100000; // HWREG(SYSTIM_BASE SYSTIM_O_CH0CC) next_compare; } // 检查其他中断源如OVFL溢出 if (mis_status SYSTIM_MIS_OVFL) { // 处理溢出事件 HWREG(SYSTIM_BASE SYSTIM_O_ICLR) SYSTIM_ICLR_OVFL; } }3.2 场景二配置Channel 0为250纳秒分辨率捕获模式测量脉冲宽度假设我们需要测量一个外部数字信号的高电平脉冲宽度精度要求很高。步骤1配置事件管理器这是捕获模式最容易忽略的一步SYSTIM的捕获输入信号来自芯片的事件管理器Event Manager。你必须先告诉事件管理器将哪个外部引脚或内部事件路由到SYSTIM Channel 0的捕获输入。 这通常通过配置SOC_AON.TMEVTCTL寄存器来完成。例如将某个GPIO引脚的事件映射到SYSTIM0输入。这部分配置高度依赖于具体的硬件引脚映射和SDK需要参考TI的SDK文档或示例代码。假设我们已经通过SDK API配置好事件源为EVENT_ID_GPIOx_RISING。步骤2配置SYSTIM Channel 0为捕获模式// 1. 首先确保通道未武装可选但安全 HWREG(SYSTIM_BASE SYSTIM_O_ARMCLR) SYSTIM_ARMCLR_CH0; // 2. 配置CH0CFG寄存器 // MODE 1 (捕获模式) // INP 0 (上升沿捕获) 或 1 (下降沿) 或 2 (双边沿)。假设我们先捕获上升沿。 // REARM 0 (单次捕获捕获后自动解除武装) 或 1 (连续捕获)。假设单次。 // RES 1 (选择250ns分辨率) uint32_t ch0cfg_value 0; ch0cfg_value | (1 0); // MODE 1 捕获模式 ch0cfg_value | (0 1); // INP 0 上升沿捕获 ch0cfg_value | (0 3); // REARM 0 单次 ch0cfg_value | (1 4); // RES 1 250ns分辨率 HWREG(SYSTIM_BASE SYSTIM_O_CH0CFG) ch0cfg_value;配置完成后通道即进入武装状态等待指定边沿的事件。步骤3等待捕获事件并读取时间戳当配置的GPIO引脚出现上升沿时SYSTIM会立即将当前的34位时间值锁存到CH0CC寄存器并触发捕获事件RIS.EVT0置位。 在应用程序中你可以通过轮询RIS寄存器或使用中断来感知捕获完成。// 轮询方式 while (!(HWREG(SYSTIM_BASE SYSTIM_O_RIS) SYSTIM_RIS_EVT0)) { // 等待... } // 读取捕获到的时间戳低32位250ns分辨率 uint32_t capture_time_low HWREG(SYSTIM_BASE SYSTIM_O_CH0CC); // 如果需要完整34位时间还需要读取TIME1U寄存器并进行组合计算。 // 注意读取CH0CC会自动清除RIS.EVT0标志位要测量脉冲宽度你需要分别捕获上升沿和下降沿的时间戳。一种方法是配置为双边沿捕获INP2但这样你无法区分是哪条边沿。更可靠的方法是先配置为上升沿捕获捕获到时间T1后重新配置为下降沿捕获再捕获时间T2。脉冲宽度 (T2 - T1) * 250 ns。3.3 场景三使用ARMSET和ARMCLR进行灵活的通道控制除了直接写CHnCC比较模式和设置CHnCFG.MODE捕获模式来武装通道SYSTIM提供了更灵活的ARMSET和ARMCLR寄存器进行状态管理。ARMSET向某通道位写1会尝试将该通道武装为比较模式。但前提是该通道当前未武装ARMSTA[x]0或者已武装但处于比较模式。如果通道已处于捕获模式则写ARMSET无效。这个寄存器常用于动态重载比较值先通过CHnCCSR别名寄存器写入不影响通道状态预加载一个新的比较值然后通过写ARMSET来武装通道使其基于新值开始比较。ARMCLR向某通道位写1会解除该通道的武装无论它当前处于比较还是捕获模式。通道状态变为UNARMEDCHnCFG.MODE位被清零。实战技巧实现一个可更新、可取消的软件定时器// 假设我们想实现一个可更新的单次定时器 void StartSoftwareTimer(uint32_t delay_us) { // delay_us: 延迟微秒数 uint32_t current_time HWREG(SYSTIM_BASE SYSTIM_O_TIME1U); uint32_t target_time current_time delay_us; // 1. 使用别名寄存器安全地更新比较值不会意外触发事件 HWREG(SYSTIM_BASE SYSTIM_O_CH0CCSR) target_time; // 2. 武装通道如果之前未武装则武装如果已武装则基于新值重新武装 HWREG(SYSTIM_BASE SYSTIM_O_ARMSET) SYSTIM_ARMSET_CH0; } void CancelSoftwareTimer(void) { // 解除通道武装取消尚未触发的定时 HWREG(SYSTIM_BASE SYSTIM_O_ARMCLR) SYSTIM_ARMCLR_CH0; }4. 高级功能、调试与常见问题排查4.1 TIMEBIT寄存器向事件管理器输出特定时间位TIMEBIT寄存器是一个很有用的功能。它允许你将SYSTIM计数器的某一个特定位bit[17:2]范围内直接输出到事件管理器。例如如果你将TIMEBIT.VAL设置为0x0004即二进制...0100这意味着将SYSTIM计器的bit[4]输出。由于SYSTIM以250ns递增bit[4]的翻转周期是 2^5 * 250ns 32 * 250ns 8μs。这就相当于生成了一个频率为125kHz的方波信号无需CPU干预可以直接用于触发其他外设。这在需要生成固定频率触发信号时非常高效。4.2 调试支持EMU寄存器当你在调试器如JTAG中暂停CPU时大多数外设会继续运行。但对于定时器这可能不是你想要的。EMU.HALT位给了你控制权EMU.HALT 0调试器暂停CPU时SYSTIM继续自由运行。EMU.HALT 1调试器暂停CPU时SYSTIM也立即冻结。这在你调试时间敏感的代码时非常有用可以确保暂停时定时器状态保持不变便于分析。4.3 常见问题与排查技巧实录在实际开发中你可能会遇到以下问题问题1配置了比较模式但中断始终不触发。检查清单时钟使能确认CLKCFG.EN和SYSTIM_CTRL.MEM_SYSTIM_ENCLK都已置1。SYSTIM没有时钟计数器不会动。计数器运行读取STA.VAL位确认它为1表示系统定时器正在运行。中断屏蔽检查IBM寄存器或MIS寄存器。RIS有标志但MIS没有说明中断被屏蔽了。确保已通过IMSET或直接写IBM使能了对应通道的中断。比较值是否合理如果你写入的比较值是一个“遥远的过去”超过1秒或4秒的阈值且当前时间已经远超它根据规则事件应该立即触发。检查RIS寄存器如果RIS有标志但你没收到中断问题在中断屏蔽或CPU NVIC配置。如果RIS都没标志检查写入的比较值是否真的大于当前时间考虑34位溢出。一个调试技巧是写入一个非常近的未来时间比如当前时间10看是否触发。CPU NVIC配置确保在CPU层面NVIC已经使能了SYSTIM的中断向量。问题2捕获模式能触发事件但读取的CHnCC值总是0或不变。检查清单事件路由这是最常见的原因SYSTIM的捕获输入必须通过事件管理器配置。你配置了CHnCFG.INP但如果没有在SOC_AON.TMEVTCTL寄存器中将具体的事件源如GPIO中断事件映射到SYSTIM的通道输入捕获永远不会发生。务必参考TI SDK的Event Manager配置示例。分辨率设置确认CHnCFG.RES设置与你读取时间戳的方式匹配。如果你配置为250ns模式读取的是CHnCC低32位还需要结合TIME1U高32位来获得完整时间吗对于大多数脉冲宽度测量只关心差值用CHnCC的低32位相减即可但要考虑低32位溢出约107秒后。自动重武装如果你设置了REARM1通道在捕获后会立即重新武装。如果你在第一次捕获后没有及时读取CHnCC第二次捕获会覆盖第一次的值。确保你的读取速度跟得上事件频率或者使用中断并在ISR中立即读取。问题3使用ARMSET重新武装定时器不生效。原因分析ARMSET只在通道当前未武装UNARMED或已武装但处于比较模式时才有效。如果你的通道当前处于捕获模式CHnCFG.MODE1写ARMSET是无效的。解决方案在写ARMSET之前先使用ARMCLR解除通道武装或者先将CHnCFG.MODE清零。问题4定时器中断似乎丢失了或者响应不及时。检查清单中断标志清除时机回忆一下读取捕获寄存器(CHnCC)或写入比较寄存器(CHnCC)会自动清除RIS标志。如果你在ISR中先读取了捕获值或者更新了比较值那么RIS标志在你读取MIS之前就已经被清除了。确保你的ISR逻辑是先读MIS判断中断源再进行其他操作。中断服务程序耗时SYSTIM定时器精度很高如果中断服务程序执行时间过长可能会错过下一个定时事件。优化ISR只做最必要的操作将非实时任务放到主循环中。中断嵌套与优先级如果系统中有更高优先级的中断长时间执行会阻塞SYSTIM中断。根据系统需求合理配置中断优先级。问题5如何获取SYSTIM自启动以来的完整运行时间挑战TIME1U和TIME250N是两个独立的寄存器在读取过程中计数器可能在递增导致读取的高低位不匹配比如读完TIME1U后TIME250N已经溢出并进位了。解决方案需要实现一个“原子性”的读取操作。标准做法是连续读取两次确保高位部分在两次读取间没有变化。uint64_t GetFullSystemTime(void) { uint32_t high1, low, high2; do { high1 HWREG(SYSTIM_BASE SYSTIM_O_TIME1U); low HWREG(SYSTIM_BASE SYSTIM_O_TIME250N); high2 HWREG(SYSTIM_BASE SYSTIM_O_TIME1U); } while (high1 ! high2); // 如果高位变了说明读取过程中发生了进位重新读取 // 组合TIME1U是bit[33:2]所以需要左移2位。TIME250N是bit[31:0]。 uint64_t full_time ((uint64_t)high1 2) | ((uint64_t)low 30); // 注意此组合方式取决于你对完整34位的解读方式。另一种常见方式是 // full_time ((uint64_t)high1 * 4) (low / 1000000000); // 假设需要纳秒单位 // 具体组合公式需根据你的时间单位需求来定。 return full_time; }掌握SYSTIM的配置与调试是释放CC35xx无线MCU高精度计时能力的关键。从简单的延时到复杂的协议时序测量这个模块都是你可靠的底层支撑。务必结合TI官方的SDK和驱动程序库来使用这些库通常已经封装了上述复杂的寄存器操作和临界区保护能让你更专注于应用逻辑的开发。