【STM32】标准库与HAL库对照学习教程五--外部中断详讲与配置


STM32全部教程【STM32】标准库与HAL库对照学习系列教程大全

一、前言

中断一直是单片机的重要组成部分,本文章讲解中断并使用标准库与cubemx分别配置外部中断,以按键中断为例,通过本篇文章可以加深你对外部中断的理解。

二、准备工作

  • STM32开发板(我用的是普中的STM32F103ZE的Z200系列)
  • STM32cubemx软件、keil5(MDK)
  • 开发板原理图

三、STM32的中断系统

1、中断理解

CPU执行程序时,由于发生了某种随机的事件(包括外部或内部),让CPU暂时停下正在运行的程序,转而去执行一段处理事件的程序(中断服务子程序或中断处理程序)当这段程序执行完时(事件处理完时),程序又返回被停下的程序继续执行,这一过程称为中断。引发中断的称为中断源。

例如:我在看电视时(CPU正在执行程序)突然电话响了(事件发生),那么电话响就相当于中断源,我要先接了电话(CPU转去执行事件程序),接完电话后(事件程序执行完),在看电视(回到被中断的程序继续执行)。

中断程序执行中又出现了中断,那么这种情况又叫做中断的嵌套。

2、STM32的中断

STM32F10x芯片有84个中断通道,包括16个内核中断68 个可屏蔽中断,这些中断通道已按照不同优先级顺序固定分配给相应的外部设备。

在这里插入图片描述

图片在《STM32F10x中文参考手册》的中断与事件章节。

这里说明一下,内核外设片上外设都在芯片上,内核是由RAM公司制定,片上外设是由ST公司制定,所以会有内核中断可屏蔽中断的区别。

3、NVIC的介绍

NVIC英文全称是Nested Vectored Interrupt Controller,中文意思就是嵌套向量中断控制器,通俗的讲它就是一个开关与选择器。它属于M3内核的一个外设,控制着芯片的中断相关功能。由于ARM公司给NVIC预留了非常多的功能,但对于使用M3内核设计芯片的公司(例如ST公司)可能就不需要这么多功能,于是就需要在NVIC上裁剪。ST公司的STM32F103芯片内部中断数量就是NVIC裁剪后的结果。

中断控制相关寄存器固件库core_cm3.h文件的NVIC结构体内(HAL库一样)。

4、中断优先级

  • 多个中断同时到来时,CPU该执行那个中断呢?这个时候就需要用到中断优先级给中断的执行顺序排个序优先级高的可以比优先级低的先执行甚至优先级高的可以打断正在执行的优先级低的中断程序
  • STM32F103芯片支持60个可屏蔽中断通道,每个中断通道都具备自己的中断优先级控制字节(8位,但是STM32F103中只使用4位高4位有效),用于表达优先级高4位又可以组成抢占式优先级响应优先级,通常也把响应优先级称为“亚优先级”或“副优先级”,每个中断源都需要被指定这两种优先级。
抢占式优先级相应式优先级
0位4位
1位3位
2位2位
3位1位
4位0位

不管怎么分,总共只有16个优先级顺序。数字小的优先级大。

  • 高抢占式优先级的中断事件会打断当前的主程序或者低抢占式优先级中断程序的运行。在抢占式优先级相同的情况下,高响应优先级的中断优先被响应。
  • 当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理那一个。

5、中断配置大致步骤

要使用中断我们就需要先配置它,通常都需经过这几步:
(1)使能外设某个中断。
(2)设置中断优先级分组,初始化 NVIC_InitTypeDef 结构体。
(3)编写中断服务函数。

6、标准库的NVIC结构体与配置

typedef struct
{
  uint8_t NVIC_IRQChannel;                    //中断源
  uint8_t NVIC_IRQChannelPreemptionPriority;  //抢占优先级
  uint8_t NVIC_IRQChannelSubPriority;         //响应优先级
  FunctionalState NVIC_IRQChannelCmd;         //中断使能或失能   
} NVIC_InitTypeDef;
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)

7、HAL的NVIC配置

HAL库直接采用函数配置:

void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
  • IRQn_Type:外部中断通道
  • PreemptPriority:抢占式优先级
  • SubPriority:响应式优先级

四、外部中断

1、外部中断介绍

(1)EXTI简介

STM32F10x外部中断/事件控制器(EXTl),包含多达20 个用于产生事件/中断请求的边沿检测器。EXTI的每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发),还可独立地被屏蔽。

