STM32 中断

一、嵌套向量中断控制器

简介

NVIC是嵌套向量中断控制器,控制整一个芯片的中断功能,它是属于内核的一个外设。STM32中的NVIC是对内核的NVIC进行裁剪的,相当于NVIC中的一个子集。

NVIC寄存器

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

二、中断编程

配置中断优先级分组

在中断编程之前,先配置中断优先级的分组,设置抢占优先级和子优先级,抢占优先级高于子优先级,数字小的优先级高,当抢占优先级一样时,在比较子优先级,当子优先级相同时,比较硬件中断向量表。在NVIC中,一共有五个分组,分别是:

优先级分组主优先级子优先级描述
NVIC_PriorityGroup_000-15主-0bit 子-4bit
NVIC_PriorityGroup_10-10-7主-1bit 子-3bit
NVIC_PriorityGroup_20-30-3主-2bit 子-2bit
NVIC_PriorityGroup_30-70-1主-3bit 子-1bit
NVIC_PriorityGroup_40-150主-4bit 子-0bit

固件库函数:

/**
  * @brief  Configures the priority grouping: pre-emption priority and subpriority.
	*   (配置优先级分组:抢占优先级和子优先级)
  * @param  NVIC_PriorityGroup: specifies the priority grouping bits length.
  *    (参数:选择特定的优先级分组)	
  *   This parameter can be one of the following values:
	*    (这个参数可以是以下值中的一个)
  *     @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
  *                                4 bits for subpriority
	*     (0个抢占优先级,4个子优先级)
  *     @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
  *                                3 bits for subpriority
  *     @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
  *                                2 bits for subpriority
  *     @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
  *                                1 bits for subpriority
  *     @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
  *                                0 bits for subpriority
  * @retval None
  */
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
 // 设置优先级分组,NVIC的分组主要是内核外设的寄存器SCB控制
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

NVIC初始化结构体

typedef struct
{
  uint8_t NVIC_IRQChannel;  // 指定中断源,不同的中断中断源不一样,这个参数的值参考stm32f10x.h                                    头文件里面的IRQn_Type结构体的定义,里面包含了完整的中断源
                              
  uint8_t NVIC_IRQChannelPreemptionPriority;  //设置抢占优先级,这个参数具体的值由优先级分组来确定                                          
  uint8_t NVIC_IRQChannelSubPriority;  //设置抢占优先级,这个参数具体的值由优先级分组来确定                                     
  FunctionalState NVIC_IRQChannelCmd;   //设置中断使能或者失能                                         
                                                  
} NVIC_InitTypeDef;

编写中断服务函数

每个中断都有对应的中断服务函数,中断服务函数在启动文件 startup_stm32f10x_hd.s预先已经写好,目的是为了初始化中断向量表。因此,每一个中断函数的函数名必须与启动文件预先写好的函数名一致,不一致将会导致系统在中断向量表中找不到中断服务函数的入口,直接跳入到了预先写好的空函数,在里面无限循环,实现不了中断。

三、外部中断/事件控制器(EXTI)

简介

EXTI外部中断/事件控制器,管理着20个中断/事件线,每一个都对应着一个边沿检测器,用来实现输入信号的上升沿检测和下降沿检测。EXTI能够对每一个中断或者事件进行单独的配置,即能够单独配置为中断或者事件,以及触发事件的属性

功能框图

在这里插入图片描述

EXTI可分为两个部分的功能,一个是产生中断,一个是产生事件。

输入线:EXTI控制器有19个中断/事件输入线(互联型是20个),这些输入线通过寄存器设置成任意的GPIO,和一些外设事件。输入线得到的是电平变化的信号。

接下来进入到边沿检测电路:它能够根据上升沿触发选择寄存(EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发,有效输出1,无效则输出0。可以是只有上升沿触发,只有下降沿触发,或者上升沿和下降沿都触发。

之后经过或门电路,一个输入是边沿检测电路出来的信号,另外一个是来自软件中断事件寄存器,两个输入中只要有一个输出1,结果都为1.

上面与门:与中断屏蔽器(EXTI_IMR)一起输出。与门电路要求输入都为 1 才输出 1,因此当中断屏蔽器为零,则停止中断。通过中断屏蔽器去实现是否产生中断。当中断屏蔽器为1时,电路的输出信号会被保存到挂起寄存器 (EXTI_PR) 内,如果确定最终电路输出为 1 就会把 EXTI_PR 对应位置置1。然后将EXTI_PR寄存器内容输出到NVIC内,从而实现系统中断事件控制。

下面与门:一个输入来自前面的信号,另一个输入来自事件屏蔽寄存器 (EXTI_EMR)。如果EXTI_EMR设置为0时,那不管前面的输入信号,结果都为0,起到了屏蔽事件的作用。

脉冲发生器电路:输入端输入信号为1,即输入有效,产生一个脉冲,如果输入端是无效信号(为0),则不输出脉冲。这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC等。

产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输属于硬件级的。

AFIO端口复用

程序内部,部分外设使用是复用IO口,便可以开该复用时钟。在STM32F103VET6中USART3的IO口为TX/PB10,RX/PB11。假若你要是将给外设IO口映射到(TX/PC10,RX/PC11)或者(TX/PD8,RX/PD9),便需要开启AFIO时钟即可正常工作。

而GPIO的初始功能没有中断功能,因此需要用到端口的映射。AFIO中EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1) 的 EXTI0[3:0] 位选择配置为 PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或者 PI0。其他EXTI中断线同样的配置。一共有4个外部中断寄存器去实现GPIO映射到外部中断。

