看门狗定时器

1. 看门狗

看门狗: 用于设备在 程序异常(死机) 时 可以自动重启设备

实现原理:  通过定时器 进行定时 , 在定时器时间结束前 进行 "喂狗" 重置定时器时间

若时间到,还没有"喂狗",系统重启 

本质就是一个定时器, 如何定时?

定时器 本质是对 晶振时钟进行 计数 

2. 看门狗定时器实验

实验需求,  5S 无操作 复位重启 

1. uart  printf移植  

2. 原理图 内部控制器 无外部电路 

3. 看门狗控制器 

2.1 工作原理图

如图所示 

看门狗定时器_第1张图片

看门狗定时器_第2张图片

2.2 输出定时器信号的 频率

输出定时器信号的 频率 =  总线时钟 / (分频值+1) / 重载值 ;

输出定时器信号的 频率 =  总线时钟 /固定分频/ (分频值+1) / 重载值 ;

看门狗系统时钟为100M  446页

看门狗定时器_第3张图片

例如 5S --->  0.2hz

0.2hz = 100 MHz/ (分频值+1) / 重载值 ;    //取固定分频为128  100Mhz/128 = 781250

0.2hz = 781250 / (预分频值+1) / 重载值 ;

0.2hz = 781250  / (249+1)/ 重载值 ;

0.2hz = 3125 / 重载值 ;

重载值 = 15625

2.3 寄存器配置 : 

看门狗定时器_第4张图片

WTCON 看门狗控制寄存器

WTCON   [15:8]  配置预分频值  范围[0-255] =249

[5]     = 1 使能 wdt  开始定时

[4:3]    固定分频  16  32  64  128

[2]     时间到,是否触发中断信号  = 1 触发中断 

[0]     时间到,是否触发复位信号  = 1 触发复位 

WTDAT 重载寄存器

WTDAT 重载寄存器   [15:0]  取值范围 0-65535

WTCNT 计数寄存器

WTCNT 计数寄存器  [15:0]    取值范围 0-65535

WTCLRINT  中断清除寄存器

WTCLRINT 中断清除寄存器 

2.4 写程序

watch_dog------main


#include"exynos_4412.h"
#include"uart.h"
//看门狗-----复位模式
//初始化wdt实现5S内 不操作  重启
void wdt_init()
{
	//预分频 249
	WDT.WTCON = (WDT.WTCON & ~(0XFF<<8)) | (249<<8);
	//固定分频 128----对应的原理图的0x3
	WDT.WTCON = (WDT.WTCON & ~(0X3<<3)) | (0x3<<3);
	//触发复位 [0] 时间到,是否触发复位信号  = 1 触发复位 
	WDT.WTCON |= 1; 

	//5s时间	
    // 重载寄存器 
	WDT.WTDAT = 15625;
    //计数寄存器
	WDT.WTCNT = 15625;

	//启动wdt
	WDT.WTCON |= 1<<5;
}

//接收一个字符
char getc()
{
	//等待数据到来
	while( ! (UART2.UTRSTAT2 & 1) );
	return UART2.URXH2 & 0XFF;
}



int main()
{
	char c;
    int a = 100;
    uart_init();
    printf("hello!a=%d\r\n",a);

    //初始化看门狗
    wdt_init();
	while(1)
	{
		c = getc();//通过串口 获取字符
		putc(c);//输出到串口
		//喂狗,再次将计数寄存器重置
		WDT.WTCNT = 15625;
	}
    return 0;
}


看门狗定时器_第5张图片

3. 实现 wdt 中断模式  每秒触发一次中断 使led3闪烁

中断模式:  wdt中断 id 75

1hz = 100 MHz/ (分频值+1) / 重载值 ;    //取固定分频为128  100Mhz/128 = 781250

1hz = 781250 / (预分频值+1) / 重载值 ;

1hz = 781250  / (249+1)/ 重载值 ;

1hz = 3125 / 重载值 ;

重载值 = 3125

dog_test----main.c


#include"exynos_4412.h"
#include"uart.h"

//实现 wdt 中断模式  每秒触发一次中断

