CC13x2/CC26x2 WDT与TRNG寄存器级配置:嵌入式系统可靠性与安全基石

📅 2026/7/26 10:27:39 👁️ 阅读次数 📝 编程学习
CC13x2/CC26x2 WDT与TRNG寄存器级配置:嵌入式系统可靠性与安全基石

1. 项目概述与核心价值

在嵌入式系统,尤其是电池供电的无线物联网设备开发中,系统的健壮性与安全性是产品能否成功落地的关键。想象一下,一个部署在野外用于环境监测的传感器节点,如果因为软件异常而“死机”,可能需要人工现场复位,成本高昂;如果其通信数据因随机数质量不佳而被轻易破解,则毫无安全可言。这正是看门狗定时器和真随机数生成器这两个外设存在的根本意义。

看门狗定时器,常被开发者亲切地称为“系统守护者”。它的工作原理简单而粗暴:你需要定期去“喂狗”,如果程序跑飞或陷入死循环而忘记了这项任务,看门狗就会“咬人”——触发系统复位,让设备从头再来。这就像给系统设置了一个必须按时完成的“心跳”任务,丢失心跳则意味着生命终结并重启。在CC13x2/CC26x2这类面向低功耗无线应用的MCU中,WDT的设计尤为关键,因为它需要在极低功耗的睡眠模式下依然可靠工作,确保设备能从任何异常中恢复。

真随机数生成器则是安全体系的基石。与软件生成的伪随机数不同,TRNG依赖于芯片内部的物理噪声源(如环形振荡器的热噪声、半导体结噪声),其输出在理论上是不可预测的。这对于生成加密密钥、初始化向量、会话随机数等至关重要。一个弱的随机源会直接导致整个加密体系形同虚设。CC13x2/CC26x2集成的TRNG模块,基于24个自由运行振荡器,为在资源受限的终端设备上实现高等级安全提供了硬件保障。

本文将深入剖析TI CC13x2/CC26x2系列MCU中WDT与TRNG模块的寄存器级配置。我不会仅仅罗列寄存器字段,而是结合我多年在低功耗无线产品开发中的实战经验,带你理解每个配置位背后的设计意图,分享从寄存器配置到稳定可靠、安全可用的实际代码之间的关键步骤与常见陷阱。无论你是正在评估该平台,还是已经深陷调试泥潭,相信这些细节都能为你提供直接的帮助。

2. WDT模块:从原理到可靠配置

2.1 模块框图与工作流程解析

CC13x2/CC26x2的看门狗定时器是一个32位递减计数器。其核心工作流程可以概括为:加载初值 -> 启动计数 -> 定期“喂狗”(重载)-> 超时则触发动作。模块框图清晰地展示了几个关键部分:LOAD寄存器用于设置超时值;VALUE寄存器用于实时读取当前计数值;CTL寄存器控制中断使能、复位使能及中断类型;RIS/MIS寄存器提供中断状态;而LOCK寄存器则用于防止关键配置被意外修改。

这里有一个至关重要的细节:WDT的时钟源是MCU电源与时钟管理模块提供的INFRASTRUCTURE时钟。这意味着,只要MCU没有进入完全的掉电模式,WDT就会持续运行。即使在低功耗睡眠模式下,只要该时钟存在,WDT就在默默计数。这确保了看门狗在绝大多数异常情况下依然有效。

注意:在配置深度睡眠模式时,务必确认PRCM模块对WDT时钟的配置。有些超低功耗场景可能会关闭部分时钟域,需确保WDT时钟不受影响,否则看门狗将失效。

2.2 关键寄存器详解与配置策略

2.2.1 LOAD与VALUE寄存器:设定生命的“时长”

LOAD寄存器是看门狗超时周期的设定器。它是一个可读写的32位寄存器,复位值为0xFFFFFFFF。当你向LOAD寄存器写入一个新值时,这个值会立即加载到32位递减计数器中,并重新开始计数。

