中断(STM32FXX系列单片机)

  1. 中断概述
    1.1 什么是中断?
    1.2 中断的作用
  2. 中断的实现
    2.1 中断的入口
    2.2 中断优先级
  3. ARM单片机中断体系
    3.1 NVIC优先级说明
    3.2 NVIC优先级分配方式
    3.3 STM32fxx中断体系
  4. STM32中断配置
    4.1NVIC相关配置函数
    4.2设置优先级分组函数
    4.3设置中断优先级函数
    4.4NVIC响应中断使能
  5. 内部中断
  6. 外部中断
  7. 软件中断

中断概述

什么是中断?

当开始学习中断时,首先需要了解中断在STM32是什么,他是怎么来的,是用来干什么的,又是怎么干的,这是在学之前心里必须要提出的问题。
那么什么是中断呢?
中断就是程序在正常运行的过程中发生了不正常的事情,必须要暂停一下去处理这个不正常的事情,然后跑回来继续干正常的事情。
对于STM32来说,正常运行的程序是主函数(main),代码是由CPU运行的。CPU在主函数里运行是正常的执行过程,当在这个过程中突然发生了异常事件(中断),CPU必须暂停当前的工作(设下断点),然后跑去能处理这个异常事件的函数中做异常处理(中断服务函数),处理完这个异常事件后(执行完中断服务函数),CPU就会跑回刚才的断点处,继续正常运行下去。然而在程序运行当中,中断发生的时间并不确定、所以一旦发生必须立马处理、处理完异常再执行正常事件。(快进快出)。

中断是用来干什么的?

在这里举个例,例如串口收发数据时,查询式用法在每次应用在主程序当中,都需要经过一个查询是否有数据收发的过程,而在此过程当中,都需要经过一个while()的循环进行查询,而在循环执行的过程当中,就会导致程序卡死在此处,必须等执行完才能进入下一步,而中断便是解决这方面问题,在需要处理的时候让主函数去处理,不需要的时候正常的执行程序。

中断的实现

中断的入口

在主程序的运行过程中,想要执行一些其他的事件,这时候该事件抢占CPU去优先处理它,那要去处理它就必须要有一个入口,而这个入口便是在暂停主程序时产生的断点,通过该断点进入响应的中断服务函数入口,进入中断,而对于中断来说,它不能去干扰主函数中之前做过的事,这边存在了一个将数据保存下来的一个操作,即数据入栈,数据出栈
在这里插入图片描述

中断优先级

既然中断是异常事件,那么难保不会同时出现多个异常事件,那对于程序来说,事件的处理顺序又该怎么办呢,那么优先级这一概念就出来了。
优先级的分类
对于STM32来说,中断优先级分三类,即:占先(抢占)优先级,次级(响应)优先级,自然优先级。
中断优先级数字越小,优先级越高
UCOSII/III: 数字越小优先级越高
FreeRTOS:数字越小优先级越低
中断嵌套
在这里插入图片描述

ARM单片机中断体系

在我们了解的中断的相关含义,我们再来思考一下,中断优先级,中断服务函数这些又是什么来定义划分的呢?
首先,我们知道ARM公司是制作芯片架构的公司,而STM32系列单片机作为ARM架构下的单片机所采用的中断体系都是同一套而中断体系就是管理中断的一种体系,在芯片内核里专门有一个管理中断机制的模块–NVIC控制器
NVIC控制器属于内核级的模块,专门做中断管理,包括中断响应,优先级的设置,相关中断的使能。
在这里插入图片描述

NVIC优先级说明

如果同时到来两个事件、先对比占先、占先一致再对比次级

事件占先次级
A15
B22
C23
事件A与B同时到来、先处理A
事件B与C同时到来、先处理B
事件C正在执行、事件B到来、不能打断C的执行、占先一致
事件C正在执行、事件A到来、直接打断执行A

总结:1.占先优先级相同的形况下,事件之间不能进行打断
   2.占先优先级相同的情况再比较次级优先级
   3.自然优先级不可更改,它是内核当中已经规定好的
   4.中断嵌套时,当高占先优先级的中断执行时,低占先优先级或同占先优先级的中断产生了,低优先级的中断不响应。