//初始化led3
void led3_init(){
    //配置引脚模式
    GPX1.CON = (GPX1.CON & ~(0xf<<0)) | (0x1 << 0);
    //配置数据寄存器
    // GPX1.DAT |= 1;
    GPX1.DAT &= ~1;
    //配置上下拉寄存器
    GPX1.PUD &= ~(0x3<<0);
}

//中断初始化
exit_init(){

    //GIC 面向中断源
    //开启总中断
    ICDDCR = 1;

    //配置端口中断使能
    //中断模式:  wdt中断 id 75
    //75/32=2   75%32=11
    ICDISER.ICDISER2 |= 1<<11;

    //配置端口优先级 优先级设置为5
    //75/4= 18    75%4=3  ----  [15:8]
    ICDIPR.ICDIPR18 = (ICDIPR.ICDIPR18 & ~(0xff<<24)) | (5<<24);

    //配置中断源送去哪个cpu处理 0x1表示直送cpu0
    //寄存器分步格局 与ICDIPR 完全一样 
    ICDIPTR.ICDIPTR18 = (ICDIPTR.ICDIPTR18 & ~(0xff<<24)) | (0x1<<24);
    
    //GIC 面向cpu
    //cpu响应中断使能  =1 使能  =0 不使能
    CPU0.ICCICR = 1;

    //配置cpu过滤优先级
    CPU0.ICCPMR = 255;

}

//中断响应,c语言入口函数,在汇编汇总调用,当irq异常触发时
void do_irq(){
    //获取中断号
    int id = CPU0.ICCIAR;
    printf("irq_id = %d\n",id);

    //根据中断id来处理对应的事件
    switch(id){
        case 75:
        printf("exit enable!!!");

        //点亮led3
        GPX1.DAT ^= (1<<0); 

        //先清除中断 源头的挂起 看门狗的中断
        //写入任何值清除
        WDT.WTCLRINT = 8;

        //在清除GIC分配器层中断挂起 与ICDISER_CPU 结构一样 id:57
        //置为1  清除
        ICDICPR.ICDICPR2 |= 1<<11;
        break;

    }

    //最后清除cpu中断挂起
    //写入中断id清除对应中断挂起
    CPU0.ICCEOIR = id;

}

//看门狗初始化
void dog_init(){
    //预分频
    WDT.WTCON = (WDT.WTCON &~(0xff<<8)) | (249<<8);
    //固定分频
    WDT.WTCON = (WDT.WTCON &~(0x3<<3)) | (3<<3);
    //触发中断信号  [2] 时间到,是否触发中断信号  = 1 触发中断 
    WDT.WTCON |= 1<<2;

    //重载寄存器
    WDT.WTDAT = 3125;
    //计数寄存器
    WDT.WTCNT =3125;

    //开启看门狗
    WDT.WTCON |= 1<<5;

}

int main()
{
    int a = 100;
    uart_init();
    printf("hello!a=%d\r\n",a);

    led3_init();
    dog_init();
    exit_init();
    
    
    
    while(1);
    return 0;
}


看门狗定时器_第6张图片

4. 使用WDT定时器 中断功能 实现 精确ms延时 msleep(nms)

中断模式:  wdt中断 id 75

1s = 1000ms

1ms = 0.001s = 10^-3 s

1000hz = 100 MHz/ (分频值+1) / 重载值 ;    //取固定分频为128  100Mhz/16 = 6250000

1000hz = 6250000 / (预分频值+1) / 重载值 ;

1000hz = 6250000  / (24+1)/ 重载值 ;1000hz

1000hz = 250000 / 重载值 ;

重载值 = 250

dog_delay----main.c


#include"exynos_4412.h"
#include"uart.h"

//实现 wdt 中断模式  每毫秒触发一次中断
volatile unsigned int count = 0;

//延时  ms
void delay(unsigned short ms){

    unsigned int tmp = count;//存放刚进入delay函数时 count 的数值

    // while(count <= tmp+ms);//这种算法没有规避溢出问题

    while(count-tmp < ms);//这种算法规避了溢出问题,不必纠结算法,用就完事了

}


