目录
一:中断
1:简历
2:AFIO
3:EXTI
4:NVIC基本结构
5:使用步骤
6:设计中断函数
二:中断的应用
A:对外式红外传感计数器
1:硬件介绍
2:计数代码
B:旋转编码计数器
1:硬件介绍
2:旋转编码器代码
C:按键控制LED
D:代码总结
一:中断
1:简历
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回; 注意那种优先级可以插队,那种不能插队;见下面的NVIC基本结构
STM32中断: 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级 (EXTI可以产生中断的,众多外设之一)
STM32外部中断简图
2:AFIO
AFIO主要用于引脚复用功能的选择和重定义
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
注意配置那个引脚为中断引脚的时候,中断函数要一致。
PX5--PX9对应中断服务函数为:EXTI9_5_IRQHandler
PX10--PX15对应中断服务函数为:EXTI15_10_IRQHandler
中断服务函数在启动的汇编文件中定义;中断请求号在stm32f103xb.h文件中定义(STM32F103C8T6)
3:EXTI
EXTI简介:
EXTI(Extern Interrupt)外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断 (PA1、PB1、PC1这样的,PAO和PBO这样的相同的Pin只能选1个作为中断引脚) PA6和PA7、PA9和PB15、PBO和PB1这样的都可以---对应上面的AFIO EXTI和IO的对应关系
原因:AFOI会在APIOA,APIO,APIOC中选择一个GPIO的16个引脚连接到后面的EXTI
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
触发响应方式:中断响应/事件响应
基本结构:
exit0,exit1,exit2,exit3,exit4,exit9_5,exit15_10; 一共有7个中断服务函数,而5-9共用一个中断服务函数,10-15共用一个中断服务函数,
EXTI框图
4:NVIC基本结构
嵌套中断向量控制器: 用来统一分配断优先级和管理中断的
基本概念
注意那种优先级可以插队,那种不能插队
NVIC优先级分组:
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级(更加紧急)和低4-n位的响应优先级(紧急)抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队 (中断号:EXTI简历中的优先级)
抢占优先级>响应优先级>自然优先级
优先级的数是值越小,优先级越高,0就是最高优先级
分组为0: 0位抢占优先级(没有抢占优先级),4位响应优先级(2^4=16--16个响应优先级)
分组为1: 1位抢占优先级(2^1=2,2个抢占优先级),3位响应优先级(2^3=8--8个响应优先级)
.................
配置NVIC的步骤:
在HAL_Init(); HAL库初始化函数中已经分组过了,所以这就以为这我们可以不要在进行分组。
中断请求号在stm32f103xb.h文件中定义; 第二步和第三步的第一个形参中断请求号在stm32f103xb.h文件中定义
5:使用步骤
步骤1:的使能时钟函数在stm32f1xx _hal_rcc.h定义
步骤2:开启AFIO函数在stm32f1xx hal _gpio.h定义
步骤3,4,5:是配置NVIC的步骤在stm32f1xx hal _gpio.h定义了
步骤6:自己设计中断服务函数(发生中断了要干什么事情)
6:设计中断函数
实际上就是 5:使用步骤里面的步骤6的讲解,以为和标准库不太一样
中断服务函数:已经固定好了,使用那个EXIT线就选择那个,在汇编文件中固定(实际上就是函数的名字已经固定好了,必须使用人家固定的函数名字)
HAL库中断处理公用函数:HAL_GPIO_EXTI_IRQHandler 在stm32f1xx hal gpio.h文件中定义
HAL库数据处理回调函数:HAL_GPIO_EXTI_Callback 在stm32f1xx hal gpio.h文件中定义
下面为ST公司HAL库中使写的
/**
* @brief This function handles EXTI interrupt request.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
/**
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
这段代码是用于处理外部中断(EXTI)的回调函数。在STM32微控制器中,外部中断可以由外部事件触发,例如按键按下、传感器信号变化等。
HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
: 这是一个公共中断处理函数,用于处理外部中断。当外部中断发生时,这个函数会被调用。函数参数GPIO_Pin
表示触发中断的GPIO引脚。
- 首先,函数通过
__HAL_GPIO_EXTI_GET_IT(GPIO_Pin)
检查指定GPIO引脚是否有中断事件发生。如果有,它会返回一个非零值。- 如果检测到中断,函数会首先通过
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin)
清除中断标志,确保下次中断事件可以再次触发。- 然后,它会调用
HAL_GPIO_EXTI_Callback(GPIO_Pin)
函数,这是一个回调函数,用于处理具体的中断事件。
HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
: 这是一个回调函数,用于处理具体的外部中断事件。默认情况下,这个函数是空的,没有任何操作。用户可以在自己的代码中实现这个函数,以处理特定的中断事件。
- 在这个函数的实现中,
UNUSED(GPIO_Pin);
这一行用于防止编译器因为GPIO_Pin
参数未被使用而发出警告。如果在实际使用中,用户需要知道是哪个GPIO引脚触发了中断,他们可以在这个函数中处理GPIO_Pin
参数。- 注释中提到了:“当需要回调函数时,可以在用户文件中实现
HAL_GPIO_EXTI_Callback
”。这意味着用户可以在自己的代码中重写这个函数,以实现特定的中断处理逻辑。总的来说,这段代码提供了一个处理外部中断的框架,用户可以在此基础上添加自己的中断处理逻辑。
自己写的中断函数
1:写出中断函数,在中断函数调用公共中断处理函数;不放心的话也可以在清除一遍我们的中断标志位(可选,因为在我们的公共中断处理函数已经清理过了)。
2:然后在写回调函数,回调函数里面写发生中断要干的事情。(回调函数的名字要和ST公司的HAL回调函数的名字一样,不能更改)
下面是一个实例:
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20);
if(GPIO_Pin == GPIO_PIN_4)
{
if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)==0)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
}
}
二:中断的应用
A:对外式红外传感计数器
AO(模拟输出)是指传感器输出一个连续变化的电压或电流信号,这个信号与传感器检测到的物体距离、速度或其他参数成比例关系。在红外传感计数器中,AO输出可能用于表示检测到的物体数量或通过的频率,以模拟信号的形式进行传输。这种输出方式适用于需要精确测量或连续监控的应用场景。
DO(数字输出)则是指传感器输出一个离散的电平信号,通常是高电平或低电平,用于表示检测到的物体状态或计数结果。在红外传感计数器中,DO输出可能用于指示物体是否通过、计数是否达到预设值等离散事件。这种输出方式适用于需要简单判断或控制的应用场景
本实验:我们这里只使用DO线不使用AD;使用一个中断
1:硬件介绍
2:计数代码
对外式红外传感计数器的亮灭来控制产生中断,然后开关灯
void LED_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
}
void LED_Exit_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}
void Exit_Init1(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_IT_RISING; //中断的下降沿触发
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Pull=GPIO_PULLUP; //上拉模式
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);
}
//中断处理函数;发生中断来这里
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_5)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);
}
}
}
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
Exit_Init1();
while(1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
}
}
B:旋转编码计数器
本实验使用的是EC11旋转编码器,这是一种增量式旋转编码器,拥有A、B、C三个输出通道,其中A、B两相输出正交信号,相位差为90°,C相输出零脉冲信号,用于标识位置。当编码器正转时,A相的输出信号超前B相90°;当编码器反转时,A相滞后B相90°。我们在程序中可以根据A、B两相信号输出的先后顺序,来判断旋转编码器是正转还是反转。
我们使用A和B,不使用c;使用了二个中断
1:硬件介绍
我们使用判断正反转的条件:
正转-----B相下降沿和A相低由平时同时满足时;
反转----在A相下降沿和B相低电频同时满足时;
2:旋转编码器代码
void LED_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
}
void LED_Exit_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}
void Exit_Encoder_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
///配置GPIOB的第12个引脚为下降沿中断
GPIO_InitTypeDef GPIO_InitType1;
GPIO_InitType1.Mode=GPIO_MODE_IT_FALLING;
GPIO_InitType1.Pin=GPIO_PIN_12;
GPIO_InitType1.Pull=GPIO_PULLUP; //上拉模式
GPIO_InitType1.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType1);
// 配置GPIOB的第13个引脚为上升沿中断
GPIO_InitTypeDef GPIO_InitType2;
GPIO_InitType2.Mode=GPIO_MODE_IT_FALLING;
GPIO_InitType2.Pin=GPIO_PIN_13;
GPIO_InitType2.Pull=GPIO_PULLUP; //上拉模式
GPIO_InitType2.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType2);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
void EXTI15_10_IRQHandler(void)
{
// 检查是否是GPIOB的第12个引脚触发的中断
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_12) != RESET)
{
// 清除GPIOB的第12个引脚的中断标志位
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_12);
// 调用HAL库的中断处理回调函数
HAL_GPIO_EXTI_Callback(GPIO_PIN_12);
}
// 检查是否是GPIOB的第13个引脚触发的中断
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET)
{
// 清除GPIOB的第13个引脚的中断标志位
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_13);
// 调用HAL库的中断处理回调函数
HAL_GPIO_EXTI_Callback(GPIO_PIN_13);
}
}
//中断处理函数;发生中断来这里
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//正转
if(GPIO_Pin==GPIO_PIN_13)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13)==0)
{
delay_ms(10);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
delay_ms(10);
}
}
//反转
if(GPIO_Pin==GPIO_PIN_13)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_12)==0)
{
delay_ms(10);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);
delay_ms(10);
}
}
}
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
Exit_Encoder_Init();
while(1)
{
}
}
在STM32微控制器中,当中断被触发时,对应的中断标志位会被硬件置为1。但具体的中断标志位值并不是一个简单的数字,而是与具体的中断源和寄存器相关。每个中断源在特定的寄存器中都有自己对应的中断标志位,通常是一个二进制位
C:按键控制LED
正常工作的时候一个LED正常亮灭,当按键按下的时候控制LED的开和关,使用一个中断
void LED_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
}
void LED_Exit_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}
void Exit_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_IT_FALLING; //中断的下降沿触发
GPIO_InitType.Pin=GPIO_PIN_1;
GPIO_InitType.Pull=GPIO_PULLUP; //上拉模式
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(EXTI1_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
}
void EXTI1_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
}
//中断处理函数;发生中断来这里
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(10);
if(GPIO_Pin==GPIO_PIN_1)
{
//按下
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==0)
{
delay_ms(10);
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==0);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);
}
}
}
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
Exit_Init();
while(1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
}
}
D:代码总结
不管有几个中断我们,我们自己写的中断服务函数和处理中断的函数不要我们定义,在HAL库中已经定义了。
void EXTI1_IRQHandler(void)
{
//注意名字要中断相同
}
//中断处理函数;发生中断来这里
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//不管几个中断函数,这个函数只能写一个,不然不能编译}
在中断模式下选择GPIO的模式(GPIO_InitType.Mode):应该选择那个方式触发中断(而不是常规的8中模式)
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Pull=GPIO_NOPULL;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
本节参考资料:
链接:https://pan.baidu.com/s/1peVcD-rf3Chd5b-A-0SEtg?pwd=5j2p
提取码:5j2p
--来自百度网盘超级会员V2的分享