一、中断的概念

1.1.1中断的基本概念

中断:单片机应对突发事件的一种方式

先暂停常规程序,先去实现响应函数,再实现常规程序的过程

1.2.1为什么要用到中断

我们以之前串口发送举例

int main(void)
{
uint8_t byte;
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//亮灯
		Delay(byte);//延迟
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//灭灯
		Delay(byte);//延迟
		
		if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)== SET)
		{
			 byte =USART_ReceiveData(USART1);//根据收到的数据改变闪灯速度
		}
 	}
}

假如单片机的波特率是115200,串口一次性发送一个起始位8个数据位一个停止位(共十个字)那么完成一次发送需要【1/(115200/10)】*1000~~0.1ms

完成一次LED亮灭共耗时200ms,一次循环发送单片机只需要0.1ms就能完成,而等待需要200ms 这样等待这200ms单片机可以做其它的事情,所以我们引入中断

2.1.1中断编程举例

int main(void)
{
uint8_t byte;
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//亮灯
		Delay(byte);//延迟
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//灭灯
		Delay(byte);//延迟
 	}
}
void USART_IRQHandler(void)
{
//接收数据
	uint8_t data = USART_ReceiveData(USART1);
	//对收到的数据进行处理
}

二、中断优先级

 1.1.1中断优先级的概念

中断优先级:用数字表示中断的优先程度

注:                                                                                                TXE 这些标志位都是中断标志位

2.1.1NVIC相当于中断管理员(中断嵌套控制器Nested Vectored Interrupt Controller)

3.1.1NVIC内部

中断优先级分组可以移动有如图5种情况,

以分组0举例:此时抢占优先级没有数值为0,子优先级有四个空位,假如四位数字为1010,则抢占优先级为0,子优先级为8+2=10;

以分组1举例:假如四位数字为1010,则抢占优先级为1,子优先级为2;

4.1.1抢占优先级与中断嵌套之间的关系

数字越小抢占优先级越高

5.1.1子优先级与中断排队

三、中断实验(用中断控制LED亮灭)

1.1.1连接电路

1.2.1编写闪灯代码

1.3.1初始化板载LED

2.1.1初始化串口(先对收发引脚进行初始化再对内部进行初始化)

3.1.1让RxNE标志位触发中断

4.1.1配置NVIC模块

5.1.1代码部分(main.c)

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
uint32_t blinkInterval =1000;
void USART1_IRQHandler(void);
void APP_OnBoardLED_Init(void);
void APP_USART1_Init(void);
int MY_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size);
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	 APP_OnBoardLED_Init();
	APP_USART1_Init();
	My_USART_SendString(USART1,"HELLO WORLD.\r\n");
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
		Delay(blinkInterval);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
		Delay(blinkInterval);
		USART1_IRQHandler();
 	}
}


int MY_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size)
{
//#1等待总线空闲
	while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BUSY)==SET)
	
	//#2发送起始位
	I2C_GetFlagStatus(I2Cx,ENABLE);
	
	while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB)==RESET)
		
	//#3寻址阶段
	I2C_ClearFlag(I2Cx,I2C_FLAG_AF);
	 
	I2C_SendData(I2Cx,Addr & 0xFE);
	while(1)
{
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR)==SET)
	{
	break;
	}
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
	{
	I2C_GenerateSTOP(I2Cx,ENABLE);
		return -1;//寻址失败
	}
}
//清除ADDR
	I2C_ReadRegister(I2Cx, I2C_Register_SR1);
	I2C_ReadRegister(I2Cx, I2C_Register_SR2);

//#4发送数据
for(uint16_t i=0;i<Size;i++)
{
	while(1)
	{
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
	{
	
		I2C_GenerateSTOP(I2Cx,ENABLE);
		return -2;//寻址失败
	}
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BTF)==SET)
	{
	break;
	}
}
}
//#5发送停止位
I2C_GenerateSTOP(I2Cx,ENABLE);
return 0;

}
void APP_OnBoardLED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_OD;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
	
	GPIO_Init(GPIOC,&GPIO_InitStruct);

}
void APP_USART1_Init(void)
{
	//#1.初始化IO引脚
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//PA9 AF_PP
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//PA10 IPU
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 ;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//#2初始化USART1
	//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	//初始化USART1
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate =115200;
	USART_InitStruct.USART_HardwareFlowControl =USART_HardwareFlowControl_RTS;
	USART_InitStruct.USART_Mode =USART_Mode_Rx |USART_Mode_Tx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits =USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	
	USART_Init(USART1,&USART_InitStruct);
	
	//闭合总开关
	USART_Cmd(USART1,ENABLE);
 	
	//#3.配置中断
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	//#4配置NVIC
	NVIC_InitTypeDef  NVIC_InitSTruct;
	
	NVIC_InitSTruct.NVIC_IRQChannel =USART1_IRQn;
	NVIC_InitSTruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitSTruct.NVIC_IRQChannelSubPriority =0;
	NVIC_InitSTruct.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitSTruct);

}