计算公式与实例: 假设INFRASTRUCTURE时钟频率为48 MHz,你希望看门狗超时时间为1秒。

  1. 计算所需计数值:Timeout_Count = Clock_Frequency * Timeout_Time = 48,000,000 Hz * 1 s = 48,000,000
  2. 由于计数器递减至0触发,LOAD值应为0xFFFFFFFF - Timeout_Count + 1。但通常我们直接使用超时计数值,因为计数器从LOAD值开始递减至0。
  3. 更简单的做法:LOAD_Value = Timeout_Time (s) * Clock_Frequency (Hz)。因此,LOAD_Value = 1 * 48e6 = 48,000,000 (0x02DC6C00)

向LOAD写入0会导致立即产生中断。这是一个需要警惕的陷阱:在初始化序列中,如果先写LOAD为0再配置其他寄存器,可能会立即触发看门狗中断。安全的做法是,先配置CTL等寄存器,最后再写入有效的LOAD值并解锁。

VALUE寄存器是只读的,用于读取当前计数器的值。这在调试时非常有用,可以判断程序“喂狗”的间隔是否稳定,或者查看在进入低功耗模式前计数器还剩多少时间。

2.2.2 CTL寄存器:控制行为的“大脑”

CTL寄存器虽然只有3个有效控制位,但每一个都至关重要:

  • INTEN (位0): 中断使能。置1后,看门狗超时会先产生一个中断。一旦置位,只有硬件复位才能清除此位。这意味着,如果你使能了中断,就必须在中断服务程序中进行关键的错误处理或系统状态保存,因为无法通过软件禁止它来阻止后续的复位(如果RESEN也使能了)。
  • RESEN (位1): 复位使能。置1后,看门狗超时(在中断之后,如果中断使能)将触发一个系统复位。这是看门狗最核心的功能。通常,在需要捕获超时错误日志的场景,可以同时使能INTEN和RESEN,在中断服务程序中快速保存错误上下文到非易失存储器,然后等待复位。
  • INTTYPE (位2): 中断类型选择。0为可屏蔽中断,1为非可屏蔽中断。NMI的优先级最高,不能被全局中断开关禁止。在极其严苛的实时性要求下,可以选择NMI以确保超时事件能被立即响应。但对于大多数应用,标准中断即可。

配置心得:对于大多数需要高可靠性的应用,我的建议是同时使能中断和复位。在中断服务程序中,不要进行复杂操作,仅记录关键错误码(例如,将程序计数器、栈指针或某个状态标志写入备份寄存器或Flash的特定区域),然后等待复位。复位后,启动代码可以检查这个错误码,从而判断上次复位是否为看门狗触发,并采取相应的恢复策略。

2.2.3 ICR、RIS、MIS寄存器:理解中断状态流

这三个寄存器构成了完整的中断状态管理链:

  1. RIS (Raw Interrupt Status): 原始中断状态位。只要计数器超时,WDTRIS位就会置1,无论INTEN是否使能。你可以通过轮询此位来实现一种“查询式”的看门狗超时检测,但这通常不是好主意,因为它违背了看门狗作为独立监控器的初衷。
  2. MIS (Masked Interrupt Status): 屏蔽后的中断状态位。WDTMIS = WDTRIS & INTEN。只有当中断使能时,超时事件才会反映到这里。这是中断控制器实际“看到”的状态。
  3. ICR (Interrupt Clear): 中断清除寄存器。向该寄存器写入任意值,即可清除WDTRIS位(同时也会清除MIS位)。这是清除看门狗中断标志的唯一方法。同时,这个操作会自动将LOAD寄存器的值重新加载到计数器中。也就是说,“喂狗”操作是通过写ICR寄存器完成的,而不是重新写LOAD。

关键操作:“喂狗”的正确姿势是WDT->ICR = 0x1;(写入任何值均可)。这个操作一举两得:清除了挂起的中断,并重置了计数器。

2.2.4 LOCK寄存器:最后的保险丝

