STM32F103系列单片机中断总结:概念和使用方法


前言

随富随贫且欢乐,不开口笑是痴人。-----白居易

中断是学习单片机必须掌握的内容!正因为有了中断,单片机即使不搭载操作系统运行大型程序也一样的纵享丝滑。正因为有了中断,我们才能快速便捷的编写出有多线程那味儿的程序。STM32F103系列单片机中断非常强大,每个外设都可以产生中断,F103 在内核水平上搭载了一个中断响应系统, 支持为数众多的系统中断和外部中断。本文对其稍微总结一下下,包括其相关寄存器及其相应的库函数设置方法(了解即可),中断的设置步骤(掌握),最后给出一个模板给套。希望大家争取看一遍便会完全了解并使用。


一、中断の概念

1.术语解释

单片机中断:指单片机处理程序运行中出现的“紧急事件”的整个过程,程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,单片机立即中止现行程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行,这整个过程称为程序中断。
中断可分为可屏蔽中断和不可屏蔽中断两类:可由程序控制其屏蔽的中断称为可屏蔽中断,屏蔽时,单片机将不接受中断(即不进入中断服务程序),反之,不能由程序控制其屏蔽,单片机一定要立即处理的中断称为不可屏蔽中断。

个人理解:
众所周知,学习过程中使用类比学习法,通过对比新知识和旧知识的区别和联系,可以加深印象。
在这里插入图片描述
委托式事件模型见下图:
在这里插入图片描述
委托式事件处理:我们通过为组件注册特定的事件监听器,当发生事件后,组件就可以由自己动态解决发生的事件,然后把大量的组件有机的组合起来,从而提高响应速度,同时解放生产力。
中断可以类比委托式事件处理:为单片机的特定组件指定中断的触发条件,编写中断服务函数处理发生的中断,这样编写单片机程序时即可站得高,望得远,把握主流程,从细枝末节中抽身。

2.F103系列单片机的中断及其管理器

其支持的系统中断有 8 个 ,外部中断有 60个,除了个别中断的优先级被定死外,其它中断的优先级都是可编程的。想知道所有的的系统中断和外部中断吗?那么请在标准库文件 stm32f10x.h 这个头文件查询IRQn_Type 这个枚举定义,里面包含了 F103 系列全部的中断声明。可以清楚地看到内核使用的,F103系列通用的中断号,再往下面便是不同容量的子产品特有的中断。

NVIC:嵌套向量中断控制器,控制着整个芯片中断相关的功能,是Cortex-M3内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3内核里面的 NVIC进行裁剪,把不需要的部分去掉,即 STM32F103系列的 NVIC 是阉割过得。

二、中断相关的寄存器及其设置方法

参看库文件 core_cm3.h ,找到NVIC_Type结构体,查看其封装的寄存器:

 typedef struct {
 __IO uint32_t ISER[8]; // 中断使能寄存器
 uint32_t RESERVED0[24];
 __IO uint32_t ICER[8]; // 中断清除寄存器
 uint32_t RSERVED1[24];
 __IO uint32_t ISPR[8]; // 中断使能悬起寄存器
 uint32_t RESERVED2[24];
 __IO uint32_t ICPR[8]; // 中断清除悬起寄存器
 uint32_t RESERVED3[24];
 __IO uint32_t IABR[8]; // 中断有效位寄存器
 uint32_t RESERVED4[56];
 __IO uint8_t IP[240]; // 中断优先级寄存器(8Bit wide)
 uint32_t RESERVED5[644];
 __O uint32_t STIR; // 软件触发中断寄存器
 } NVIC_Type;

在配置中断的时候我们一般只用 ISER,ICER 和 IP 这三个寄存器, ISER 用来使能中断,ICER 用来失能中断,IP 用来设置中断优先级。

翻到固件库文件 core_cm3.h 的最后,看到了寄存器的设置方法,如下:
void NVIC_EnableIRQ(IRQn_Type IRQn) 使能中断
void NVIC_DisableIRQ(IRQn_Type IRQn) 失能中断
void NVIC_SetPendingIRQ(IRQn_Type IRQn) 设置中断悬起位
void NVIC_ClearPendingIRQ(IRQn_Type IRQn) 清除中断悬起位
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) 获取悬起中断编号
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) 设置中断优先级
uint32_t NVIC_GetPriority(IRQn_Type IRQn) 获取中断优先级
void NVIC_SystemReset(void) 系统复位

