STM32F4中断(Interrupt)详解

STM32F4中断(Interrupt)详解

一、中断是什么?

中断就是在程序正常运行时,突然发生一件"不正常"的事,CPU就需要暂停正在处理的事立马去处理这件"不正常"的事,处理完后再回到原来的事情

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。 — 百度百科

可以拿平常生活中的例子来说,一场考试,监考老师在监考时会不停的在教室里转圈(CPU正常执行既定程序),突然有个考生举手说想上厕所(意外情况),监考老师立马走过去(暂停正常任务),然后示意他去上厕所,随后监考老师继续巡视(返回正常任务)

二、STM32的中断体系

2.1 STM32的中断分类

  • 内核中断—NVIC嵌套向量控制器控制的片上外设异常
  • 外部中断—由外部中断线接收来自外部的电平异常
  • 软件中断—由外部中断线接收来自软件产生的异常

2.2 STM32中断优先级

为使系统能及时响应并处理发生的所有中断,系统根据引起中断事件的重要性和紧迫程度,硬件将中断源分为若干个级别,称作中断优先级。

在实际系统中,常常遇到多个中断源同时请求中断的情况,这时CPU必须确定首先为哪一个中断源服务,以及服务的次序。解决的方法是中断优先排队,即根据中断源请求的轻重缓急,排好中断处理的优先次序即优先级( Priority ),又称优先权,先响应优先级最高的中断请求。另外,当CPU正在处理某一中断时,要能响应另一个优先级更高的中断请求,而屏蔽掉同级或较低级的中断请求,形成中断嵌套 —百度百科

  • ARM公司规定中断优先级分为: 抢占/占先优先级 响应/次级优先级 自然优先级

  • ARM公司用8位二进制数来表示这三个优先级

  • ST(意法半导体)公司只用了4位二进制数来表示这三个优先级,故最多可以配置16个优先级

  • 优先级**序号越小,优先级越高**

  • 每个project只能有一个优先级分组

在查找优先级分组时,要去<内核手册>里查看,不同版本分组会有不同

分组号抢占优先级所占位响应优先级所占位抢占优先级范围响应优先级范围
3(0b011)400-15None
4(0b100)310-70-1
5(0b101)220-30-3
6(0b110)130-10-7
7(0b111)04None0-15
2.2.1 抢占优先级

不管中断产生的先后顺序,只比较抢占优先级的高低,优先级高的立即执行,例如:如果一个中断执行过程中,又有一个更高抢占优先级中断产生,CPU则会暂停当前中断跑去执行更高抢占优先级中断**,实现中断嵌套**

2.2.2 响应优先级

如果两个相同抢占优先级的中断产生,那么两个中断会按照响应优先级顺序进行排队,优先级高的先执行

2.2.3 自然优先级

自然优先级是事先已经规定好的优先级序列当抢占优先级、响应优先级都相同时,先执行自然优先级高的中断

2.3 STM32中断实现方法

2.3.1 中断执行过程

在这里插入图片描述

2.3.2 中断实现

STM32中断由嵌套向量中断控制器(NVIC)控制,它与处理器接口紧密配合,所有中断信号都要经过NVIC来进行控制,包括外部中断和软件中断

在这里插入图片描述

2.4 中断的具体应用场景

  • 时间片(非阻塞程序)的编写
  • 串口接受数据
  • 按键的触发等等

三、STM32中断的配置

3.1 NVIC配置方法:

3.1.1 NVIC配置函数

(1)NVIC分组函数:NVIC_SetPriorityGrouping

参数:7-分组号

返回:void

(2)NVIC优先级编码:NVIC_EncodePriority

参数1:分组号(同分组函数参数)

参数2:抢占优先级

参数3:响应优先级

返回:u32的一个编码数

功能:这个函数负责**把分组方式,抢占优先级,响应优先级的数值编码成一个32位的数字返回,方便在优先级设置函数里作为参数使用**(所以需要定义一个临时变量去存储该函数的返回值)

(3)NVIC优先级设置:NVIC_SetPriority

参数1:中断源编号(已经在stm32f4xx.h里定义成枚举类型)

