本篇总结
使用 GPIO 外部中断检测 PG6 按键,10ms 消抖后串口打印按键事件。
🔑 KEIL MDK、UART调试、GPIO、EXTI外部中断
整改说明
原例程代码使用 GPIOG Pin_5 / EXTI_Line5,但实际板载 USER 按键连接在 PG6 ,导致按下按键无任何反应。本次整改将引脚改为 GPIO_Pin_6 / GPIO_PinSource6 / EXTI_Line6,并修正原中断服务函数中错误使用 GPIO_ReadOutputDataBit() 读取输入引脚电平的 bug,改为正确的 GPIO_ReadInputDataBit()。代码已重新验证。
本示例演示 W55MH32 通过 GPIO 外部中断方式检测板载 USER 按键。按键连接 PG6 ,一端接地,内部上拉,按下时引脚被拉低产生下降沿,触发 EXTI9_5_IRQn 中断,在中断服务函数中经 10ms 软件消抖后打印按键按下提示。
前置工具准备
开始本例程前,请确认以下工具已安装:
WIZ UartTool V1.0 :串口收发测试工具:点击下载
硬件连接
本例程使用 PG6 引脚作为板载 USER 按键输入。按键一端接 GND,另一端接 PG6,配置为内部上拉输入模式(GPIO_Mode_IPU),未按下时 PG6 为高电平,按下时被拉低产生下降沿,触发外部中断。USART1(PA9/PA10)用于输出调试信息。
引脚 功能 方向 模式
PG6 USER 按键输入 外部 → 开发板 上拉输入 (IPU)
PA9 USART1_TX 开发板 → PC 复用推挽输出 (AF_PP)
PA10 USART1_RX PC → 开发板 浮空输入 (IN_FLOATING)
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\GPIO_Input\,核心文件如下:
GPIO_Input/
├── USER/
│ ├── main.c ← GPIO配置 + EXTI外部中断 + 按键消抖
│ ├── w55mh32_conf.h ← 外设头文件集中包含
│ ├── w55mh32_it.c/h ← 中断服务函数
│ ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│ └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
└── delay/
└── delay.c/h ← SysTick 延时函数
步骤1:GPIO 配置与外部中断按键检测
文件:USER/main.c — 使能 GPIOG 和 AFIO 时钟,配置 PG6 为上拉输入,映射到 EXTI_Line6 下降沿触发,使能 EXTI9_5_IRQn 中断。中断服务函数中 10ms 消抖后用 GPIO_ReadInputDataBit() 确认按键按下。
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/* 使能 GPIOG 和 AFIO 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG | RCC_APB2Periph_AFIO, ENABLE);
/* PG6: 上拉输入(按键,按下拉低) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* 将 PG6 映射到外部中断线 EXTI6 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource6);
/* PG6 -> EXTI6,属于 EXTI9_5 中断 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* 配置 EXTI_Line6:中断模式,下降沿触发 */
EXTI_InitStructure.EXTI_Line = EXTI_Line6;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
int main(void)
{
RCC_ClocksTypeDef clocks;
delay_init();
UART_Configuration(115200);
RCC_GetClocksFreq(&clocks);
/* 打印系统时钟信息 */
printf("\n");
printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
(float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000,
(float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000,
(float)clocks.ADCCLK_Frequency / 1000000);
printf("GPIO IO Input Test: PG6 USER key (active low).\n");
GPIO_Configuration();
/* 主循环空闲,等待外部中断触发 */
while (1)
{
}
}
/* EXTI9_5 中断服务函数 */
void EXTI9_5_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line6) == SET)
{
delay_ms(10); /* 10ms 软件消抖 */
if (GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_6) == RESET)
{
printf("The key is pressed\n");
}
}
EXTI_ClearITPendingBit(EXTI_Line6);
}
GPIO 配置: 使能 GPIOG 和 AFIO 时钟,配置 PG6 为上拉输入模式(GPIO_Mode_IPU)。未按下时 PG6 为高电平(内部上拉),按下时被拉低。
外部中断线映射: 通过 GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource6) 将 PG6 映射到 EXTI_Line6。
NVIC 配置: 由于 EXTI_Line6 处于 EXTI5~EXTI9 范围内,共用 EXTI9_5_IRQn 中断通道。设置抢占优先级为 2,子优先级为 3,并使能该中断通道。
EXTI 配置: 配置外部中断线 EXTI_Line6 为中断模式,下降沿触发 (按键按下时 PG6 由高变低),并使能该中断线。
中断处理流程: 检查 EXTI_Line6 中断标志位 → 延时 10ms 消抖 → 使用 GPIO_ReadInputDataBit() 读取 PG6 输入电平,若为低电平(RESET)则确认按键按下,打印 "The key is pressed" → 清除中断标志位。主循环为空,等待外部中断触发。
修正原 bug 说明
原例程代码在中断服务函数中错误使用了 GPIO_ReadOutputDataBit() 读取输入引脚电平——该函数读取的是输出数据寄存器(ODR),而非输入数据寄存器(IDR),用于检测输入引脚电平逻辑上是错误的。本次整改改用正确的 GPIO_ReadInputDataBit(),确保读到的是引脚真实输入电平。
如果你想自定义
换按键引脚 → 修改 GPIO 端口、引脚号和 EXTI 线,如改用 GPIO_Pin_7 / EXTI_Line7(仍属 EXTI9_5)
改消抖时间 → 修改 delay_ms(10) 参数,一般 10~50ms
改触发方式 → 将 EXTI_Trigger_Falling 改为 EXTI_Trigger_Rising(上升沿/释放触发)或 EXTI_Trigger_Rising_Falling(双边沿)
改中断优先级 → 调整 NVIC_IRQChannelPreemptionPriority 和 NVIC_IRQChannelSubPriority
编译与下载
Keil MDK 中打开 GPIO_Input.uvprojx,F7 编译,确认 0 Error(s)。连接 DEBUG USB 口到 PC,F8 下载。
运行验证
打开 WIZ UartTool V1.0 ,波特率 115200 / 8N1。下载固件并按 RESET 后,串口首先打印系统时钟信息。每按一次板载 USER 按键(PG6),串口打印一行 "The key is pressed"。
SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 72.0Mhz
GPIO IO Input Test: PG6 USER key (active low).
The key is pressed
The key is pressed
The key is pressed
WIZ UartTool V1.0 显示按键检测:每按一次 USER 键打印一行 "The key is pressed"
常见问题
Q: 按键无反应
确认 PG6 引脚连接了 USER 按键。检查 GPIO 是否配置为上拉输入模式(GPIO_Mode_IPU)。确认 GPIOG 和 AFIO 时钟已使能。检查 EXTI_Line6 是否正确配置为下降沿触发,以及 EXTI9_5_IRQn 中断通道是否已使能。用万用表测量 PG6 电平,未按下应为 3.3V,按下应为 0V。
Q: 串口频繁打印按键事件(抖动)
增加消抖延时时间(如 delay_ms(20))。也可在硬件上加 0.1uF 滤波电容。确认消抖逻辑正确:中断触发后延时 → 再次读取确认电平。
Q: 中断服务函数中读取电平始终为高
检查是否误用了 GPIO_ReadOutputDataBit()(读取输出寄存器 ODR),应使用 GPIO_ReadInputDataBit()(读取输入寄存器 IDR)获取引脚真实输入电平。确认 GPIO 配置为输入模式而非输出模式。
总结
本文基于 W55MH32 的 GPIO 输入功能使用外部中断实现了按键检测:
PG6 上拉输入 :板载 USER 按键一端接 GND,未按下为高电平,按下为低电平
外部中断 EXTI_Line6 :下降沿触发,属于 EXTI9_5_IRQn 中断通道,实时响应按键动作
10ms 软件消抖 :中断触发后延时 10ms,再用 GPIO_ReadInputDataBit() 读取确认,消除机械抖动
修正原例程误用 GPIO_ReadOutputDataBit() 的 bug,改为正确的 GPIO_ReadInputDataBit() 读取输入电平