之前学过用按键点灯,但是会有一种缺点,就是单片机在执行别的任务时,不能及时响应按键
为了解决这个问题,我们可以学习到新的功能----中断
可以用外部中断,在按下按钮的时候,立刻处理按钮的事件,完成按钮事件后,再跳回正常程序
按键引脚PA0,LED引脚PC13
(二)讲解
引脚的外部中断配置流程
- 开启时钟
- 配置GPIO的模式
- 使能NVIC中断并配置优先级
- 配置GPIO中断
- 使能中断和清除中断标志位
- 编写中断服务函数
我们可以复制工程模板,同时在System文件夹内,创建一个库,"zhong.c","zhong.h"里面用来写我们的中断函数
记得在keil5里面添加
我们创建一个zhong_star函数,用来初始化中断
- void zhong_star()
- {
- }
开启时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
配置GPIO,因为按键另一端是GND,所以引脚模式选择上拉输入
- // 配置GPIO口
- GPIO_InitTypeDef GPIO_InitStruct;
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
- GPIO_Init(GPIOA,&GPIO_InitStruct);
配置中断
配置外部中断线(EXTI Line)与GPIO端口和引脚的连接。
- GPIO_PortSourceGPIOA:指定EXTI线与GPIOA端口相连。
- GPIO_PinSource0:指定EXTI线与GPIOA端口的第0号引脚(PA0)相连。
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
创建变量, 存储EXTI的配置参数
- EXTI_InitTypeDef EXTI_InitStruct;
配置
EXTI_Line0:选择中断线为EXTI Line 0。
ENABLE:使能该EXTI线。
EXTI_Mode_Interrupt:设置EXTI的模式为中断响应。
EXTI_Trigger_Rising:设置EXTI触发方式为上升沿触发。
函数:EXTI_Init,用于初始化EXTI配置。
- EXTI_InitStruct.EXTI_Line = EXTI_Line0;
- EXTI_InitStruct.EXTI_LineCmd = ENABLE;
- EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
- EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
- EXTI_Init(&EXTI_InitStruct);
配置NVIC
NVIC负责管理微控制器的中断和异常,确保它们能够按照既定的优先级和顺序得到处理
设置NVIC优先级为2
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
创建变量,存储NVIC的配置参数
- NVIC_InitTypeDef NVIC_InitStruct;
配置NVIC
EXTI0_IRQn:指定要启用的IRQ通道为EXTI0中断。
ENABLE:使能该IRQ通道。
NVIC_IRQChannelPreemptionPriority:设置抢占优先级为1。
NVIC_IRQChannelSubPriority:设置响应优先级为1(在指定的优先级分组下)。
- 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);
完成以上操作,我们可以实现
- 将GPIOA的第0号引脚(PA0)配置为EXTI Line 0的外部中断输入。
- 设置EXTI Line 0为中断模式,上升沿触发。
- 配置NVIC,使能EXTI0中断,并设置其抢占优先级和响应优先级。
(三)主函数
先引用我们所需的库
- #include "stm32f10x.h" // Device header
- #include "Delay.h"
- #include "zhong.h"
这里我初始化了板载LED,PC13引脚,当然可以把这一大段封装成库,这样看起来好看一点
同时调用了中断初始化的函数zhong_star();
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
- zhong_star();
while循环内可以不写程序,模拟一种运行while循环的程序
- while (1)
- {
- }
EXTI0_IRQHandler用于处理外部中断 0(EXTI0)的中断服务函数
意味着,当检测到中断时,会立刻跳转到这个函数内
EXTI_GetITStatus(EXTI_Line0)这个是中断标志位,当有中断时,标志位为SET否则就是RESET
当完成我们的函数后,需要将标志位清除EXTI_ClearITPendingBit(EXTI_Line0);
- void EXTI0_IRQHandler(void)
-
- { if(EXTI_GetITStatus(EXTI_Line0) == SET)//判断是否为EXTI0中断
- {
- if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13) == 0) //获取输出寄存器的状态,如果当前引脚输出低电平
- {
- GPIO_SetBits(GPIOC, GPIO_Pin_13); //则设置PA1引脚为高电平
- Delay_ms(100);
- }
- else //否则,即当前引脚输出高电平
- {
- GPIO_ResetBits(GPIOC, GPIO_Pin_13); //则设置PA1引脚为低电平
- Delay_ms(100);
- }
- EXTI_ClearITPendingBit(EXTI_Line0);// 清除中断标志位
- }
- }
(四)程序现象