关闭

stm32 独立看门狗[操作寄存器+库函数]

290人阅读 评论(0) 收藏 举报
分类:
以单片机为核心的微型计算机系统中,单片机经常会受到来自外界电磁场的干扰。
造成程序跑飞,只是程序的正常运行状态被打断而进入死循环,从而使单片机控制的系统无法正常工作。看门狗就是一种专门用于检测单片机程序运行状态的硬件结构。
 
stm32也是如此。
 
stm32 的独立看门狗由内部专门的40Khz低速时钟驱动,即使主时钟发生故障时,它也仍然有效。这里需要注意的是独立看门狗的时钟是一个内部时钟,所以不是准确的40Khz,而是在30~60Khz之间的一个可变化的时钟,看门狗的时钟对时间的要求不是很精确,所以时钟有偏差可以接受。
 
本例直接操作寄存器实现验证独立看门狗的复位功能,设定一个800ms的喂狗时间,在主函数中实现LED闪烁,如果设定一个1s的延时,则触发独立看门狗复位,LED常亮。
库函数实现当外部中断发生(按下PA0按键),长时间不喂狗,引发独立看门狗复位时,向外用串口输出复位提示。
 
 
直接操作寄存器
 
使用独立看门狗,需要了解一下寄存器:
 
键值寄存器:(IWDG_KR)
低16位有效的寄存器,只写寄存器,读出值恒为0x0000.
软件必须以一定的间隔写入0xAAAA,否则,当计数器为0时,看门狗会产生复位。
写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器。
写入0xCCCC,启动看门狗工作。
 
预分频寄存器:(IWDG_PR)
第三位有效寄存器,用于设置看门狗的分频系数,最低为4,最高位256.
通过设置PR[2:0]:位来选择计数器时钟的预分频因子。要改变预分频因子,IWDG_SR寄存器的PVU位必须为0。
  • 000: 预分频因子=4                 100: 预分频因子=64
  • 001: 预分频因子=8                 101: 预分频因子=128
  • 010: 预分频因子=16               110: 预分频因子=256
  • 011: 预分频因子=32               111: 预分频因子=256

重装载寄存器:(IWDG_RLR)

低12位有效,RL[11:0]。用于定义看门狗计数器的重装载值。
每当向IWDG_KR寄存器写入0xAAAA时,重装载值会被传送到计数器中。随后计数器从这个值开始递减计数。看门狗超时周期可通过此重装载值和时钟预分频值来计算。 只有当IWDG_SR寄存器中的RVU位为0时,才能对此寄存器进行修改。 
 
状态寄存器:(IWDG_SR)
只有低两位有效。都由硬件置’1’和 清’0’。
RVU[1]: 看门狗计数器重装载值更新
PVU[0]: 看门狗预分频值更新
 