LOCK寄存器是配置安全的关键。向该寄存器写入0x1ACC_E551这个“魔法数字”后,WDT配置寄存器(如LOAD, CTL)将被解锁,允许写入。写入任何其他值,则会立即重新上锁。

为什么需要锁?为了防止跑飞的程序意外修改看门狗的配置(例如,将超时时间设得极长或禁用复位),导致看门狗形同虚设。因此,标准的配置流程是:解锁 -> 配置 -> 上锁。上锁后,即使程序跑飞,也无法篡改看门狗的核心设定了。

2.2.5 TEST与INT_CAUS寄存器:调试与测试的利器

这两个寄存器主要用于芯片测试和高级调试。

  • TEST.STALL: 当使用调试器暂停CPU时,此位决定WDT计数器是否也暂停。在调试阶段,建议启用此功能(置1),否则在你单步调试时,看门狗可能超时导致系统复位,无法调试。
  • TEST.TEST_EN: 测试模式使能。置1时,看门狗超时不会产生外部复位信号,而是置位INT_CAUS.CAUSE_RESET并产生中断。这允许你在不引起系统复位的情况下,测试看门狗中断服务程序。
  • INT_CAUS: 用于在测试模式下,区分中断是由复位事件模拟产生(CAUSE_RESET)还是常规超时产生(CAUSE_INTR,即RIS的副本)。

实操建议:在产品开发阶段,特别是调试与硬件测试阶段,可以合理利用TEST寄存器。但在最终量产软件中,应确保TEST寄存器保持复位默认值(0),以保证看门狗复位功能的绝对有效。

2.3 WDT配置与使用实战代码

理解了寄存器之后,我们来看一个完整的、具备错误恢复能力的WDT初始化与使用流程。以下代码基于TI的DriverLib库风格,但聚焦于寄存器操作原理。

// 假设基地址定义 #define WDT_BASE 0x40085800 #define WDT_LOAD (*(volatile uint32_t *)(WDT_BASE + 0x000)) #define WDT_CTL (*(volatile uint32_t *)(WDT_BASE + 0x008)) #define WDT_ICR (*(volatile uint32_t *)(WDT_BASE + 0x00C)) #define WDT_LOCK (*(volatile uint32_t *)(WDT_BASE + 0xC00)) // 定义“魔法钥匙” #define WDT_LOCK_KEY 0x1ACCE551 // 系统时钟频率 (假设为48MHz) #define SYS_CLK_FREQ_HZ 48000000UL // 期望看门狗超时时间(秒) #define WDT_TIMEOUT_S 2 void WDT_InitWithRecovery(void) { // 步骤1:解锁WDT以进行配置 WDT_LOCK = WDT_LOCK_KEY; // 步骤2:计算并设置超时加载值 // 注意:LOAD是32位递减计数器的初始值。超时时间 = LOAD / Clock_Freq。 // 为防止立即超时,先写一个较大的值,最后再写实际值是一种安全做法。 uint32_t loadValue = SYS_CLK_FREQ_HZ * WDT_TIMEOUT_S; // 检查是否溢出,LOAD是32位寄存器 if (loadValue > 0xFFFFFFFFUL) { // 处理错误:超时时间过长 loadValue = 0xFFFFFFFFUL; // 设置为最大可能值 } WDT_LOAD = loadValue; // 步骤3:配置控制寄存器 uint32_t ctlValue = 0; ctlValue |= (1 << 0); // INTEN = 1, 使能中断(以便记录错误) ctlValue |= (1 << 1); // RESEN = 1, 使能复位(核心功能) // INTTYPE = 0 (默认),使用标准中断 WDT_CTL = ctlValue; // 步骤4:清除任何可能已挂起的中断(上电后或复位后) WDT_ICR = 0x1; // 步骤5:重新上锁,防止配置被意外修改 WDT_LOCK = 0x0; // 写入任何非钥匙值即可上锁 // 步骤6:配置NVIC,启用WDT中断(假设使用标准中断) // NVIC_EnableIRQ(WDT_IRQn); // NVIC_SetPriority(WDT_IRQn, 0); // 通常设为最高或次高优先级 } // 看门狗中断服务程序 // 注意:此函数应尽可能短小,因为复位可能紧随其后发生。 void WDT_IRQHandler(void) { // 步骤1:清除中断标志(通过写ICR,同时也完成了“喂狗”) // 但注意!在中断里“喂狗”会重置计数器,延迟复位。 // 如果我们的目的是记录错误后立即复位,则不应在此喂狗。 // WDT_ICR = 0x1; // 通常不在中断里喂狗 // 步骤2:记录错误上下文到非易失性存储(如备份寄存器、Flash的一页) // 例如,将程序计数器(PC)、链接寄存器(LR)或自定义错误码写入特定位置。 // *((volatile uint32_t *)0x20000000) = 0xDEADBEEF; // 示例:写入SRAM备份区域(复位后会丢失) // 对于需要持久化的错误,需要写入Flash或FRAM。 // 步骤3:(可选)执行关键的安全关闭操作,如断开外部电源、保存关键数据等。 // 步骤4:由于CTL.RESEN=1,在中断服务程序退出后,硬件会自动产生复位。 // 我们无需软件触发复位。 }

