nRF52832 看门狗定时器寄存器级驱动实现

目录

概述

1 寄存器详解与关键操作

1.1  配置寄存器 (CONFIG)

 1.2 超时设置 (CRV)

1.3  通道使能 (RREN)

 1.4 喂狗操作

 1.5 启动看门狗

2 低功耗优化技巧

2.1 睡眠模式配置

2.2  动态超时调整

2.3 窗口看门狗的低功耗实现

3 看门狗状态监控

3.1 运行时状态检查

3.2 复位后诊断

4 性能分析和注意事项

4.1 性能分析

4.2 注意事项

5 应用实例

5.1 多任务看门狗系统

5.2 低功耗集成示例

5.3 寄存器级WDT驱动


概述

本文主要介绍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 性能分析

  1. 代码大小

    • 基础驱动:~300字节 (Thumb2指令集)

    • 完整功能:~1KB

  2. 内存占用

    • RAM: 8-32字节 (取决于配置)

    • 寄存器: 使用WDT专用寄存器

  3. 执行时间

    • 初始化:~2μs

    • 喂狗操作:~0.1μs

    • 状态检查:~0.5μs

  4. 功耗影响

    • 运行模式:增加~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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值