历史上的今天

今天是:2024年09月01日(星期日)

正在发生

2021年09月01日 | STM32—中断详解(配合按键中断代码,代码亲测)

发布者:快乐微笑 来源: eefocus关键字:STM32  中断  按键中断 手机看文章 扫描二维码
随时随地手机看文章

在STM32中执行中断主要分三部分:

1.配置NVIC_Config()函数

2.配置EXTI_Config()函数

3.编写中断服务函数

(注:本文章所用代码为中断按键代码,实现了按键进入中断从而控制LED亮灭)


配置NVIC_Config()函数

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。

NVIC_Config()函数代码如下:


static void NVIC_Config(void) /* 主要是配置中断源的优先级与打开使能中断通道 */

{

NVIC_InitTypeDef NVIC_InitStruct ;

/* 配置中断优先级分组(设置抢占优先级和子优先级的分配),在函数在misc.c */

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ;

/* 配置初始化结构体 在misc.h中 */

/* 配置中断源 在stm32f10x.h中 */

NVIC_InitStruct.NVIC_IRQChannel = KEY1_EXTI_IRQN ;

/* 配置抢占优先级 */

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;

/* 配置子优先级 */

NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ;

/* 使能中断通道 */

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;

/* 调用初始化函数 */

NVIC_Init(&NVIC_InitStruct) ;

/* 对key2执行相同操作 */

NVIC_InitStruct.NVIC_IRQChannel = KEY2_EXTI_IRQN ;

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;

NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;

NVIC_Init(&NVIC_InitStruct) ;

}


配置NVIC_Config()的目的是选择中断源的优先级以及打开中断通道,主要功能通过配置NVIC初始化结构体NVIC_InitStruct来完成。通俗的讲,STM32中有很多中断,而当有多个中断同时发生时就涉及到中断执行的先后问题了,所以引入了中断优先级的概念,中断优先级越高中断就越先执行。在这里我们只讨论外部中断的优先级,在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级。优先级高低的比较包括抢占优先级和子优先级,先比较抢占优先级,如果抢占优先级相同就比较子优先级,从而得出中断之间的优先级高低。NVIC的主要任务就是给对应的中断源分配中断优先级。 中断优先级分配的原理繁杂,但固件库编程的好处就是化繁为简,我们只需要按照NVIC_InitStruct()中的内容进行配置就行。


接下来简单讲解一下NVIC_Config()函数的内容:


1.首先设置中断优先级分组

中断优先级分组其实是确立一个大纲,中断优先级寄存器 NVIC_IPRx中有4个位用来确定优先级,中断优先级的分组就是把这4个位分配在抢占优先级和子优先级中。比如设定一个位配置抢占优先级,其余三个位配置子优先级。通过函数NVIC_PriorityGroupConfig() ; 实现分组,详细代码如下:


1 /**

2 * 配置中断优先级分组:抢占优先级和子优先级

3 * 形参如下:

4 * @arg NVIC_PriorityGroup_0: 0bit for 抢占优先级

5 *                       4 bits for 子优先级

6 * @arg NVIC_PriorityGroup_1: 1 bit for 抢占优先级

7 *                            3 bits for 子优先级

8 * @arg NVIC_PriorityGroup_2: 2 bit for 

9 *                            2 bits for 子优先级

10 * @arg NVIC_PriorityGroup_3: 3 bit for 抢占优先级

11 *                           1 bits for 子优先级

12 * @arg NVIC_PriorityGroup_4: 4 bit for 抢占优先级

13 *                           0 bits for 子优先级

14 * @注意 如果优先级分组为 0,则抢占优先级就不存在,优先级就全部由子优先级控制

15 */

16 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)

17 {

18 // 设置优先级分组

19 SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;

20 }


2.优先级分组完毕后,是配置NVIC初始化结构体


typedef struct {

2 uint8_t NVIC_IRQChannel; // 中断源

3 uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级

4 uint8_t NVIC_IRQChannelSubPriority; // 子优先级

5 FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能

6 } NVIC_InitTypeDef;