参数2:中断优先级编码,使用的就是NVIC_EncodePriority函数的返回值

返回:void

(4)NVIC中断使能:NVIC_EnableIRQ

参数:中断源编号(已经在stm32f4xx.h里定义成枚举类型)

返回:void

3.1.2 初始化NVIC控制器的一般步骤:
  1. 找设置优先级组别函数:在<core_cm4.h>里寻找
  2. 找配置抢占优先级、响应优先级的函数:在<core_cm4.h>里寻找(配置抢占/响应优先级时,可调用NVIC_EncodePriority()函数对参数进行组合)
  3. 找NVIC中断使能函数:在<core_cm4.h>里寻找

例:初始化USART1的NVIC控制器为第5组,抢占优先级为1,自然优先级为3

usart1_Init();//事先要写好并调用usart1的初始化函数
void nvic_Init(void)
{
    u32 temp;
    
    NVIC_SetPriorityGrouping(7-5);//第五组写成7-5更具易读性
    temp = NVIC_EncodePriority(7-5,1,3);//temp存储返回值
    NVIC_SetPriority(USART1_IRQn,temp);//去文件里找中断源编号
    NVIC_EnableIRQ(USART1_IRQn);//NVIC中断使能
}

3.2 开启中断触发条件

3.2.1 片上外设(串口,定时器等)—内部中断

只需开启其相应的中断位即可

例:串口接收到数据时产生中断

USART1->CR1 |= (1<<5);//接收数据中断使能
3.2.2 片外外设(按键,指纹模块等)—外部中断

则还需要配置外部中断(EXTI寄存器和SYSCFG寄存器)

3.3 外部中断

如同配置内部中断一样,但像按键,指纹模块等外部设备,系统没有固定的中断源供使用,这时候就需要配置外部中断控制器

3.3.1 外部中断配置流程

(1)时钟使能SYSCFG寄存器—APB2–14位

(2)配置SYSCFG外部中断配置寄存器SYSCFG_EXTICR[0:3]

引脚0~3:SYSCFG_EXTICR[0]

引脚4~7:SYSCFG_EXTICR[1]

以此类推

(3)配置EXTI寄存器

  1. 中断屏蔽寄存器(EXTI_IMR)置1
  2. 事件屏蔽寄存器(EXTI_EMR)置0
  3. 上升/下降沿触发选择寄存器(EXTI_RTSR/EXTI_FTSR):根据需要配置
  4. 软件中断事件寄存器(EXTI_SWIER)置0

(4)配置NVIC控制器

(5)按内部中断的方法配置NVIC控制器,但中断源是外部中断线EXTI

例:配置GPIOC端口13引脚的Key2下降沿触发中断

中断为第五组,抢占优先级为1,响应优先级为2

Key2_Init();//事先要写好并调用Key2的初始化函数
void exti15_10_Init(void)
{
    u32 priority;
    
    //时钟使能SYSCFG寄存器—APB2–14位
    RCC->APB2ENR |= (0x01<<14);
    //配置SYSCFG外部中断配置寄存器SYSCFG_EXTICR[0:3]
    SYSCFG->EXTICR[3] |= (0x02<<4);//PC13
    //EXTI寄存器配置
    EXTI->IMR	|=	(0x01<<X10_15);//开放来自13线的中断请求
	EXTI->EMR	&= ~(0x01<<X10_15);//屏蔽来自13线的事件请求
	EXTI->RTSR	&= ~(0x01<<X10_15);//屏蔽13线上升沿触发
	EXTI->FTSR	|=	(0x01<<X10_15);//允许13线下降沿触发
	EXTI->SWIER	&= ~(0x01<<X10_15);//使能模块级中断
    //NVIC控制器配置
    NVIC_SetPriorityGrouping(7-5);//第五组
    priority = NVIC_EncodePriority(7-5,1,2);//抢占1,响应2
    NVIC_SetPriority(EXTI15_10_IRQn,priority);//由于没有EXTI13,则使用EXTI15_10_IRQn中断号
    NVIC_EnableIRQ(EXTI15_10_IRQn);//使能中断线
}