“喂狗”的最佳位置: “喂狗”操作(WDT_ICR = 0x1;)应该放在系统的主循环主任务中,确保在正常运行时定期执行。绝对不要放在定时器中断等可能因某个任务阻塞而依然会定期执行的地方,因为这可能掩盖主循环卡死的故障。一个常见的模式是,在多个关键任务完成后设置一个“任务完成”标志,在主循环中检查所有标志后执行“喂狗”。

3. TRNG模块:从物理噪声到密码学安全随机数

3.1 熵源原理与模块架构

CC13x2/CC26x2的TRNG模块的熵源基于24个自由运行振荡器。每个FRO本质上是一个环形振荡器,其振荡频率受制程、电压、温度以及最关键的——半导体噪声的影响。由于这些噪声在物理上是随机的,因此每个FRO的输出边沿在时间上具有不可预测的“抖动”。

模块的核心思想是并行采样与熵积累

  1. 采样:系统时钟以一定频率(由CFG0.SMPL_DIV控制)去异步采样这24个FRO的输出。由于FRO频率与采样时钟频率非整数倍关系且存在抖动,采样结果(0或1)具有不确定性,即熵。
  2. 混合与积累:采样到的24位数据经过一个复杂的非线性组合电路(混合器)进行混淆,然后输入到一个81位的线性反馈移位寄存器中。LFSR在这里的作用不是生成伪随机序列,而是作为一个熵池,将多个时钟周期内产生的、微小的随机性(每个采样点可能只有零点几比特的熵)累积起来。
  3. 输出:当累积的熵达到足够量(由CTL.STARTUP_CYCLESCFG0.MIN_REFILL_CYCLES决定)时,LFSR中的状态被处理并转移到64位的输出寄存器OUT0OUT1中,供CPU读取。

关键参数:熵与周期

  • 启动周期:模块从使能到产生第一个随机数所需的采样周期数。STARTUP_CYCLES* 28。
  • 最小/最大重填周期:在读取一个随机数后,生成下一个随机数所需的最小/最大采样周期数。MIN_REFILL_CYCLES* 64,MAX_REFILL_CYCLES* 28。
  • 采样分频SMPL_DIV,控制采样频率。设为0表示每个系统时钟都采样。为了确保能采集到FRO的跳变,采样周期必须大于最慢FRO周期的一半。

3.2 核心寄存器配置与熵管理策略

3.2.1 控制与配置寄存器:平衡速度与质量

CTL寄存器

  • TRNG_EN:总使能位。为0时,模块处于空闲状态,功耗最低。
  • STARTUP_CYCLES:决定首次生成随机数所需的熵积累时间。TI手册建议,所有24个FRO使能时,约5ms的启动时间可产生64比特熵。计算示例:若系统时钟48MHz,SMPL_DIV=0,则每秒采样48M次。5ms内采样次数=48e6 * 0.005 = 240,000次。STARTUP_CYCLES= 采样次数 / 28 ≈ 8571 (0x217B)。这是高安全应用的关键配置点