代码如下:  (system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置
User/main.c
#include <stm32f10x_lib.h>	 
#include "system.h" 
#include "wdg.h" 	

#define LED1 PAout(4)
#define LED2 PAout(5)

void Gpio_Init(void);

int main(void)
{				  

	Rcc_Init(9); 			 //系统时钟设置

	Gpio_Init();

	Iwdg_Init(3,1000);  //设定为800ms内喂狗

	while(1){
		
		LED1 = !LED1;

		delay(100000);	  //延时100ms后喂狗,LED闪烁

		//delay(1000000);	  //延时1000ms,引发独立看门狗复位,LED不闪烁

		Iwdg_Feed();  //喂狗

	}		

}


void Gpio_Init(void)
{
	RCC->APB2ENR|=1<<2;    //使能PORTA时钟 	   	 	  

	GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
	GPIOA->CRL|=0x33334444; 
  
} 
Library/wdg.c   (此文件包含了独立看门狗和窗口看门狗的驱动函数)
#include <stm32f10x_lib.h>
#include "wdg.h"

/********************************************
 *
 *本文件包含窗口看门狗和独立看门口的相关函数
 *
 *********************************************/

u8 Wwdg_Cnt = 0x7F;  //计数器值,默认为最大值127

//独立看门狗初始化
//参数说明:
//			pre:分频数(0~7),相应分频因子为4*(2^pre)
//			rlr:低12位有[11:0]
// 喂狗时间计算: T =  (4*(2^pre)*rlr)/40;(ms)			
void Iwdg_Init(u8 pre,u16 rlr)
{
	IWDG ->KR = 0x5555;		//使能对PR RLR寄存器的写操作
	IWDG ->PR = pre;		//设置分频数
	IWDG ->RLR = rlr;		//设定重装值
	IWDG ->KR =  0xAAAA;    //装载RLR值到看门狗计数器,即喂狗
	IWDG ->KR =  0xCCCC;    //启动看门狗
}

//独立看门狗喂狗
void Iwdg_Feed()
{
	IWDG -> KR = 0xAAAA;  //喂狗
}

//窗口看门狗初始化
//参数说明: 
//			cnt  	计数器的值,最大 127,0x7F
//			w_cnt 	窗口值,最大 127,0x7F
//			pre 	预分频器的时基值,低两位有效;实际时钟为: PLCK1/4096/2^pre
//需要再主函数中开启中断 WWDG_IRQChannel
//设定喂狗时间范围必须在:(WWDG时钟为PCLK1,36Mhz)
//			Tmax =(4096*2^pre*(cnt-63)/36)	(us)
//			Tmin =(4096*2^pre*(cnt-w_cnt)/36)	(us)
//超出次时间喂狗复位

void Wwdg_Init(u8 cnt,u8 w_cnt,u8 pre)
{
	u8 Cnt_Max = 0x7f;	  //计数器最大值

	Wwdg_Cnt = Cnt_Max&cnt;   //设定计数器的值,防止溢出

	RCC->APB1ENR |= 1<<11;

	WWDG -> CFR |= pre <<7;  //设定预分频器的时基,实际分频值我
	WWDG -> CFR |= 1<<9; 	 //使能中断

	WWDG -> CFR &= 0xFF80;   //初始化低七位,即窗口值清0
	WWDG -> CFR |= w_cnt;    // 设定窗口值

	WWDG -> CR  |= Wwdg_Cnt|(1<<7);  //设定计数器值,并激活开门狗

}

//窗口看门狗喂狗

void Wwdg_Feed()
{
	WWDG->CR |= (Wwdg_Cnt&0x7F);
	
} 
Library/wdg.h
#include <stm32f10x_lib.h>

void Iwdg_Init(u8 pre,u16 rlr);
void Iwdg_Feed(void);

void Wwdg_Init(u8 cnt,u8 w_cnt,u8);
void Wwdg_Feed(void);
 
需要注意的是 独立看门狗没有响应的中断。
 
库函数操作
 
main.c
#include "stm32f10x.h"
#include "stdio.h"

#define	 PRINTF_ON  1

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART_Configuration(void);
void IWDG_Configuration(void);
void EXTI_Configuration(void);

vu32 DelayTime;

int main(void)
{
  	RCC_Configuration();
  	GPIO_Configuration();
	NVIC_Configuration();
	USART_Configuration();
	EXTI_Configuration();
	IWDG_Configuration();

	while(1){	
		if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)
		{
			printf("\r\n The Stm32 has been reset by IWDG .\r\n");
			RCC_ClearFlag();
		}

		//do sth. here 
		DelayTime = 100000;
		while(--DelayTime);			
		// 延时17ms
				
		IWDG_ReloadCounter();	//80ms不喂狗复位
		GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1- GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));
	}	
}

void EXTI_Configuration(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;

	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);

	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

}

void GPIO_Configuration(void)
{
  	GPIO_InitTypeDef GPIO_InitStructure;                                                                                     
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure);
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 	 


  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 

}

void IWDG_Configuration(void)
{
 	RCC_LSICmd(ENABLE);                              //打开LSI
    while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY)==RESET);

	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	IWDG_SetPrescaler(IWDG_Prescaler_32);
	IWDG_SetReload(100);	  //80ms ,max 0xFFF  0~4095  
	IWDG_ReloadCounter();
	IWDG_Enable();
}


void RCC_Configuration(void)
{
	/* 定义枚举类型变量 HSEStartUpStatus */
	ErrorStatus HSEStartUpStatus;

  	/* 复位系统时钟设置*/
  	RCC_DeInit();
  	/* 开启HSE*/
  	RCC_HSEConfig(RCC_HSE_ON);
  	/* 等待HSE起振并稳定*/
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	/* 判断HSE起是否振成功,是则进入if()内部 */
  	if(HSEStartUpStatus == SUCCESS)
  	{
    	/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    	RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    	/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    	RCC_PCLK2Config(RCC_HCLK_Div1); 
    	/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    	RCC_PCLK1Config(RCC_HCLK_Div2);
    	/* 设置FLASH延时周期数为2 */
    	FLASH_SetLatency(FLASH_Latency_2);
    	/* 使能FLASH预取缓存 */
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    	/* 使能PLL */ 
    	RCC_PLLCmd(ENABLE);
    	/* 等待PLL输出稳定 */
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    	/* 选择SYSCLK时钟源为PLL */
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    	/* 等待PLL成为SYSCLK时钟源 */
    	while(RCC_GetSYSCLKSource() != 0x08);
  	} 
  	/* 打开APB2总线上的GPIOA时钟*/
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);

	//RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG, ENABLE);
		
}

 
void USART_Configuration(void)
{
	USART_InitTypeDef USART_InitStructure;
	USART_ClockInitTypeDef USART_ClockInitStructure;

	USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
	USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
	USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                      
	USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
	USART_ClockInit(USART1 , &USART_ClockInitStructure);

	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART_Init(USART1,&USART_InitStructure);

 	USART_Cmd(USART1,ENABLE);
}


void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;


	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);


}


#if	 PRINTF_ON

int fputc(int ch,FILE *f)
{
	USART_SendData(USART1,(u8) ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
	return ch;
}

#endif
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    最新评论
    个人资料
    • 访问:56032次
    • 积分:72
    • 等级:
    • 排名:千里之外
    • 原创:6篇
    • 转载:111篇
    • 译文:1篇
    • 评论:2条