目录
  1. 1. EXTI外部中断和看门狗实验
  2. 2. 独立看门狗
    1. 2.1. 独立看门狗工作原理
    2. 2.2. 独立看门狗超时时间
    3. 2.3. 独立看门狗寄存器
    4. 2.4. 独立看门狗操作步骤
  3. 3. 窗口看门狗
    1. 3.1. 窗口看门狗工作过程
    2. 3.2. 窗口看门狗超时时间
    3. 3.3. 窗口看门狗配置过程
EXTI外部中断和看门狗实验

EXTI外部中断和看门狗实验

IO口外部中断:

①STM32的每个IO都可以作为外部中断输入。

②每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。

GPIO和中断线映射关系:

GPIO和中断线映射关系

外部中断线59分配一个中断向量,共用一个服务函数外部中断线1015分配一个中断向量,共用一个中断服务函数。

13 可设置 EXTI0 EXTI线0中断
14 可设置 EXTI1 EXTI线1中断
14 可设置 EXTI2 EXTI线2中断
16 可设置 EXTI3 EXTI线3中断
17 可设置 EXTI4 EXTI线4中断
30 可设置 EXTI9_5 EXTI线[9:5]中断
47 可设置 EXTI15_10 EXTI线[15:10]中断

外部中断配置

外部中断的中断线映射配置和触发方式都是在GPIO初始化函数中完成:

1
2
3
4
5
GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA,&GPIO_Initure);

和串口中断一样,HAL库同样提供了外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler,我们在外部中断服务函数中会调用该函数处理中断。

1
2
3
4
5
6
7
8
9
10
//中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);//调用中断处理公用函数
}

void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);//调用中断处理公用函数
}

外部中断的一般配置步骤

① 使能IO口时钟。

② 初始化IO口,设置触发方式:HAL_GPIO_Init();

③ 设置中断优先级,并使能中断通道。

④ 编写中断服务函数:

函数中调用外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler。

⑥ 编写外部中断回调函数:HAL_GPIO_EXTI_Callback;

外部中断实验exti.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "exti.h"
#include "led.h"
#include "delay.h"
#include "key.h"

void EXTI_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟

GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);

GPIO_Initure.Pin=GPIO_PIN_13; //PC13
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure);

GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3
HAL_GPIO_Init(GPIOH,&GPIO_Initure);

HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能EXTI0中断通道
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级2,响应优先级0

HAL_NVIC_EnableIRQ(EXTI2_IRQn); //使能EXTI2中断通道
HAL_NVIC_SetPriority(EXTI2_IRQn,2,1); //抢占优先级2,响应优先级1

HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能EXTI3中断通道
HAL_NVIC_SetPriority(EXTI3_IRQn,2,2); //抢占优先级2,响应优先级2

HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能EXTI15_10_IRQn中断通道
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3); //抢占优先级2,响应优先级3
}

void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}

void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
}

void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(100);
switch(GPIO_Pin)
{
//WK_UP
case GPIO_PIN_0:
if(WK_UP==0)
{
LED1=!LED1;
LED0=!LED1;
break;
}

//KEY1
case GPIO_PIN_2:
if(KEY1==0)
{
LED1=!LED1;
break;
}

//KEY0
case GPIO_PIN_3:
if(KEY0==0)
{
LED0=!LED0;
LED1=!LED1;
break;
}

//KEY2
case GPIO_PIN_13:
if(KEY2==0)
{
LED0=!LED0;
break;
}
}
}

独立看门狗

在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。 在系统跑飞(程序异常执行)的情况,使系统复位,程序重新执行。

独立看门狗工作原理

在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。

无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。

如果程序异常,就无法正常喂狗,从而系统复位。

独立看门狗超时时间

溢出时间计算:

Tout=((4×2^prer) ×rlr) /32 (M4) prer 预分频系数 rlr 重装载值

独立看门狗寄存器

① IWDG_KR:键值寄存器,0~15位有效

② IWDG_PR:预分频寄存器,0~2位有效。

具有写保护功能,要操作先取消写保护

③ IWDG_RLR:重装载寄存器,0~11位有效。

具有写保护功能,要操作先取消写保护。

④ IWDG_SR:状态寄存器,0~1位有效

独立看门狗操作步骤

①初始化看门狗:预分频系数,重装载值。

1
HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct
{
IWDG_TypeDef *Instance; /*!< Register base address */

IWDG_InitTypeDef Init; /*!< IWDG required parameters */

HAL_LockTypeDef Lock; /*!< IWDG Locking object */

__IO HAL_IWDG_StateTypeDef State; /*!< IWDG communication state */
}IWDG_HandleTypeDef;

typedef struct
{
uint32_t Prescaler; /*!预分频系数< Select the prescaler of the IWDG.
This parameter can be a value of @ref IWDG_Prescaler */

uint32_t Reload; /*!<重装载值 Specifies the IWDG down-counter reload value.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */
}IWDG_InitTypeDef;

该函数在操作PR和RLR寄存器之前会取消

写保护。

②独立看门狗初始化回调函数

