前言
...
独立看门狗简介
独立看门狗定时器(FWDGT)有独立的时钟源(IRC40K)。因此就算是主时钟失效了,它仍然能保持工作状态,这非常适合于需要独立环境且对计时精度要求不高的场合。
当内部向下计数器的计数值达到0,独立看门狗会产生一个复位。使能独立看门狗的寄存器写保护功能可以避免寄存器的值被意外的配置篡改。
独立看门狗主要特征
自由运行的12位向下计数器;
如果看门狗定时器被使能,那么当向下计数器的值达到0时产生系统复位;
独立时钟源,独立看门狗定时器在主时钟故障(例如待机和深度睡眠模式下)时仍能工作;
独立看门狗定时器硬件控制位,可以用来控制是否在上电时自动启动独立看门狗定时器;
可以配置独立看门狗定时器在调试模式下选择停止还是继续工作
独立看门狗功能说明
独立看门狗定时器带有一个8级预分频器和一个12位的向下递减计数器。 图14-1. 独立看门狗定时器框图的独立看门狗定时器的功能模块。
RUD | 独立看门狗定时器计数器重装载值更新 |
PUD | 独立看门狗定时器预分频值更新 |
独立看门狗硬件结构
独立看门狗初始化
fwdgt_config(uint16_t reload_value, uint8_t prescaler_div)
这个库函数第一个参数是重装载计数器的值,第二个参数是预分频器的值
看门狗初始化函数
// 独立看门狗初始化
void WdgDrvInit(void)
{
/* 关闭FWDGT_PSC和FWDGT_RLD的写保护 */
/* 配置预分频器和重装载寄存器 */
fwdgt_config
(
2500,
FWDGT_PSC_DIV32 // 分频后40KHz / 32 = 1.25KHz,周期0.8ms,2500 * 0.8 = 2000ms
);
// 开启独立看门狗
fwdgt_enable();
}
看门狗喂狗函数
喂狗指的是不让计数器里面的值递减为0,而是恢复为重装载寄存器里面的值
// 看门狗喂狗
void FeedDog(void)
{
// 配置看门狗重转载计数器
fwdgt_counter_reload();
}
完整代码
#include <stdint.h>
#include "gd32f30x.h"
// 独立看门狗初始化
void WdgDrvInit(void)
{
/* 关闭FWDGT_PSC和FWDGT_RLD的写保护 */
/* 配置预分频器和重装载寄存器 */
fwdgt_config
(
2500,
FWDGT_PSC_DIV32 // 分频后40KHz / 32 = 1.25KHz,周期0.8ms,2500 * 0.8 = 2000ms
);
// 开启独立看门狗
fwdgt_enable();
}
// 看门狗喂狗
void FeedDog(void)
{
// 配置看门狗重转载计数器
fwdgt_counter_reload();
}
头文件代码:
#ifndef _WDG_DRV_H_
#define _WDG_DRV_H_
/**
***********************************************************
* @brief 独立看门狗初始化
* @param
* @return
***********************************************************
*/
void WdgDrvInit(void);
/**
***********************************************************
* @brief 喂狗
* @param
* @return
***********************************************************
*/
void FeedDog(void);
#endif
应用层代码
将代码写在应用层的目的是让驱动层和应用层实现分离,在没有操作系统的情况下实现程序架构
#include <stdio.h>
#include "wdg_drv.h"
// 应用层看门狗喂狗处理程序
void WdgTask(void)
{
printf("********喂狗***********");
// 调用看门狗重装载值进行喂狗
FeedDog();
}
应用层头文件代码
#ifndef _WDG_APP_H_
#define _WDG_APP_H_
/**
***********************************************************
* @brief 看门狗任务处理函数
* @param
* @return
***********************************************************
*/
void WdgTask(void);
#endif
main 函数测试
主函数的设计采用的也是模块化的程序架构方式,在没有操作系统的情况下,通过一个个的时间片实现程序架构的独立性。
#include <stdint.h>
#include <stdio.h>
#include "led_drv.h"
#include "key_drv.h"
#include "systick.h"
#include "usb2com_drv.h"
#include "delay.h"
#include "wdg_drv.h"
#include "hmi_app.h"
#include "wdg_app.h"
typedef struct
{
uint8_t run; // 调度标志,1:调度,0:挂起
uint16_t timCount; // 时间片计数值
uint16_t timRload; // 时间片重载值
void (*pTaskFuncCb)(void); // 函数指针变量,用来保存业务功能模块函数地址
} TaskComps_t;
static TaskComps_t g_taskComps[] =
{
{0, 5, 5, HmiTask},
{0, 1000, 1000, WdgTask},
/* 添加业务功能模块 */
};
#define TASK_NUM_MAX (sizeof(g_taskComps) / sizeof(g_taskComps[0]))
static void TaskHandler(void)
{
for (uint8_t i = 0; i < TASK_NUM_MAX; i++)
{
if (g_taskComps[i].run) // 判断时间片标志
{
g_taskComps[i].run = 0; // 标志清零
g_taskComps[i].pTaskFuncCb(); // 执行调度业务功能模块
}
}
}
/**
***********************************************************
* @brief 在定时器中断服务函数中被间接调用,设置时间片标记,
需要定时器1ms产生1次中断
* @param
* @return
***********************************************************
*/
static void TaskScheduleCb(void)
{
for (uint8_t i = 0; i < TASK_NUM_MAX; i++)
{
if (g_taskComps[i].timCount)
{
g_taskComps[i].timCount--;
if (g_taskComps[i].timCount == 0)
{
g_taskComps[i].run = 1;
g_taskComps[i].timCount = g_taskComps[i].timRload;
}
}
}
}
static void DrvInit(void)
{
SystickInit();
LedDrvInit();
KeyDrvInit();
DelayInit();
Usb2ComDrvInit();
WdgDrvInit();
}
static void AppInit(void)
{
TaskScheduleCbReg(TaskScheduleCb);
}
int main(void)
{
DrvInit();
AppInit();
printf("*******看门狗测试*******\n");
while (1)
{
TaskHandler();
}
}
测试结果
后记:
...