CFG0寄存器

  • SMPL_DIV:采样分频。通常设为0以获得最高采样率,从而最快积累熵。
  • MIN_REFILL_CYCLES:最小重填周期。如果设为0,则使用MAX_REFILL_CYCLES的值,这意味着每次生成的随机数都具有相同的熵(由最大值保证)。为了确保每个随机数质量一致,建议将此值设为0
  • MAX_REFILL_CYCLES:最大重填周期。它定义了两个关键时间:1) 生成下一个随机数的最大等待时间;2)模块空闲超时时间。如果随机数生成后,在MAX_REFILL_CYCLES时间内未被读取,TRNG将自动进入空闲模式以省电。

配置策略

  • 高性能/低安全场景(如生成随机延迟):使用较小的STARTUP_CYCLESMAX_REFILL_CYCLES,例如STARTUP_CYCLES = 1*28MAX_REFILL_CYCLES = 1*28。这能在几十个时钟周期内产生随机数,但熵极低。
  • 高安全场景(如生成密钥):必须使用足够大的周期。按照TI建议,设置STARTUP_CYCLES使得启动时间≥5ms,并将MIN_REFILL_CYCLES设为0,MAX_REFILL_CYCLES设为与STARTUP_CYCLES相同或更大的值。
3.2.2 FRO管理寄存器:健康状态监控

TRNG的健壮性依赖于足够多的FRO正常工作。模块内置了FRO告警机制

  • FROEN.FRO_MASK:24位,每位控制一个FRO的开关。默认全开(0x00FFFFFF)。
  • ALARMCNT.ALARM_THR:告警阈值。当一个FRO的输出在连续多个采样周期内呈现重复模式(如0,1,0,1),则认为该FRO可能与采样时钟发生“锁定”,熵质量下降。连续检测到重复模式的次数超过此阈值,则触发告警。
  • ALARMMASK:记录哪个FRO触发了告警。
  • ALARMSTOP:如果某个FRO在已经告警(ALARMMASK对应位为1)的情况下再次告警,则其对应的ALARMSTOP位被置1,并且该FRO被强制关闭(FROEN中对应位被硬件清零)。
  • ALARMCNT.SHUTDOWN_THR:FRO关闭数量阈值。当被关闭的FRO数量(即ALARMSTOP中1的位数)超过此阈值,会触发SHUTDOWN_OVF中断,提示系统TRNG的熵源质量可能已严重下降。

FRO去调谐FRODETUNE寄存器。将某个FRO对应的位置1,可使其运行频率加快约5%,这有助于打破FRO与采样时钟之间的频率锁定。重要规则:只能在该FRO被关闭(FROEN对应位为0)时,修改其FRODETUNE位。

3.2.3 中断与状态寄存器
  • IRQFLAGSTAT.RDY:随机数就绪标志。当新的64位随机数已存入OUT0/OUT1时置位。
  • IRQFLAGSTAT.SHUTDOWN_OVF:FRO关闭溢出标志。
  • IRQFLAGMASK:对应中断的使能位。
  • IRQFLAGCLR:写1清除对应的状态标志。

3.3 TRNG实战编程:轮询与中断模式

3.3.1 初始化序列

以下是基于寄存器操作的高安全级别TRNG初始化代码。假设系统时钟48MHz,目标启动熵积累时间约5ms。