NVIC优先级分配方式

知道了优先级的作用,我们需要了解一下NVIC中断体系中,又是如何去分配优先级的呢,既然自然优先级不能更改,那占先和次级优先级又是如何分配的呢。
在内核的中断体系当中,对于占先和次级优先级的分配与设置都是在一个8位寄存器当中实现的,在该寄存器来防止设置的占先和次级优先级,它们一起被放在该寄存器中,那对于寄存器中优先级位数的划分又是怎么规定的呢?
在ARM的中断体系当中,因为一共有8位来防止优先级,那么对于占先和次级优先级的分配有8种
在这里插入图片描述

STM32fxx中断体系

对于ARM的优先级分配方式,其实不是每个芯片都利用8位来设置优先级,对于STM32单片机而言,它所用的是其中四位[7:4],且在我们编写工程当中,每个工程只能有一个分组号,写优先级时第一步必须先分组、才能写入占先和次级的值。
在这里插入图片描述
了解完中断及其体系的理论知识过后,接下来了解STM32单片机相关的中断配置。

STM32中断配置

NVIC相关配置函数

优先级相关的配置函数、只需要配置相关即可
在这里插入图片描述
NVIC_SetPriorityGrouping:设置优先级分组
NVIC_EnableIRQ:使能优先级
NVIC_SetPriority:设置优先级

设置优先级分组函数

函数原型:void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
函数功能:优先级分组
形参:PriorityGroup
返回值:无
参数说明:参数+分组号=7 分组号=7-参数
例如需要配置分组为第五组:NVIC_SetPriorityGrouping(7-5)
注: 对于该分组函数的使用,我查阅网上的一些资料,认为此处函数形参就是分组号,但是所学的内容是上面的理解,如果有疑问的可以观察该函数原型,查找相关资料自行理解。

设置中断优先级函数

函数原型:void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
函数功能:设置优先级(占先+次级)
形参:
IRQn:Interrupt number 中断号
Priority:Priority to set 设置优先级
返回值:无
例如:
NVIC_SetPriorityGrouping(7-5)//第五组
NVIC_SetPriority(USART1_IRQn,5) 占先=1 次级=1

NVIC_SetPriorityGrouping(7-6)//第六组
NVIC_SetPriority(USART2_IRQn,6) //占先=0 次级=6
该函数第二参数优先级的设置也可通过 NVIC_EncodePriority 函数合成占先和次级优先级
即:NVIC_SetPriority(EXTI15_10_IRQn,NVIC_EncodePriority(7-5,1,1))

NVIC响应中断使能

函数原型:void __NVIC_EnableIRQ(IRQn_Type IRQn)
函数功能:使能
形参:USART1_IRQn 中断号(中断号可查询系统中断向量表知道)
返回值:

内部中断

了解完NVIC相关的中断配置后,那么我们就要了解下中断的请求是哪来的,又该如何配置。
对于内部中断而言,我所了解到的是关于芯片片上外设上的中断,而且每一种外设不止一种中断,那么对于程序而言,要使中断能实现,需要打开两个中断开关,一个是片上外设的中断开关,一个是NVIC中断控制器上的。例如:串口USART1上的RXNE接受中断使能。
在这里插入图片描述
在这里插入图片描述
由上可知,串口不知一种中断,那么在对于串口的中断进入的又是同一个中断服务函数,我们要做的就是要在中断服务函数当中要判断是哪种中断
注: 且外设上产生的中断,我们配置中断之前配置好外设的各种寄存器。
内部中断的相关初始化如下:
配置流程:
1.配置好相关片上外设
2.打开片上外设上相关中断的响应
3.设置优先级分组,优先级和NVIC中断响应等
4.片上外设使能
5.中断服务函数
6.进入中断服务函数先判断是哪种中断,在进行相关操作。
7.最后判断程序是否进入中断,可以自定义相关的功能函数,如电灯,串口通信等操作,调用至中断服务函数中,主函数中不需要进行调用,查看功能是否实现,或定义全局变量中断标志,到主函数中通过标志位的变化,查看是否进入中断。

