时钟频率
40KHZ
看门狗简介
IWDG主要性能
IWDG功能描述
键寄存器(IWDG_KR)
预分频寄存器(IWDG_PR)
重装载寄存器(IWDG_RLR)
IWDG库函数
看门狗蜂鸣器
main.c
/*看门狗*/
#include"iwdg.h"
#include"led.h"
#include"fmq.h"
#include"key.h"
#include"delay.h"
int main(void)
{
int i=0,j=0;
Led_Init();
Key_Init();
Fmq_Init();
delay_init();
iwdg_init(5);
Fmq_On();
delay_ms(2000);
Fmq_Off();
for(i=0;i<3;i=(i+1)%3)
{
iwdg_fee_dog();
Led_On(i);
delay_ms(500);
Led_Off(i);
delay_ms(500);
}
return 0;
}
iwdg.h
#ifndef _IWDG_H
#define _IWDG_H
#include "stm32f10x_conf.h"
extern void iwdg_init(int nu);
extern void iwdg_fee_dog(void);
#endif
iwdg.c
#include"iwdg.h"
void iwdg_init(int nu)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //关闭寄存器写保护
IWDG_SetPrescaler(IWDG_Prescaler_64); //625设置预分频值
IWDG_SetReload(nu*625); //设置重装载值
IWDG_ReloadCounter(); //重载计数值喂狗
IWDG_Enable(); //启动关门狗
}
void iwdg_fee_dog(void)
{
IWDG_ReloadCounter();//喂狗
}
delay.c
#include "delay.h"
//利用系统滴答定时,编写的延时函数
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数
/****************************************************************************
* 名 称: delay_init()
* 功 能:延时函数初始化
* 入口参数:无
* 返回参数:无
* 说 明:
****************************************************************************/
void delay_init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//为系统定时器选择时钟为 HCLK 8分频 72MHz / 8 = 9MHz
fac_us = SYSCLK / 8;
//微秒的倍乘数 = 72 / 8 = 9
//X * fac_us就相当于有X个微妙
fac_ms = (u16)fac_us * 1000;
//每个ms需要的systick时钟数(1ms = 1000us),所以fac_us*1000
//X * fac_ms就相当于有X个毫妙
}
/****************************************************************************
* 名 称: void delay_us(u32 nus)
* 功 能:延时nus
* 入口参数:要延时的微秒数
* 返回参数:无
* 说 明:nus的值,不要大于1864135us
****************************************************************************/
/*
通过相关手册我们了解到,备份值寄存器和当前值寄存器的值最大为16M - 1
我们的微秒级的延时函数的倍乘数为9所以16M - 1 / 9 = 1864135
*/
void delay_us(u32 nus)
{
u32 midtime; //保存寄存器的状态
SysTick->LOAD = nus * fac_us; //时间加载(相当于有nus个微妙)
SysTick->VAL = 0x00; //清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //使能系统定时器计数
do
{
midtime = SysTick->CTRL;
}
while((midtime & 0x01) && !(midtime & (1 << 16)));//等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL = 0X00; //清空计数器
}
/****************************************************************************
* 名 称: void delay_xms(u16 nms)
* 功 能:延时nms
* 入口参数:要延时的毫妙数
* 返回参数:无
* 说 明:SysTick->LOAD为24位寄存器,所以,最大延时为: nms<=0xffffff*8*1000/SYSCLK
对9M条件下,nms<=1864ms
****************************************************************************/
void delay_xms(u16 nms)
{
u32 midtime;
SysTick->LOAD = (u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL = 0x00; //清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开始倒数
do
{
midtime = SysTick->CTRL;
}
while((midtime & 0x01) && !(midtime & (1 << 16)));//等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL = 0X00; //清空计数器
}
/****************************************************************************
* 名 称: void delay_ms(u16 nms)
* 功 能:延时nms
* 入口参数:要延时的毫妙数
* 返回参数:无
* 说 明:nms:0~65535
****************************************************************************/
void delay_ms(u16 nms)
{
u8 repeat = nms / 540; //记录有多少个整的540ms
u16 remain = nms % 540; //记录有多少个不够540ms
while(repeat)
{
delay_xms(540);
repeat--;
}
if(remain)delay_xms(remain);
}
delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x_conf.h"
#define SYSCLK 72 //系统时钟的基数(注:是72而不是72M)
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
void delay_init(void); //延时函数的初始化
void delay_ms(u16 nms); //毫秒级的延时函数(参数为毫秒数)
void delay_us(u32 nus); //微秒级的延时函数(参数为微秒数)
#endif
fmq.h
#ifndef __FMQ_H
#define __FMQ_H
#include "stm32f10x_conf.h"
extern void Fmq_Init(void);//初始化FMQ
extern void Fmq_On(void);//蜂鸣器响
extern void Fmq_Off(void);//蜂鸣器不响
#endif
fmq.c
#include"key.h"
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_VALUE;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA,ENABLE);
GPIO_VALUE.GPIO_Mode=GPIO_Mode_IPU;
GPIO_VALUE.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_8;
GPIO_Init(GPIOC,&GPIO_VALUE);
GPIO_VALUE.GPIO_Mode=GPIO_Mode_IPD;
GPIO_VALUE.GPIO_Pin=GPIO_Pin_0;
GPIO_Init(GPIOC,&GPIO_VALUE);
}
int Key_status(int nu)
{
int ret=0;
switch(nu)
{
case 0:ret=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_9);break;
case 1:ret=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_8);break;
case 2:ret=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);ret=!ret;break;
}
return !ret;
}
led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x_conf.h"
extern void Led_Init(void);
extern void Led_On(int opt);
extern void Led_Off(int opt);
#endif
led.c
#include"led.h"
void Led_Init(void)
{
GPIO_InitTypeDef GPIO_VALUE; //???
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???
GPIO_VALUE.GPIO_Mode=GPIO_Mode_Out_PP;//???? ????
GPIO_VALUE.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;//????
GPIO_VALUE.GPIO_Speed=GPIO_Speed_50MHz;//????
GPIO_Init(GPIOC,&GPIO_VALUE);//???
}
void Led_On(int nu)
{
switch(nu)
{
case 0:GPIO_SetBits(GPIOC,GPIO_Pin_1);break;
case 1:GPIO_SetBits(GPIOC,GPIO_Pin_2);break;
case 2:GPIO_SetBits(GPIOC,GPIO_Pin_3);break;
}
}
void Led_Off(int nu)
{
switch(nu)
{
case 0:GPIO_ResetBits(GPIOC,GPIO_Pin_1);break;
case 1:GPIO_ResetBits(GPIOC,GPIO_Pin_2);break;
case 2:GPIO_ResetBits(GPIOC,GPIO_Pin_3);break;
}
}
/*
#include"led.h"
#include"bitband.h"
void Led_Init(void)
{
GPIO_InitTypeDef GPIO_VALUE; //???
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???
GPIO_VALUE.GPIO_Mode=GPIO_Mode_Out_PP;//???? ????
GPIO_VALUE.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;//????
GPIO_VALUE.GPIO_Speed=GPIO_Speed_50MHz;//????
GPIO_Init(GPIOC,&GPIO_VALUE);//???
}
void Led_On(int nu)
{
switch(nu)
{
case 0:PCOut(1)=1;break;
case 1:PCOut(2)=1;break;
case 2:PCOut(3)=1;break;
}
}
void Led_Off(int nu)
{
switch(nu)
{
case 0:PCOut(1)=0;break;
case 1:PCOut(2)=0;break;
case 2:PCOut(3)=0;break;
}
}
*/
main.c
/*看门狗*/
#include"iwdg.h"
#include"led.h"
#include"fmq.h"
#include"key.h"
#include"delay.h"
int main(void)
{
int i=0,j=0;
Led_Init();
Key_Init();
Fmq_Init();
delay_init();
iwdg_init(5);
Fmq_On();
delay_ms(2000);
Fmq_Off();
for(i=0;i<3;i=(i+1)%3)
{
iwdg_fee_dog();
Led_On(i);
delay_ms(500);
Led_Off(i);
delay_ms(500);
}
return 0;
}
看门狗过程
1)取消寄存器写保护(向 IWDG_KR 写入 0X5555)
通过这步,我们取消 IWDG_PR 和 IWDG_RLR 的写保护,使后面可以操作这两个寄存器,设置 IWDG_PR 和 IWDG_RLR 的值。这在库函数中的实现函数是:
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
这个函数的功能开启/取消写保护,即使能/失能写权限。
2)设置独立看门狗的预分频系数和重装载值
设置看门狗的分频系数的函数是:
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); //设置 IWDG 预分频值
设置看门狗的重装载值的函数是:
void IWDG_SetReload(uint16_t Reload); //设置 IWDG 重装载值
设置好看门狗的分频系数 prer 和重装载值就可以知道看门狗的喂狗时间(也就是看门狗溢出时间),该时间的计算方式为:
Tout=((4×2^prer) ×rlr) /40
其中 Tout 为看门狗溢出时间(单位为 ms);prer 为看门狗时钟预分频值(IWDG_PR 值),范围为 0~7;rlr 为看门狗的重装载值(IWDG_RLR 的值);
比如我们设定 prer 值为 4,rlr 值为 625,那么就可以得到 Tout=64×625/40=1000ms,这样,看门狗的溢出时间就是 1s,只要你在一秒钟之内,有一次写入 0XAAAA 到 IWDG_KR,就不会导致看门狗复位(当然写入多次也是可以的)。这里需要提醒大家的是,看门狗的时钟不是准确的 40Khz,所以在喂狗的时候,最好不要太晚了,否则,有可能发生看门狗复位。
3)重载计数值喂狗(向 IWDG_KR 写入 0XAAAA)
库函数里面重载计数值的函数是:
IWDG_ReloadCounter(); //按照 IWDG 重装载寄存器的值重装载 IWDG 计数器
通过这句,将使 STM32 重新加载 IWDG_RLR 的值到看门狗计数器里面。即实现独立看门狗的喂狗操作。
4) 启动看门狗(向IWDG_KR 写入 0XCCCC)
库函数里面启动独立看门狗的函数是:
IWDG_Enable(); //使能 IWDG
通过这句,来启动 STM32 的看门狗。
注意 IWDG 在一旦启用,就不能再被关闭!想要关闭,只能重启,并且重启之后不能打开 IWDG,否则问题依旧,如果不用 IWDG 的话,就不要去打开它,免得麻烦。