目录
概述
本文主要介绍nRF52832基于看门狗定时器寄存器级实现驱动程序,文中给了多个完全基于寄存器操作的nRF52832看门狗定时器(WDT)驱动程序,不依赖SDK,适用于低功耗场景。寄存器级的WDT驱动提供了对nRF52832看门狗定时器的精细控制,特别适合需要超低功耗的应用场景。通过直接操作寄存器,可以消除SDK的开销,实现更高效的电源管理。
1 寄存器详解与关键操作
1.1 配置寄存器 (CONFIG)
WDT_BASE->CONFIG = (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos) |
(WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) |
(WDT_CONFIG_WINDOW_Open << WDT_CONFIG_WINDOW_Pos);
位字段说明:
SLEEP
(位0): 睡眠模式行为
0
= 暂停计数
1
= 继续计数
HALT
(位1): CPU暂停行为
0
= 暂停计数
1
= 继续计数
WINDOW
(位2): 窗口模式
0
= 开放窗口
1
= 窗口模式
1.2 超时设置 (CRV)
// 设置30秒超时
uint32_t reload_value = (30000 * 32768) / 1000 - 1;
WDT_BASE->CRV = reload_value;
计算公式:
超时时间(秒) = (CRV + 1) / 32768
CRV = (超时时间(秒) * 32768) - 1
1.3 通道使能 (RREN)
// 启用通道0和1
WDT_BASE->RREN = (1 << 0) | (1 << 1);
注意:
每个位对应一个刷新通道
必须至少启用一个通道
最多支持8个通道
1.4 喂狗操作
// 刷新通道0
*((volatile uint32_t *)(WDT_BASE + 0x600) + 0) = 0x6E524635;
关键点:
必须写入特定值
0x6E524635
(ASCII "NRF5")每个通道有独立的刷新寄存器
地址计算:
WDT_BASE + 0x600 + channel_id * 4
1.5 启动看门狗
WDT_BASE->TASKS_START = 1;
重要特性:
一旦启动,无法停止
只能通过系统复位禁用
启动后立即开始计数
2 低功耗优化技巧
2.1 睡眠模式配置
// 在深度睡眠时暂停看门狗
void configure_wdt_for_deep_sleep(void) {
// 暂停在睡眠和暂停时
WDT_BASE->CONFIG = (WDT_CONFIG_SLEEP_Pause << WDT_CONFIG_SLEEP_Pos) |
(WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
// 睡眠前喂狗确保最大睡眠时间
wdt_feed();
}
2.2 动态超时调整
void adjust_wdt_timeout_based_on_sleep(uint32_t sleep_ms) {
// 计算新超时值 (睡眠时间 + 50%余量)
uint32_t new_timeout = sleep_ms * 1.5;
WDT_BASE->CRV = wdt_calculate_reload_value(new_timeout);
// 刷新所有启用通道
for (int i = 0; i < 8; i++) {
if (WDT_BASE->RREN & (1 << i)) {
*((volatile uint32_t *)(WDT_BASE + 0x600) + i) = 0x6E524635;
}
}
}
2.3 窗口看门狗的低功耗实现
void low_power_window_wdt(uint32_t min_ms, uint32_t max_ms) {
// 配置窗口看门狗
WDT_BASE->CONFIG = (WDT_CONFIG_WINDOW_Window << WDT_CONFIG_WINDOW_Pos) |
(WDT_CONFIG_SLEEP_Pause << WDT_CONFIG_SLEEP_Pos);
// 设置窗口时间
WDT_BASE->CRV = wdt_calculate_reload_value(min_ms);
WDT_BASE->WINDOW = wdt_calculate_reload_value(max_ms);
// 启用通道
WDT_BASE->RREN = 1 << WDT_CHANNEL_ID;
WDT_BASE->TASKS_START = 1;
}
// 安全喂狗函数
void safe_window_feed(void) {
uint32_t counter = WDT_BASE->COUNTERVALUE;
uint32_t min_val = WDT_BASE->CRV;
if (counter > min_val) {
wdt_feed();
} else {
// 太早喂狗 - 等待安全窗口
while (WDT_BASE->COUNTERVALUE <= min_val);
wdt_feed();
}
}
3 看门狗状态监控
3.1 运行时状态检查
void check_wdt_status(void) {
// 检查是否正在运行
bool is_running = WDT_BASE->RUNSTATUS;
// 检查当前计数器值
uint32_t counter = WDT_BASE->COUNTERVALUE;
// 检查最后喂狗时间
static uint32_t last_feed = 0;
uint32_t time_since_feed = (counter - last_feed) * 1000 / 32768;
last_feed = counter;
}
3.2 复位后诊断
void post_reset_diagnostics(void) {
uint32_t reset_reason = NRF_POWER->RESETREAS;
if (reset_reason & POWER_RESETREAS_DOG_Msk) {
// 看门狗复位
uint32_t *backup_ram = (uint32_t *)0x20000000;
// 恢复关键数据
if (backup_ram[0] == 0xDEADBEEF) {
// 执行恢复操作
}
}
// 清除复位标志
NRF_POWER->RESETREAS = 0xFFFFFFFF;
}
4 性能分析和注意事项
4.1 性能分析
-
代码大小:
-
基础驱动:~300字节 (Thumb2指令集)
-
完整功能:~1KB
-
-
内存占用:
-
RAM: 8-32字节 (取决于配置)
-
寄存器: 使用WDT专用寄存器
-
-
执行时间:
-
初始化:~2μs
-
喂狗操作:~0.1μs
-
状态检查:~0.5μs
-
-
功耗影响:
-
运行模式:增加~15μA
-
睡眠模式:增加0-1μA (取决于配置)
-
4.2 注意事项
1) 启动顺序:
// 正确顺序
wdt_init();
// ...其他初始化...
wdt_start();
2) 临界区保护:
void critical_section(void) {
// 禁用中断
__disable_irq();
// 喂狗后再进入临界区
wdt_feed();
// 执行关键操作
// ...
// 恢复中断
__enable_irq();
}
3) 调试支持:
void debug_wdt_disable(void) {
// 检查是否在调试模式下
if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) {
// 模拟看门狗禁用
m_wdt_state = WDT_STATE_DISABLED;
}
}
4) 错误处理:
void handle_imminent_reset(void) {
// 保存关键数据到保留内存
uint32_t *backup = (uint32_t *)0x20000000;
backup[0] = 0xDEADBEEF;
backup[1] = __get_LR();
backup[2] = __get_PC();
// 等待复位
while(1);
}
5 应用实例
5.1 多任务看门狗系统
#define MAX_TASKS 3
typedef struct {
uint32_t last_feed;
uint32_t timeout;
bool active;
} wdt_task_t;
static wdt_task_t tasks[MAX_TASKS] = {0};
// 注册任务
int register_task(uint32_t timeout_ms) {
for (int i = 0; i < MAX_TASKS; i++) {
if (!tasks[i].active) {
tasks[i].active = true;
tasks[i].timeout = timeout_ms;
tasks[i].last_feed = WDT_BASE->COUNTERVALUE;
return i;
}
}
return -1; // 无可用槽位
}
// 任务心跳
void task_heartbeat(int task_id) {
if (task_id >= 0 && task_id < MAX_TASKS) {
tasks[task_id].last_feed = WDT_BASE->COUNTERVALUE;
}
}
// 监控任务
void monitor_tasks(void) {
uint32_t current_time = WDT_BASE->COUNTERVALUE;
for (int i = 0; i < MAX_TASKS; i++) {
if (tasks[i].active) {
uint32_t elapsed = (current_time - tasks[i].last_feed) * 1000 / 32768;
if (elapsed > tasks[i].timeout) {
// 任务超时处理
handle_task_timeout(i);
}
}
}
// 所有任务正常时喂狗
wdt_feed();
}
5.2 低功耗集成示例
#include "nrf_gpio.h"
#define LED_PIN 17
#define SLEEP_DURATION_MS 10000
// 系统睡眠函数
void system_sleep(uint32_t sleep_ms) {
// 1. 准备看门狗
wdt_prepare_for_sleep();
// 2. 配置所有GPIO为低功耗状态
for (int i = 0; i < 32; i++) {
NRF_GPIO->PIN_CNF[i] = GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos |
GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos |
GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos |
GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos |
GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos;
}
// 3. 配置唤醒源 (例如按钮)
NRF_GPIO->PIN_CNF[13] = GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos |
GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos;
// 4. 进入System ON睡眠
__SEV();
__WFE();
__WFE();
}
int main(void) {
// 初始化LED
NRF_GPIO->DIRSET = (1 << LED_PIN);
NRF_GPIO->OUTSET = (1 << LED_PIN);
// 检查复位原因
uint32_t reset_reason = get_reset_reason();
if (reset_reason & POWER_RESETREAS_DOG_Msk) {
// 看门狗复位 - 执行恢复操作
NRF_GPIO->OUTCLR = (1 << LED_PIN); // LED亮表示复位
}
clear_reset_reasons();
// 初始化看门狗 (在睡眠时暂停以节省功耗)
wdt_init(false); // run_in_sleep = false
wdt_start();
while (1) {
// 1. 执行应用任务
NRF_GPIO->OUTSET = (1 << LED_PIN); // LED灭
nrf_delay_ms(100);
// 2. 喂狗
wdt_feed();
// 3. 检查剩余时间
uint32_t remaining = wdt_get_remaining_time();
if (remaining < 5000) { // 少于5秒
NRF_GPIO->OUTCLR = (1 << LED_PIN); // LED亮警告
}
// 4. 进入低功耗睡眠
system_sleep(SLEEP_DURATION_MS);
}
}
5.3 寄存器级WDT驱动
#include <stdbool.h>
#include "nrf52.h"
// 看门狗配置
#define WDT_BASE NRF_WDT_BASE
#define WDT_TIMEOUT_MS 30000 // 30秒超时
#define WDT_CHANNEL_ID 0 // 使用通道0
// 看门狗状态
typedef enum {
WDT_STATE_DISABLED,
WDT_STATE_ENABLED,
WDT_STATE_RUNNING
} wdt_state_t;
static volatile wdt_state_t m_wdt_state = WDT_STATE_DISABLED;
// 计算超时重载值 (基于32.768kHz时钟)
static inline uint32_t wdt_calculate_reload_value(uint32_t timeout_ms) {
// 周期 = (CRV + 1) / 32768
// CRV = (timeout_ms * 32768) / 1000 - 1
return (timeout_ms * 33) - 1; // 近似计算 (32768/1000 ≈ 32.768 ≈ 33)
}
// 初始化看门狗
void wdt_init(bool run_in_sleep) {
// 1. 配置看门狗行为
WDT_BASE->CONFIG = (run_in_sleep ? WDT_CONFIG_SLEEP_Run : WDT_CONFIG_SLEEP_Pause) << WDT_CONFIG_SLEEP_Pos |
(WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
// 2. 设置超时时间
WDT_BASE->CRV = wdt_calculate_reload_value(WDT_TIMEOUT_MS);
// 3. 启用指定通道
WDT_BASE->RREN = 1 << WDT_CHANNEL_ID;
// 4. 设置状态
m_wdt_state = WDT_STATE_ENABLED;
}
// 启动看门狗
void wdt_start(void) {
if (m_wdt_state == WDT_STATE_DISABLED) {
wdt_init(true); // 默认在睡眠时运行
}
// 启动看门狗
WDT_BASE->TASKS_START = 1;
m_wdt_state = WDT_STATE_RUNNING;
}
// 喂狗
void wdt_feed(void) {
if (m_wdt_state != WDT_STATE_RUNNING) return;
// 向刷新寄存器写入0x6E524635 (magic number)
*((volatile uint32_t *)(WDT_BASE + 0x600) + WDT_CHANNEL_ID) = 0x6E524635;
}
// 获取当前计数器值
uint32_t wdt_get_counter(void) {
return WDT_BASE->COUNTERVALUE;
}
// 获取剩余时间 (毫秒)
uint32_t wdt_get_remaining_time(void) {
uint32_t counter = WDT_BASE->COUNTERVALUE;
uint32_t reload = WDT_BASE->CRV;
// 剩余时间 = (CRV - COUNTERVALUE) / 32.768
return ((reload - counter) * 1000) / 32768;
}
// 检查看门狗是否已触发
bool wdt_is_triggered(void) {
return (WDT_BASE->RUNSTATUS == 1);
}
// 低功耗睡眠处理
void wdt_prepare_for_sleep(void) {
if (m_wdt_state != WDT_STATE_RUNNING) return;
// 如果配置为睡眠时暂停,则在睡眠前喂狗
if ((WDT_BASE->CONFIG & WDT_CONFIG_SLEEP_Msk) == (WDT_CONFIG_SLEEP_Pause << WDT_CONFIG_SLEEP_Pos)) {
wdt_feed();
}
}
// 窗口看门狗配置
void wdt_configure_window(uint32_t window_start_ms, uint32_t window_end_ms) {
// 1. 确保看门狗已禁用
if (m_wdt_state == WDT_STATE_RUNNING) {
// 看门狗一旦启动无法停止,需要系统复位
return;
}
// 2. 设置窗口模式
WDT_BASE->CONFIG = WDT_CONFIG_WINDOW_Window << WDT_CONFIG_WINDOW_Pos |
(WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos);
// 3. 设置窗口时间
WDT_BASE->CRV = wdt_calculate_reload_value(window_start_ms);
WDT_BASE->WINDOW = wdt_calculate_reload_value(window_end_ms);
// 4. 启用通道
WDT_BASE->RREN = 1 << WDT_CHANNEL_ID;
m_wdt_state = WDT_STATE_ENABLED;
}
// 获取复位原因
uint32_t get_reset_reason(void) {
return NRF_POWER->RESETREAS;
}
// 清除复位标志
void clear_reset_reasons(void) {
NRF_POWER->RESETREAS = POWER_RESETREAS_RESETPIN_Msk |
POWER_RESETREAS_DOG_Msk |
POWER_RESETREAS_SREQ_Msk |
POWER_RESETREAS_LOCKUP_Msk |
POWER_RESETREAS_OFF_Msk |
POWER_RESETREAS_LPCOMP_Msk |
POWER_RESETREAS_DIF_Msk;
}
// 看门狗中断处理 (用于调试)
__WEAK void WDT_IRQHandler(void) {
// 自定义中断处理
if (WDT_BASE->EVENTS_TIMEOUT) {
WDT_BASE->EVENTS_TIMEOUT = 0;
// 执行紧急操作
// emergency_shutdown();
}
// 可以添加其他事件处理
}
// 启用看门狗中断 (用于调试)
void wdt_enable_interrupts(void) {
// 启用超时中断
WDT_BASE->INTENSET = WDT_INTENSET_TIMEOUT_Msk;
// 设置中断优先级并使能
NVIC_SetPriority(WDT_IRQn, 7); // 最低优先级
NVIC_EnableIRQ(WDT_IRQn);
}