利用外部中断来实现按键控制LED灯的亮灭。
实验工具:MDK5,STM32F103ZET6开发板
使用固件库编程
LED灯引脚PD13,按键PE0
STM32 的每个 IO 都可以作为外部中断的中断输入口,STM32F103 的中断控制器支持 19 个外部中断/事件请求。每个中断设有状位,每个中断/事件都有独立的触发和屏蔽设置。
STM32F103的 19 个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。
所以这次要用到的就是线 0~15。
下图是IO口语中断线的一一映射关系
以线 0 为例:它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、GPIOD.0、GPIOE.0、 GPIOF.0、 GPIOG.0
在此之前先写一个LED与按键KEY的程序
led.c
#include "led.h"
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStrtuct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
GPIO_InitStrtuct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStrtuct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStrtuct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD,&GPIO_InitStrtuct);
}
key.c
#include "key.h"
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStrtucter;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitStrtucter.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStrtucter.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOE,&GPIO_InitStrtucter);
}
uint8_t Key_scan(void)
{
if (KEY == 1)
{
//松手检测
while(KEY == 1)
return 1;
}
else
return 0;
}
然后就可以根据上面的步骤一步步配置中断了
#include "exti.h"
#include "key.h"
#include "delay.h"
#include "led.h"
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStructer;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
KEY_Init();//初始化按键
//中断线的映射以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);//中断线映射函数
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 模式为interrupt
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_Init(&EXTI_InitStruct);
//配置中断分组并使能中断
NVIC_InitStructer.NVIC_IRQChannel = EXTI0_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructer.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
NVIC_InitStructer.NVIC_IRQChannelSubPriority = 0x02; //子优先级1
NVIC_Init(&NVIC_InitStructer);
}
//中断服务函数
void EXTI0_IRQHandler()
{
delay_ms(10); //消抖
if(KEY==1)
{
LED=!LED;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除EXTI0线路标志位
}
说明一下
NVIC_InitStructer.NVIC_IRQChannel = EXTI0_IRQn;
它的配置是在stm32f10x.h里面,
PxN管脚共用外部中断线EXTIN和外部中断向量EXTIN_IRQn和中断服务程序入口EXTIN_IRQHandler,但是需要注意[9…5]共用EXTI9_5_IRQn和EXTI9_5_IRQHandler、[15…10]共用EXTI15_10_IRQn和EXTI15_10_IRQHandler。
而中断服务函数void EXTI0_IRQHandler()是在 startup_stm32f10x_hd.s 里面
具体的分配关系如表 所示:
抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。
这里需要注意两点:
第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级
中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。
结合实例说明一下:
假定设置中断优先级组为 2,然后设置中断 3(RTC 中断)的抢占优先级为 2,响应优先级为 1。中断 6(外部中断 0)的抢占优先级为 3,响应优先级为 0。中断 7(外部中断 1)的抢占优先级为 2,响应优先级为 0。
那么这 3 个中断的优先级顺序为:中断 7>中断 3>中断 6。
上面例子中的中断 3 和中断 7 都可以打断中断 6 的中断。而中断 7 和中断 3 却不可以相互打断!
结论:
1.抢占优先级越小,优先级越高;相同抢占优先级的中断不能相互打断;
2.相同抢占优先级N个中断发生时,响应优先级越小的中断首先执行,如果响应优先级也均相同,则根据各中断对应向量表的位置来确定,向量表中越靠前的中断先响应。
相关程序链接:
https://pan.baidu.com/s/1fH0TareZkEfV4zeOPz_d1w
提取码:hfvw