#define TRNG_BASE 0x40028000 #define TRNG_SWRESET (*(volatile uint32_t *)(TRNG_BASE + 0x1FF0)) #define TRNG_CTL (*(volatile uint32_t *)(TRNG_BASE + 0x0014)) #define TRNG_CFG0 (*(volatile uint32_t *)(TRNG_BASE + 0x0018)) #define TRNG_FROEN (*(volatile uint32_t *)(TRNG_BASE + 0x0020)) #define TRNG_ALARMCNT (*(volatile uint32_t *)(TRNG_BASE + 0x001C)) void TRNG_InitHighSecurity(void) { // 步骤1:执行软件复位,并等待复位完成 TRNG_SWRESET = 0x1; while((TRNG_SWRESET & 0x1) != 0); // 轮询等待复位完成 // 步骤2:配置采样分频(每个时钟采样一次) uint32_t cfg0Value = 0; cfg0Value &= ~(0xF << 8); // 清除SMPL_DIV位域 cfg0Value |= (0x0 << 8); // SMPL_DIV = 0 TRNG_CFG0 = cfg0Value; // 步骤3:配置启动周期 (目标 ~5ms @48MHz) // 计算: 5ms * 48e6 Hz = 240,000 次采样 // STARTUP_CYCLES = 采样次数 / 28 = 240000 / 28 ≈ 8571 (0x217B) uint32_t startupCycles = 8571; uint32_t ctlValue = (startupCycles << 16); // 步骤4:配置重填周期(使用最大值,确保熵一致) // 设置 MIN_REFILL_CYCLES = 0, 使用 MAX_REFILL_CYCLES 控制 cfg0Value &= ~(0xFFFF << 16); // 清除 MAX_REFILL_CYCLES cfg0Value |= (startupCycles << 16); // 使用与启动相同的周期 cfg0Value &= ~(0xFF); // 清除 MIN_REFILL_CYCLES // MIN_REFILL_CYCLES = 0, 表示使用 MAX_REFILL_CYCLES 作为固定重填周期 TRNG_CFG0 = cfg0Value; // 步骤5:配置告警阈值(使用默认值0xFF,较高的容错性) TRNG_ALARMCNT = 0x000000FF; // ALARM_THR = 0xFF // 步骤6:配置FRO关闭阈值(例如,允许最多关闭6个FRO) uint32_t alarmcnt = TRNG_ALARMCNT; alarmcnt &= ~(0x1F << 16); // 清除 SHUTDOWN_THR 位域 alarmcnt |= (6 << 16); // SHUTDOWN_THR = 6 TRNG_ALARMCNT = alarmcnt; // 步骤7:使能所有FRO TRNG_FROEN = 0x00FFFFFF; // 24个FRO全开 // 步骤8:使能TRNG模块,开始熵积累 ctlValue |= (1 << 10); // TRNG_EN = 1 TRNG_CTL = ctlValue; // 步骤9:(可选)使能中断 // 如果需要中断方式获取随机数,在此配置 NVIC 和 IRQFLAGMASK }
3.3.2 轮询模式获取随机数

轮询模式简单直接,适用于不频繁获取随机数且对实时性要求不高的场景。