初始化结构体的作用是,收集中断源的信息(包括配置的是哪一个中断源、中断源的抢占优先级是多少、中断源的子优先级是多少、中断源的使能是否开启)。


NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。


NVIC_IRQChannelPreemptionPriority和NVIC_IRQChannelSubPriority 分别设置抢占优先级和子优先级,具体的值要根据中断优先级分组来确定。


NVIC_IRQChannelCmd:设置中断使能(ENABLE)或者失能(DISABLE),相当于一个电源总开关。


3.最后借助NVIC初始化函数将NVIC初始化结构体中的信息写入相应的寄存器中 (体现了固件库编程的优点,不需要我们深入到寄存器层次去,只需要掌握相应函数的配置即可)


配置EXTI_Config()函数

EXTI(External interrupt/event controller):外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。


按我的理解,EXTI是一个有着多达20个接口的控制器,它可以为每一个接入接口的信号源配置中断(或事件)线、设置信号的检测方式、设置触发事件的性质,也就是说,传入EXTI的仅仅是一个信号,EXTI的功能就是根据信号传入的“线”对信号做出相应的处理,然后将处理后的信号转向NVIC。 就像一个分拣机器,传入的东西经过筛选处理被送往不同的地方,只是EXTI分拣的是信号罢了。 如果说NVIC是配置中断源,那么EXTI就是向NVIC传送中断信号。


EXTI功能框图:

在这里插入图片描述

EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,线路1-2-4-5是产生中断的流程,20/代表着有20条相同的线路。


接下来讲解一下EXTI_Config()函数代码:


void EXTI_Config() /* 主要是连接EXTI与GPIO */

{

GPIO_InitTypeDef GPIO_InitStruct ;

EXTI_InitTypeDef EXTI_InitStruct ;

NVIC_Config();


/* 初始化要与EXTI连接的GPIO */

/* 开启GPIOA与GPIOC的时钟 */

RCC_APB2PeriphClockCmd(KEY1_EXTI_GPIO_CLK | KEY2_EXTI_GPIO_CLK, ENABLE) ;

GPIO_InitStruct.GPIO_Pin = KEY1_EXTI_GPIO_PIN ;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;

GPIO_Init(KEY1_EXTI_GPIO_PORT , &GPIO_InitStruct) ;

GPIO_InitStruct.GPIO_Pin = KEY2_EXTI_GPIO_PIN ;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;

GPIO_Init(KEY2_EXTI_GPIO_PORT , &GPIO_InitStruct) ;

/* 初始化EXTI外设 */

/* EXTI的时钟要设置AFIO寄存器 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) ;

/* 选择作为EXTI线的GPIO引脚 */

GPIO_EXTILineConfig( KEY1_GPIO_PORTSOURCE , KEY1_GPIO_PINSOURCE) ;

/* 配置中断or事件线 */

EXTI_InitStruct.EXTI_Line = KEY1_EXTI_LINE ;

/* 使能EXTI线 */

EXTI_InitStruct.EXTI_LineCmd = ENABLE ;

/* 配置模式:中断or事件 */

EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ;

/* 配置边沿触发 上升or下降 */

EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising ;

EXTI_Init(&EXTI_InitStruct) ;

GPIO_EXTILineConfig( KEY2_GPIO_PORTSOURCE , KEY2_GPIO_PINSOURCE) ;

EXTI_InitStruct.EXTI_Line = KEY2_EXTI_LINE ;

EXTI_InitStruct.EXTI_LineCmd = ENABLE ;

EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ;

EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling ;

EXTI_Init(&EXTI_InitStruct);

}


代码可大体分为三部分:

配置GPIO相应引脚、配置EXTI并连接GPIO引脚、传入NVIC_Config()


1.配置GPIO相应引脚

该代码是通过按键产生一个电平信号,然后经EXTI处理传入NVIC产生中断的,所以要配置连接按键的GPIO引脚,主要是设置相应的引脚模式为浮空输入 。老规矩,先开启相应GPIO的时钟,然后配置引脚初始化结构体,再利用初始化函数将初始化结构体写入寄存器中。