//中断初始化
exit_init(){

    //GIC 面向中断源
    //开启总中断
    ICDDCR = 1;

    //配置端口中断使能
    //中断模式:  wdt中断 id 75
    //75/32=2   75%32=11
    ICDISER.ICDISER2 |= 1<<11;

    //配置端口优先级 优先级设置为5
    //75/4= 18    75%4=3  ----  [15:8]
    ICDIPR.ICDIPR18 = (ICDIPR.ICDIPR18 & ~(0xff<<24)) | (5<<24);

    //配置中断源送去哪个cpu处理 0x1表示直送cpu0
    //寄存器分步格局 与ICDIPR 完全一样 
    ICDIPTR.ICDIPTR18 = (ICDIPTR.ICDIPTR18 & ~(0xff<<24)) | (0x1<<24);
    
    //GIC 面向cpu
    //cpu响应中断使能  =1 使能  =0 不使能
    CPU0.ICCICR = 1;

    //配置cpu过滤优先级
    CPU0.ICCPMR = 255;

}

//中断响应,c语言入口函数,在汇编汇总调用,当irq异常触发时
void do_irq(){
    //获取中断号
    int id = CPU0.ICCIAR;

    //根据中断id来处理对应的事件
    switch(id){
        case 75:

        count++;

        //先清除中断 源头的挂起 看门狗的中断
        //写入任何值清除
        WDT.WTCLRINT = 8;

        //在清除GIC分配器层中断挂起 与ICDISER_CPU 结构一样 id:57
        //置为1  清除
        ICDICPR.ICDICPR2 |= 1<<11;

        break;

    }

    //最后清除cpu中断挂起
    //写入中断id清除对应中断挂起
    CPU0.ICCEOIR = id;

}

//看门狗初始化
void dog_init(){
    //预分频
    WDT.WTCON = (WDT.WTCON &~(0xff<<8)) | (24<<8);
    //固定分频
    WDT.WTCON = (WDT.WTCON &~(0x3<<3)) | (0<<3);
    //触发中断信号  [2] 时间到,是否触发中断信号  = 1 触发中断 
    WDT.WTCON |= 1<<2;

    //重载寄存器
    WDT.WTDAT = 250;
    //计数寄存器
    WDT.WTCNT =250;

    //开启看门狗
    WDT.WTCON |= 1<<5;
}

int main()
{
    int a = 100;
    uart_init();
    printf("hello!a=%d\r\n",a);

    dog_init();
    exit_init();

    while(1){
        delay(2000);
        printf("hello world \r\n");
        delay(5000);
        printf("hhhhhhhhh \r\n");
    }
    return 0;
}




看门狗定时器_第7张图片

5. 简述 static和volatile 关键字的含义和作用。(10分)

(1)static 关键字:

对于变量:static 关键字使得局部变量具有静态存储期,在程序执行过程中只被初始化一次,并且它们的值在多次执行函数时会保持持久性。

对于函数:static 关键字将函数的作用域限制在文件内部,只能在当前文件中调用函数。

对于类中的成员:static 关键字可用于创建静态成员,这些成员在所有类对象之间是共享的,而不是每个对象拥有自己的副本。

static 关键字也可以用于限制代码的可见性。通过将函数和变量声明为 static,可以将它们的作用域限制在当前文件或当前模块,对其他文件或模块不可见。

(2)volatile 关键字:

volatile 用于修饰变量,用来指示变量可能会被程序以外的因素修改,因此编译器在对这些变量进行优化时应该小心处理。

volatile 关键字的主要作用是告诉编译器不要对变量进行优化,每次访问变量时都要从内存中读取,而不是使用缓存的值。

使用 volatile 关键字可以确保对变量的读取和写入操作不会被编译器优化掉,适用于多线程环境、硬件寄存器和中断服务程序等场景。

总结:static 关键字用于控制对象的存储方式、作用域和可见性;volatile 关键字用于指示变量的可能变动性以防止编译器优化。它们在不同的场景中有不同的作用,并且在编写代码时需要根据需要正确使用它们。

你可能感兴趣的:(exynos4412接口编程,嵌入式硬件,arm开发,c语言)