EXTI初始化结构体

typedef struct { 2 uint32_t EXTI_Line; // 中断/事件线
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; // 触发类型
FunctionalState EXTI_LineCmd; // EXTI 使能
 } EXTI_InitTypeDef;
  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)。

四、编程流程

  1. 初始化需要产生中断的GPIO
  2. 配置中断嵌套中断向量表NVIC
  3. 初始化EXTI
  4. 编写中断服务函数

代码

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  /* 配置NVIC为优先级组1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  /* 配置中断源:按键 */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
  /* 配置抢占优先级 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	EXTI_InitTypeDef EXTI_InitStructure;

	/*开启按键GPIO口的时钟和AFIO端口复用*/	RCC_APB2PeriphClockCmd((RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO),ENABLE);
												
	/* 配置 NVIC 中断*/
	NVIC_Configuration();
	/* 选择按键用到的GPIO */	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    /* 配置为浮空输入 */	
  GPIO_InitStructure.GPIO_Mode =        GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

	/* 选择EXTI的信号源 */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, 		  GPIO_PinSource0); 
  EXTI_InitStructure.EXTI_Line = EXTI_Line0;
	
	/* EXTI为中断模式 */
  EXTI_InitStructure.EXTI_Mode =  EXTI_Mode_Interrupt;
	/* 上升沿中断 */
  EXTI_InitStructure.EXTI_Trigger =  EXTI_Trigger_Rising;
  /* 使能中断 */	
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}
void LED_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;
		/*开启LED相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,     ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5;

		/*设置引脚模式为通用推挽输出*/
		GPIO_InitStructure.GPIO_Mode =      GPIO_Mode_Out_PP;   
		/*设置引脚速率为50MHz */   
		GPIO_InitStructure.GPIO_Speed =  GPIO_Speed_50MHz; 
		/*调用库函数,初始化GPIO*/
		GPIO_Init(GPIOB, &GPIO_InitStructure);	
       GPIO_SetBits(GPIOB, GPIO_Pin_5);
    
}

void KEY1_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		// LED 取反		
		GPIOB->ODR ^=  GPIO_Pin_5;
        //清除中断标志位
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}
int main(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	EXTI_Key_Config(); 
	/* 等待中断,由于使用中断方式,CPU不用轮询按键 */
	while(1)                            
	{
	}
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32微控制器中,中断处理函数的实现是通过编写对应的中断服务程序(Interrupt Service Routine,简称ISR)来完成的。下面是一个简单的示例: ```c #include "stm32f4xx.h" // 定义一个用于处理外部中断的函数 void EXTI0_IRQHandler(void) { // 在这里编写处理外部中断的代码 // 可以执行一些操作或改变某些状态等 // 例如,可以读取外部信号值,然后根据信号值执行不同的逻辑 // 请注意,中断处理函数应该尽量保持简洁和高效,避免耗时操作 // 处理完中断后,需要清除中断标志位,以便允许新的中断发生 EXTI_ClearITPendingBit(EXTI_Line0); } int main(void) { // 初始化外部中断配置 GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStruct); SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); EXTI_InitStruct.EXTI_Line = EXTI_Line0; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); while (1) { // 在主循环中可以执行其他任务 // 中断中断主循环的执行,当中断发生后会跳转到对应的中断处理函数 // 中断处理函数执行完成后,会返回到主循环继续执行 } } ``` 以上示例代码实现了一个外部中断的处理函数 `EXTI0_IRQHandler()`,使用的是PA0引脚作为外部中断触发源。在 `main()` 函数中,通过初始化相关寄存器和配置中断优先级等,将 `EXTI0_IRQHandler` 函数与外部中断绑定起来。在主循环中可以执行其他任务,当外部中断发生时,控制器会跳转到对应的中断处理函数执行。处理完成后,会返回到主循环继续执行其他任务。 请注意,以上代码是基于STM32F4系列微控制器编写的示例,对于其他型号的STM32微控制器,可能需要做一些相应的修改。另外,具体的中断配置和功能实现还需要参考相关的芯片手册和官方文档。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值