2.配置EXTI并连接GPIO引脚

要操作外设,首先要打开相关的时钟,EXTI挂载在APB2总线上,并且开启时钟时要操作AFIO寄存器 ,准备工作就绪后连接GPIO相应的引脚到EXTI中,前面说了EXTI有20个接口,所以特定的引脚有特定的接口,所以要根据GPIO_EXTILineConfig();函数选择用作EXTI线的GPIO引脚,函数说明如下


/**

  * @brief  Selects the GPIO pin used as EXTI Line.

  * @param  GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.

  *   This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).

  * @param  GPIO_PinSource: specifies the EXTI line to be configured.

  *   This parameter can be GPIO_PinSourcex where x can be (0..15).

  * @retval None

  */

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

{

  uint32_t tmp = 0x00;

  /* Check the parameters */

  assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));

  assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));

  

  tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));

  AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;

  AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));

}


其实对应的EXTI线就对应GPIO引脚号,这样看起来还比较直观。


连接好GPIO引脚与EXTI后就该配置EXTI的初始化结构体了,结构体如下:


typedef struct 

{

uint32_t EXTI_Line; // 中断/事件线

EXTIMode_TypeDef EXTI_Mode; // EXTI 模式

EXTITrigger_TypeDef EXTI_Trigger; // 触发类型

FunctionalState EXTI_LineCmd; // EXTI 使能

 } EXTI_InitTypeDef;


配置此结构体主要是:选择相应的EXTI线 、选择触发模式、选择产生的结果(中断还是事件)、是否使能EXTI线。


EXTI_Line:中断线选择,可选 EXTI_0 至 EXTI_19(一共20个)。既然刚才配置好了与GPIO引脚对应的EXTI线,所以初始化结构体中的EXTI线就是与GPIO连接的那个线。


EXTI_Mode: EXTI 模式选择,可选为产生中断或者产生事件。就是决定信号的发展方向,是产生中断呢?还是产生事件呢?此处是中断。


EXTI_Trigger: EXTI 边沿触发模式,可选上升沿触发、下降 沿 触 发 或 者 上 升 沿 和 下 降 沿 都 触 发。触发信号。


EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线或禁用。


初始化结构体配置完毕后交由初始化函数写入相应的寄存器中。


3.传入NVIC_Config()

之后就自动传入NVIC中了。。。


编写中断服务函数

到这里就万事俱备只欠东风了,中断的触发与处理及优先级定义都已经安排上了,最后一步就是编写中断函数的内容了,只要进入中断就会执行中断函数中的代码,所以这是收尾工作。STM32的中断服务函数不同于51单片机中的中断服务函数,STM32的所有中断函数都被偷偷安排了,每个中断都有其固定的名字,只有找到这个名字,在这个固定的函数名下编写中断服务函数才是有效的,所有中断函数的编写都要在stm32f10x_it.c 中,如示:

在这里插入图片描述

从所给的信息可得知外设的中断服务函数的名字都存放在startup_stm32f10x_xx.s 中,而且是由汇编语言编写,如示:

在这里插入图片描述

可知EXTI线0到EXTI线4线都是单独的中断函数名、EXTI线5到EXTI线9共用一个中断函数名、EXTI线10线到EXTI线15线共用一个中断函数名。


我们要做的就是以相应的EXTI线的中断函数名字在stm32f10x_it.c中编写中断函数 如下:


void EXTI0_IRQHandler(void)

{

if(  EXTI_GetITStatus(KEY1_EXTI_LINE)!=RESET)

{

LED1_TOGGLE;   //LED1的亮灭状态反转

}

EXTI_ClearITPendingBit(KEY1_EXTI_LINE);

}


void EXTI15_10_IRQHandler(void)

{

if(  EXTI_GetITStatus(KEY2_EXTI_LINE)!=RESET)

{

LED2_TOGGLE;   //LED2的亮灭状态反转

}

EXTI_ClearITPendingBit(KEY2_EXTI_LINE);

}


