STM32F4xx开发学习_中断系统

STM32F4xx中断

在主程序运行过程中出发了特定中断条件,使得CPU暂停当前正在运行的程序转而去处理中断程序,处理完成后返回主程序暂停位置。每个外设都可以产生中断,当有多个中断源同时申请中断时,CPU会根据优先级啊依次响应中断。

NVIC

NVIC(嵌套矢量中断控制器),统一管理中断相关的功能,是内核里面的一个外设,每个中断通道都拥有16个可编程的优先等级(4位进行设置),可对优先级进行分组,进一步设置抢占优先级和响应优先级。这里给出固件库头文件core_cm4.h文件中NVIC结构体定义

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用来设置中断优先级。在NVIC 有一个专门的寄存器:中断优先级寄存器NVIC_IPRx(在F407中,x=0…981)用来配置外部中断的优先级,IPR宽度为8bit, 原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高
NVIC中断优先级
处于精简设计目的,在F407中只使用了高4bit进行优先级分组,只能表示16级中断嵌套,这4bit又分为抢占优先级(高n位)和子优先级(4-n 位),抢占优先级高于子优先级。优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的PRIGROUP[10:8]位决定,F407分为了5组,具体如下
NVIC中断优先级分组
设置优先级分组可调用库函数NVIC_PriorityGroupConfig()实现,仅需设置一次覆盖所有中断,有关NVIC中断相关的库函数都在库文件misc.c和misc.h中。

/**
* 配置中断优先级分组:抢占优先级和子优先级
* 形参如下:
* @arg NVIC_PriorityGroup_0: 0bit for抢占优先级
*                            4 bits for 子优先级
* @arg NVIC_PriorityGroup_1: 1 bit for抢占优先级
*                            3 bits for 子优先级
* @arg NVIC_PriorityGroup_2: 2 bit for抢占优先级
*                            2 bits for 子优先级
* @arg NVIC_PriorityGroup_3: 3 bit for抢占优先级
*                            1 bits for 子优先级
* @arg NVIC_PriorityGroup_4: 4 bit for抢占优先级
*                            0 bits for 子优先级
* @注意 如果优先级分组为0,则抢占优先级就不存在,优先级就全部由子优先级控制
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
    // 设置优先级分组
    SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

NVIC中断优先级分组
可参考stm32f4xx.h头文件里面的IRQn_Type结构体定义,这个结构体包含了所有的中断源。

EXTI

EXTI(External interrupt/event controller )外部中断/事件控制器,管理MCU多达** 23 个软件事件/中断请求**,每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。EXTI在APB2总线
EXTI框图
EXTI主要有两个功能,产生中断产生事件。输入线可以通过寄存器设置为任意一个GPIO,也可以是外设事件。其中中断是在NVIC中控制,事件是由脉冲触发,中断和事件都可以被屏蔽。总结可得:产生中断线路目的是把输入信号输入到NVIC,属于软件级;产生事件线路目的就是传输一个脉冲信号给其他外设,属于硬件级。

中断/事件线

EXTI有23个中断/事件线,其中GPIO占用EXTI0至EXTI15,另外七根用于特定外设事件。
EXTI_特定外设事件
EXTI可以检测指定GPIO口的电平信号,当检测到特定电平信号时,EXTI将向NVIC发出中断申请,由NVIC根据优先级依次通知CPU响应中断。
EXTI_GPIO映射
例如EXTI0线,通过SYSCFG_EXTICR1寄存器的EXTI0[3:0]位选择对应的GPIO口。最终在实现上是完成EXTI到NVIC之间的通路即可。标准库函数对每个外设都建立了一个初始化结构体,EXTI结构体定义在stm32f4xx_exti.h文件中,初始化库函数定义在stm32f4xx_exti.c文件中

typedef struct {
    uint32_t EXTI_Line;                 // 中断/事件线
    EXTIMode_TypeDef EXTI_Mode;         // EXTI模式
    EXTITrigger_TypeDef EXTI_Trigger;   // 触发事件
    FunctionalState EXTI_LineCmd;       // EXTI控制
} EXTI_InitTypeDef;

中断函数名是固定的,在startup_stm32f40xx.s启动文件中有定义
EXTI中断函数名

中断案例

内部串口中断

当串口接收到正确数据帧产生中断

/*
**********************************************************************************
*   @brief  USART1串口初始化
*			PA9是USART1_TX,PA10是USART1_RX
*			GPIO是AHB1总线,USART1是APB2总线
*   @param  none
*   @return none
*   @use	Serial_Init()
**********************************************************************************
*/
void Serial_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	//USART1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	//配置PA9、PA10引脚
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//配置USART1
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;											//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//硬件流控制,这里不选用
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;						//双工
	USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位,这里不选用
	USART_InitStructure.USART_StopBits = USART_StopBits_1;								//1位长的停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;							//字长8位
	USART_Init(USART1, &USART_InitStructure);
	USART_ClearFlag(USART1, USART_FLAG_RXNE);											//清除接收数据寄存器非空标志位
	//配置USART中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);										//配置USART接收数据寄存器非空中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);										//选择第二组中断
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;									//配置USART1中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;										//使能NVIC
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;							//先占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;									//子优先级
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}
/*
**********************************************************************************
*   @brief 	中断函数,接收不定长文本数据帧,包头@,包尾\r\n
*   @param 	none
*   @return none
*   @use	中断事件触发会自动跳转到此函数
**********************************************************************************
*/
void USART1_IRQHandler()
{
	static uint8_t RxState = 0;								//接收状态
	static uint8_t pRxPacket = 0;							//接收指针
	if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1);			//接收到的数据
		if(RxState == 0)									//默认状态0
		{
			if(RxData == '@')
			{
				RxState = 1;								//成功接收到帧头,进入状态1
				pRxPacket = 0;
			}
		}
		else if(RxState == 1)								//状态1
		{
			if(RxData == '\r') RxState = 2;					//在状态1阶段看是否接收到帧尾,否则一直接收数据
			else
			{
				Serial_RxPacket[pRxPacket] = RxData;
				pRxPacket ++;
			}
		}
		else if(RxState == 2)								//状态2
		{
			if(RxData == '\n')
			{
				RxState = 0;								//成果接收到帧尾,返回状态0
				Serial_RxPacket[pRxPacket] = '\0';
				printf("接收完毕!!\n");
				for(uint8_t i = 0; i < pRxPacket; i++)
				{
					Serial_SendOneByte(Serial_RxPacket[i]);
				}
			}
		}
		else printf("error\n");
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//如果正常读取RDR数据会自动清除标志位
	}
}

