2024年C C++最新中断-NVIC与EXTI外设详解(超全面),2024年最新干货

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。

在这里插入图片描述

  • NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定。
  • NVIC_IRQChannelSubPriority:子优先级(响应优先级),具体的值要根据优先级分组来确定 。
  • NVIC_IRQChannelCmd:中断使能(ENABLE)或者(DISABLE)。操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。

配置好 NVIC_InitTypeDef 结构体然后就调用NVIC_Init()函数,由函数将参数写入寄存器

现在来具体来分析一下这个函数加深我们对NVIC寄存器的理解

void NVIC\_Init(NVIC_InitTypeDef\* NVIC_InitStruct)
{
  uint32\_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
  
  /\* Check the parameters \*/
  assert\_param(IS\_FUNCTIONAL\_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
  assert\_param(IS\_NVIC\_PREEMPTION\_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));  
  assert\_param(IS\_NVIC\_SUB\_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
    
  if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
  {
    /\* Compute the Corresponding IRQ Priority --------------------------------\*/    
    tmppriority = (0x700 - ((SCB->AIRCR) & (uint32\_t)0x700))>> 0x08;
    tmppre = (0x4 - tmppriority);
    tmpsub = tmpsub >> tmppriority;

    tmppriority = (uint32\_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
    tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
    tmppriority = tmppriority << 0x04;
        
    NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
    
    /\* Enable the Selected IRQ Channels --------------------------------------\*/
    NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32\_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8\_t)0x1F);
  }
  else
  {
    /\* Disable the Selected IRQ Channels -------------------------------------\*/
    NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32\_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8\_t)0x1F);
  }
}


在这里插入图片描述
3、编写中断服务函数
在启动文件 startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。
在这里插入图片描述

如果一切配置完毕,当中断来临时,CPU会根据相应的中断去中断向量表找到对应的中断函数的地址,然后调用执行中断服务函数。

二.EXTI—外部中断/事件控制器

这里的外部中断是指由外部条件触发例如按键触发(GPIO),对于互联型产品(F107),外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品(我们这里是F103),则有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求
在这里插入图片描述

1.外部中断/事件线路映像

而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。
在这里插入图片描述
EXTI16~19也作为EXTI外设的输入线
在这里插入图片描述
16个中断线的不是每个中断都有独立的中断服务函数,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数
在这里插入图片描述
从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数。
外部中断线10~15分配一个中断向量,共用一个中断服务函数。

对应的中断服务函数,直接去启动文件里面找以防写错。
在这里插入图片描述

2.EXTI功能框图

信号线上打一个斜杠并标注“20”字样,这个表示在控制器内部类似的信号线路有 20 个,这与 EXTI 总共有 20 个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他 19 个线路原理也就知道了

在这里插入图片描述
通过在软件中断/事件寄存器写’1’,也可以通过软件产生中断/事件请求
在这里插入图片描述

脉冲发生器:
在这里插入图片描述
输入一个有效信号 1 时就会产生一个脉冲,如果输入端是无效信号就不会输出脉冲。这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换

中断与事件的区别:

  • 中断:需要CPU参与,需要调用软件的中断服务函数才能完成中断后产生的结果
  • 事件:靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,触发TIM计时,AD转换等,事件不要软件的参与,降低了CPU的负荷,而且硬件速度快于软件速度

详情推荐一篇文章:《中断与事件的区别》

接下来逐一介绍用到的寄存器,进一步理解框图原理:

  • 外部中断配置寄存器

在这里插入图片描述

  • 上升&下降沿触发选择寄存器

在同一中断线上,可以同时设置上升沿和下降沿触发。即任一边沿都可触发中断
在这里插入图片描述
在这里插入图片描述

  • 软件中断事件寄存器(EXTI_SWIER)

在这里插入图片描述

  • 挂起寄存器

在这里插入图片描述
中断或事件屏蔽寄存器
在这里插入图片描述

3.选择中断线与EXTI 初始化结构体详解

  • 选择中断线
    在配置中断线时一定要先使能AFIO外设的时钟,因为配置中断线是用到ADIO外设的寄存器,我们知道配置寄存器必须要有时钟

使能时钟
在这里插入图片描述
配置中断线
在这里插入图片描述

在这里插入图片描述

  • 配置EXTI初始化结构体
    在这里插入图片描述

