中断NVIC
1.什么是中断
利用 IO 引脚高低电平切变情况,进行指定中断服务函数/中断处理函数执行。
在 STM32 中,中断 NVIC 分为 内部中断 和 外部中断 ,根据中断触发状态或者电平切变进行对应的中断触发和中断处理。主要内容包括:
- 中断服务编号/中断请求编号 IRQn ==> Interrupt ReQuesr Number
- 中断服务处理函数 IRQHandler
- 中断优先级 Priority 占先优先级 和 次级优先
- 中断触发标志位 MCU 内部相关的 内核和外设 SR 寄存器、
2.中断中的常用概念
- 断点:在 MCU 注册的中断触发位置,一般对应中断触发条件,触发寄存器标志位变化。需要通过 CR寄存器 对当前模块中断进行使能 IE==>Interrupt Enable
- 中断源:当前中断触发之后,对应的中断处理函数 IRQhandler
- 压栈:中断触发,此时 中断处理函数 IRQhandler 称为前台,中断触发位置函数称为后台函数
3.Cortex-M3中断分类
Cortex-M3内核中断分类
- 内部中断/内核中断:芯片内部/内核中断内容,也可以称之为 Exception 异常。
程序运行正常过程中,出现的非正常情况。例如 RESET 按键、内存溢出、硬件问题等,内部/内核中断重点关注 系统嘀嗒SysTick
- 外部中断
片上外设中断:STM32片上外设大多数都有对应的中断情况,例如 USART 数据发送完成中断,数据接收中断,数据总线空闲中断,数据错误中断。。。大多数中断都是片上外设完成特定任务,指定条件触发。。。
需要对应外设 SR 和 CR 两个寄存器配合使用。CR 用于指定指定是否开启,SR 判断触发的中断是哪一个。
EXIT外部中断控制线:MCU 提供给引脚连接的外部设备M,例如 SR602,外部压力传感器,烟雾传感器,利用基本的 高低电平切换出现的边沿情况 进行中断触发。扩宽外部中断控制。
4. IRQn 和 IRQHandler 解释
4.1 IRQn中断请求编号
IRQn ==>Interrupt ReQuesr Number
在 Cortex-M3 内核原码中,利用 枚举类型 对当前 MCU 执行的所有中断请求进行了编号
编号规则:中断触发设备名称_IRQn,枚举类型是 IRQn_Type
例如:USART1_IRQn,TIM2_IRQn,SPI1_IRQn,DMA2_Channel_IRQn
编号是用于在开发过程中, 注册中断 使用,告知MCU 当前执行过程中,有哪些中断存在
在 stm32f10x.h 中声明枚举 IRQn_Type 类型
4.2 IRQHandler中断处理函数
IRQHandler 与 IRQn 一一对应,当前在MCU中注册了 IRQn 中断请求编号,内核在触发中断之后会自动调用对应的 IRQnHandler 处理函数
例如:当前 NVIC 注册中断 USART1_IRQn ,程序选哟实现的函数是 void USART1_IRQnHandler(void)函数。
在 startup_stm32f10x_hd.s 文件中,告知当前支持的 IRQnHandler 中断处理函数
5. EXTI 外部中断控制线开发
5.1 EXTI 框图分析和对应映射分析
外部中断/事件线路映射
5.2 EXTI 相关寄存器配置
分析当前 EXTI 开发必要内容:
- EXTI 外部中断控制线配置对应的 GPIO 引脚
- EXTI 外沿检测,上升沿和下降沿控制
- EXTI 中断使能
5.2.1 EXTI 外部中断控制线配置对应的 GPIO 引脚
引脚复用:在 MCU 中 IO 引脚可以通过 AFIO 寄存器对当前 IO 引脚功能进行 复用配置 。
AFIO 中利用 EXTICR1~EXTICR4 四个寄存器配置 EXTI0~EXTI15 对应的外部 GPIO 引脚是哪一个
//当前采用的案例是开发板对应的可编程按键,对应引脚是 PA0 E2 PE3 PE4 ,需要控制的外部中断线对应 EXTI0 EXTI2 EXTI3 EXTI4
5.2.2 EXTI 边沿检测
当前按键:
- KEY_UP ==> PA0==> EXTI0
按键按下后,提供给 MCU 的电平情况是高电平,GPIO 引脚默认是低电平 ==》 选择上升沿触发
- KEY0==> PE4==> EXTI4
- KEY1==> PE3==> EXTI3
- KEY2==> PE2==> EXTI2
以上按键按下后,提供给 MCU 的电平情况是低电平,GPIO 引脚默认是高电平 ==》 选择下降沿触发
配置当前 EXTI 外部中断控制线,选择是上升沿还是下降沿触发中断需要根据电路原理图分析
5.2.3 EXTI 中断使能
需要开启 EXTI 外部中断线中断触发支持,需要通过两个寄存器配合完成:
- EXTI_IMR 是否开放当前对应外部中断线的中断
- EXTI_PR 判断对应外部中断是否触发
5.3 代码实现
按键中断开发分析:
#ifndef _KEY_H
#define _KEY_H#include "stm32f10x.h"
#include "delay.h"
#include "beep.h"
#include "led.h"#define GPIOA_RCC_APB2_CLOCK_ENABLE (0x01 << 2)
#define GPIOE_RCC_APB2_CLOCK_ENABLE (0x01 << 6)
#define AFIO_RCC_APB2_CLOCK_ENABLE (0x01)#define Pull_Up_Or_Down_Input (0x08)#define EXTI_GPIOAX (0x00)
#define EXTI_GPIOEX (0x04)//ÀûÓÃö¾ÙÀàÐÍÃèÊö°´¼ü±ê¼ÇÊý¾Ý
typedef enum key_value
{KEY_0_VALUE,KEY_1_VALUE,KEY_2_VALUE,KEY_UP_VALUE,NO_KEY_PRESSED
} XN_key_Value;/*
* @brief ³õʼ»¯µ±Ç°¿ª·¢°å°´¼ü£¬ÅäÖà PA0 PE2 PE3 PE4ËùÐèµÄGPIO¹¤×÷ģʽ
* PA0 ==> KEY_UP or WK_UP ÏÂÀÊäÈë
* PE2 ==> KEY2 ÉÏÀÊäÈë
* PE3 ==> KEY1 ÉÏÀÊäÈë
* PE4 ==> KEY0 ÉÏÀÊäÈë
*/
void Key_Init(void);/*
* @brief »ñÈ¡±»°´Ïµİ´¼ü
* @return ·µ»ØÖµÊDZ»°´Ï°´¼üµÄ±ê¼ÇÊý¾Ý¶ÔÓ¦µÄö¾ÙÀàÐÍ
*/
u8 Key_GetValue(void);/*
* @brief °´¼ü¶ÔÓ¦ÍⲿÖжϿØÖÆÏßʹÄÜÅäÖú¯Êý
*/
void Key_EXTI_Interrupt_Enable(void);#endif
#include "key.h"void Key_Init(void)
{//1.ʱÖÓʹÄÜRCC->APB2ENR |= GPIOA_RCC_APB2_CLOCK_ENABLE | GPIOE_RCC_APB2_CLOCK_ENABLE;//2.GPIO ÅäÖÃ//2.1 PA0 ÏÂÀÊäÈëÅäÖÃ// ÀûÓÃODRµÍµçƽÃ÷È·ÏÂÀÊäÈëGPIOA->CRL &= ~(0x0F);GPIOA->CRL |= Pull_Up_Or_Down_Input;GPIOA->ODR &= ~(0x01);//2.2 PE2 PE3 PE4 ÉÏÀÊäÈëGPIOE->CRL &= ~(0x0FFF << 8);GPIOE->CRL |= (Pull_Up_Or_Down_Input << 8)| (Pull_Up_Or_Down_Input << 12)| (Pull_Up_Or_Down_Input << 16);GPIOE->ODR |= (0x07 << 2);
}void Key_EXTI_Interrupt_Enable(void)
{//1.Éæ¼°µ½ GPIO ¸´ÓòÙ×÷£¬ÐèҪʹÄÜ AFIORCC->APB2ENR |= AFIO_RCC_APB2_CLOCK_ENABLE;//2.AFIO Íⲿ¿ØÖÆÏß EXTI ¶ÔÓ¦ GPIO Òý½ÅÅäÖÃ/*typedef struct{__IO uint32_t EVCR;__IO uint32_t MAPR;__IO uint32_t EXTICR[4];uint32_t RESERVED0;__IO uint32_t MAPR2; } AFIO_TypeDef;AFIO Òý½Å¸´ÓÃÅäÖýṹÌåÀàÐÍ£¬µ×²ã EXTICR1~EXTICR4 ²ÉÓÃÊý×é½øÐйÜÀíÊý×é´æ´¢Êý¾ÝÀàÐÍλ uint32_t ¶ÔÓ¦32λ¼Ä´æÆ÷Èç¹ûÐèÒª¶Ô EXTICR1 ½øÐÐÅäÖã¬Êµ¼Ê²Ù×÷Ϊ AFIO->EXTICR[0]ÐèÒªÅäÖÃ:EXTI0 ==> PA0 ==> EXTICR2 ¼Ä´æÆ÷ ==>¶ÔӦλ´æ´¢ÄÚÈÝ 0000EXTI2 ==> PE2 ==> EXTICR1 ¼Ä´æÆ÷ ==>¶ÔӦλ´æ´¢ÄÚÈÝ 0100EXTI3 ==> PE3 ==> EXTICR1 ¼Ä´æÆ÷ ==>¶ÔӦλ´æ´¢ÄÚÈÝ 0100EXTI4 ==> PE4 ==> EXTICR1 ¼Ä´æÆ÷ ==>¶ÔӦλ´æ´¢ÄÚÈÝ 0100 */AFIO->EXTICR[0] |= (EXTI_GPIOEX << 12) | (EXTI_GPIOEX << 8) | (EXTI_GPIOAX);AFIO->EXTICR[1] |= EXTI_GPIOEX;//3.ÅäÖÃÍⲿÖжÏÏß EXTI ¶ÔÓ¦´¥·¢ÑØ//ÅäÖà EXTI0 ÉÏÉýÑØ´¥·¢EXTI->RTSR |= 0x01;//ÅäÖà EXTI2 3 4 ϽµÑØ´¥·¢EXTI->FTSR |= (0x07 << 2);//4.ÖÐ¶ÏÆÁ±ÎÆ÷ÅäÖÃ//¿ªÆôÖ¸¶¨ EXTI ÍⲿÖжϿØÖÆÏßÖжÏ//11101 ==> 0x1D ¶ÔÓ¦¿ªÆô EXTI0 2 3 4 ÍⲿÖжÏEXTI->IMR |= 0x1D;//5.ÖжÏ×¢²á//ÀûÓà NVIC ¶Ôµ±Ç° EXTI ¶ÔÓ¦µÄ EXTIx_IRQn ½øÐÐ×¢²á//¸æÖª MCU ÄÚºË µ±Ç°³ÌÐòÔËÐйý³ÌÖдæÔÚ EXTI ÍⲿÖжϿØÖÆÏßÖжÏÄÚÈÝ//EXTI0_IRQn ==> IRQn_Type µ±Ç°ÄÚºËÖжϱàºÅ//ÉèÖà EXTIx_IRQn ¶ÔÓ¦ÖжÏÓÅÏȼ¶NVIC_SetPriority(EXTI0_IRQn,1);NVIC_SetPriority(EXTI2_IRQn,1);NVIC_SetPriority(EXTI3_IRQn,1);NVIC_SetPriority(EXTI4_IRQn,1);// NVIC ¿ªÊ¼/ÔÊÐíÖ¸¶¨ IRQ ¶ÔÓ¦Öжϴ¥·¢NVIC_EnableIRQ(EXTI0_IRQn);NVIC_EnableIRQ(EXTI2_IRQn);NVIC_EnableIRQ(EXTI3_IRQn);NVIC_EnableIRQ(EXTI4_IRQn);
}//KEY_UP ==> EXTI0_IRQn ==> EXTI0_IRQHandler
void EXTI0_IRQHandler(void)
{//Èç¹ûµ±Ç° EXTI0 ´æÔÚÖжϴ¥·¢ ÔÚEXTI->PR ¼Ä´æÆ÷ÖжÔӦλ 0 Ó¦¸ÃΪ 1if(EXTI->PR & 0x01){//Ìõ¼þÅжÏͨ¹ý£¬±íʾ¶ÔÓ¦ÖжϴæÔÚ//A¡£Çå³ý//EXTI->PR ÍⲿÖжϿØÖÆÏߣ¬ÖжÏ״̬¼Ä´æÆ÷//¼Ä´æÆ÷¶þ½øÖÆÎ»Ö§³Ö ¡¾Ö»¶Á + W1¡¿//W1 ==¡·Çå³ý²Ù×÷EXTI->PR = 0x01;//B¡£±ØÒªµÄÖжÏÈÎÎñBeep_Ctrl(0);}
}//KEY2 ==> EXTI2_IRQn ==> EXTI2_IRQHandler
void EXTI2_IRQHandler(void)
{//Èç¹ûµ±Ç° EXTI2 ´æÔÚÖжϴ¥·¢ ÔÚEXTI->PR ¼Ä´æÆ÷ÖжÔӦλ 0 Ó¦¸ÃΪ 1if(EXTI->PR & (0x01 << 2)){//Ìõ¼þÅжÏͨ¹ý£¬±íʾ¶ÔÓ¦ÖжϴæÔÚ//A¡£Çå³ý//EXTI->PR ÍⲿÖжϿØÖÆÏߣ¬ÖжÏ״̬¼Ä´æÆ÷//¼Ä´æÆ÷¶þ½øÖÆÎ»Ö§³Ö ¡¾Ö»¶Á + W1¡¿//W1 ==¡·Çå³ý²Ù×÷EXTI->PR = (0x01 << 2);//B¡£±ØÒªµÄÖжÏÈÎÎñBeep_Ctrl(1);}
}//KEY3 ==> EXTI3_IRQn ==> EXTI3_IRQHandler
void EXTI3_IRQHandler(void)
{//Èç¹ûµ±Ç° EXTI3 ´æÔÚÖжϴ¥·¢ ÔÚEXTI->PR ¼Ä´æÆ÷ÖжÔӦλ 0 Ó¦¸ÃΪ 1if(EXTI->PR & (0x01 << 3)){//Ìõ¼þÅжÏͨ¹ý£¬±íʾ¶ÔÓ¦ÖжϴæÔÚ//A¡£Çå³ý//EXTI->PR ÍⲿÖжϿØÖÆÏߣ¬ÖжÏ״̬¼Ä´æÆ÷//¼Ä´æÆ÷¶þ½øÖÆÎ»Ö§³Ö ¡¾Ö»¶Á + W1¡¿//W1 ==¡·Çå³ý²Ù×÷EXTI->PR = (0x01 << 3);//B¡£±ØÒªµÄÖжÏÈÎÎñLed0_Ctrl(1);}
}//KEY4 ==> EXTI4_IRQn ==> EXTI4_IRQHandler
void EXTI4_IRQHandler(void)
{//Èç¹ûµ±Ç° EXTI3 ´æÔÚÖжϴ¥·¢ ÔÚEXTI->PR ¼Ä´æÆ÷ÖжÔӦλ 0 Ó¦¸ÃΪ 1if(EXTI->PR & (0x01 << 4)){//Ìõ¼þÅжÏͨ¹ý£¬±íʾ¶ÔÓ¦ÖжϴæÔÚ//A¡£Çå³ý//EXTI->PR ÍⲿÖжϿØÖÆÏߣ¬ÖжÏ״̬¼Ä´æÆ÷//¼Ä´æÆ÷¶þ½øÖÆÎ»Ö§³Ö ¡¾Ö»¶Á + W1¡¿//W1 ==¡·Çå³ý²Ù×÷EXTI->PR = (0x01 << 4);//B¡£±ØÒªµÄÖжÏÈÎÎñLed0_Ctrl(0);}
}u8 Key_GetValue(void)
{//ͨ¹ý¶ÁÈ¡IDRÅжϰ´¼üÊÇ·ñ°´ÏÂ//KEY0 ==> PE4 // ÅäÖÃIO ÉÏÀÊäÈëģʽ1£¬PE4¶ÔÓ¦IDR·¢ÉúÌø±ä Ϊ0 ˵Ã÷°´¼ü°´ÏÂif(0 == (GPIOE->IDR & (0x01 << 4))){//Ïû¶¶Delay_ms(10);if(0 == (GPIOE->IDR & (0x01 << 4))){//µÈ´ý°´¼üÊÍ·Åwhile(0 == (GPIOE->IDR & (0x01 << 4)));return KEY_0_VALUE;}}//KEY1 ==> PE3 if(0 == (GPIOE->IDR & (0x01 << 3))){//Ïû¶¶Delay_ms(10);if(0 == (GPIOE->IDR & (0x01 << 3))){//µÈ´ý°´¼üÊÍ·Åwhile(0 == (GPIOE->IDR & (0x01 << 3)));return KEY_1_VALUE;}}//KEY2 ==> PE2if(0 == (GPIOE->IDR & (0x01 << 2))){//Ïû¶¶Delay_ms(10);if(0 == (GPIOE->IDR & (0x01 << 2))){//µÈ´ý°´¼üÊÍ·Åwhile(0 == (GPIOE->IDR & (0x01 << 2)));return KEY_2_VALUE;}}//KEY_up or WK_UP ==> PA0 //ÏÂÀ ÊäÈëģʽ ĬÈÏµÍµçÆ½//Èç¹û KEY_UP°´Ï ±äΪ¸ßµçƽ1//Èç¹û KEY_UP δ°´Ï ÔòΪµÍµçƽ0if(GPIOA->IDR & 0x01){//Ïû¶¶Delay_ms(10);if(GPIOA->IDR & 0x01){//µÈ´ý°´¼üÊÍ·Åwhile(GPIOA->IDR & 0x01);return KEY_UP_VALUE;}}//Èç¹ûûÓа´¼ü°´ÏÂreturn NO_KEY_PRESSED;
}
#include <stm32f10x.h>#include "stdio.h"
#include "stdlib.h"
#include "string.h"#include "led.h"
#include "beep.h"
#include "delay.h"
#include "key.h"
#include "usart1.h"int main(void)
{Led_Init();//USART1_Init(9600);Beep_Init();Key_Init();Key_EXTI_Interrupt_Enable();while(1){Led0_Ctrl(1);Delay_ms(1000);/*Led0_Ctrl(1);Led1_Ctrl(1);USART1_SendString("USART1 is RUNNING\r\n");Delay_ms(1000);*/}}