当引脚的电平从低电平变为高电平时,会产生一个上升沿,当引脚的电平从高电平变为低电平时,会产生一个下降沿。

(2)EXTI结构框图

在这里插入图片描述

(3)外部中断/事件线映射的引脚


在这里插入图片描述


在这里插入图片描述

2、外部中断步骤

(1)使能IO口时钟,配置IO口模式为输入。
(2)开启 AFIO 时钟,设置 IO 口与中断线的映射关系。
(3)配置中断分组(NVIC),使能中断。
(4)初始化EXTI,选择触发方式。
(5)编写EXTI中断服务函数。

3、硬件电路

根据自己的开发板来。


在这里插入图片描述


在这里插入图片描述

由硬件图可知,引脚配置为上拉输入模式,接高电平,没有按下按键时,引脚成高电平,按键按下时,引脚电平变为低电平,中断方式为下降沿中断。


在这里插入图片描述

由硬件图可知,引脚配置为下拉输入模式,接低电平,没有按下按键时,引脚成低电平,按键按下时,引脚电平变为高电平,中断方式为上降沿中断。


五、标准库配置中断实验(按键中断实验)

1、配置工程


(1) 复制上一节的工程并命名为5、外部中断实验。(没有的话自己找一个驱动LED的标准库工程)
在这里插入图片描述


(2) 进入工程文件,在App文件中新建Exti文件,用来放置、保存与中断相关的文件。
在这里插入图片描述


(3) 打开工程,新建文件,并命名exti.c与exti.h文件。

在这里插入图片描述

在这里插入图片描述


(4)添加文件到目录,并添加文件路径

在这里插入图片描述


在这里插入图片描述


(5) 使用中断函数需要添加相应的中断文件。
在这里插入图片描述


2、中断配置程序

.c程序为

#include "exti.h"
#include "LED.h"

/*************************************************
*函数名:   KEY_Init
*函数功能: 按键初始化
*输入:     无
*返回值:   无
**************************************************/
void KEY_Init()
{
	GPIO_InitTypeDef GPIO_InitStruct;   //GPIO相关结构体
	
	RCC_APB2PeriphClockCmd(KEY_UP_RCC|KEY_RCC, ENABLE); //引脚时钟使能
	
	//KEY_UP引脚配置
	GPIO_InitStruct.GPIO_Pin = KEY_UP_Pin;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;    //配置为下拉
	GPIO_Init(KEY_UP_GPIO_Port,&GPIO_InitStruct);
	
	//KEY1、KEY0引脚配置
	GPIO_InitStruct.GPIO_Pin = KEY1_Pin|KEY0_Pin;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;    //配置为上拉
	GPIO_Init(KEY_GPIO_Port,&GPIO_InitStruct);
	
}

/*************************************************
*函数名:    My_EXTI_Init
*函数功能:  中断初始化
*输入:      无
*返回值:    无
**************************************************/
void My_EXTI_Init()
{
	NVIC_InitTypeDef NVIC_InitStruct;   //NVIC相关结构体
	EXTI_InitTypeDef EXTI_InitStruct;   //外部中断相关结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);  //使用中断要打开相应的引脚复用时钟
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //打开KEY_UP引脚的相应通道
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3); //打开KEY1引脚的相应通道
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); //打开KEY0引脚的相应通道
	
	/* NVIC配置 */
	//EXTI0配置
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;         //要打开的中断通道
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =2; //抢占式优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;       //相应式优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;          //NVIC通道使能
	NVIC_Init(&NVIC_InitStruct);
	
	//EXTI3配置
	NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	//EXTI4配置
	NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	//外部中断配置
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;              //中断通道选择
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;     //中断响应模式
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;  //上升沿触发中断
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;               //外部中断使能
	EXTI_Init(&EXTI_InitStruct);                         //外部中断初始化
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line3|EXTI_Line4;   //中断通道选择
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;     //中断响应模式
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发中断
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;               //外部中断使能
	EXTI_Init(&EXTI_InitStruct);                         //外部中断初始化
	
}

/*************************************************
*函数名:    EXTI0_IRQHandler
*函数功能:  通道0中断函数
*输入:      无
*返回值:    无
**************************************************/
void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) == 1) //检测到中断
	{
		if(GPIO_ReadInputDataBit(KEY_UP_GPIO_Port,KEY_UP_Pin)==1)  //检测按键电平
		{
		GPIO_ResetBits(LED0_GPIO_Port, LED0_Pin);
		GPIO_ResetBits(LED1_GPIO_Port, LED1_Pin);
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line0); //清空中断标志位
}