1
void HAL_IWDG_MspInit(IWDG_HandleTypeDef *hiwdg);

③启动看门狗

1
HAL_IWDG_Start(IWDG_HandleTypeDef *hiwdg);

④喂狗:

1
HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);

(获取状态)

1
2
3
4
5
6
7
8
9
10
HAL_IWDG_StateTypeDef HAL_IWDG_GetState(IWDG_HandleTypeDef *hiwdg);

typedef enum
{
HAL_IWDG_STATE_RESET = 0x00, /*!< IWDG not yet initialized or disabled */
HAL_IWDG_STATE_READY = 0x01, /*!< IWDG initialized and ready for use */
HAL_IWDG_STATE_BUSY = 0x02, /*!< IWDG internal process is ongoing */
HAL_IWDG_STATE_TIMEOUT = 0x03, /*!< IWDG timeout state */
HAL_IWDG_STATE_ERROR = 0x04 /*!< IWDG error state */
}HAL_IWDG_StateTypeDef;

独立看门狗实验程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"

IWDG_HandleTypeDef iwdg_handler;

int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
delay_ms(100);

iwdg_handler.Instance=IWDG;
iwdg_handler.Init.Prescaler=IWDG_PRESCALER_64; //预分频系数
iwdg_handler.Init.Reload=500; //自动装载值
HAL_IWDG_Init(&iwdg_handler);

HAL_IWDG_Start(&iwdg_handler); //开启中断

LED0=0;

while(1)
{
if(KEY_Scan(0)==WKUP_PRES) //检测WKUP按键是否按下
{
HAL_IWDG_Refresh(&iwdg_handler); //喂狗
}
delay_ms(10);
}
}

窗口看门狗

之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。

而独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。

窗口看门狗工作过程

STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:

①当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。

② 当计数器的数值从0x40减到0x3F时【T6位跳变到0】。

如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。

窗口看门狗超时时间

窗口看门狗的超时公式如下

Twwdg=(4096✖2^WDGTB✖(T[5:0]+1))/Fpclk1

Twwdg: WWDG超时时间单位为ms

Fpclk1: APB1的时钟频率单位为Khz

WDGTB:WWDG的预分频系数

T[5:0]: 窗口看门狗的计数器低6位

窗口看门狗配置过程

①使能窗口看门狗时钟:

HAL_WWDG_MspInit;

② 初始化窗口看门狗:设置分频系数,窗口值,计数值等。

HAL_WWDG_Init();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct
{
WWDG_TypeDef *Instance; /*!< Register base address */

WWDG_InitTypeDef Init; /*!< WWDG required parameters */

HAL_LockTypeDef Lock; /*!< WWDG locking object */

__IO HAL_WWDG_StateTypeDef State; /*!< WWDG communication state */

}WWDG_HandleTypeDef;

typedef struct
{
uint32_t Prescaler; /*预分频系数*/

uint32_t Window; /*窗口值 */

uint32_t Counter; /*计数值 */

}WWDG_InitTypeDef;

该函数还可以使能窗口看门狗提前唤醒中断 。

③ 设置提前唤醒中断优先级:

HAL_WWDG_MspInit

④ 启动看门狗

HAL_WWDG_Start()

HAL_WWDG_Start_IT() 还开启了提前唤醒中断

⑤ 喂狗:

HAL_WWDG_Refresh();

⑥ 编写提前唤醒中断处理函数

​ 中断处理函数

​ HAL_WWDG_IRQHandler()

​ 提前唤醒回调函数

​ HAL_WWDG_EarlyWakeupCallback()

窗口看门狗源程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"

WWDG_HandleTypeDef wwdg_handler;
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
__HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟
HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断
HAL_NVIC_SetPriority(WWDG_IRQn,2,3); //设置抢断优先级为2,响应优先级为3
}

void WWDG_IRQHandler(void) //中断服务函数
{
HAL_WWDG_IRQHandler(&wwdg_handler); //中断处理函数
}

void HAL_WWDG_WakeupCallback(WWDG_HandleTypeDef *hwwdg) //提前唤醒回调函数
{
HAL_WWDG_Refresh(&wwdg_handler,0x7f); //喂狗
LED1=!LED1;
}


int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
LED0=0;
delay_ms(300);
wwdg_handler.Instance=WWDG;
wwdg_handler.Init.Prescaler=WWDG_PRESCALER_8;
wwdg_handler.Init.Window=0x5f; //上窗口的值
wwdg_handler.Init.Counter=0x7f; //计数值
HAL_WWDG_Init(&wwdg_handler);

HAL_WWDG_Start_IT(&wwdg_handler); //使能窗口看门狗并且开启了提前唤醒中断

while(1)
{
LED0=1;
}
}
文章作者: jiangzuojiben
文章链接: http://jiangzuojiben.github.io/2019/11/17/EXTI%E5%A4%96%E9%83%A8%E4%B8%AD%E6%96%AD%E5%92%8C%E7%9C%8B%E9%97%A8%E7%8B%97%E5%AE%9E%E9%AA%8C/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 jiangzuojiben