2020-07-31

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

==1.配置NVIC_Config()函数

2.配置EXTI_Config()函数

3.编写中断服务函数==

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

==配置NVIC_Config()函数==

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

NVIC_Config()函数代码如下

staticvoidNVIC_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 */16voidNVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup)17{18// 设置优先级分组19SCB->AIRCR=AIRCR_VECTKEY_MASK|NVIC_PriorityGroup;20}

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

typedefstruct{2uint8_tNVIC_IRQChannel;// 中断源3uint8_tNVIC_IRQChannelPreemptionPriority;// 抢占优先级4uint8_tNVIC_IRQChannelSubPriority;// 子优先级5FunctionalState 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()函数代码:

voidEXTI_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

  */voidGPIO_EXTILineConfig(uint8_tGPIO_PortSource,uint8_tGPIO_PinSource){uint32_ttmp=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的初始化结构体了,结构体如下:

typedefstruct{uint32_tEXTI_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中编写中断函数== 如下:

voidEXTI0_IRQHandler(void){if(EXTI_GetITStatus(KEY1_EXTI_LINE)!=RESET){LED1_TOGGLE;//LED1的亮灭状态反转}EXTI_ClearITPendingBit(KEY1_EXTI_LINE);}voidEXTI15_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"intmain(void){LED_GPIO_Config();EXTI_Config();while(1){}}

#ifndef__BSP_KEY_H#define__BSP_KEY_H#include"stm32f10x.h"#defineKEY1_EXTI_GPIO_CLK      RCC_APB2Periph_GPIOA#defineKEY1_EXTI_GPIO_PORT    GPIOA#defineKEY1_EXTI_GPIO_PIN      GPIO_Pin_0#defineKEY1_EXTI_IRQN          EXTI0_IRQn/* 对应着引脚号 */#defineKEY1_EXTI_LINE          EXTI_Line0/* 中断、事件线对应引脚号 */#defineKEY1_GPIO_PORTSOURCE    GPIO_PortSourceGPIOA#defineKEY1_GPIO_PINSOURCE    GPIO_PinSource0#defineKEY1_EXTI_IRQHANDLER      EXTI0_IRQHandler#defineKEY2_EXTI_GPIO_CLK      RCC_APB2Periph_GPIOC#defineKEY2_EXTI_GPIO_PORT    GPIOC#defineKEY2_EXTI_GPIO_PIN      GPIO_Pin_13#defineKEY2_EXTI_IRQN          EXTI15_10_IRQn#defineKEY2_EXTI_LINE          EXTI_Line13#defineKEY2_GPIO_PORTSOURCE    GPIO_PortSourceGPIOC#defineKEY2_GPIO_PINSOURCE    GPIO_PinSource13#defineKEY2_EXTI_IRQHANDLER      EXTI15_10_IRQHandlervoidEXTI_Config(void);#endif

#ifndef__BSP_LED_H#define__BSP_LED_H#include"stm32f10x.h"#defineLED1_GPIO_CLK  RCC_APB2Periph_GPIOC/*时钟*/#defineLED1_GPIO_PORT  GPIOC/*端口*/#defineLED1_GPIO_PIN  GPIO_Pin_2/*引脚*/#defineLED2_GPIO_PIN  GPIO_Pin_3#defineLED2_GPIO_CLK  RCC_APB2Periph_GPIOC#defineLED2_GPIO_PORT  GPIOC#definedigitalTOGGLE(p,i)    {p->ODR ^=i;}#defineLED1_TOGGLE            digitalTOGGLE(LED1_GPIO_PORT,LED1_GPIO_PIN)#defineLED2_TOGGLE            digitalTOGGLE(LED2_GPIO_PORT,LED2_GPIO_PIN)/* LED状态反转 */voidLED_GPIO_Config(void);#endif 

想了解更多加1090492304

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342