uint64_t TRNG_GetRandomNumberPolling(void) { volatile uint32_t *pOut0 = (volatile uint32_t *)(TRNG_BASE + 0x0000); volatile uint32_t *pOut1 = (volatile uint32_t *)(TRNG_BASE + 0x0004); volatile uint32_t *pIrqFlagStat = (volatile uint32_t *)(TRNG_BASE + 0x0008); volatile uint32_t *pIrqFlagClr = (volatile uint32_t *)(TRNG_BASE + 0x0010); uint64_t randomValue = 0; uint32_t status; // 轮询等待 RDY 标志置位 do { status = *pIrqFlagStat; } while ((status & 0x1) == 0); // 等待 RDY (bit 0) 为1 // 读取随机数(先读低字,再读高字) randomValue = (uint64_t)(*pOut0); randomValue |= ((uint64_t)(*pOut1) << 32); // 清除 RDY 标志,启动下一次熵积累 *pIrqFlagClr = 0x1; // 写1清除 RDY 位 return randomValue; }
3.3.3 中断模式与健康状态监控

对于需要高效、异步获取随机数,或需要监控TRNG健康状态的应用,中断模式更合适。

// 全局变量用于传递随机数 volatile uint64_t g_trngRandomNumber = 0; volatile bool g_trngDataReady = false; volatile bool g_trngFroError = false; void TRNG_EnableInterrupt(void) { volatile uint32_t *pIrqFlagMask = (volatile uint32_t *)(TRNG_BASE + 0x000C); // 使能 RDY 和 SHUTDOWN_OVF 中断 *pIrqFlagMask = (1 << 1) | (1 << 0); // SHUTDOWN_OVF_MASK=1, RDY_MASK=1 // 配置并使能 NVIC 中断 (此处需根据具体CMSIS版本编写) NVIC_EnableIRQ(TRNG_IRQn); } void TRNG_IRQHandler(void) { volatile uint32_t *pIrqFlagStat = (volatile uint32_t *)(TRNG_BASE + 0x0008); volatile uint32_t *pIrqFlagClr = (volatile uint32_t *)(TRNG_BASE + 0x0010); volatile uint32_t *pOut0 = (volatile uint32_t *)(TRNG_BASE + 0x0000); volatile uint32_t *pOut1 = (volatile uint32_t *)(TRNG_BASE + 0x0004); volatile uint32_t *pAlarmMask = (volatile uint32_t *)(TRNG_BASE + 0x0028); volatile uint32_t *pAlarmStop = (volatile uint32_t *)(TRNG_BASE + 0x002C); volatile uint32_t *pFroDetune = (volatile uint32_t *)(TRNG_BASE + 0x0024); volatile uint32_t *pFroEn = (volatile uint32_t *)(TRNG_BASE + 0x0020); uint32_t status = *pIrqFlagStat; // 处理随机数就绪中断 if (status & 0x1) { // RDY g_trngRandomNumber = (uint64_t)(*pOut0); g_trngRandomNumber |= ((uint64_t)(*pOut1) << 32); g_trngDataReady = true; *pIrqFlagClr = 0x1; // 清除 RDY 标志 } // 处理 FRO 关闭溢出中断(健康状态告警) if (status & 0x2) { // SHUTDOWN_OVF g_trngFroError = true; uint32_t alarmMask = *pAlarmMask; uint32_t alarmStop = *pAlarmStop; // 处理策略:尝试对已关闭的FRO进行去调谐并重新启用 for (int i = 0; i < 24; i++) { if ((alarmStop >> i) & 0x1) { // FRO i 已被关闭 // 1. 确保它在FROEN中被禁用(硬件已做) // 2. 尝试去调谐(加快5%) uint32_t detune = *pFroDetune; detune |= (1 << i); *pFroDetune = detune; // 3. 重新启用该FRO uint32_t en = *pFroEn; en |= (1 << i); *pFroEn = en; } } // 清除告警记录 *pAlarmMask = 0x0; *pAlarmStop = 0x0; // 清除 SHUTDOWN_OVF 中断标志 *pIrqFlagClr = 0x2; } }

4. 系统集成与高级应用注意事项

4.1 低功耗模式下的协同工作

在物联网设备中,MCU大部分时间处于睡眠状态。WDT和TRNG在此场景下的行为需要仔细考量。

WDT与低功耗

  • WDT时钟来自INFRASTRUCTURE,只要该时钟域在睡眠模式下保持活动,WDT就继续计数。在CC13x2/CC26x2中,通常的待机模式下,该时钟是活动的。
  • 在进入深度睡眠前,必须评估WDT超时时间与预计睡眠时长。如果睡眠时间可能超过WDT超时时间,则需要在进入睡眠前“喂狗”,或者使用具有更长超时时间的低功耗定时器来唤醒并“喂狗”。
  • 切勿在中断服务程序中长期关闭全局中断而不“喂狗”,这可能导致看门狗超时。

TRNG与低功耗

  • TRNG模块本身在空闲时会自动关闭FRO以省电(由MAX_REFILL_CYCLES控制超时)。
  • 当MCU进入深度睡眠时,TRNG的模块时钟可能被关闭。TI手册指出,直接关闭时钟不会破坏已积累的熵,但重新上电后,在生成新随机数前必须执行一次软件复位以确保随机性安全。
  • 推荐做法:在进入深度睡眠前,如果确定短期内不需要随机数,可以写TRNG_CTL.TRNG_EN = 0主动关闭TRNG,然后关闭其时钟以最大化省电。唤醒后,执行TRNG软件复位,再重新初始化。

4.2 安全应用实践:从随机数到密钥

获取到TRNG产生的随机数后,直接将其作为加密密钥使用并不总是安全的,还需要进行后处理

  1. 熵提取:虽然TRNG内部有熵积累过程,但输出可能仍存在微小偏差。对于极高安全要求,应使用密码学安全的熵提取函数或确定性随机比特生成器。
  2. 健康检查:在生成关键密钥前,应检查TRNG的健康状态。读取ALARMSTOP寄存器,确认关闭的FRO数量未超过安全阈值(例如,24个中关闭数量少于6个)。可以定期调用一个自检函数。
  3. 连续随机数测试:对于非关键应用,可以简单地对连续生成的多个随机数进行统计测试(如重复值检查),作为基本的健康判断。
bool TRNG_HealthCheck(void) { volatile uint32_t *pAlarmStop = (volatile uint32_t *)(TRNG_BASE + 0x002C); volatile uint32_t *pHwOpt = (volatile uint32_t *)(TRNG_BASE + 0x0078); uint32_t stoppedFros = *pAlarmStop; uint32_t totalFros = (*pHwOpt >> 6) & 0x3F; // 读取 NR_OF_FROS 字段 int stoppedCount = __builtin_popcount(stoppedFros); // 计算1的位数 // 如果超过1/4的FRO停止工作,认为健康状态不佳 if (stoppedCount > (totalFros / 4)) { return false; } return true; }

4.3 常见问题与调试技巧

  1. WDT不复位

    • 检查时钟:确认PRCM模块已正确使能WDT的外设时钟。
    • 检查LOCK:确认配置后已正确上锁。可以读取LOCK寄存器,若值为0x1表示已锁。
    • 检查复位源:超时后,查看PRCM:WARMRESET.WDT_STAT位是否被置位,以确认复位是由WDT触发的。
  2. TRNG读取值总是0或固定值

    • 检查使能:确认TRNG_EN位已置1,并且已等待足够的启动时间(检查IRQFLAGSTAT.RDY)。
    • 检查时钟:TRNG模块时钟必须使能(PRCM:SECDMACLKGR.TRNG_CLK_EN)。
    • 检查FRO:读取FROEN寄存器,确认不是所有FRO都被禁用。检查ALARMSTOP,看是否大量FRO因告警被关闭。
  3. TRNG产生随机数的速度慢

    • 调整周期:减小STARTUP_CYCLESMAX_REFILL_CYCLES,但这会牺牲熵质量。
    • 检查SMPL_DIV:确保其为0以获得最高采样率。
    • 使用中断而非轮询:轮询RDY标志会消耗CPU资源且可能引入延迟。
  4. 系统异常复位后如何定位原因

    • 利用WDT中断服务程序,在复位前将错误代码、程序计数器(PC)、链接寄存器(LR)等关键信息写入一个保留的RAM区域(该区域在复位时不会被初始化,或者被备份寄存器保留)。
    • 在系统启动代码中,首先检查这个保留区域是否有有效的错误码。如果有,则通过串口或其他方式输出,然后清除该区域,再进行正常初始化。这能有效区分是看门狗复位、上电复位还是其他复位源。

通过将WDT的“守护”特性与TRNG的“随机”特性结合,你能够为CC13x2/CC26x2构建起一个既稳固又安全的应用基础。记住,看门狗的配置关乎生死,务必简单可靠;随机数的质量关乎秘密,务必谨慎对待。在实际项目中,花费时间充分测试这两种外设在各种极端情况下的表现,是避免现场故障的最佳投资。