void Uart_Config(u32 brr)
{
	float div;
	u32 div_m,div_f;		//div_m  分频整数部分  div_f分频比小数部分
	NVIC_SetPriorityGrouping(7-5);	//设置中断优先级分组		抢占:2位  响应:2位
	RCC->AHB1ENR |= (1<<0);  			//打开GPIOA时钟
	RCC->APB2ENR |= (1<<4);				//打开USART1时钟
	
	GPIOA->MODER &= ~(0xf<<18);	  //清零PA9/PA10
	GPIOA->MODER |= (0xa<<18);		//GPIOA复用功能模式
	GPIOA->OTYPER &= ~(3<<9);			//输出推挽模式
	
	GPIOA->AFR[1] |= (7<<4);			//PA9  复用功能高位寄存器(UART1 AF7)
	GPIOA->AFR[1] |= (7<<8);			//PA10 复用功能高位寄存器(UART1 AF7)
	
	div = 84000000.0f/16/brr;		//84MHz 16倍过采样
	div_m = (u32)div;										//取出DIV整数部分
	div_f = (div-div_m)*16+0.5f;				//得出来得小数放不进寄存器当中需乘回16倍过采样,并进行四舍五入
	USART1->BRR |= (div_m<<4)|div_f;		//BRR寄存器需放入DIV的值
	//设置接受中断优先级
	NVIC_SetPriority(USART1_IRQn,NVIC_EncodePriority(7-2,1,1));
	//使能NVIC响应接受中断
	NVIC_EnableIRQ(USART1_IRQn);
	
	USART1->CR1 |= (1<<5);		//RXNEIE,USART1中断使能(读取数据寄存器不为空中断)
	USART1->CR1 |= (1<<4);		//IDLE检测到空闲电路,空闲中断使能
	USART1->CR1 |= (1<<2);		//接收器使能
	USART1->CR1 |= (1<<3);		//发送器使能
	USART1->CR1 &= ~(1<<10);		//禁止奇偶校验
	USART1->CR1 &= ~(1<<12);		//字长 8数据位
	USART1->CR1 &= ~(1<<15);		//16倍过采样
	USART1->CR2 &= ~(3<<12);		//1个停止位
	
	USART1->CR1 |= (1<<13);			//USART1使能
}
void USART1_IRQHandler(void)	//USART1所有中断共用一个入口
{
	u32 temp;
	//判断是哪种中断
	if(USART1->SR & (0x1 << 5))//接受中断,数据寄存器不为空
	{
		uart_data.revBuf[uart_data.position] = USART1->DR;//读DR清除标志
		uart_data.position++;									//标志下一次的位置
		
		
	}else if(USART1->SR & (0x01<<4))		//空闲中断
	{
		//清标志		空闲中断标志清零
		temp = USART1->SR;
		temp = USART1->DR;
		temp = temp;		//防止警告未使用
		
		uart_data.revOK = 1;							//数据接受完成
		uart_data.revBuf[uart_data.position] = '\0';		//拼接成字符串
		uart_data.position = 0;						//为下一次接受做准备
	}
	
}

外部中断

Stm32F4XX异常向量表(中文手册)
阴影部分是系统级的,触发必须响应的。
82个外部中断(非阴影部分)
在这里插入图片描述
…………………
在这里插入图片描述
外部中断概述
GPIO发生电平变化产生的中断(外部中断)
23个中断源(16+7)即23个EXTI外部中断线
因为系统中断中的中断向量并没有GPIO口,所以需要通过SYSCFG_EXTICR1寄存器来映射到EXTI中断线上
在这里插入图片描述
EXTI0~15对应IO引脚
在这里插入图片描述
中断线的配置流程:
例如配置PC13的中断线
1.打开SYSCFG时钟(APB2 14位)
2.SYSCFG->EXTICR4 |=(2<<4);

外部中断/事件控制器框架

