1. 看门狗
看门狗: 用于设备在 程序异常(死机) 时 可以自动重启设备
实现原理: 通过定时器 进行定时 , 在定时器时间结束前 进行 "喂狗" 重置定时器时间
若时间到,还没有"喂狗",系统重启
本质就是一个定时器, 如何定时?
定时器 本质是对 晶振时钟进行 计数
2. 看门狗定时器实验
实验需求, 5S 无操作 复位重启
1. uart printf移植
2. 原理图 内部控制器 无外部电路
3. 看门狗控制器
2.1 工作原理图
如图所示
2.2 输出定时器信号的 频率
输出定时器信号的 频率 = 总线时钟 / (分频值+1) / 重载值 ;
或
输出定时器信号的 频率 = 总线时钟 /固定分频/ (分频值+1) / 重载值 ;
看门狗系统时钟为100M 446页
例如 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 寄存器配置 :
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;
}
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;
}
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;
}
5. 简述 static和volatile 关键字的含义和作用。(10分)
(1)static 关键字:
对于变量:static 关键字使得局部变量具有静态存储期,在程序执行过程中只被初始化一次,并且它们的值在多次执行函数时会保持持久性。
对于函数:static 关键字将函数的作用域限制在文件内部,只能在当前文件中调用函数。
对于类中的成员:static 关键字可用于创建静态成员,这些成员在所有类对象之间是共享的,而不是每个对象拥有自己的副本。
static 关键字也可以用于限制代码的可见性。通过将函数和变量声明为 static,可以将它们的作用域限制在当前文件或当前模块,对其他文件或模块不可见。
(2)volatile 关键字:
volatile 用于修饰变量,用来指示变量可能会被程序以外的因素修改,因此编译器在对这些变量进行优化时应该小心处理。
volatile 关键字的主要作用是告诉编译器不要对变量进行优化,每次访问变量时都要从内存中读取,而不是使用缓存的值。
使用 volatile 关键字可以确保对变量的读取和写入操作不会被编译器优化掉,适用于多线程环境、硬件寄存器和中断服务程序等场景。
总结:static 关键字用于控制对象的存储方式、作用域和可见性;volatile 关键字用于指示变量的可能变动性以防止编译器优化。它们在不同的场景中有不同的作用,并且在编写代码时需要根据需要正确使用它们。