1.中断系统
外部中断是指来自CPU外部的突发事件所引发的中断。这些事件可能是硬件设备的信号,如定时器到期、键盘输入、数据输入/输出操作完成,或者其他外部信号。当CPU接收到外部中断信号时,它会暂停当前执行的程序,并立即跳转到中断处理程序中执行响应的处理程序。外部中断是计算机系统中常见的一种异步事件处理机制,它可以帮助处理时间敏感的任务。
中断优先级:当多个中断源同时申请中断时,CPU根据中断的轻重缓急进行分析,优先处理紧急的中断。
中断嵌套:在执行一个中断程序时又来了个优先级更高的中断,则CPU会停止当前执行的中断程序,去执行优先级更高的中断,处理完成后继续上一个中断。
在stm32中,有68个可屏蔽中断通道,包括EXTI、TIM、ADC等多个外设。通过NVIC统一管理中断,NVIC主要控制中断优先级,对于外部中断有16个优先级,0优先级最高,15最低。
2.NVIC介绍
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的相应优先级。抢占优先级高度的可以中断嵌套,相应优先级高的可以优先排队,抢占优先级和相应优先级均相同的按中断号排队。
相应优先级:相当于诊所诊断完上一个病人空闲时(外面有人排队)可以直接插队进去诊断。
抢占优先级:相当于诊所中虽然有病人在诊断,但是能一脚把别人踹一边,直接让医生诊断自己,自己诊断完后再继续诊断被踹一边的病人。
3.EXTI介绍
EXTI通过检测GPIO口的电平变化,向NVIC发出中断申请,经过NVIC处理后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。
支持的触发方式:上升沿/下降沿/双边沿/软件触发;
支持的GPIO口:所有GPIO口,但相同的PIN不能同时触发中断
通道数:16个GPIO_Pin,外加VD输出,RTC闹钟,USB唤醒,以太网唤醒
触发响应方式:中断响应/事件响应
EXTI基本结构
从GPIO口发出电平信号,AFIO进行中断引脚选择(选择16个) ,EXTI控制触发方式、触发响应方式和Pin口选择,NVIC进行将分组和响应优先级、抢占优先级配置。
4.AFIO介绍
AFIO主要功能主要有:复用功能引脚重映射、中断引脚选择。
5.使用EXTI中断实现功能的模块
本文章EXTI外部中断主要配合旋转编码器和对射式红外传感器使用。因为旋转编码器和对射式红外传感器的电平信号是持续变化的,没有中断的情况下,上一个传入的信号没处理完成,下一个信号就随之而来,导致信号处理不及时而产生错误。
旋转编码器:
旋转编码器是一种用于测量旋转角度和转速的传感器。它通常由旋转部分、编码器芯片和接口电路组成。
旋转部分通常由两个旋转部件组成,它们通过一个凸起的轴相互连接。当旋转轴旋转时,旋转部件也跟随旋转,使两个部件之间的距离发生变化。编码器芯片通过对这种距离变化的检测来计算旋转角度和转速。
接口电路通常包括数字电路和模拟电路。数字电路通常将编码器输出转换为数字信号,供外部系统使用。模拟电路通常将编码器输出转换为数字信号后,将其转换为模拟信号,以便用于控制系统。
6.具体代码
1.对射式红外传感器计次
/*
模块CountSensor.c
*/
#include "stm32f10x.h" // Device header
uint16_t CountSensor_Count;
/**
* @brief EXTI外部中断初始化
* @param 无
* @retval 无
*/
void CountSensor_Init(void)
{
//GPIO、AFIO定时器启动
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//GPIOB配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//AFIO配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
//EXTI配置
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14; //配置GPIO_Pin_14作为中断电平信号判断口
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //启用EXTI
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断相应
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //配置为下降沿触发
EXTI_Init(&EXTI_InitStructure);
//NVIC配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //配置中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启用中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //配置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //配置响应优先级
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 返回记录的次数
* @param 无
* @retval CountSensor_Count 记录的次数
*/
uint16_t GetCountSensor_Count(void)
{
return CountSensor_Count;
}
/**
* @brief 中断程序
* @param 无
* @retval 无
*/
void EXTI15_10_IRQHandler(void)
{
//判断是否中断程序被正确执行
if(EXTI_GetITStatus(EXTI_Line14) == SET)
{
//通过判断Pin口电平,提高信号捕捉准确率
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14) == 0)
CountSensor_Count++;
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
需要配合OLED模块(B站江协科技有提供)
/*
main.c模块
*/
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "CountSensor.h"
uint16_t Count;
int main(void)
{
OLED_Init();
OLED_ShowString(1,1,"Count:");
CountSensor_Init();
while(1)
{
Count = GetCountSensor_Count();
OLED_ShowNum(1,7,Count,4);
}
}
2.旋转编码器实现正反旋转计次
/*
Encoder.c模块
*/
#include "stm32f10x.h" // Device header
int16_t Encoder_Count;
void Encoder_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
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);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
int16_t Encoder_Get(void)
{
// int16_t Temp;
// Temp = Encoder_Count;
// Encoder_Count = 0;
// return Temp;
return Encoder_Count;
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
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_1) == 0)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count++;
}
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
同样要配合OLED模块
/*
main.c模块
*/
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Encoder.h"
int16_t Num;
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
Num = Encoder_Get();
OLED_ShowSignedNum(1,5,Num,5);
}
}