3.4 软件中断

软件中断即利用产生软件中断事件来发起中断,例如当需要软件中断时,设置软EXTI的挂起寄存器为1,则可以直接进入软件中断服务函数

3.4.1 配置EXTI寄存器
  1. 上升/下降沿触发器置0
  2. 软件中断事件寄存器置1
  3. 事件屏蔽寄存器置0
  4. 中断屏蔽寄存器置1
3.4.2 配置NVIC控制器

按内部中断的方法配置NVIC控制器,但中断源是外部中断线EXTI

且配置软件中断时,EXTI线可以任意选,但尽量避开有其他中断的线

例:配置EXTI7线能够产生软件中断

中断为第五组,抢占优先级为0,响应优先级为2

void software_Init(Void)
{
    u32 temp;//用于配置NVIC优先级
    
    //上升/下降沿触发器置0
    EXTI->RTSR &= ~(0x01<<7);
    EXTI->FTSR &= ~(0x01<<7);
    //软件中断事件寄存器置1
    EXTI->SWIER |= (0x01<<7);
    //事件屏蔽寄存器置0
    EXTI->EMR &= ~(0x01<<7);
    //中断屏蔽寄存器置1
    EXTI->IMR |= (0x01<<7);
    
    NVIC_SetPriorityGrouping(7-5);//分组设置
    temp = NVIC_EncodePriority(7-5,0,2);//得到优先级序列
    NVIC_SetPriority(EXTI9_5_IRQn,temp);//设置优先级
    NVIC_EnableIRQ(EXTI9_5_IRQn);//使能中断线
}

3.5 中断服务函数

在中断初始化后,就可以使用中断服务函数,即产生中断时立即跳入中断服务函数执行相应的任务

3.5.1 片上外设的中断服务函数

STM32F4的片上外设一般都有自己的中断标志位,例如USART1的CR1寄存器中就有相应的中断标志位,该位一般都由硬件置一,比如读取到数据寄存器不为空时,USART_CR1中的RXNEIE就会置一并产生中断

  1. 在<startup_stm32f40_41xxx.s>文件里找到相应的中断服务函数名
  2. 判断是由哪一个中断标志位进入的中断并**清除标志位**
  3. 编写该中断所需要的功能

注意:中断服务函数一定要去复制粘贴,不要自己写

例:USART1的中断服务函数,由该中断服务函数得到主机发送来的一个字符串并存储

char usart1_str[255];//全局变量,用于存储字符串
void USART1_IRQHandler(void)
{
    static u8 i = 0;
    u8 ch;
    
    if(USART1->SR & (0x01<<5))//若是由读取数据寄存器产生的中断
    {
        usart1_str[i++] = USART1->DR;//读取字符,在读取的时候顺便清除了标志位
        if(i == 255)
        {
            i = 0;
        }
    }
    if(USART1->SR & (0x01<<4))//若是由于检测到空闲线路产生的中断
    {
        ch = USART1->DR;//清空标志位
        usart1_str[i] = '\0';//使之成为字符串
        i = 0;
    }
}
3.5.2 片外外设中断服务函数

由于片外外设(按键,指纹模块等)没有中断标志位,故采用边沿检测的方法来触发中断

  1. 在<startup_stm32f40_41xxx.s>文件里找到相应**中断线**的中断服务函数名
  2. 判断是由哪条中断线产生的中断并**清除标志位**
  3. 编写该中断所需要的功能

例:按键按下触发外部中断线13产生中断并使LED3切换状态

void EXTI15_10_IRQHandler(void)
{
    if(EXTI->PR & (0x01<<13))//如果是由线13(Key2---PC13)产生的中断
    {
        EXTI->PR |= (0x01<<13);//写1清除
        LED_TUN(3);
    }
}
3.5.3 软件中断服务函数

软件中断由于不是由硬件触发,所以软件中断的触发条件是在需要时调用,跟普通函数很像,但可以设置高优先级以保护软件中断服务函数不被其他中断打断

  1. 在需要软件中断的地方设置EXTI_PR寄存器为1以触发软件中断
  2. 在<startup_stm32f40_41xxx.s>文件里找到相应**中断线**的中断服务函数名
  3. 判断是由哪条中断线产生的中断并**清除标志位**
  4. 编写该中断所需要的功能

