使用EXTI处理外部中断
1.任务描述
使用开发板上的KEY1按键,达到开/关LED彩灯的目的
2. 编程思路及要点
编程思路
检测GPIO中断,在中断服务函数里面,实现开/关LED彩灯
编程要点
1) 初始化用来产生中断的 GPIO;
2) 初始化 EXTI;
3) 配置 NVIC;
4) 编写中断服务函数;
3 示例代码
为了方便移植,首先定义GPIO引脚
//宏定义
#define KEY1_INT_GPIO_PORT GPIOA
#define KEY1_INT_GPIO_CLK (RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO)
#define KEY1_INT_GPIO_PIN GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE GPIO_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE GPIO_PinSource0
#define KEY1_INT_EXTI_LINE EXTI_Line0
#define KEY1_INT_EXTI_IRQ EXTI0_IRQn
#define KEY1_IRQHandler EXTI0_IRQHandler
#define KEY2_INT_GPIO_PORT GPIOC
#define KEY2_INT_GPIO_CLK (RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO)
#define KEY2_INT_GPIO_PIN GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE GPIO_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE GPIO_PinSource13
#define KEY2_INT_EXTI_LINE EXTI_Line13
#define KEY2_INT_EXTI_IRQ EXTI15_10_IRQn
#define KEY2_IRQHandler EXTI15_10_IRQHandler
void EXTI_Key_Config(void);
配置NVIC
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置 NVIC 为优先级组 1 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置中断源:按键 1 */
NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
/* 配置抢占优先级:1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级:1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* 配置中断源:按键 2,其他使用上面相关配置 */
NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
/* 配置子优先级:2 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
配置EXTI
void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/*开启按键 GPIO 口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);
/* 配置 NVIC 中断*/
NVIC_Configuration();
/*--------------------------KEY1 配置---------------------*/
/* 选择按键用到的 GPIO */
GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
/* 配置为浮空输入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
/* 选择 EXTI 的信号源 */
GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, \
KEY1_INT_EXTI_PINSOURCE);
EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
/* EXTI 为中断模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中断 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中断 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/*开启按键 GPIO 口的时钟*/
RCC_APB2PeriphClockCmd(KEY2_INT_GPIO_CLK,ENABLE);
/*--------------------------KEY2 配置------------------*/
/* 选择按键用到的 GPIO */
GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
/* 配置为浮空输入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);
/* 选择 EXTI 的信号源 */
GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, \
KEY2_INT_EXTI_PINSOURCE);
EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
/* EXTI 为中断模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 下降沿中断 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
/* 使能中断 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
EXTI 中断服务函数
void KEY1_IRQHandler(void)
{
//确保是否产生了 EXTI Line 中断
if (EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) {
// LED1 取反
LED1_TOGGLE;
//清除中断标志位
EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
}
}
void KEY2_IRQHandler(void)
{
//确保是否产生了 EXTI Line 中断
if (EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) {
// LED2 取反
LED2_TOGGLE;
//清除中断标志位
EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
}
}
LED 相关的宏
// R-红色
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PIN GPIO_Pin_5
// G-绿色
#define LED2_GPIO_PORT GPIOB
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED2_GPIO_PIN GPIO_Pin_0
// B-蓝色
#define LED3_GPIO_PORT GPIOB
#define LED3_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED3_GPIO_PIN GPIO_Pin_1
/* 直接操作寄存器的方法控制 IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制 IO 的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED3_TOGGLE digitalToggle(LED2_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_OFF digitalHi(LED2_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_ON digitalLo(LED2_GPIO_PORT,LED3_GPIO_PIN)
/* 基本混色,后面高级用法使用 PWM 可混出全彩颜色,且效果更好 */
//红
#define LED_RED \
LED1_ON;\
LED2_OFF\
LED3_OFF
//绿
#define LED_GREEN \
LED1_OFF;\
LED2_ON\
LED3_OFF
//蓝
#define LED_BLUE \
LED1_OFF;\
LED2_OFF\
LED3_ON
//黄(红+绿)
#define LED_YELLOW \
LED1_ON;\
LED2_ON\
LED3_OFF
//紫(红+蓝)
#define LED_PURPLE \
LED1_ON;\
LED2_OFF\
LED3_ON
//青(绿+蓝)
#define LED_CYAN \
LED1_OFF;\
LED2_ON\
LED3_ON
//白(红+绿+蓝)
#define LED_WHITE \
LED1_ON;\
LED2_ON\
LED3_ON
//黑(全部关闭)
#define LED_RGBOFF \
LED1_OFF;\
LED2_OFF\
LED3_OFF
#define LED_GPIO_PORT GPIOB
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_G_GPIO_PIN GPIO_Pin_0
#define LED_B_GPIO_PIN GPIO_Pin_1
#define LED_R_GPIO_PIN GPIO_Pin_5
#define ON 1
#define OFF 0
//通过异或运算来亮灭LED
#define LED_TOGGLE(pin) (LED_GPIO_PORT->ODR ^= pin)
typedef enum
{
LED_Type_G = 0,
LED_Type_R,
LED_Type_B
}LED_Type_E;
void GPIO_LED_Config(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void LED_Switch(LED_Type_E ledType, uint8_t opt);
void LED_GPIO_Config(void);
主函数
int main(void)
{
#if 0
HSE_SetSysClk(RCC_PLLMul_9);
// 流水灯
while(1)
{
GPIO_LED_Config(LED_GPIO_PORT, LED_G_GPIO_PIN);
Delay(0xfffff);
LED_Switch(LED_Type_G, ON);
Delay(0xfffff);
LED_Switch(LED_Type_G, OFF);
Delay(0xfffff);
GPIO_LED_Config(GPIOB, LED_R_GPIO_PIN);
Delay(0xfffff);
LED_Switch(LED_Type_R, ON);
Delay(0xfffff);
LED_Switch(LED_Type_R, OFF);
Delay(0xfffff);
GPIO_LED_Config(GPIOB, LED_B_GPIO_PIN);
Delay(0xfffff);
LED_Switch(LED_Type_B, ON);
Delay(0xfffff);
LED_Switch(LED_Type_B, OFF);
Delay(0xfffff);
}
#elif 0
//通过按键来亮灭LED
GPIO_LED_Config(LED_GPIO_PORT, LED_R_GPIO_PIN);
GPIO_KEY_Config(KEY1_GPIO_PORT, KEY1_GPIO_CLK, KEY1_GPIO_PIN);
while(1)
{
if( KEY_ON == key_scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) )
LED_TOGGLE(LED_R_GPIO_PIN);
}
// GPIO_LED_Config(LED_GPIO_PORT, LED_B_GPIO_PIN);
// GPIO_KEY_Config(KEY2_GPIO_PORT, KEY2_GPIO_CLK, KEY2_GPIO_PIN);
//
// while(1)
// {
// if( KEY_ON == key_scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) )
// LED_TOGGLE(LED_B_GPIO_PIN);
// }
#else
/* LED 端口初始化 */
LED_GPIO_Config();
/* 初始化 EXTI 中断,按下按键会触发中断,
* 触发中断会进入 stm32f4xx_it.c 文件中的函数
* KEY1_IRQHandler 和 KEY2_IRQHandler,处理中断,反转 LED 灯。
*/
EXTI_Key_Config();
/* 等待中断,由于使用中断方式,CPU 不用轮询按键 */
while (1) {
}
#endif
}
LED 配置
void LED_GPIO_Config(void)
{
/*定义一个 GPIO_InitTypeDef 类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启 LED 相关的 GPIO 外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*选择要控制的 GPIO 引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为 50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化 GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的 GPIO 引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*调用库函数,初始化 GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的 GPIO 引脚*/
GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
/*调用库函数,初始化 GPIOF*/
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有 led 灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 关闭所有 led 灯 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
/* 关闭所有 led 灯 */
GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}