文章参考:STM32时钟树分析、STM32时钟树详解、STM32 F103 时钟树详解、IWDG — 独立看门狗、时钟,定时器 玩了这么久单片机,这次终于搞懂! STM32的SYSTICK、STM32之系统滴答定时器、stm32笔记:Systick系统滴答定时器、浅谈STM32F10X芯片SysTick系统时钟定时器
硬件:STM32F107VC( Cortex-M3)
软件:Keil μVision4,JLINK
一、定时器介绍
1. 定时器的基本概念
定时器是对周期固定的脉冲信号进行计数,如MCU内部的外设时钟(APB)。
计数器是对周期固定或者不固定的脉冲信号进行计数,如MCU的I/O引脚所引入的外部脉冲信号。
所以,定时器和计数器本质上都是计数器,定时器是计数器的一种特例。
2. STM32定时器家族
3. 时钟树
STM32为了实现低功耗,而设计的功能完善构成复杂的时钟系统,称之时钟树。了解了时钟树,在使用各个外设的时候就明白时钟信号的来源了。使外设功能的时钟可自配置。因为STM32外设众多,而不同的项目用到的外设参差不齐,所以可控的时钟可以实现降低产品功耗。所有的外设在使用之前都必须设置时钟信号,才可以正常工作。
AHB,是Advanced High performance Bus的缩写,译作高级高性能总线,这是一种“系统总线”。AHB主要用于高性能模块(如CPU、DMA和DSP等)之间的连接。AHB 系统由主模块、从模块和基础结构(Infrastructure)3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。
APB,是Advanced Peripheral Bus的缩写,这是一种外围总线。APB主要用于低带宽的周边外设之间的连接,例如UART、1284等,它的总线架构不像 AHB支持多个主模块,在APB里面唯一的主模块就是APB 桥。再往下,APB2负责AD,I/O,高级TIM,串口1;APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。
这两者都是总线,符合AMBA规范。ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB系统总线和APB外围总线。
1) STM32的四个时钟源
外部时钟:
- 高速外部时钟(HSE,High Speed External ):外部时钟源,晶振频率范围为4~16MHz,一般采用8MHz的晶振。
- 低速外部时钟(LSE,Low Speed External):外部时钟源,主要提供给实时时钟模块,一般采用32.768KHz。
内部时钟:
- 高速内部时钟(HSI,High Speed Internal ):由内部RC振荡器产生,频率为8MHz,但不稳定,时钟频率精度较差,可直接作为系统时钟或在2分频后作为PLL输入
- 低速内部时钟(LSI,Low Speed Internal ):由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。
2)以最常用的高速外部时钟(HSE)为例
- 左边红色框框时钟源2 两个外部引脚OSC_OUT和OSC_IN接8M晶振的两端。
- 8M时钟遇到第一个分频器PLLXTPRE,也就是HSE后面的第一个节点,不分频。
- 时钟来到PLL Source Mux,可选输入的时钟信号有外部高速时钟(HSE)和内部高速时钟(HSI),选择HSE。
- 接着信号走到锁相环PLL,具有倍频作用,我们选择倍频因子(PLL Mul),可取值2,3,...14,15,16,我们选择9倍频。现在时钟信号为8*9=72M。
- 来到系统时钟源输入选择,可选时钟有HSE(8M)、HSI(8M)和经过倍频的PLL CLK(72M),选择PLL CLK作为系统时钟,此时系统时钟为72M。
- 系统时钟(SYSCLK)来到AHB预分频器,可选分频系数:1,2,4,8,16,32,64,128,256,512。选择不分频,直接来到挂载低速外设的(APB1)PCLK1和挂载高速外设的(APB2)PCLK2。
- PCLK1低速外设时钟的最大频率为36M,所以最低进行2分频。PCLK2高速外设时钟的最大频率是72M,可选择不分频。
选择使用HSE也是因为外部时钟更稳定精准,经过倍频给SYSCLK提供最大的时钟频率,发挥CPU的最优性能。
3)系统时钟SYSCLK
系统时钟SYSCLK,是供STM32中绝大部分部件工作的时钟源,它可选择为PLL输出、HSI或者HSE,(一般程序中采用PLL倍频到72Mhz)在选择时钟源前注意要判断目标时钟源是否已经稳定振荡。Max=72MHz,它分为2路:
1路送给I2S2、I2S3使用的I2S2CLK,I2S3CLK; 另外1路通过AHB分频器分频(1-512)分频后送给以下8大模块使用:
① 送给SDIO使用的SDIOCLK时钟。
② 送给FSMC使用的FSMCCLK时钟。
③ 送给AHB总线、内核、内存和DMA使用的HCLK时钟。
④ 通过8分频后送给Cortex的系统定时器时钟(SysTick)。
⑤ 直接送给Cortex的空闲运行时钟FCLK。
⑥ 送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer2-7)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4、5、6、7使用。
⑦ 送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer1、Timer8)1、2倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后得到ADCCLK时钟送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。
⑧ 2分频后送给SDIO AHB接口使用(HCLK/2)。
4) 用户可通过多个预分频器配置AHB总线、高速APB2总线和低速APB1总线的频率。
-
AHB和APB2域的最大频率是72MHZ。
-
APB1域的最大允许频率是36MHZ。
-
SDIO接口的时钟频率固定为HCLK/2。
-
40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。
-
实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。
-
RTC的时钟源通过RTCSEL[1:0]来选择。
-
STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。
-
STM32还可以选择一个PLL输出的2分频、HSI、HSE、或者系统时钟SYSCLK 输出到MCO脚(PA8)上。
5) 时钟输出的使能控制
当需要使用某模块时,必需先使能对应的时钟。需要注意的是定时器的倍频器,当APB的分频为1时,它的倍频值为1,否则它的倍频值就为2。
连接在APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、 Timer2、Timer3、Timer4。注意USB模块虽然需要一个单独的48MHz时钟信号,但它应该不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。
连接在APB2(高速外设)上的设备有:GPIO_A-E、USART1、ADC1、ADC2、ADC3、TIM1、TIM8、SPI1、AFIO。
二、系统节拍定时器(SysTick)
1. SysTick的定义
SysTick 是一个24位的系统节拍定时器system tick timer,具有自动重载和溢出中断功能,所有基于Cortex_M3处理器的微控制器都可以由这个定时器获得一定的时间间隔。这个定时器是专用于实时操作系统,也可当成一个标准的递减计数器。
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。Cortex-M3处理器内部包含了一个SysTick定时器,因此所有的Cortex-M3芯片都带有这个定时器,SysTick能用于Cortex-M3芯片的快速移植,软件在不同的Cortex-M3芯片的移植工作大为化简。
它具有下述特性:
- 24位的递减计数器
- 自动重加载功能
- 当计数器为0时能产生一个可屏蔽中断
- 可编程时钟源
系统时基定时器的时钟源,来自于AHB总线,不分频为等于AHB频率和AHB/8两种分频情况。它可以是内部时钟(FCLK,Cortex上的自由运行时钟),或者是外部时钟(Cortex处理器上的SYSCLK信号)。
2. SysTick的作用
在单任务引用程序中,因为其架构就决定了它执行任务的串行性,使用实时操作系统(RTOS)。因为RTOS以并行的架构处理任务,单一任务的崩溃并不会牵连到整个系统。这样用户出于可靠性的考虑可能就会基于RTOS来设计自己的应用程序。SYSTICK存在的意义就是提供必要的时钟节拍,为RTOS的任务调度提供一个有节奏的“心跳”。
所有基于ARM Cortex_M3内核的控制器都带有SysTick定时器,这样就方便了程序在不同的器件之间的移植。而使用RTOS的第一项工作往往就是将其移植到开发人员的硬件平台上,由于SYSTICK的存在无疑降低了移植的难度。
SysTick定时器除了能服务于操作系统之外,还能用于作为一个闹铃,用于测量时间等。当处理器在调试期间被喊停(halt)时,则SysTick定时器亦将暂停运作。
3. SysTick的结构框图
图中,表明系统节拍定时器有4个相关的寄存器,了解了这4个寄存器的内容,即可掌握系统节拍定时器的工作原理。
在core_cm3.h里定义了SysTick的4个寄存器,分别是控制与状态寄存器SysTick_CTRL、重装值寄存器SysTick_LOAD、当前计数值寄存器SysTick_VAL、校正值寄存器SysTick_CALIB。
typedef struct
{
__IO uint32_t CTRL; /*!< SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< SysTick Reload Value Register */
__IO uint32_t VAL; /*!< SysTick Current Value Register */
__I uint32_t CALIB; /*!< SysTick Calibration Register */
} SysTick_Type;
在stm32f10x.h中则定义了SysTick的4个寄存器里每个bit的地址和名称。
/******************************************************************************/
/* */
/* SystemTick */
/* */
/******************************************************************************/
/***************** Bit definition for SysTick_CTRL register *****************/
#define SysTick_CTRL_ENABLE ((uint32_t)0x00000001) /*!< Counter enable */
#define SysTick_CTRL_TICKINT ((uint32_t)0x00000002) /*!< Counting down to 0 pends the SysTick handler */
#define SysTick_CTRL_CLKSOURCE ((uint32_t)0x00000004) /*!< Clock source */
#define SysTick_CTRL_COUNTFLAG ((uint32_t)0x00010000) /*!< Count Flag */
/***************** Bit definition for SysTick_LOAD register *****************/
#define SysTick_LOAD_RELOAD ((uint32_t)0x00FFFFFF) /*!< Value to load into the SysTick Current Value Register when the counter reaches 0 */
/***************** Bit definition for SysTick_VAL register ******************/
#define SysTick_VAL_CURRENT ((uint32_t)0x00FFFFFF) /*!< Current value at the time the register is accessed */
/***************** Bit definition for SysTick_CALIB register ****************/
#define SysTick_CALIB_TENMS ((uint32_t)0x00FFFFFF) /*!< Reload value to use for 10ms timing */
#define SysTick_CALIB_SKEW ((uint32_t)0x40000000) /*!< Calibration value is not exactly 10 ms */
#define SysTick_CALIB_NOREF ((uint32_t)0x80000000) /*!< The reference clock is not provided */
1)控制与状态寄存器SysTick_CTRL
具体每一位的含义如下:
第0位:ENABLE,Systick 使能位
第1位:TICKINT,Systick 中断使能位
第2位:CLKSOURCE,Systick时钟源选择
第16位:COUNTFLAG,Systick计数比较标志,SysTick系统定时器的中断标志位
2) 重装值寄存器SysTick_LOAD
Systick是一个递减的定时器,当定时器递减至0时,重载寄存器中的值就会被重装载,继续开始递减。**SysTick_LOAD**重载寄存器是个24位的寄存器最大计数0xFFFFFF。
3)当前计数值寄存器SysTick_VAL
4)校正值寄存器SysTick_CALIB
要使用systick定时器,只需调用SysTick_Config(uint32_t ticks)函数即可,其定义在core_cm3.h中,具体如下:
/* SysTick constants */
#define SYSTICK_ENABLE 0 /* Config-Bit to start or stop the SysTick Timer */
#define SYSTICK_TICKINT 1 /* Config-Bit to enable or disable the SysTick interrupt */
#define SYSTICK_CLKSOURCE 2 /* Clocksource has the offset 2 in SysTick Control and Status Register */
#define SYSTICK_MAXCOUNT ((1<<24) -1) /* SysTick MaxCount */
/**
* @brief Initialize and start the SysTick counter and its interrupt.
*
* @param uint32_t ticks is the number of ticks between two interrupts
* @return none
*
* Initialise the system tick timer and its interrupt and start the
* system tick timer / counter in free running mode to generate
* periodical interrupts.
*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SYSTICK_MAXCOUNT) return (1); /* Reload value impossible */
//重装载值必须小于0XFF FFFF,因为这是一个24位的递减计数器,返回1表示出错
SysTick->LOAD = (ticks & SYSTICK_MAXCOUNT) - 1; /* set reload register */
//设置重装载值
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
//设置SysTick中断优先级(最低)
SysTick->VAL = (0x00); /* Load the SysTick Counter Value */
// 向SysTick_VAL寄存器写入任意值,例如写入0,清除CURRENT的值,同时清除SysTick_CTRL的COUNTFLAG标志;
SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT); /* Enable SysTick IRQ and SysTick Timer */
//1 << SYSTICK_CLKSOURCE 即 1<<2 就是 CLKSOURCE=1 ,选择系统时钟为系统节拍定时器时钟源
//1<<SYSTICK_ENABLE 即 1<<0 就是 ENABLE=1 ,启动系统节拍定时器
//1<<SYSTICK_TICKINT 即 1<<1 就是 TICKINT=1,开放系统节拍定时器定时中断
return (0); /* Function successful */
}
#endif
static __INLINE uint32_t SysTick_Config(uint32_t ticks)的uint32_t为自定义的无符号32位整型类型,__STATIC_INLINE即static inline,用于定义静态内敛函数。
SysTick_Config(uint32_t ticks)函数中第1行 if (ticks > SYSTICK_MAXCOUNT) return (1);``SYSTICK_MAXCOUNT为宏常量0xFF FFFF,这是因为系统定时器是24位的减计数器,最大值为0xFF FFFF,所以,当if结果为真时,说明参数ticks的值超过了系统定时器的最大计数值,故返回1,表示出错。
第2行 SysTick->LOAD = (ticks & SYSTICK_MAXCOUNT) - 1; 将ticks计数值减去1的值作为初值赋给LOAD寄存器(即系统节拍定时器重装值寄存器SysTickLOAD)。
第3行NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 调用CMSIS库函数NVIC_SetPriority设置系统节拍定时器异常的优先级号为15。
第4行 SysTick->VAL = (0x00); 向VAL寄存器(即系统节拍定时器当前计数值寄存器SysTick_VAL)写入0,使得LOAD内的值装入VAL寄存器中。
第5行SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT);选择系统时钟为系统节拍定时器时钟源,启动系统节拍定时器,并且打开系统节拍定时器中断,其中,宏常量SysTick_CLKSOURCE、SysTick_TICKINT和SysTick_ENABLE依次为(1<<2)、(1<<1)和(1<<0)。
SysTick的使用总结如下:
- 要使用systick定时器,只需调用
SysTick_Config(uint32_t ticks)函数即可,函数自动完成:重装载值的装载,时钟源选择,计数寄存器复位,中断优先级的设置(最低),开中断,开始计数的工作。 - 要修改时钟源调用
SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource),也可按照SysTick_Config()中默认设置FCLK不变。 - 要修改中断优先级调用
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
SysTick的应用说明:
- 因systick是一个24位的定时器,故重装值最大值为2的24次方=16 777 215,要注意不要超出这个值。
- systick是cortex_m3的标配,不是外设。故不需要在RCC寄存器组打开它的时钟。
- 每次systick溢出后会置位计数标志位和中断标志位,计数标志位在计数器重装载后被清除,而中断标志位也会随着中断服务程序的响应被清除,所以这两个标志位都不需要手动清除。
- 采用使用库函数的方法,只能采用中断的方法响应定时器计时时间到,如要采用查询的方法,那只能采用设置systick的寄存器的方法,具体操作可参考STM32之系统滴答定时器。
4. SysTick的使用
systick定时器是24位的递减计数器,设定初值并使能它后,它会每个系统时钟周期计数器减1,计数到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。
if (SysTick_Config(SystemFrequency / 1000)),表示1ms,其中SystemFrequency=72000000,
配置的时钟使用 72MHz作为系统时钟,那么计数器每次减一次所用的时间是 1/72MHz,假设我们的计数器初值是72000,那么每次计数器减到 0,时间经过(1/72M) * 72000 = 0.001,即1ms。(可以理解为:72MHz的时钟频率,那它表示 1s计数72000000次(周期数),那 1ms就计数72000次,所以计数值为72000)
void SysTick_Configuration(void)
{
/* SysTick interrupt each 250ms with counter clock equal to 9MHz */
if (SysTick_Config((SystemFrequency/8) / 4))
{
/* Capture error */
while (1);
}
/* Select HCLK/8 as SysTick clock source */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
}
SystemFrequency是72MHz时,SystemFrequency/8=9MHz,系统时基定时器的递减频率设为9MHz/4。计数器的时钟源是AHB/8=9MHz,所以计数器每次减一次所用的时间是 1/9MHz,那么每次计数器减到 0,时间经过(1/9M) * 9M/4 = 0.25,即250ms。
其他例子,系统时钟是72MHz时,系统时基定时器的递减频率设为9MHz,在这个条件下,把系统定时器的初始值设置为90000,就能够产生10ms的时间基值,如果开启中断,则产生10ms的中断。
5. SysTick的具体例子
示例程序来自《ULRM3-NMA实验指导手册0.97.doc》程序23-SysTick系统定时器。
实验中,每1ms产生一个Systick中断,在Systick的中断处理函数中对Delay(u32 nTime)延时函数的nTime进行减一操作,当Delay(u32 nTime)检测到nTime已经减到了0,函数才退出循环,从而实现延时。每隔一定的延时时间便将LED状态翻转,从而实现板上D5~D8的闪烁。
主函数main.c如下:
#include "main.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
static __IO uint32_t TimingDelay;
/* Private function prototypes -----------------------------------------------*/
void Delay(__IO uint32_t nTime);
/* Private functions ---------------------------------------------------------*/
int main(void)
{
/* Setup the microcontroller system. Initialize the Embedded Flash Interface,
initialize the PLL and update the SystemFrequency variable. */
SystemInit();
/* Initialize the LCD */
STM3210C_LCD_Init();
/* Clear the LCD */
LCD_Clear(White);
/* Set the LCD Text Color */
LCD_SetTextColor(Black);
printf(" STM3210C-EVAL \n");
printf(" systemtick usage");
/* Initialize Leds mounted on STM3210X-EVAL board */
STM_EVAL_LEDInit(LED1);
STM_EVAL_LEDInit(LED2);
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
/* Turn on LED1 and LED3 */
STM_EVAL_LEDOn(LED1);
STM_EVAL_LEDOn(LED3);
/* Setup SysTick Timer for 1 msec interrupts */
if (SysTick_Config(SystemFrequency / 1000))
{
/* Capture error */
while (1);
}
while (1)
{
/* Toggle LED2 and LED4 */
STM_EVAL_LEDToggle(LED2);
STM_EVAL_LEDToggle(LED4);
printf("\n--delay 500ms");
/* Insert 500 ms delay */
Delay(500);
/* Toggle LED1 and LED3 */
STM_EVAL_LEDToggle(LED1);
STM_EVAL_LEDToggle(LED3);
printf("\n++delay 1000ms");
/* Insert 1000 ms delay */
Delay(1000);
}
}
/**
* @brief Inserts a delay time.
* @param nTime: specifies the delay time length, in milliseconds.
* @retval None
*/
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
/**
* @brief Decrements the TimingDelay variable.
* @param None
* @retval None
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
stm32f10x_it.c中的中断服务函数SysTick_Handler :
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}
三、独立看门狗(IWDG)
1. IWDG简介
STM32F10x有两个嵌入的看门狗外围设备,能够提供高度的安全、准确的计时和灵活的运用。所有的看门狗外围设备(独立的或视窗的)都是用来检测和解决由于软件运行失败而引起的故障,当定时器到达一个给定的超时值时,看门狗就会触发一个中断或者重启系统。独立看门狗是由它自身所带的专门的低速时钟(32KHZ)计时的,因此即使主时钟不工作时它也能正常运行。视窗看门狗的时钟由APB1时钟分频而来,并拥有一个可配置可编程的定时窗口,能够检测到不正常的迟到或早到的应用行为。
独立看门狗就是一个 12 位的递减计数器,当计数器的值从某个值一直减到 0 的时候,系统就会产生一个复位信号,即 IWDG_RESET。如果在计数没减到 0 之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。看门狗功能由 VDD 电压域供电,在停止模式和待机模式下仍能工作。
2. IWDG功能框图剖析
①独立看门狗时钟
独立看门狗的时钟由独立的 RC振荡器 LSI提供,即使主时钟发生故障它仍然有效,非常独立。LSI的频率一般在 30~60KHZ之间,根据温度和工作场合会有一定的漂移,我们一般取 40KHZ,所以独立看门狗的定时时间并一定非常精确,只适用于对时间精度要求比较低的场合。
②计数器时钟
递减计数器的时钟由 LSI经过一个 8位的预分频器得到,我们可以操作预分频器寄存器 IWDG_PR 来设置分频因子,分频因子可以是:[4,8,16,32,64,128,256,256],分频因子=4 * 2^PRV,计数器时钟CK_CNT= 40/ (4 * 2^PRV),一个计数器时钟,计数器就减一。
例如,PRV=3,分频因子=4 * 2^PRV=4 * 2^3=32,正对应IWDG_PR里PR[2:0]=011。
③计数器
独立看门狗的计数器是一个 12 位的递减计数器,最大值为 0XFFF,当计数器减到 0时,会产生一个复位信号:IWDG_RESET,让程序重新启动运行,如果在计数器减到 0 之前刷新了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作,我们俗称喂狗。
④重装载寄存器IWDG_RLR
重装载寄存器是一个12位的寄存器,里面装着要刷新到计数器的值,这个值的大小决定着独立看门狗的溢出时间,取值范围为:0-0XFFF。超时时间 Tout = (4 * 2^prv) / 40 * rlv =(分频因子 /40) * rlv,单位是ms,prv是预分频器寄存器的值,rlv是重装载寄存器的值。
⑤键值寄存器
键值寄存器IWDG_KR可以说是独立看门狗的一个控制寄存器,主要有三种控制方式,往这个寄存器写入下面三个不同的值有不同的效果。
通过往键值寄存器写 0XCCC 来启动看门狗是属于软件启动的方式,一旦独立看门狗启动,它就关不掉,只有复位才能关掉。
⑥状态寄存器
状态寄存器 SR只有位 0:PVU和位 1:RVU有效,这两位只能由硬件操作,软件操作不了。RVU:看门狗计数器重装载值更新,硬件置 “1”表示重装载值的更新正在进行中,更新完毕之后由硬件清零。PVU: 看门狗预分频值更新,硬件置“1”指示预分频值的更新正在进行中,当更新完成后,由硬件清零。所以只有当 RVU/PVU等于 0 的时候才可以更新重装载寄存器/预分频寄存器。
3. IWDG操作
设置好看门狗的计数值,如果计数到0,则产生复位,但是程序在Systick的中断处理函数中通过不断的对看门狗计数器重新写计数值,从而避免了程序复位。否则,会导致独立看门狗计数到0,程序复位(从头开始运行)。
4. IWDG实验
示例程序来自《ULRM3-NMA实验指导手册0.97.doc》示例程序17-独立看门狗定时器。
该示例程序用于演示在systick中断服务程序里面周期性地喂狗,看门狗的超时时间是280毫秒。Systick每250毫秒产生一次中断,在中断服务程序中,点亮D6(引脚PD13),重载独立看门狗的计数器值,使系统不产生看门狗复位。USER按钮(引脚PB9)所连接的GPIO口被设置成外部中断输入在每个下降沿产生中断。在系统的中断控制器中,GPIO所产生的这个外部中断的优先级比Systick高。在这个外部中断的服务程序中用来模拟程序错误:当通过按下USER按钮触发中断服务程序中, 不清除中断挂起位,因此,该中断服务程序会不停地执行,于是Systick中断服务程序就不能正常执行,这就导致了看门狗复位。系统复位之后,会检查复位状态,如果是由于看门狗引起的复位,LCD上会显示。同时D5(引脚PD7)会点亮。
主函数main.c如下:
其中超时时间=(分频因子 /40)* rlv=(32/40)*350=280ms
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "stm3210c_eval_lcd.h"
#include "stm32_eval.h"
#include <stdio.h>
ErrorStatus HSEStartUpStatus;
void SysTick_Configuration(void);
int main(void)
{
/* Setup the microcontroller system. Initialize the Embedded Flash Interface,
initialize the PLL and update the SystemFrequency variable. */
SystemInit();
/* Initialize the LCD */
STM3210C_LCD_Init();
/* Clear the LCD */
LCD_Clear(White);
/* Set the LCD Text Color */
LCD_SetTextColor(Black);
printf(" STM3210C-EVAL \n");
printf(" How to use IWDG \n");
/* Initialize LED1 and Key Button mounted on STM3210X-EVAL board */
STM_EVAL_LEDInit(LED1);
STM_EVAL_LEDInit(LED2);
STM_EVAL_PBInit(Button_KEY, Mode_EXTI);
/* Configure SysTick to generate an interrupt each 250ms */
SysTick_Configuration();
/* 1 bits for pre-emption priority and 3 bits for subpriority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Set Button EXTI Interrupt priority to 0 (highest) */
NVIC_SetPriority(KEY_BUTTON_EXTI_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0,0));
/* Set SysTick interrupt vector Preemption Priority to 1 */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1,0));
/* Check if the system has resumed from IWDG reset */
//检查是否为独立看门狗复位
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
/* IWDGRST flag set */
/* Turn on LED1 */
//独立看门狗复位 ,LED1亮
STM_EVAL_LEDOn(LED1);
printf("RESET from IWDG\n"),
/* Clear reset flags */
//清除标志
RCC_ClearFlag();
}
else
{
/* IWDGRST flag is not set */
/* Turn off LED1 */
// 不是独立看门狗复位(可能为上电复位或者手动按键复位之类的) ,LED1不亮
STM_EVAL_LEDOff(LED1);
printf("normal reset\n");
}
/* IWDG timeout equal to 280 ms (the timeout may varies due to LSI frequency
dispersion) */
//超时时间=(分频因子 /40)* rlv=(32/40)*350=280ms
/* Enable write access to IWDG_PR and IWDG_RLR registers */
// 使能 预分频寄存器PR和重装载寄存器RLR可写
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
/* IWDG counter clock: 40KHz(LSI) / 32 = 1.25 KHz */
// 设置预分频器值32
IWDG_SetPrescaler(IWDG_Prescaler_32);
/* Set counter reload value to 349 */
// 设置重装载寄存器值349
IWDG_SetReload(349);
/* Reload IWDG counter */
// 把重装载寄存器的值放到计数器中
IWDG_ReloadCounter();
printf("Press USER to kill IWDG reload!\n"),
/* Enable IWDG (the LSI oscillator will be enabled by hardware) */
// 使能 IWDG
IWDG_Enable();
while (1)
{}
}
/**
* @brief Configures SysTick to generate an interrupt each 250ms.
* @param None
* @retval None
*/
void SysTick_Configuration(void)
{
/* SysTick interrupt each 250ms with counter clock equal to 9MHz */
if (SysTick_Config((SystemFrequency/8) / 4))
{
/* Capture error */
while (1);
}
/* Select HCLK/8 as SysTick clock source */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
}
stm32f10x_it.c中的中断服务函数EXTI9_5_IRQHandler:
void EXTI9_5_IRQHandler(void)
{
if (EXTI_GetITStatus(KEY_BUTTON_EXTI_LINE) != RESET)//判断某个线上的中断是否发生
{
/* Turn off LED2 */
STM_EVAL_LEDOn(LED2);
printf("kill IWDG!!!");
/* As KEY_BUTTON_EXTI_LINE pending bit is not cleared, the CPU will execute indefinitely
this ISR and when the IWDG counter reaches 00h the IWDG reset occurs */
}
}
stm32f10x_it.c中的中断服务函数SysTick_Handler :
void SysTick_Handler(void)
{
/* Reload IWDG counter */
IWDG_ReloadCounter();
/* Toggle LED2 */
STM_EVAL_LEDToggle(LED2);
}
5. 使用的函数
1)NVIC_PriorityGroupConfig
2)NVIC_GetPriorityGrouping
3)NVIC_EncodePriority
4)NVIC_SetPriority
5)RCC_GetFlagStatus
6)RCC_ClearFlag
7)IWDG_WriteAccessCmd
8)IWDG_SetPrescaler
9)IWDG_SetReload
10)IWDG_ReloadCounter
11)IWDG_Enable