版权声明:本文为博主原创文章,转载请附上原文出处链接。
本次讲解STC8A8K64S4A12系列单片机系统复位的方式,掌握看门狗定时器的原理;掌握单片机看门狗定时器的寄存器配置及程序设计。
STC8A8K64S4A12系列单片机的复位分为硬件复位和软件复位两种。
硬件复位时,所有寄存器的值会复位到初始值,系统会重新读取所有的硬件选项。同时根据硬件选项所设置的上电等待时间进行上电等待。
软件复位时,除与时钟相关的寄存器保持不变外,其余的所有寄存器的值会复位到初始值,软件复位不会重新读取所有的硬件选项。
STC8A8K64S4A12系列单片机软件复位是通过IAP控制寄存器IAP_CONTR的SWRST使能的,IAP_CONTR寄存器具体如下。
☆注:如果开启软件复位后,还需操作寄存器SWBS位以确定开始执行代码的区域。
在工业控制/汽车电子/航空航天等需要高可靠性的系统中,为了防止“系统在异常情况下,受到干扰,MCU/CPU程序跑飞,导致系统长时间异常工作”的情况发生,而设计了一种用于检测程序运行状态的芯片或电路,俗称看门狗(即Watchdog)。
看门狗通常有一个输入信号和一个输出信号。输入信号也称“喂狗”,输出信号关联MCU的复位端。系统运行后启动看门狗计数器,看门狗开始计数,当计数满后未收到清零信号(喂狗),看门狗计数器溢出,触发看门狗中断,输出控制MCU复位。
看门狗可分为独立于单片机芯片外的看门狗芯片和集成于单片机芯片内的看门狗外设两种。按照工作原理,独立看门狗和窗口看门狗是最为常见的两种看门狗类型。
☆注:以上描述是针对看门狗的通用性描述,不同型号的单片机看门狗可能会有差异。
STC8A8K64S4A12系列单片机看门狗实际上是一个15位的定时器或计数器,简称WDT。单片机运行工作中,如果开启了看门狗外设,则看门狗会对单片机机器周期脉冲不停计数,单片机程序如果不能及时对看门狗的定时器复位(俗称“喂狗”),看门狗就会在其定时器计满溢出时产生一个复位信号,从而使系统重启。
下图可比较形象的诠释看门狗原理,单片机CPU必须在看门狗计数器没有溢出前喂狗,否则计数器一旦溢出,则系统重启。
☆注:所谓的喂狗,就是将看门狗计数器计数清零。
看门狗控制寄存器WDT_CONTR不支持位寻址,该寄存器的B0~B2位用于设置看门狗定时器时钟分频系数,寄存器B3位是单片机空闲模式时的看门狗控制位,寄存器B4位是清零看门狗计数器计数控制位,寄存器B5位是看门狗使能控制位,寄存器B7位是看门狗溢出标志位。
下面给出看门狗溢出时间计算公式:
☆注:STC官方手册上面没有给出看门狗溢出时间的推导过程,也没有明确说明看门狗定时器的时钟频率,以系统时钟12分频后给看门狗定时器提供时钟是最合理的解释。
举例,配置WDT_CONTR寄存器B2位PS2为0,配置WDT_CONTR寄存器B1位PS1和B0位PS0均为1,系统时钟为11.0592MHZ,计算看门狗溢出时间。
针对STC8A8K64S4A12系列单片机看门狗配置过程如下:
☆注:实验例程即是按照上述配置步骤操作寄存器相关位实现,后有详述。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | led | .c | 包含与用户led控制有关的用户自定义函数 |
2 | wdt | .c | 看门狗有关的用户自定义函数 |
3 | delay | .c | 包含用户自定义延时函数 |
■ 需要引用的头文件
#include "delay.h"
#include "wdt.h"
#include "led.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | led.h、wdt.h和delay.h头文件在该路径,所以要包含 |
2 | …\User | STC8.h头文件在该路径,所以要包含 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在wdt.c文件中编写看门狗初始化函数WDT_Init,代码如下。
程序清单:看门狗初始化函数
/***************************************************************************
* 描 述 : 看门狗初始化
* 入 参 : 无
* 返回值 : 无
**************************************************************************/
void WDT_Init(void)
{
WDT_CONTR &= 0xF7; //IDLE_WDT位置0,看门狗定时器在空闲模式下不计数
WDT_CONTR &= 0xFB; //PS2位置0,设置看门狗定时器预分频值
WDT_CONTR |= 0x03; //PS1、PS0位置1,设置看门狗定时器预分频值
WDT_CONTR &= 0x7F; //WDT_FLAG位置0,看门狗溢出标志位清零
WDT_CONTR |= 0x20; //EN_WDT位置1,开启看门狗定时器
}
然后,编写喂狗函数,代码如下。
程序清单:喂狗函数
/***************************************************************************
* 描 述 : 喂狗
* 入 参 : 无
* 返回值 : 无
**************************************************************************/
void WDG_Feed(void)
{
WDT_CONTR |= 0x10; //CLR_WDT位置1,看门狗定时器重新计数
}
最后,在主函数中先控制用户指示灯D3亮,主循环中有软件延时和喂狗操作,喂狗之后会翻转用户指示灯D4并熄灭用户指示灯D3,以方便观察实验现象。
代码清单:主函数
int main()
{
leds_off(); //熄灭开发板上的4个指示灯(D1、D2、D3、D4)
WDT_Init(); //看门狗初始化,看门狗溢出时间约568.8ms(11.0592MHZ)
delay_ms(200);
led_on(LED_3); //点亮用户指示灯D3
while(1)
{
//软件延时200ms,该延时是模拟喂狗前实际执行用户应用代码所用时长
delay_ms(200);
//喂狗
WDG_Feed();
led_off(LED_3); //熄灭用户指示灯D3
led_toggle(LED_4); //翻转用户指示灯D4
//软件延时200ms,该延时是模拟喂狗后实际执行用户应用代码所用时长
delay_ms(200);
}
}
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-7-1:WDT看门狗实验(及时喂狗)”部分。
首先,在wdt.c文件中编写看门狗初始化函数WDT_Init,代码如下。
程序清单:看门狗初始化函数
/***************************************************************************
* 描 述 : 看门狗初始化
* 入 参 : 无
* 返回值 : 无
**************************************************************************/
void WDT_Init(void)
{
WDT_CONTR &= 0xF7; //IDLE_WDT位置0,看门狗定时器在空闲模式下不计数
WDT_CONTR &= 0xFB; //PS2位置0,设置看门狗定时器预分频值
WDT_CONTR |= 0x03; //PS1、PS0位置1,设置看门狗定时器预分频值
WDT_CONTR &= 0x7F; //WDT_FLAG位置0,看门狗溢出标志位清零
WDT_CONTR |= 0x20; //EN_WDT位置1,开启看门狗定时器
}
然后,编写喂狗函数,代码如下。
程序清单:喂狗函数
/***************************************************************************
* 描 述 : 喂狗
* 入 参 : 无
* 返回值 : 无
**************************************************************************/
void WDG_Feed(void)
{
WDT_CONTR |= 0x10; //CLR_WDT位置1,看门狗定时器重新计数
}
最后,在主函数中先控制用户指示灯D3亮,主循环中有软件延时和喂狗操作,喂狗之后会翻转用户指示灯D4并熄灭用户指示灯D3,以方便观察实验现象。
代码清单:主函数
int main()
{
leds_off(); //熄灭开发板上的4个指示灯(D1、D2、D3、D4)
WDT_Init(); //看门狗初始化,看门狗溢出时间约568.8ms(11.0592MHZ)
delay_ms(200);
led_on(LED_3); //点亮用户指示灯D3
while(1)
{
//软件延时200ms,该延时是模拟喂狗前实际执行用户应用代码所用时长
delay_ms(200);
//喂狗
WDG_Feed();
led_off(LED_3); //熄灭用户指示灯D3
led_toggle(LED_4); //翻转用户指示灯D4
//软件延时500ms,该延时是模拟喂狗后实际执行用户应用代码所用时长
delay_ms(500);
}
}