在这里插入图片描述
两个线:红色和绿色
红色的表示产生中断
绿色的表示产生事件
分析红色:

  1. 输入线:一共23根, GPIO占16根,另外七根是片上外设
  2. 边沿检测器:对应着有两个寄存器分为上升沿触发选择寄存器和下降沿触发选择寄存器。如果输入线产生的边沿和上升沿触发寄存器或者下降沿触发寄存器匹配,就会输出一个有效信号”1”;
  3. 或门:信号输入端有两个:边沿检测器输出端和软件中断事件寄存器,因为我们要做外部电平变化,所以要将软件中断事件寄存器输出”0”,此时或门输出端才受到边沿检测电路的输出端控制。
  4. 与门:信号来源有两个:或门的输出端和中断屏蔽寄存器,如果想要与门输出端输出有效信号,必须中断屏蔽寄存器输出”1”,此时在产生一个有效电平触发。
  5. 挂起寄存器:标志位

分析绿色部分
前面1-3都是与中断一样
6. 与门:信号来源有两个:或门的输出端和事件屏蔽寄存器,如果想要与门输出端输出有效信号,事件屏蔽寄存器输出”1”,此时产生一个有效电平触发。
7. 脉冲信号发生器:与门输出端输出有效信号“1”,就能让脉冲信号发生器产生一个脉冲去触发硬件工作。
中断:产生异常事件,CPU就回去执行中断服务函数
事件:产生异常事件,不需要CPU参与,只能去触发其它硬件工作
相应的外部配置函数如下:
配置流程:
1.打开系统配置控制时钟(挂接再APB2总线上)
2.将相应GPIO口管脚映射至EXTI中断线上
3.配置相应上升沿/下降沿触发选择寄存器
4.打开中断屏蔽寄存器
5.关闭软件中断寄存器
6.设置中断优先级,使能中断响应
7.中断服务函数中通过判断PR寄存器进入中断,再将PR寄存器写1清零。

void EXTI13_Config(void)
{
	RCC->APB2ENR |= (1<<14);					//打开系统配置控制寄存器时钟
	SYSCFG->EXTICR[3] |= (0x2<<4);		//PC13管脚(按键)映射到EXTI外部中断线上	
	EXTI->RTSR &= ~(1<<13);						//禁止上升沿触发选择寄存器
	EXTI->FTSR |= (1<<13);						//打开下降沿触发选择寄存器
	EXTI->IMR |= (1<<13);							//打开中断屏蔽寄存器
	EXTI->EMR &= ~(1<<13);						//关闭事件屏蔽寄存器
	EXTI->SWIER &= ~(1<<13);				  //关闭软件中断寄存器
	
	NVIC_SetPriority(EXTI15_10_IRQn,NVIC_EncodePriority(7-5,1,1));		//设置EXTI13的中断优先级
	
	NVIC_EnableIRQ(EXTI15_10_IRQn);				//使能EXTI13中断响应

}

void EXTI15_10_IRQHandler(void)
{
	if(EXTI->PR & (0x01<<13))					//进入EXTI13中断
	{	
		EXTI->PR |= (0x01 << 13);				//挂起寄存器写1清零
		Exti_Flag = 1;
		while(EXTI->PR & (0x01<<13));		//等待清零完成
	}
}

软件中断

软件中断的使用其实和外部中断(硬件中断)没有很大区别,只是不需要去配置GPIO映射EXTI中断线,边沿检测那些,后续与外部中断无多大差别,在配置软件中断时,先将软件中断寄存器清零,等待需要进入软件中断时再将之置1。

void EXTI6_soft_Config(void)
{
	EXTI->SWIER &= ~(0x1<<6);				//EXTI6软件中断寄存器指令
	EXTI->IMR 	|=  (0x01<<6);			//打开中断屏蔽寄存器
	EXTI->EMR   &= ~(0x01<<6);			//关闭事件屏蔽寄存器

	NVIC_SetPriority(EXTI9_5_IRQn,NVIC_EncodePriority(7-5,0,2));		//设置EXTI6中断优先级
	
	NVIC_EnableIRQ(EXTI9_5_IRQn);		//使能EXTI6中断响应
}
void EXTI9_5_IRQHandler(void)
{
	if(EXTI->PR & (0x01<<6))
	{
		EXTI->PR |= (0x01<<6);
		LED3_TOGGLE;
	}
}  

以上就是我对中断的学习过程,如有疏漏,谢谢指教。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值