/*************************************************
*函数名:    EXTI3_IRQHandler
*函数功能:  通道3中断函数
*输入:      无
*返回值:    无
**************************************************/
void EXTI3_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line3) == 1)
	{
		if(GPIO_ReadInputDataBit(KEY_GPIO_Port,KEY1_Pin)==0)
		GPIO_SetBits(LED0_GPIO_Port, LED0_Pin);
	}
	EXTI_ClearITPendingBit(EXTI_Line3);
}

/*************************************************
*函数名:    EXTI4_IRQHandler
*函数功能:  通道4中断函数
*输入:      无
*返回值:    无
**************************************************/
void EXTI4_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line4) == 1)
	{
		if(GPIO_ReadInputDataBit(KEY_GPIO_Port,KEY0_Pin)==0)
		GPIO_SetBits(LED1_GPIO_Port, LED1_Pin);
	}
	EXTI_ClearITPendingBit(EXTI_Line4);
}

.h程序为:

#ifndef EXTI_H_
#define EXTI_H_

#include "stm32f10x.h"
/* 这边用按键验证中断 */
//KEY_UP按键接电源
#define KEY_UP_GPIO_Port GPIOA
#define KEY_UP_Pin       GPIO_Pin_0
#define KEY_UP_RCC       RCC_APB2Periph_GPIOA
//KEY1与KEY0按键接地
#define KEY_GPIO_Port    GPIOE
#define KEY1_Pin         GPIO_Pin_3
#define KEY0_Pin         GPIO_Pin_4
#define KEY_RCC          RCC_APB2Periph_GPIOE

void KEY_Init(void);
void My_EXTI_Init(void);

#endif

main,c程序为:

#include "LED.h"
#include "Delay.h"
#include "exti.h"

/*************************************************
*函数名:    main
*函数功能: 主函数
*输入:     无  
*返回值:   无
**************************************************/
int main()
{
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //抢占式优先级与响应式优先级的分组
	LED_Init();
	KEY_Init();
	My_EXTI_Init();
	while(1)
	{

	}
}

3、实验效果

在这里插入图片描述

六、使用cubemx配置中断实验(按键中断实验)

1、配置工程


(1) 进入cubemx新建工程,选择自己开发板的芯片。
在这里插入图片描述


(2) 配置RCC
在这里插入图片描述


(3) 配置相应的引脚。

  • 一个LED引脚。(我的开发板是PB5,LED引脚接的是高电平)
  • 一个中断引脚。(连接按键,按键一端接高电平)
    ①LED:
    在这里插入图片描述
    ②中断:
    在这里插入图片描述

(4) GPIO的模式设置
在这里插入图片描述
关于GPIO不清楚的可以看这篇文章:【STM32】标准库与HAL库对照学习教程特别篇–GPIO详讲


(5) 外部中断设置(EXIT)
①设置为上升沿触发中断
在这里插入图片描述
②引脚设置为下拉
在这里插入图片描述


(6) 中断向量表(NVIC)设置
在这里插入图片描述


(7) 时钟树设置
在这里插入图片描述
关于时钟树不清楚的可以看这篇文章:【STM32】STM32标准库与HAL库对照学习教程特别篇–系统时钟RCC详讲


(8) 项目文件设置,并生成工程。
①(路径和工程名都不要带中文
在这里插入图片描述

在这里插入图片描述


2、cubemx生成中断函数

cubemx生成的中断函数为:

在这里插入图片描述

在这里插入图片描述

相应文件在stm32f1xx_it.c与stm32f1xx_hal_gpio.c文件中

3、回调函数

在cubemx生成的中断函数中,我们可以看到,函数最后进入了一个叫HAL_GPIO_EXTI_Callback()的函数中,我们这个函数就是回调函数,函数是弱定义形式需要用户写入对应的执行程序(也可以不写,中断就什么也不干)。
因此在main.c中我们可以写入我们要执行的程序。
程序为:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_0)  //按键按下
		HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin); //LED电平翻转
	__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);  //清除中断标志
}

4、实验效果

在这里插入图片描述
在这里插入图片描述

  • 36
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修成真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值