STM32的定时器

1,047 阅读12分钟

文章参考:STM32时钟树分析STM32时钟树详解STM32 F103 时钟树详解IWDG — 独立看门狗时钟,定时器 玩了这么久单片机,这次终于搞懂! STM32的SYSTICKSTM32之系统滴答定时器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