每次进入中断函数后,靠ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)读取中断是否执行 ,执行完之后要利用void EXTI_ClearITPendingBit(uint32_t EXTI_Line)清除清除中断标志位,以免不断进入中断


大功告成

到此完整的中断系统就已经完成,主函数只需调用即可!!!

(附上主函数及俩个头文件)

希望可以一起交流学习

qq:2723808286


#include "stm32f10x.h"

#include "bsp_led.h"

#include "bsp_key.h"


int main(void)

LED_GPIO_Config();

EXTI_Config();

while(1) 

{

}

}


#ifndef __BSP_KEY_H

#define __BSP_KEY_H


#include "stm32f10x.h"


#define KEY1_EXTI_GPIO_CLK      RCC_APB2Periph_GPIOA

#define KEY1_EXTI_GPIO_PORT     GPIOA

#define KEY1_EXTI_GPIO_PIN      GPIO_Pin_0

#define KEY1_EXTI_IRQN          EXTI0_IRQn      /* 对应着引脚号 */

#define KEY1_EXTI_LINE          EXTI_Line0      /* 中断、事件线对应引脚号 */

#define KEY1_GPIO_PORTSOURCE    GPIO_PortSourceGPIOA

#define KEY1_GPIO_PINSOURCE     GPIO_PinSource0

#define  KEY1_EXTI_IRQHANDLER       EXTI0_IRQHandler


#define KEY2_EXTI_GPIO_CLK      RCC_APB2Periph_GPIOC

#define KEY2_EXTI_GPIO_PORT     GPIOC

#define KEY2_EXTI_GPIO_PIN      GPIO_Pin_13

#define KEY2_EXTI_IRQN          EXTI15_10_IRQn

#define KEY2_EXTI_LINE          EXTI_Line13

#define KEY2_GPIO_PORTSOURCE    GPIO_PortSourceGPIOC

#define KEY2_GPIO_PINSOURCE     GPIO_PinSource13

#define  KEY2_EXTI_IRQHANDLER       EXTI15_10_IRQHandler


void EXTI_Config(void);

#endif


#ifndef __BSP_LED_H

#define __BSP_LED_H


#include "stm32f10x.h"


#define LED1_GPIO_CLK   RCC_APB2Periph_GPIOC   /*时钟*/

#define LED1_GPIO_PORT  GPIOC                  /*端口*/

#define LED1_GPIO_PIN   GPIO_Pin_2             /*引脚*/



#define LED2_GPIO_PIN   GPIO_Pin_3

#define LED2_GPIO_CLK   RCC_APB2Periph_GPIOC

#define LED2_GPIO_PORT  GPIOC


#define digitalTOGGLE(p,i)     {p->ODR ^=i;}

#define LED1_TOGGLE            digitalTOGGLE(LED1_GPIO_PORT,LED1_GPIO_PIN)

[1] [2]
关键字:STM32  中断  按键中断 引用地址:STM32—中断详解(配合按键中断代码,代码亲测)

上一篇:STM32—重定向printf和getchar函数到串口
下一篇:STM32—串口通讯详解

推荐阅读