例:在USART1中断服务函数里,当字符接收完后,调用软件中断

该软件中断功能:比较USART1得到的字符串与目标字符串是否一致,一致则做相应操作

/**************************USART1中断服务函数里***************************************/
char usart1_str[255];//全局变量,用于存储字符串
void USART1_IRQHandler(void)
{
    static u8 i = 0;
    u8 ch;
    
    if(USART1->SR & (0x01<<5))//若是由读取数据寄存器产生的中断
    {
        usart1_str[i++] = USART1->DR;//读取字符,在读取的时候顺便清除了标志位
        if(i == 255)
        {
            i = 0;
        }
    }
    if(USART1->SR & (0x01<<4))//若是由于检测到空闲线路产生的中断
    {
        ch = USART1->DR;//清空标志位
        usart1_str[i] = '\0';//使之成为字符串
        i = 0;
        EXTI->PR |= (0x01<<7);//调用中断线7的软件中断
    }
}
/*********************************************************************************/

//软件中断服务函数
void EXTI9_5_IRQHandler(void)
{
    if(EXTI->PR & (0x01<<7))//若是由线7产生的软件中断
    {
        EXTI->PR |= (0x01<<7);//写1清除标志位
        if(EXTI->PR & (1<<7))
        {
            //清除标志位
            EXTI->PR |= (1<<7);
            if(strcmp(usart1_str,"open") == 0)
            {
                LED_ON(3);
                LED_ON(4);
                motor_drive(OPEN);
            }else if(strcmp(usart1_str,"lock") == 0)
            {
                LED_OFF(3);
                LED_OFF(4);
                motor_drive(LOCK);
            }else
            {
                printf("请重新输入!\r\n");
            }
        }
    }
}
  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32F4系列微控制器有多个外部中断线路,每个中断线路可以连接到不同的外部设备,如按键、传感器等。以下是配置STM32F4外部中断的基本步骤: 1. 选择要使用的外部中断线路,并将其连接到外部设备上。例如,如果要使用PA0引脚作为外部中断输入,则需要将外部设备连接到PA0引脚上。 2. 配置GPIO引脚为外部中断输入模式。可以使用STM32CubeMX软件或手动编写代码来完成此操作。例如,如果要将PA0引脚配置为外部中断输入模式,则需要执行以下代码: ``` GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟 GPIO_InitStruct.Pin = GPIO_PIN_0; //选择PA0引脚 GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; //配置为上升沿触发中断 GPIO_InitStruct.Pull = GPIO_PULLDOWN; //选择上拉或下拉电阻 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //初始化GPIOA ``` 3. 配置外部中断触发方式。可以选择上升沿、下降沿、双边沿等多种触发方式。例如,如果要将外部中断配置为上升沿触发,则需要执行以下代码: ``` EXTI_InitTypeDef EXTI_InitStruct; __HAL_RCC_SYSCFG_CLK_ENABLE(); //使能SYSCFG时钟 EXTI_InitStruct.Line = EXTI_LINE_0; //选择外部中断线路0,对应PA0引脚 EXTI_InitStruct.Mode = EXTI_MODE_INTERRUPT; //配置为中断模式 EXTI_InitStruct.Trigger = EXTI_TRIGGER_RISING; //配置为上升沿触发中断 HAL_EXTI_Init(&EXTI_InitStruct); //初始化外部中断 ``` 4. 编写中断服务程序来处理外部中断事件。中断服务程序应该包含在STM32F4的启动文件中,以便在外部中断发生时自动调用。例如,如果要编写一个中断服务程序来处理外部中断事件,则需要执行以下代码: ``` void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //处理中断事件 //执行其他处理操作 } ``` 以上是配置STM32F4外部中断的基本步骤。具体实现方式可能因不同的外部设备和应用场景而有所不同。因此,建议参考STM32F4系列微控制器的官方文档和示例代码进行详细学习和实践。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值