按键输入-GPIO输入
相比简单的51单片机,STM32的每个IO口都可以作为外部中断输入
STM32中断控制器支持19个外部中断/事件请求:
线0-15:外部IO口输入中断
线16:连接到PVD输出
线17:连接到RTC闹钟事件
线18:连接到USB唤醒事件
每个外部中断线可配置独立的触发方式,触发/屏蔽.专用状态位
三种中断触发方式:
上升沿触发
下降沿触发
双边沿触发
STM32F103ZET6共112引脚,线0-15为外部IO口输入中断
映射关系:
GPIOx.0映射EXTI0
GPIOx.1映射EXTI1
...
GPIOx.15映射EXTI15
理论上所有IO口都可以作为中断输入
如图:
中断0,1,2,3,4单独使用一个地址(中断向量)
中断5-9共用一个中断向量
中断10-15共用一个中断向量
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
// 设置IO口与中断线的映射关系
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
// 初始化中断线:触发方式等
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//判断中断线中断状态,是否发生
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//清除中断线上的中断标志位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
stm32f10x_exit.h头文件中找到EXTI_Init函数声明:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
stm32f10x_exit.h头文件中找到EXTI_InitTypeDef结构体定义
/** * @brief EXTI Init Structure definition */
typedef struct
{
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled. This parameter can be any combination of @ref EXTI_Lines */
//指定要配置的中断线
EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines. This parameter can be a value of @ref EXTIMode_TypeDef */
//模式:事件 OR中断
EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines. This parameter can be a value of @ref EXTIMode_TypeDef */
//触发方式:上升沿/下降沿/双沿触发
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines. This parameter can be set either to ENABLE or DISABLE */
//使能 OR失能
}EXTI_InitTypeDef;
mode属性有效性校验IS_EXTI_MODE
/** * @brief EXTI mode enumeration */
typedef enum
{
EXTI_Mode_Interrupt = 0x00, //中断
EXTI_Mode_Event = 0x04 //事件
}EXTIMode_TypeDef;
#define IS_EXTI_MODE(MODE) (((MODE) == EXTI_Mode_Interrupt) || ((MODE) == EXTI_Mode_Event))
EXTI_Trigger属性有效性校验IS_EXTI_TRIGGER
/** * @brief EXTI Trigger enumeration */
typedef enum
{
EXTI_Trigger_Rising = 0x08, //上升沿
EXTI_Trigger_Falling = 0x0C, //下降沿
EXTI_Trigger_Rising_Falling = 0x10 //双边沿
}EXTITrigger_TypeDef;
#define IS_EXTI_TRIGGER(TRIGGER) (((TRIGGER) == EXTI_Trigger_Rising) || \
((TRIGGER) == EXTI_Trigger_Falling) || \
((TRIGGER) == EXTI_Trigger_Rising_Falling))
配置实例:
EXTI_InitStructure.EXTI_Line=EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
stm32f10x_gpio.c文件中找到GPIO_EXTILineConfig函数源码:
/** * @brief Selects the GPIO pin used as EXTI Line. * @param GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines. * This parameter can be GPIO_PortSourceGPIOx where x can be (A..G). * @param GPIO_PinSource: specifies the EXTI line to be configured. * This parameter can be GPIO_PinSourcex where x can be (0..15). * @retval None */
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}
GPIO_PortSource参数有效性验证 IS_GPIO_EXTI_PORT_SOURCE:
/** @defgroup GPIO_Port_Sources * @{ */
#define GPIO_PortSourceGPIOA ((uint8_t)0x00)
#define GPIO_PortSourceGPIOB ((uint8_t)0x01)
#define GPIO_PortSourceGPIOC ((uint8_t)0x02)
#define GPIO_PortSourceGPIOD ((uint8_t)0x03)
#define GPIO_PortSourceGPIOE ((uint8_t)0x04)
#define GPIO_PortSourceGPIOF ((uint8_t)0x05)
#define GPIO_PortSourceGPIOG ((uint8_t)0x06)
#define IS_GPIO_EXTI_PORT_SOURCE(PORTSOURCE) (((PORTSOURCE) == GPIO_PortSourceGPIOA) || \
((PORTSOURCE) == GPIO_PortSourceGPIOB) || \
((PORTSOURCE) == GPIO_PortSourceGPIOC) || \
((PORTSOURCE) == GPIO_PortSourceGPIOD) || \
((PORTSOURCE) == GPIO_PortSourceGPIOE) || \
((PORTSOURCE) == GPIO_PortSourceGPIOF) || \
((PORTSOURCE) == GPIO_PortSourceGPIOG))
GPIO_PinSource参数有效性验证IS_GPIO_PIN_SOURCE:
/** @defgroup GPIO_Pin_sources * @{ */
#define GPIO_PinSource0 ((uint8_t)0x00)
#define GPIO_PinSource1 ((uint8_t)0x01)
#define GPIO_PinSource2 ((uint8_t)0x02)
#define GPIO_PinSource3 ((uint8_t)0x03)
#define GPIO_PinSource4 ((uint8_t)0x04)
#define GPIO_PinSource5 ((uint8_t)0x05)
#define GPIO_PinSource6 ((uint8_t)0x06)
#define GPIO_PinSource7 ((uint8_t)0x07)
#define GPIO_PinSource8 ((uint8_t)0x08)
#define GPIO_PinSource9 ((uint8_t)0x09)
#define GPIO_PinSource10 ((uint8_t)0x0A)
#define GPIO_PinSource11 ((uint8_t)0x0B)
#define GPIO_PinSource12 ((uint8_t)0x0C)
#define GPIO_PinSource13 ((uint8_t)0x0D)
#define GPIO_PinSource14 ((uint8_t)0x0E)
#define GPIO_PinSource15 ((uint8_t)0x0F)
#define IS_GPIO_PIN_SOURCE(PINSOURCE) (((PINSOURCE) == GPIO_PinSource0) || \
((PINSOURCE) == GPIO_PinSource1) || \
((PINSOURCE) == GPIO_PinSource2) || \
((PINSOURCE) == GPIO_PinSource3) || \
((PINSOURCE) == GPIO_PinSource4) || \
((PINSOURCE) == GPIO_PinSource5) || \
((PINSOURCE) == GPIO_PinSource6) || \
((PINSOURCE) == GPIO_PinSource7) || \
((PINSOURCE) == GPIO_PinSource8) || \
((PINSOURCE) == GPIO_PinSource9) || \
((PINSOURCE) == GPIO_PinSource10) || \
((PINSOURCE) == GPIO_PinSource11) || \
((PINSOURCE) == GPIO_PinSource12) || \
((PINSOURCE) == GPIO_PinSource13) || \
((PINSOURCE) == GPIO_PinSource14) || \
((PINSOURCE) == GPIO_PinSource15))
1,初始化IO口为输入。
GPIO_Init();
2,开启IO口复用时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
3,设置IO口与中断线的映射关系。
void GPIO_EXTILineConfig();
4,初始化线上中断,设置触发条件等。
EXTI_Init();
5,配置中断分组(NVIC),并使能中断。
NVIC_Init();
6,编写中断服务函数。
EXTIx_IRQHandler();
7,清除中断标志位
EXTI_ClearITPendingBit();
使用按键触发外部中断
KEY0 KEY1 KEY2接低电平,所以设置IO口PE2,PE3,E4为上拉输入
按键按下时, IO口输入低电平,检测下降沿即可判断按键按下
相反,WK_UP连接VCC,需设置IO口PA0为下拉输入
按键按下时,IO口输入高电平,检测上升沿即可判断按键按下
HARDWARE/EXIT文件夹,新建exit.h文件
#ifndef __EXTI_H
#define __EXIT_H
#include "sys.h"
void EXTIX_Init(void); // 外部中断初始化
#endif
HARDWARE/EXIT文件夹,新建exit.c文件实现EXTIX_Init(void)外部中断初始化函数
#include "exti.h"
#include "led.h"
#include "key.h” // 按键端口初始化
#include "delay.h"
#include "usart.h"
#include "beep.h"
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1,按键端口初始化 - 初始化IO口为输入(详细参考按键部分)
KEY_Init();
// 使能AFIO复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// 配置中断线映射-GPIOE.2映射到中断线2-按键KEY2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
//初始化外部中断-线2
EXTI_InitStructure.EXTI_Line=EXTI_Line2; // 中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断OR事件
// 触发方式-下降沿触发(因为连接GND)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能
EXTI_Init(&EXTI_InitStructure);
// 配置中断线映射-GPIOE.3 映射到中断线3-按键KEY1
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
// 初始化外部中断-线3
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_Init(&EXTI_InitStructure);
// 配置中断线映射-GPIOE.4 映射到中断线4-按键KEY0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
//初始化外部中断-线4
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_Init(&EXTI_InitStructure);
// 配置中断线映射-GPIOA.0映射到中断线0-按键WK_UP
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
// 初始化外部中断-线0
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
// 触发方式-上升沿触发(因为连接VCC)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);
// 中断优先级配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 通道0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; // 响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//线0中断函数
void EXTI0_IRQHandler(void)
{
delay_ms(10); // 延时防止按键抖动
if(WK_UP==1) // 等待跳过抖动后再次判断是否按下
{
BEEP=!BEEP; // 蜂鸣器取反
}
EXTI_ClearITPendingBit(EXTI_Line0); // 手动清除中断标志-线1
}
//线2中断函数
void EXTI2_IRQHandler(void)
{
delay_ms(10);
if(KEY2==0)
{
LED0=!LED0; // LED0取反
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
//线3中断函数
void EXTI3_IRQHandler(void)
{
delay_ms(10);
if(KEY1==0)
{
LED1=!LED1; // LED1取反
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
//线4中断函数
void EXTI4_IRQHandler(void)
{
delay_ms(10);
if(KEY0==0)
{
LED0=!LED0; // LED0取反
LED1=!LED1; // LED1取反
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
USER文件夹新建main.c文件,编写主函数
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "exti.h"
#include "beep.h"
int main(void)
{
delay_init(); // 延迟初始化
//设置中断优先级分组2 2位抢占2位响应
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200); // 初始化串口 设置波特率为115200
LED_Init(); // LED端口初始化
BEEP_Init(); // 蜂鸣器端口初始化
KEY_Init(); // 按键端口初始化
EXTIX_Init(); // 外部中断初始化
LED0=0; // LED0点亮
while(1)
{
printf("打印输入\r\n");
delay_ms(1000);
}
}