按键外部中断

按不同按键,串口打印不同按键值

#include "Key.h"
#include "Serial.h"
#include "Delay.h"

/*
**********************************************************************************
*   @brief  NVIC配置
*   @param 	none
*   @return none
*   @use	NVIC_Configuration()
**********************************************************************************
*/
static void NVIC_Configuration()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//按键1中断
	NVIC_InitStructure.NVIC_IRQChannel 						= EXTI3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd 					= ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 	= 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority 			= 1;
	NVIC_Init(&NVIC_InitStructure);
	
	//按键2中断
	NVIC_InitStructure.NVIC_IRQChannel 						= EXTI4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority 			= 2;
	NVIC_Init(&NVIC_InitStructure);
	
	//WK_UP按键
	NVIC_InitStructure.NVIC_IRQChannel 						= EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority 			= 3;
	NVIC_Init(&NVIC_InitStructure);
}



/*
**********************************************************************************
*   @brief  板载按键初始化,PE3、PE4分别对应KEY1、KEY0,按下是拉低电平,需要配置为上拉输入
			按键WK_UP对应PA0,按下是拉高电平,需要配置为下拉输入
*   @param 	none
*   @return none
*   @use	Key_Init()
**********************************************************************************
*/
void Key_Init()
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);							//使用GPIO外部中断时必须使能SYSCFG时钟
	
	NVIC_Configuration();
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IN;									//配置输入模式
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_3 | GPIO_Pin_4;						//配置引脚
	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP;									//配置为上拉输入
	GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;								//配置引脚速度
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_0;									//配置引脚
	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_DOWN;								//配置为下拉输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//配置EXTI
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);					//PA0连接到EXTI0
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);					//PE3连接到EXTI3
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);					//PE4连接到EXTI4
	
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line 		= EXTI_Line3 | EXTI_Line4;					//PE3和PE4中断/事件线
	EXTI_InitStructure.EXTI_LineCmd 	= ENABLE;									//使能中断/事件线
	EXTI_InitStructure.EXTI_Mode 		= EXTI_Mode_Interrupt;						//选择中断
	EXTI_InitStructure.EXTI_Trigger 	= EXTI_Trigger_Falling;						//由于按下拉低电平,下降沿触发
	EXTI_Init(&EXTI_InitStructure);	

	EXTI_InitStructure.EXTI_Line 		= EXTI_Line0;								//PE0中断/事件线
	EXTI_InitStructure.EXTI_Trigger 	= EXTI_Trigger_Rising;						//由于按下拉高电平,上升沿触发
	EXTI_Init(&EXTI_InitStructure);	
	
}
/*
**********************************************************************************
*   @brief  获取板载按键值
*   @param 	none
*   @return 0、1、2
*   @use	Key_GetNum()
**********************************************************************************
*/
uint8_t Key_GetNum()
{
	uint8_t KeyNum;
	//K0按键置为1
	if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)
	{
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	//K1按键置为2
	if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) == 0)
	{
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	//WK_UP按键置为0
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
	{
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1);
		Delay_ms(20);
		KeyNum = 0;
	}
	return KeyNum;
}

//中断函数
/*
**********************************************************************************
*   @brief  WK_UP按键触发中断
*   @param 
*   @return 
*   @use	
**********************************************************************************
*/
void EXTI0_IRQHandler(void)
{
	printf("Key0\n");
	EXTI_ClearITPendingBit(EXTI_Line0);
}
/*
**********************************************************************************
*   @brief  WK_UP按键触发中断
*   @param 
*   @return 
*   @use	
**********************************************************************************
*/
void EXTI3_IRQHandler(void)
{
	printf("Key1\n");
	EXTI_ClearITPendingBit(EXTI_Line3);
}
/*
**********************************************************************************
*   @brief  WK_UP按键触发中断
*   @param 
*   @return 
*   @use	
**********************************************************************************
*/
void EXTI4_IRQHandler(void)
{
	printf("Key2\n");
	EXTI_ClearITPendingBit(EXTI_Line4);
}





  • 38
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值