中断优先级:
当同时有两个及以上的中断时,就必须设置中断优先级了,在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx, 用来配置外部中断的优先级,其宽度为 8bit,每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数减少,在F103中,只使用了高 4bit。
分成抢占优先级(主优先级)和子优先级。而且为了管理这用于表达优先级的这高4bit,于是又封装了一个优先级组,通过选择不同的组可以得到不同的优先级数目的比例,参看库文件misc.h:

  ============================================================================================================================
    NVIC_PriorityGroup   | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority  | Description
  ============================================================================================================================
   NVIC_PriorityGroup_0  |                0                  |            0-15             |   0 bits for pre-emption priority
                         |                                   |                             |   4 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------
   NVIC_PriorityGroup_1  |                0-1                |            0-7              |   1 bits for pre-emption priority
                         |                                   |                             |   3 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_2  |                0-3                |            0-3              |   2 bits for pre-emption priority
                         |                                   |                             |   2 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_3  |                0-7                |            0-1              |   3 bits for pre-emption priority
                         |                                   |                             |   1 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_4  |                0-15               |            0                |   4 bits for pre-emption priority
                         |                                   |                             |   0 bits for subpriority                       
  ============================================================================================================================

显然,大多数时候,
人们都喜欢选择折中的,即 NVIC_PriorityGroup_2。

当有多个中断同时请求(注意是还没执行中断服务函数)时,抢占优先级(主优先级)高的就会先得到执行,如果抢占优先级(主优先级)相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,就越先响应。

中断嵌套:
假定在选定了优先级组后,在组里添加了两个中断,当单片机正在执行中断1的服务函数过程中,突然,又来一个中断2的请求,那么:
若中断2的主优先级比中断1的主优先级小,那么单片机暂停中断1的服务函数,进入中断2的服务函数,执行完之后,才返回中断1的服务函数。形成了所谓的嵌套。
若中断2的主优先级比中断1的主优先级大,那么就必须等待中断1的服务函数执行完毕才能响应中断2。不能形成嵌套。

三、配置中断的步骤及其库函数

好了,如果前面不太彻底明白的话也没关系,搬砖的只需要记住在哪里搬,搬去哪里,怎么搬就行了,至于这块砖是怎么生产的就不需要了解了。进入正题
在这里插入图片描述
在配置每个中断的时候一般有4个步骤:
1、结合当前使用的外设,确定自己需要的中断源(单片机外设)。比如单片机用于通信的固件外设(I2C,SPI,USART等)有发送完成,接收完成中断。GPIO引脚也可设置上升沿,下降沿中断。定时器的计时中断。。。所有的中断类型参看stm32f10x.h 这个头文件IRQn_Type 这个枚举定义。

2、 初始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。参看固件库文件 misc.h:

typedef struct
{
  uint8_t NVIC_IRQChannel;  //用来设置中断源                   
  uint8_t NVIC_IRQChannelPreemptionPriority;  //抢占优先级(主优先级)
  uint8_t NVIC_IRQChannelSubPriority;   //子优先级     
  FunctionalState NVIC_IRQChannelCmd;  //中断使能( ENABLE)或者失能( DISABLE)      
} NVIC_InitTypeDef;

此结构体配置完后,使用库函数
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);配置NVIC。

3、 编写中断服务函数,在固件库启动文件 startup_stm32f10x_md.s 中预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表:

WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler

实际的中断服务函数都需要我们重新编写, 放在 stm32f10x_it.c 这个库文件中,可以看到那里面已经有了一些写好的系统中断的服务函数,我们只需跟着在文件末尾继续写就行了。中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,实现不了中断。 注意:中断服务函数里不应该处理耗时的操作!

4.使能外设的某个中断,具体由此外设的相关中断使能函数控制,在此外设的固件库文件找到函数原型:
void xxx_ITConfig(xxx_TypeDef* xxx, uint16_t xxx_IT, FunctionalState NewState);

四、中断配置的模板

这个中断配置函数放在自己的外设初始化相关的文件里,在外设引脚初始化后调用即可。

/**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC为优先级组1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  /* 配置中断源:按键1 */
  NVIC_InitStructure.NVIC_IRQChannel =(中断源);
  /* 配置抢占优先级 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

接下来这个中断服务函数需要放在stm32f10x_it.c里

/**
  * @brief  中断服务函数
  * @param  无
  * @retval 无
  */
void (需要的外设名)_IRQHandler(void)
{
  //确保是否产生了中断
	if((需要的外设名)_GetITStatus(中断标志位) != RESET) 
	{
		//TODO:
		/*别放置耗时间的操作!!!!!!!!*/
    //清除中断标志位
		(需要的外设名)_ClearITPendingBit(中断标志位);     
	}  
}

最后这条语句在外设初始完成后调用:(在外设的固件库找到具体的函数原型)

xxx_ITConfig( xxx, xxx, ENABLE);

总结

本文包括中断相关寄存器及其相应的库函数设置方法(了解即可),中断的设置步骤(掌握)。

最后问大家一个问题:什么条件下,孤立小系统里的时间可以倒流?
在这里插入图片描述

  • 46
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖工人_0803号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值