中断,英文名为Interrupt,计算机的世界里处处都有中断,任何工作都离不开中断,可以说整个计算机系统就是由中断来驱动的。那么什么是中断?简单来说就是CPU停下当前的工作任务,去处理其他事情,处理完后回来继续执行刚才的任务,这一过程便是中断。
一、中断系统
中断执行流程
STM32中断
中断向量表
定义:中断向量表(interrupt vector table)包含中断服务程序地址的特定内存区域,这些服务程序是处理外部硬件中断请求的代码。
特点:这些中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。
作用:在 CPU 接收到外部中断请求时,它会先查询中断向量表,以查找到相应的中断服务程序地址。首先会保护现场,然后,CPU 会将控制转移到该地址,以执行相应的中断服务。完成中断服务程序后,控制会恢复到中断发生前的程序流程。
二、NVIC基本结构
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。
NVIC优先级分组
寄存器(Register)的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。
触发器:能够记忆1位二进制信号的基本单元电路;
特点:1.有两个能自行保持的状态,用来表示0和1;2.根据输入信号可以置成0或1
抢占优先级和响应优先级
STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由NVIC_IRQChannelPreemptionPriority 的参数配置。
而响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断
三、EXTI外部中断
软件触发:引脚不触发
中断响应是正常流程,引脚电平变化触发中断(外部中断信号通向CPU)
事件响应不会触发中断,而是触发别的外设外设操作,属于外设之间的联合工作(外部中断信号不会通向CPU)
EXTI基本结构
AFIO复用IO口
AFIO(alternate-function I/O),指GPIO端口的复用功能,GPIO除了用作普通的输入输出(主功能),还可以作为片上外设的复用输入输出,如串口、ADC、这些就是复用功能。
STM32芯片内部有很多外设,这些外设的引脚都是与IO口复用的。也就是说,一个IO口如果可以复用为内置外设的功能引脚,那么当这个IO口作为内部外设使用的时候,就叫做复用。
重映射是指在微控制器中,将某个外设的功能引脚从默认的位置映射到其他的引脚上。这样做的目的是为了在设计电路板时提供更大的灵活性,尤其是当默认的引脚已经被占用或者为了优化布线和减少信号干扰时。在STM32微控制器中,重映射是一个常见的功能,它允许设计者根据需要重新分配外设引脚。
重映射的实现方式:在STM32中,重映射是通过设置特定的寄存器来实现的。
EXTI框图
四、中断示例代码
void Encoder_Init(void)
{
//1.配置外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//2.配置GPIO,选择端口为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,默认高电平//不清楚可以查看参考手册,进行模式配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//3.配置AFIO,选择用到的GPIO,连接后面的EXTI
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
//4.配置EXTI,选择边沿触发方式,选择触发响应方式
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);
//5.配置NVIC,选择一个合适的优先级,最后通过NVIC外部中断信号就能进入CPU,CPU收到跳转信号,照转到中断处理程序
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
}
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
/*建议,中断程序中不要执行耗时过长的代码
最好不要在主函数和中断函数,调用相同的函数,或者操作可能产生冲突的硬件
在中断中,操作变量或者标志位
*/
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count--;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count++;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}