void USART1_IRQHandler(void)
{
	if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
	{
	uint8_t dataRcvd =USART_ReceiveData(USART1);
	
	if(dataRcvd == '0')
	{
	blinkInterval = 1000;
	}
	else if(dataRcvd == '1')
	{
	blinkInterval = 200;
	}
	else if(dataRcvd == '2')
	{
	blinkInterval = 50;
	}
  }
}

四、EXTI工作原理

1.1.1EXTI模块简介:

功 能:可以实现输入信号的上升沿检测和下降沿的检测(检测输入信号上升沿和下降沿的电压),可以实现对每个中断/事件线进行单独配置,以及触发事件的属性。

2.1.1EXTI特性(了解)

● 每个中断/事件都有独立的触发和屏蔽
● 每个中断线都有专用的状态位
● 支持多达20个软件的中断/事件请求
● 检测脉冲宽度低于APB2时钟宽度的外部信号

通过查阅参考手册我们可以得知内部

3.1.1EXTI产生中断

要产生中断,必须先配置好并使能中断线,
根据需要的边沿检测设置2个触发寄存器,同时在中
断屏蔽寄存器的相应位写’1’允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个
中断请求,对应的挂起位也随之被置’1’。在挂起寄存器的对应位写’1’,将清除该中断请求。如图

4.1.1EXTI产生事件

如果需要产生事件,必须先配置好并使能事件线。根据需要的边沿检测通过设置2个触发寄存
器,同时在事件屏蔽寄存器的相应位写’1’允许事件请求。当事件线上发生了需要的边沿时,将
产生一个事件请求脉冲,对应的挂起位不被置’1’
通过在软件中断/事件寄存器写’1’,也可以通过软件产生中断/事件请求。

 简化后长这样

5.1.1线的概念

EXTI内部共有20条线,其中连接GPIOD的线有16条,每条单独连接37个GPIO引脚
例如EXTI0连接PA0;EXTI1连接PA0;EXTI0连接PA8;各算一条,参考下图


 

五、EXTI按钮实验

1.1.1搭建电路

按下左边按钮LED点亮,按下右边按钮LED熄灭;

2.1.1代码示例(main.c部分)

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"


void APP_OnBoardLED_Init(void);

void EXTI9_5_IRQHandler(void);
void APP_Button_Init(void);

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	 APP_OnBoardLED_Init();
	APP_Button_Init();
EXTI9_5_IRQHandler();
	while(1)
	{

 	}
}



void APP_OnBoardLED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_OD;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
	
	GPIO_Init(GPIOC,&GPIO_InitStruct);

}



void APP_Button_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//PA5
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 ;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	//PA6
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 ;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//#2.为EXTI5和EXTI6分配引脚
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO,ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
	
	//#3.初始化EXTI的线
	EXTI_InitTypeDef EXTI_InitStruct={0};
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line5;
	EXTI_InitStruct.EXTI_Mode =EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;	
	EXTI_InitStruct.EXTI_LineCmd =ENABLE;
	EXTI_Init(&EXTI_InitStruct);
	
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line6;
	EXTI_InitStruct.EXTI_Mode =EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;	
	EXTI_InitStruct.EXTI_LineCmd =ENABLE;
	EXTI_Init(&EXTI_InitStruct);
	
	//#4.配置中断
	NVIC_InitTypeDef  NVIC_InitSTruct;
	
	NVIC_InitSTruct.NVIC_IRQChannel =USART1_IRQn;
	NVIC_InitSTruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitSTruct.NVIC_IRQChannelSubPriority =0;
	NVIC_InitSTruct.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitSTruct);
}

void EXTI9_5_IRQHandler(void)//清除标志位
{
	if(EXTI_GetFlagStatus(EXTI_Line5)==SET)
	{
	EXTI_ClearFlag(EXTI_Line5);
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
	}
	if(EXTI_GetFlagStatus(EXTI_Line6)==SET)
	{
	EXTI_ClearFlag(EXTI_Line5);
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
	}
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