Zephyr看门狗-硬件看门狗
硬件看门狗是一个专用独立的硬件定时器,当系统出现故障(如死循环、崩溃)时,如果未能在规定时间内"喂狗",它会重启系统。Zephyr在zephyr/include/zephyr/drivers/watchdog.h中为硬件看门狗抽象了专门的接口,以屏蔽不同SoC的硬件看门狗差异。
硬件看门狗接口 链接到标题
API 链接到标题
int wdt_setup(const struct device *dev, uint8_t options):配置看门狗实例,需要在安装超时配置后调用。- 参数:设备实例
dev和配置选项options。- WDT_OPT_PAUSE_IN_SLEEP:在CPU睡眠时暂停看门狗计时器。
- WDT_OPT_PAUSE_HALTED_BY_DBG:在CPU被调试器暂停时暂停看门狗计时器。
- 返回值:成功返回0,失败返回错误码。
- 参数:设备实例
int wdt_disable(const struct device *dev):禁用看门狗实例,会自动卸载所有超时配置。- 参数:设备实例
dev。 - 返回值:成功返回0,失败返回错误码。
- 参数:设备实例
int wdt_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg):安装新的超时配置。- 参数:设备实例
dev和超时配置结构体cfg。 - 返回值:成功返回通道ID,失败返回错误码。
- 参数:设备实例
int wdt_feed(const struct device *dev, int channel_id):对指定的看门狗超时进行喂狗操作。- 参数:设备实例
dev和通道IDchannel_id。 - 返回值:成功返回0,失败返回错误码。
- 参数:设备实例
配置数据结构&类型 链接到标题
typedef void (*wdt_callback_t)(const struct device *dev, int channel_id);
struct wdt_timeout_cfg {
struct wdt_window window; // 时间窗口配置
wdt_callback_t callback; // 超时回调函数
uint8_t flags; // 看门狗行为标志
};
struct wdt_window {
uint32_t min; // 最小超时时间(毫秒)
uint32_t max; // 最大超时时间(毫秒)
};
看门狗行为标志:
- WDT_FLAG_RESET_NONE:不重启。
- WDT_FLAG_RESET_CPU_CORE:重启CPU核心。
- WDT_FLAG_RESET_SOC:重启SoC。
使用方法 链接到标题
在Devicetree中开启硬件看门狗,不同的SoC,看门狗的节点名不一样,下面示例esp32c3的Devicetree配置:
/ {
model = "Espressif ESP32C3-DevkitC";
compatible = "espressif,esp32c3";
aliases {
watchdog0 = &wdt0;
};
};
&wdt0 {
status = "okay";
};
Kconfig配置启动WDT
CONFIG_WATCHDOG=y
看门狗API使用方法
//步骤1:获取设备
const struct device *const wdt = DEVICE_DT_GET(DT_ALIAS(watchdog0));
if (!device_is_ready(wdt)) {
printk("%s: device not ready.\n", wdt->name);
return 0;
}
//步骤2:配置超时参数
struct wdt_timeout_cfg wdt_config = {
.flags = WDT_FLAG_RESET_SOC, // 超时后重启整个SoC
.window.min = 0, // 最小时间窗口
.window.max = 1000, // 最大时间窗口(1秒)
.callback = my_callback, // 可选的回调函数
};
//步骤3:安装超时配置
int wdt_channel_id = wdt_install_timeout(wdt, &wdt_config);
if (wdt_channel_id < 0) {
printk("Failed to install watchdog timeout\n");
return -1;
}
//步骤4:启动看门狗
int err = wdt_setup(wdt, WDT_OPT_PAUSE_HALTED_BY_DBG);
if (err < 0) {
printk("Failed to setup watchdog\n");
return -1;
}
//步骤5:定期喂狗
while (1) {
// 应用程序主循环
do_something();
// 定期喂狗,防止系统重启, 如果该主循序因不能执行,WDT超时会重启SoC
wdt_feed(wdt, wdt_channel_id);
k_sleep(K_MSEC(500)); // 每500ms喂一次
}
硬件看门狗驱动的实现 链接到标题
驱动实现方式 链接到标题
硬件看门狗的驱动实现在zephyr/drivers/watchdog下, Zephyr下绝大部分的SoC 硬件看门狗驱动都有实现,其实现范式和其它驱动模型一样,不同的SoC的硬件驱动各自按照下面接口定义调用hal实现对应的功能, 并使用DEVICE_DT_DEFINE注册即可
- 看门狗的驱动实现在
zephyr/drivers/watchdog下,Zephyr下绝大部分的SoC WDT驱动都有实现,其实现范式和其它驱动模型一样,不同的soc的WDT驱动各自按照下面接口定义调用hal实现对应的功能, 并使用DEVICE_DT_DEFINE注册即可
__subsystem struct wdt_driver_api {
wdt_api_setup setup;
wdt_api_disable disable;
wdt_api_install_timeout install_timeout;
wdt_api_feed feed;
};
typedef int (*wdt_api_setup)(const struct device *dev, uint8_t options);
typedef int (*wdt_api_disable)(const struct device *dev);
typedef int (*wdt_api_install_timeout)(const struct device *dev,
const struct wdt_timeout_cfg *cfg);
typedef int (*wdt_api_feed)(const struct device *dev, int channel_id);
例如esp32的zephyr/drivers/watchdog/xt_wdt_esp32.c
static DEVICE_API(wdt, esp32_xt_wdt_api) = {
.setup = esp32_xt_wdt_setup,
.disable = esp32_xt_wdt_disable,
.install_timeout = esp32_xt_wdt_install_timeout,
.feed = esp32_xt_wdt_feed
};
DEVICE_DT_DEFINE(DT_NODELABEL(xt_wdt),
&esp32_xt_wdt_init,
NULL,
&esp32_xt_wdt_data0,
&esp32_xt_wdt_config0,
POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&esp32_xt_wdt_api);
Kconfig配置 链接到标题
公共配置 链接到标题
CONFIG_WDT_DISABLE_AT_BOOT 在系统启动时主动禁用看门狗, 不同的SoC支持情况不同,如果对应驱动的Kconfig中有HAS_WDT_DISABLE_AT_BOOT就是支持,例如Kconfig.esp32j就是支持
config WDT_ESP32
bool "ESP32 Watchdog (WDT) Driver"
default y
depends on DT_HAS_ESPRESSIF_ESP32_WATCHDOG_ENABLED
select HAS_WDT_DISABLE_AT_BOOT
help
Enable WDT driver for ESP32.
CONFIG_WDT_MULTISTAGE 启用多阶段看门狗超时机制,同的SoC支持情况不同,如果对应驱动的Kconfig中有HAS_WDT_DISABLE_AT_BOOT就是支持,目前只有OpenTitan支持,例如Kconfig.opentitan
config WDT_OPENTITAN
bool "OpenTitan Always-On (AON) Timer"
depends on DT_HAS_LOWRISC_OPENTITAN_AONTIMER_ENABLED
default y
select HAS_WDT_MULTISTAGE
help
This option enables support for the watchdog portion of the OpenTitan AON
timer.
私有配置 链接到标题
不同的SoC的WDT有不同的特性,会有自己的Kconfig选项,这些可以从不同的Kconfig中找到并根据需要配置,例如zephyr/drivers/watchdog/Kconfig.renesas_ra中可以配置WDT产生NMI中断
config WDT_RENESAS_RA_NMI
bool "NMI interrupt enable"
default y
select RUNTIME_NMI
help
Watchdog timer generates NMI at value 0
自动配置项 链接到标题
并不是所以的SoC都支持WDT Timeout的callback,和实际的驱动实现与配置相关,是否支持callback由HAS_WDT_NO_CALLBACKS来控制体现,例如zephyr/drivers/watchdog/Kconfig.gd32中使用FWDGT就支持callback而使用WWDGT就不支持callback
config FWDGT_GD32
bool "GD32 Free watchdog timer (FWDGT) driver"
default y
depends on DT_HAS_GD_GD32_FWDGT_ENABLED
select HAS_WDT_DISABLE_AT_BOOT
select USE_GD32_FWDGT
help
Enable the Free watchdog timer (FWDGT) driver for GD32 SoCs.
config WWDGT_GD32
bool "GD32 Window watchdog timer (WWDGT) Driver"
default y
depends on DT_HAS_GD_GD32_WWDGT_ENABLED
select HAS_WDT_NO_CALLBACKS
select USE_GD32_WWDGT
help
Enable the Window watchdog timer (WWDGT) driver for GD32 SoCs.
将普通的硬件Counter作为WDT 链接到标题
在设备树中做如下配置,可以将普通的硬件Counter作为WDT
&timer0 {
status = "okay";
interrupts = <8 0>;
zli;st, <&wdt_counter>;
};
&wdt0 {
status = "disabled";
};
/ {
wdt_counter: wdt-counter {
compatible = "zephyr,counter-watchdog";
status = "okay";
counter = <&timer0>;
};
};
zephyr/drivers/watchdog/Kconfig通过设备树生成的DT_HAS_ZEPHYR_COUNTER_WATCHDOG_ENABLED选中WDT_COUNTER
config WDT_COUNTER
bool "Counter based watchdog"
default y
depends on DT_HAS_ZEPHYR_COUNTER_WATCHDOG_ENABLED
select COUNTER
zephyr/drivers/watchdog/CMakeLists.txt发现有CONFIG_WDT_COUNTER将counter实现wdt的驱动wdt_counter.c加入编译
zephyr_library_sources_ifdef(CONFIG_WDT_COUNTER wdt_counter.c)
参考 链接到标题
https://docs.zephyrproject.org/latest/hardware/peripherals/watchdog.html