STM32
直播中

康辅佑

8年用户 1000经验值
擅长:控制/MCU
私信 关注
[问答]

怎么实现STM32F4按键中断?

STM32F4外部中断是什么?
怎么实现STM32F4按键中断?

回帖(1)

李雨晨

2021-11-18 14:34:35
中断概述

       CPU执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程称为中断。





EXTI

EXTI:外部中断/事件控制器
        外部中断/事件控制器包含多达 23 个用于产生事件/中断请求的边沿检测器。每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。每根输入线还可单独屏蔽。挂起寄存器用于保持中断请求的状态线。
外部中断控制框图:





外部中断

STM32F4的每个IO都可以作为外部中断输入
STM32F4的中断控制器支持23个外部中断/事件请求:



  • EXTI线0~15:对应外部IO口的输入中断。
  • EXTI线16:连接到PVD输出。
  • EXTI线17:连接到RTC闹钟事件。
  • EXTI线18:连接到USB OTG FS唤醒事件。
  • EXTI线19:连接到以太网唤醒事件。
  • EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。
  • EXTI线21:连接到RTC入侵和时间戳事件。
  • EXTI线22:连接到RTC唤醒事件。

触发机制

下降沿:数字威廉希尔官方网站 中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间(时刻)叫作下降沿。
上升沿:数字威廉希尔官方网站 中,数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间(时刻)叫作上升沿。
外部中断~中断线的映射






GPIOx.0映射到EXTI0
GPIOx.1映射到EXTI1

GPIOx.15映射到EXTI15
注意:



  • 一条中断线的在同一时间只能被一个IO口映射。
  • 外部中断线5~9分配一个中断向量,共用一个服务函数,外部中断线10~15分配一个中断向量,共用一个中断服务函数。

中断服务函数列表:

EXTI0_IRQHandler           
EXTI1_IRQHandler
EXTI2_IRQHandler           
EXTI3_IRQHandler           
EXTI4_IRQHandler           
EXTI9_5_IRQHandler         
EXTI15_10_IRQHandler   
  NVIC

NVIC:嵌套向量中断控制器 (NVIC)
NVIC 特性

嵌套向量中断控制器 NVIC 包含以下特性:



  • STM32F405xx/07xx 和 STM32F415xx/17xx 具有 82 个可屏蔽中断通道, STM32F42xxx
  • 和 STM32F43xxx 具有多达 86 个可屏蔽中断通道(不包括 Cortex™-M4F 的 16 根中断线)
  • 16 个可编程优先级(使用了 4 位中断优先级)
  • 低延迟异常和中断处理
  • 电源管理控制
  • 系统控制寄存器的实现

嵌套向量中断控制器 (NVIC) 和处理器内核接口紧密配合,可以实现低延迟的中断处理和晚到中断的高效处理。

注意:
NVIC有两个重要功能:通道的设置及优先级设置
中断优先级:数字越小,优先级别越高。
中断管理

首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。分组配置是在寄存器SCB->AIRCR中配置(注意:数字越小,优先级越高):



  • 第0组:所有4位用于指定响应优先级
  • 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
  • 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
  • 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
  • 第4组:所有4位用于指定抢占式优先级












抢占优先级 & 响应优先级区别:

高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
NVIC中断优先级分组

中断优先级分组函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//例子:设置系统的抢占优先级分组为组2NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  特别说明:
        一般情况下,系统代码执行过程中,只设置一次中断优先级分组(只执行一次设置),比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。

按键中断实现

中断实现要添加库文件:stm32f4xx_exti.c和stm32f4xx_syscfg.c
实现步骤

1、NVIC分组(一个工程当中只能配置一次分组)
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); 2、使能SYSCFG时钟
void RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 3、初始化IO口为输入
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) 4、 设置IO口与中断线的映射关系
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex) 5、初始化线上中断,设置触发条件等
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct) 6、 配置中断分组(NVIC),并使能中断
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 7、编写中断服务函数。(中断服务函数在startup_stm32f40_41xxx.S当中)
void EXTIx_IRQHandler();

按键中断实现例程
exti.c
#include "exti.h"

//粗延时
void  delayms(int n)
{
        int i, j;
       
        for(i=0; i                 for(j=0; j<40000; j++);

}

//PA0引脚按键KEY1中断初始化
void Exti_PA0_KEY1_Init(void)
{
       
//        GPIO_InitTypeDef  GPIO_InitStruct;
        EXTI_InitTypeDef  EXTI_InitStruct;
        NVIC_InitTypeDef  NVIC_InitStruct;
       
        //使能GPIO A组时钟,
//        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
       
//        GPIO_InitStruct.GPIO_Pin        = GPIO_Pin_0;                //引脚0
//        GPIO_InitStruct.GPIO_Mode        = GPIO_Mode_IN;                //输入模式
//        GPIO_InitStruct.GPIO_PuPd        = GPIO_PuPd_UP;     //上拉
//        GPIO_Init(GPIOA, &GPIO_InitStruct);       
       
        //使能SYSCFG时钟:
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
       

       
        //设置IO口与中断线的映射关系。确定什么引脚对应哪个中断线 PA0 -- EXTI0 (可变)
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
       
       
        EXTI_InitStruct.EXTI_Line        = EXTI_Line0;                 //中断线0 (可变)
        EXTI_InitStruct.EXTI_Mode        = EXTI_Mode_Interrupt;        //中断模式
        EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling;     //下降沿触发
        EXTI_InitStruct.EXTI_LineCmd= ENABLE;                   //中断线使能
        //初始化线上中断,设置触发条件等。
    EXTI_Init(&EXTI_InitStruct);
       
        NVIC_InitStruct.NVIC_IRQChannel                                = EXTI0_IRQn;          //NVIC通道,在stm32f4xx.h可查看通道 (可变)
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority        = 0x01;                //抢占优先级
        NVIC_InitStruct.NVIC_IRQChannelSubPriority                = 0x01;                //响应优先级
        NVIC_InitStruct.NVIC_IRQChannelCmd                        = ENABLE;        //使能
        //配置中断分组(NVIC),并使能中断。
    NVIC_Init(&NVIC_InitStruct);       
       
}




//编写中断服务函数。这个函数不需要程序员在主函数调用,满足条件CPU自行调用的函数
void EXTI0_IRQHandler(void)
{
        //判断中断标志是否为1
        if(EXTI_GetITStatus(EXTI_Line0) == SET)
        {
                delayms(15);  //延时消抖
                //判断是否按下
                if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
                {
                        //变更灯状态
                        GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
                }
        }
        //清空标志位
        EXTI_ClearITPendingBit(EXTI_Line0);

}
main.c
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"

int main(void)
{
        //NVIC分组(一个工程当中只能配置一次分组)抢占优先级2位,值范围:0~3;响应优先级2位,值范围:0~3;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        //LED灯初始化
        Led_Init();
        //按键初始化
        Key_Init();
        //中断
        Exti_PA0_KEY1_Init();

        while(1)
        {
                GPIO_ToggleBits(GPIOE, GPIO_Pin_14);
                delayms(1000);
        }
        return 0;
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分