1) EXTI_Line:EXTI 中断/事件线选择,可选 EXTI0 至 EXTI19。

2) EXTI_Mode:EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。

3) EXTI_Trigger:EXTI 边沿触发事件可选上升沿触发(EXTI_Trigger_Rising)、下降沿触发 ( EXTI_Trigger_Falling) 或者上升沿和下降沿都触发( EXTI_Trigger_Rising_Falling)。

4) EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用(DISABLE)。

最后调用EXTI_Init函数,将结构体配置好的参数写入对应的寄存器,这个比较简单我就不讲了,这里提一下这个结构体中断/事件线的选择并不是配置中断线/事件线,配置线的函数上面已经提及,这里选择线是为了知道中断线在寄存器哪个位置(019)对应中断线(EXTI019),好配置相应的寄存器。

三.外部中断控制实验

实验目的:利用按键产生一个下降沿,让系统产生一个中断,执行中断服务函数,函数将GPIO电平翻转使得灯,按一下亮按一下灭。

实验原理

在这里插入图片描述
EXTI程序框图分析:
在这里插入图片描述

编程要点

1) 初始化用来产生中断的 GPIO;
2) 初始化 EXTI;
3) 配置 NVIC;
4) 编写中断服务函数;

直接上代码咯:

exti.c

#include "exti.h"

static void NVIC\_EXTI\_Config(void)
{
	NVIC_InitTypeDef  NVIC_InitStruct;
	//中断优先级分组这里是组1
	NVIC\_PriorityGroupConfig(NVIC_PriorityGroup_1);
	//选择中断源
	NVIC_InitStruct.NVIC_IRQChannel= EXTI15_10_IRQn;
	//设置抢占式优先级
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =1;
	//设置响应式优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority =1;
	//使能中断源
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	//调用NVIC初始化函数
	NVIC\_Init(&NVIC_InitStruct);
}


void EXTI\_Key\_Config(void)
{		

	 GPIO_InitTypeDef  GPIO_InitStruct;
	 EXTI_InitTypeDef  EXTI_InitStruct;
  
	 RCC\_APB2PeriphClockCmd(EXTI_Key1_GPIO_CLK,ENABLE);
	 //上拉输入
	 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	 GPIO_InitStruct.GPIO_Pin= EXTI_Key1_GPIO_PIN ;
   GPIO\_Init(EXTI_Key1_GPIO_POTR,&GPIO_InitStruct);
	 //配置NVIC中断
	 NVIC\_EXTI\_Config();
   //一定要使能外设AFIO外设的时钟
	 RCC\_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	 //选择信号源
	 GPIO\_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
	 //选择中断线
	 EXTI_InitStruct.EXTI_Line = EXTI_Line15;
	 //选择模式这里选择中断模式
	 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	 //下降沿模式
	 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
	 //使能中断
	 EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	 EXTI\_Init(&EXTI_InitStruct);
	
}


exti.h

#ifndef \_\_EXTI\_H
#define \_\_EXTI\_H


#include "stm32f10x.h"


#define EXTI\_Key1\_GPIO\_PIN GPIO\_Pin\_15
#define EXTI\_Key1\_GPIO\_POTR GPIOA
#define EXTI\_Key1\_GPIO\_CLK RCC\_APB2Periph\_GPIOA

void EXTI\_Key\_Config(void);
#endif /\*\_\_EXTI\_H \*/



main.c

#include "stm32f10x.h"
#include "led.h"
#include "exti.h"

#define SOFT\_DELAY Delay(0x0FFFFF);

void Delay(__IO u32 nCount); 


int main(void)
{	
	/\* LED 端口初始化 \*/
	LED\_GPIO\_Config();	 
  EXTI\_Key\_Config();
	LED\_G(OFF);
	LED\_R(OFF);

	while(1)
	{
	}
}

void Delay(__IO uint32\_t nCount)	 //简单的延时函数
{


![img](https://img-blog.csdnimg.cn/img_convert/cebf4253869c6415fe656cae415be420.png)
![img](https://img-blog.csdnimg.cn/img_convert/db7272bff779756ded4ad4e809de9fcd.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

_IO uint32\_t nCount)	 //简单的延时函数
{


[外链图片转存中...(img-NI4Ou71o-1715560078394)]
[外链图片转存中...(img-z9DYg05G-1715560078395)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值