欢迎关注 youcansxidian【嵌入式软件AI编程】专栏【STM32C5教程】1STM32C5系列介绍【STM32C5教程】2STM32CubeMX2 安装与STM32C5闪灯【STM32C5教程】3GPIO 按键输入实验【STM32C5教程】4GPIO 外部中断实验1. 项目简介1.1 实验目标本实验在 NUCLEO-C542RC 开发板上将用户按键 B1PC13配置为外部中断输入按下按键触发中断在中断回调中切换板载 LEDPA5的状态掌握 STM32C5 系列 EXTI 外部中断的配置与使用方法。功能需求按下用户按键PC13时触发外部中断在中断回调中切换板载 LEDPA5状态消抖时间 20 ms开发板和 MCU 型号NUCLEO-C542RC 开发板搭载 STM32C542RCT6 MCU。1.2 实验条件硬件NUCLEO-C542RC 开发板支持数据传输的 USB Type-C 线用于连接 ST-LINK 接口软件STM32CubeMX2用于引脚、时钟配置与代码生成VS Code STM32Cube 插件用于编译、烧录、调试2. 硬件说明2.1 NUCLEO-C542RC 开发板NUCLEO-C542RC 是 ST 基于 STM32C542RCT6 推出的 Nucleo-64 开发板。主要特性MCUSTM32C542RCT6Arm Cortex-M33 内核最高主频 144 MHz256 KB Flash64 KB RAM调试器板载 STLINK-V3EC支持大容量存储、虚拟串口和调试端口三种功能用户 LEDLD1连接 PA5用户按键B1连接 PC13复位按键B2扩展接口Arduino Uno V3 和 ST morpho 连接器本实验使用板载的 LD1 和 B1无需额外外设。2.2 引脚说明引脚别名功能方向有效电平PA5LD1板载用户 LEDGPIO 输出高电平点亮PC13B1用户按键EXTI 中断输入按下为高电平松开为低电平PA5LD1配置为推挽输出输出高电平时 LED 点亮输出低电平时 LED 熄灭。复位后初始状态为低电平LED 熄灭。PC13B1配置为外部中断输入。按键按下时引脚为低电平松开时为高电平。本实验采用上升沿触发即中断发生在按键松开的瞬间。3. STM32CubeMX2 工程配置3.1 新建工程本实验采用 MCU 方式创建工程即以芯片型号 STM32C542RC 为起点而非开发板方式。进入 MCU 选择界面启动 STM32CubeMX2在首页点击 MCU进入芯片型号选择列表。搜索并选中芯片在 MCU name 输入框中输入 STM32C542RC或完整型号 STM32C542RCT6从筛选出的列表中选择对应型号。填写项目信息输入项目名称 GPIO_EXTI 和项目保存路径。项目名称避免使用特殊字符路径建议使用纯英文目录。3.2 基础配置工程创建后软件自动进入 Pinout 视图。时钟配置保持默认即可CubeMX2 创建工程后的默认系统主频为48 MHz时钟源为 HSI 分频本实验无需修改。注意本工程的系统时钟为 48 MHz这是 CubeMX2 的默认配置不影响 GPIO 功能。3.3 GPIO 配置1PA5 配置为输出在 Pinout 视图中找到PA5引脚。右键点击 PA5选择Assign signal→GPIO。再次右键点击 PA5选择Configure GPIO进入 GPIO 详细配置界面。将 PA5 的Mode设置为Output。保持默认参数Output level 为 LowOutput mode 为 Push PullPull-up/Pull-down 为 No pull-up and no pull-downMaximum output speed 为 Low。在User Label输入框中填入LD1。设置别名后CubeMX2 会自动生成宏定义LD1_PORT和LD1_PIN。2PC13 配置为外部中断在 Pinout 视图中找到PC13引脚。右键点击 PC13选择Assign signal→GPIO。再次右键点击 PC13选择Configure GPIO进入 GPIO 详细配置界面。将 PC13 的Mode设置为Interrupt。将Trigger detection设置为Rising edge上升沿触发。将Interrupt设置为Enabled启用中断CubeMX2 会自动选择 EXTI13。将Pull-up/Pull-down设置为No pull-up and no pull-down。PC13 外部已有下拉电阻 R13内部无需再设上下拉避免与外部电阻形成分压。在User Label输入框中填入B1。3.4 项目配置点击Project Settings进入工程设置页面。在IDE Format下拉选项中选择CMake。在CMake Toolchain下拉选项中选择GCC。确认配置无误后点击Generate Code。CubeMX2 会执行配置检查如果没有错误开始在指定路径下生成 CMake 工程文件。生成完成后项目路径下会出现标准 CMake 工程结构包含Core/、Drivers/、CMakeLists.txt等。4. 编程实现4.1 实现思路采用“EXTI 边沿中断 时间戳消抖 回调中翻转 LED”的方案用户操作 B1PC13 │ ├─ 按下 → 引脚由低变高 ──────┐ │ │ 硬件 EXTI13 检测到上升沿 │ ▼ │ EXTI13_IRQHandler() │ │ │ ▼ │ HAL_EXTI_IRQHandler() │ │ │ ▼ │ HAL_EXTI_TriggerCallback() │ │ │ ┌───────┴────────┐ │ │ 距上次响应 20ms? │ │ └───┬───────────┬───┘ │ │ 是 │ 否 有效触发 │ ▼ ▼ │ 视为抖动 更新 last_tick │ 忽略 翻转 LED │ └─ 松开 → 引脚由高变低 → 下降沿不配置触发无中断与轮询方案的区别轮询版在主循环while(1)中不停地读引脚判断是否按下本实验用硬件 EXTI 边沿产生中断由中断回调响应主循环保持空闲。中断中不能用HAL_Delay()阻塞延时会导致中断服务程序长时间占用 CPU影响系统实时性。本实验改用HAL_GetTick()时间戳比较实现非阻塞消抖。各步骤使用的 HAL 函数步骤HAL 函数作用中断入口HAL_EXTI_IRQHandler()清中断标志调用触发回调读当前时间HAL_GetTick()获取系统毫秒节拍用于消抖计时翻转 LEDHAL_GPIO_TogglePin(LD1_PORT, LD1_PIN)切换 PA5 输出电平4.2 关键代码在 VS Code 中打开生成的 CMake 工程找到Core/Src/main.c添加以下代码。1文件头部添加宏定义与变量/* Private define ------------------------------------------------------------*/#defineDEBOUNCE_TIME_MS20U/* 消抖时间毫秒 *//* Private variables ---------------------------------------------------------*/staticuint32_tlast_tick0U;/* 上一次有效按键触发时的系统时间戳用于消抖 *//* Private functions prototype -----------------------------------------------*/staticvoidhandle_key_press(void);2主函数intmain(void){if(mx_system_init()!SYSTEM_OK){return(-1);}else{/* 记录上电时刻作为消抖时间戳的初始化基准 */last_tickHAL_GetTick();/* 主循环空闲等待按键事件由 EXTI13 中断触发处理 */while(1){}}}3按键处理函数/** * brief: 按键有效按下时的处理函数。 * retval: 无 */staticvoidhandle_key_press(void){/* 切换板载 LEDPA5状态每次有效按下翻转一次 */HAL_GPIO_TogglePin(LD1_PORT,LD1_PIN);}4EXTI 触发回调函数/** * brief: EXTI 触发回调覆盖默认实现。 * 当 PC13B1 用户按键产生上升沿中断时被调用。 * PC13 外部下拉、空闲为低、按下为高一次上升沿对应一次按下动作。 * param: hexti EXTI 句柄 * param: trigger 触发沿本工程仅配置了上升沿 * retval: 无 */voidHAL_EXTI_TriggerCallback(hal_exti_handle_t*hexti,hal_exti_trigger_ttrigger){uint32_ttick;/* 仅处理上升沿触发本工程只有 PC13/B1 配置了 EXTI 上升沿 */if(trigger!HAL_EXTI_TRIGGER_RISING){return;}tickHAL_GetTick();/* 基于时基的软件消抖间隔小于消抖时间则视为抖动忽略本次触发 */if((tick-last_tick)DEBOUNCE_TIME_MS){return;}/* 更新最后一次有效触发时刻作为下一次消抖比较基准 */last_ticktick;/* 执行有效按键动作切换板载 LED */handle_key_press();/* 避免未使用形参的编译警告 */STM32_UNUSED(hexti);}代码说明LD1_PORT、LD1_PIN两个宏由 CubeMX2 生成在generated/hal/mx_gpio_default.h通过main.h链入在main.c中可直接使用。回调函数HAL_EXTI_TriggerCallback()是 HAL 驱动中已经声明好的回调入口。用户在自己的源文件中重写这个同名函数中断发生时 HAL 就会自动调用它无需修改驱动源码。回调中判断trigger ! HAL_EXTI_TRIGGER_RISING后提前返回。当前工程只配置了上升沿这个判断是防御性写法——如果将来同一个工程里还有其他触发边沿的 EXTI 配置这段代码可以保证只响应上升沿。消抖使用HAL_GetTick()获取当前毫秒时刻与上次有效触发时刻比较。时间差 20ms才判定为有效触发并更新时间戳。这是非阻塞消抖不会阻塞中断。STM32_UNUSED(hexti)用于避免未使用形参的编译告警工程开启了-Wall。5. 编译烧录与运行测试5.1 编译与烧录打开工程用 VS Code 打开 CubeMX2 生成的 CMake 工程文件夹如GPIO_EXTI_cmake。编译点击 VS Code 底部状态栏的Build按钮或使用命令面板执行 CMake 构建。编译完成后终端应输出Build finished with exit code 0无 error。连接开发板用支持数据传输的 USB Type-C 线连接 NUCLEO-C542RC 的 ST-LINK USB 口CN1。确认设备识别打开设备管理器在“端口 (COM 和 LPT)”下应能看到STLink COMx。烧录点击 VS Code 中的运行按钮将固件下载到开发板。5.2 运行与测试烧录完成后程序自动开始运行。依次验证以下功能测试 1初始状态复位开发板按下黑色复位按键 B2观察 LD1。复位后 LD1 应保持熄灭状态说明 GPIO 初始化正确空闲时没有误触发。测试 2按下触发按下 B1观察 LD1 是否翻转。由于配置为上升沿触发中断发生在按下的瞬间按下时 LD1 立即翻转松开时 LD1 状态不变。反复测试几次确认每次按下都能可靠触发翻转。测试 3消抖验证快速连续按下并松开 B1 多次观察 LED 响应。如果消抖正常每次有效触发只翻转一次不会因按键机械抖动产生多次翻转。测试结果复位后 LD1 保持熄灭无按键时不会误触发按下 B1 瞬间LD1 翻转一次松开 B1 时LD1 状态不变快速连按无重复误翻转20 ms 消抖生效编译、烧录、运行均正常功能正常。6. 小结本实验通过外部中断方式实现了按键控制 LED 状态翻转掌握了 STM32C5 EXTI 外部中断的配置与使用方法。核心逻辑可以概括为“EXTI 边沿触发 → 中断服务 → 回调消抖 → 翻转 LED”。有两点需要注意第一触发边沿的选择。本实验采用上升沿触发中断发生在按键按下瞬间。PC13 外部下拉空闲为低按下为高所以上升沿正好对应按下动作。如果希望松开瞬间触发应改为下降沿Falling edge。第二中断回调中不能用HAL_Delay()。阻塞式延时会导致中断服务程序长时间占用 CPU影响系统实时性。本实验改用HAL_GetTick()时间戳比较实现非阻塞消抖这是中断中处理消抖的标准做法。与上一篇轮询方案相比中断方案由硬件边沿触发响应、主循环保持空闲而轮询方案需要主循环持续读取引脚状态这是两者最本质的技术差异。版权声明欢迎关注 youcansxidian【嵌入式软件AI编程】专栏转发必须注明原文链接【STM32C5教程】4GPIO 外部中断实验Copyright by ai4mcuqq.com 2026Crated2026-10