最近,在做基于stm32f401串口的ModBus协议通信,遇到了stm32串口发送数据的问题。花了一整天去查找问题,从ModBus协议格式、调度算法到串口配置,最终终于把问题解决,记录下来。 问题描述: ModBus协议中配置stm32f401串口为奇校验,8位数据位,1位停止位,程序如下:void uart_init( u32 bound ){ //GPIO端口...
“传统工业机器人发展受到了一定制约,新一代的机器人增长乏力,新的公司在不断倒闭关门,是不是机器人的冬天来了?还是机器人正处于进入春天的过渡期,也就是所谓的‘倒春寒’,即将迎来新的发展机遇?”在2019世界机器人大会现场,新松机器人自动化股份有限公司创始人、总裁曲道奎抛出了这个问题。 根据国际机器人联合会数据,在全球经济发展滞缓的环...
  串口通信是单片机学习的一个最基本、最重要的功能之一。串口通信可以间接的当做调试接口使用,实现单片机与电脑之间的通信。当然可以与一些模块(比如蓝牙、wifi)通信,也可以作为和其他单片机通信的工具。  STM8S的通用异步收发器(UART)主要特性:  ● 全双工的,异步通信  ● 可编程数据字长度(8位或9位)  ● 可配置的停止位-支持1或2个停止...
台积电上周传出计划调涨芯片价格10%到20%,涨价压力将透过供应链,传导到智慧手机、汽车、数据中心服务器等终端产品,但成本增幅主要取决于每种设备内部的半导体含量占比,预料这对想买车的人影响不大,智慧手机买主比较伤荷包,压力最大的是采购服务器的企业资讯长(CIO)。富比世杂志专栏作家葛文纳普(Linley Gwennap)指出,在汽车制造成本中,芯片占...

史海拾趣

问答坊 | AI 解惑

电子技术自立学习法

人的一生中使用自立学习法的时间最长,自己看书、自己动手就是自立学习法。1.具备基本条件事半功倍为了高效率运用自立学习法进行电子技术的学习,应该具备下列一些基本条件:①自主学习的信心和精神非常重要,这种学习过程中要时刻牢记靠“两条腿 ...…

查看全部问答∨

FPGA设计的基本原则、技巧与时序电路设计

FPGA设计的基本原则 面积与速度折衷原则 面积和速度是ASIC芯片设计中一对相互制约、影响成本和性能的指标,贯穿FPGA设计的始终。在FPGA设计中,面积是指一个设计消耗的FPGA内部逻辑资源的数量,可以用消耗的触发器和查找表的个数或者是 ...…

查看全部问答∨

06月29日 上新的电路图

新的电路图修改的地方有:增加了两个按键,可以替代编码开关,因为编码开关比较难买点 把原来用三极管进行3.3V和5V电平转换的电路换成了专用电平转换芯片,这样没有被取反的情况存在…

查看全部问答∨

具体谈谈中国LED和国外LED封装的差异之二芯片的差异--原创

目前我国的LED芯片企业约有十家左右,起步较晚,规模不够大,主要是厦门三安和大连路美等  这两年中国大陆的LED芯片企业发展速度较快,技术水平提升也很快,中小尺寸芯片(指22mil以下)已能基本满足国内封装企业的需求,大尺寸芯片技术还没更上 ...…

查看全部问答∨

急!!串口发送汉字的问题

我现在要用vc编写pc方软件实现通过串口想下位机(用的是51系列)发送汉字以便汉字能在下位机所接的LCD(TG12864)上显示。 现在主要问题是:pc方是先调用软件区出要发送汉字的字模再发送,下位机接受了以后存起来,以便显示,如何实现下位机??? ...…

查看全部问答∨

复制到WINCE的WINDOWS目录的文件在重新启动后丢失!

请教为什么复制到WINCE的WINDOWS目录的文件在重新启动后丢失!怎么样可以让它不丢失呀?…

查看全部问答∨

这个实现有难度(高手请进)

目前要做一个项目 芯片为430系列的,配LAN接口传送大量数据 同时支持WEB浏览 请问:大量的html 词语、语言如何存储、调用? 这个没弄过,大家给些建议啊…

查看全部问答∨

对于UART的FIFO有点搞不明白

是这样的,有个16位的外部数据,想通过9B96板子上的以太网口传输到PC里(CPU不处理数据),本人想利用DMA的UART接收通道和以太网的发送通道完成传输。困惑的是,外部数据如何传到UART的FIFO中,然后又如何传到以太口的FIFO中?求高人解答。。。 或者 ...…

查看全部问答∨

基于MSP430的 三相直流无刷电机控制参考设计

直流无刷电机相比于有刷电机来讲,有很多优势,因此BLDC直流无刷成为越来越流行的趋势。TI官方网站提供了关于直流无刷的参考设计。对于一些小型的设计,诸如航模设计等都可以使用到。   可以从TI官方网站下载: http://www.ti.com.cn/mcu/c ...…

查看全部问答∨

求easyarm1138视频教程

跪求easyarm1138视频教程,谢谢了…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved