公司动态
GD32F470ZKT6单片机开发板入门学习(三)GPIO 按键轮询模式
前言GD32F470ZKT6 是兆易创新GigaDevice推出的一款基于 ARM Cortex-M4 内核的高性能微控制器主频高达 240MHz内置 1MB Flash 和 256KB SRAM并集成了丰富的外设资源。本系列教程旨在帮助初学者快速上手 GD32F470ZKT6 开发板从最基础的开发环境搭建开始逐步深入掌握其应用开发。本文作为系列的第三篇将详细介绍基于 GD32F470ZKT6 单片机开发板的 GPIO 按键轮询模式编程。通过这个实践项目读者将学习如何配置 GPIO 输入模式、读取按键状态、实现轮询检测逻辑并掌握按键消抖等关键编程技巧为后续中断、定时器等更复杂的应用开发打下坚实基础。一、准备工作1.1 硬件准备GD32F470ZKT6 开发板或核心板一块USB 数据线用于供电和程序下载/调试一台 Windows 电脑1.2 软件准备Keil MDK-ARM版本建议 V5.30 或以上。可从 ARM 官网或 Keil 官网下载安装。1.3 文档支持《GD32F4xx_用户手册_Rev3.3》这是 GD32F4xx 系列微控制器的官方核心参考文档。手册详细记录了该系列芯片的完整资源、功能特性、寄存器定义及操作指南是开发过程中不可或缺的权威资料。主要内容包括 在开发过程中遇到任何硬件功能或寄存器配置问题都应首先查阅此手册获取最准确的技术细节。芯片概述介绍 GD32F4xx 系列的产品线、内核特性、性能参数和封装信息。存储器架构详细说明 Flash、SRAM、备份寄存器的地址映射和访问方式。系统与外设深入讲解时钟系统RCU、电源管理、GPIO、中断控制器NVIC等所有外设模块的功能、寄存器配置和编程模型。通信接口涵盖 USART、I2C、SPI、CAN、USB、以太网等通信协议控制器的使用方法。模拟功能包括 ADC、DAC、比较器等模块的配置和校准说明。定时器与高级控制详细描述基本定时器、高级定时器、PWM、输入捕获等功能的操作。附录与电气特性提供引脚定义、封装尺寸、电气参数、勘误表等实用信息《GD32F470xx_Datasheet_Rev2.2》这是 GD32F470xx 系列芯片的官方数据手册。它提供了该系列芯片的详细电气特性、引脚定义、封装信息、工作条件、直流/交流参数以及订购信息等关键硬件规格。数据手册是进行硬件设计如原理图绘制、PCB 布局、电源设计、外设选型时必须参考的核心文档用于确保芯片在目标应用中的可靠运行。二、独立按键基础知识在开始编写按键检测的代码之前有必要先了解独立按键的基本工作原理和连接方式。本节将简要介绍独立按键的结构、工作原理以及按键消抖的必要性。2.1 独立按键的结构与类型独立按键Push Button Switch是一种常用的电子开关通过按下或释放按钮来接通或断开电路。其基本结构主要包括以下几个部分按钮帽用户直接接触的部分通常由塑料或金属制成用于传递按压力。触点按键的核心导电部分通常由金属弹片或簧片组成。按下时触点闭合电路导通释放时触点断开电路断开。外壳保护内部结构并固定按键的塑料或金属壳体。引脚用于焊接在电路板上的金属引脚通常有2脚、4脚等不同类型。常见的独立按键类型包括常开型NO默认状态下触点断开按下时闭合。常闭型NC默认状态下触点闭合按下时断开。自锁型按下后保持状态再次按下才恢复。轻触开关最常见的类型按下时导通松开后自动弹起断开。2.2 独立按键的工作原理独立按键的工作原理基于机械触点的通断其工作过程如下初始状态按键未按下时内部机械弹簧使触点保持分离状态电路处于断开常开型或导通常闭型状态。按下过程当用户按下按钮时施加的力克服弹簧阻力使动触点与静触点接触电路状态改变常开型变为导通常闭型变为断开。保持状态按键保持按下期间触点保持接触状态电路维持导通或断开。释放过程用户松开按钮后弹簧力使触点恢复初始位置电路状态恢复。在单片机系统中通常使用常开型轻触开关。按键一端接地GND另一端通过上拉电阻连接到电源VCC和单片机GPIO引脚。未按下时GPIO引脚通过上拉电阻保持高电平按下时引脚直接接地变为低电平。2.3 按键消抖原理与必要性机械按键在按下和释放的瞬间由于机械触点的弹性振动会产生多次快速的通断现象这种现象称为“按键抖动”。如果不进行处理单片机可能会误判为多次按键操作。按键抖动的主要特点抖动时间通常持续5-20毫秒具体时间取决于按键质量和按压力度。抖动波形在电平转换期间出现多次快速的高低电平跳变。影响可能导致单次按键被误识别为多次按键影响程序的稳定性和可靠性。常用的消抖方法硬件消抖使用RC滤波电路或施密特触发器来平滑抖动信号。软件消抖通过延时检测或状态机的方式在程序中消除抖动影响这是嵌入式系统中最常用的方法。2.4 按键电路原理图开发板上独立按键的典型连接原理图如图所示。通过上面的原理图可以了解到独立按键的一端连接到电源地GND另一端通过一个上拉电阻如10kΩ连接到电源VCC同时连接到单片机的GPIO引脚。当按键未按下时GPIO引脚通过上拉电阻保持高电平当按键按下时GPIO引脚直接接地电平被拉低至0V。在单片机系统中读取按键状态的基本流程是将GPIO配置为输入模式通常带上拉周期性地读取引脚电平。当检测到低电平时表示按键被按下当检测到高电平时表示按键被释放。通过软件消抖算法可以准确识别出有效的按键动作。三、代码编写3.1 创建工程首先创建一个新的 Keil 工程文件命名为2_GPIO_Key_Polling_mode。具体的创建步骤可以参考本系列教程的第一篇文章。工程创建完成后点击编译按钮编译结果如下图所示。编译成功后接下来开始编写GPIO 按键轮询模式的核心代码。3.2 创建源文件和头文件1. 创建源文件并命名为 my_key在 Keil 工程中展开左侧的Hardware分组右键点击该分组选择Add New Item to Group Hardware...。在弹出的对话框中选择C File (.c)文件类型在Name框中填写my_key在Location框中选择本例程文件夹下的Hardware文件夹具体操作如下图所示。2 创建头文件并命名为 my_key在 Keil 工程中展开左侧的Hardware分组右键点击该分组选择Add New Item to Group Hardware...。在弹出的对话框中选择Header File (.h)文件类型在Name框中填写my_key在Location框中选择本例程文件夹下的Hardware文件夹具体操作如下图所示。3.3 源文件代码编写在上一节的按键原理图中我们已经明确了独立按键与控制引脚的对应关系具体连接情况如下图所示。根据原理图可知要检测按键的按下与释放状态需要配置对应的 GPIO 端口为输入模式。从图中可以看到KEY2 连接至 PA0 引脚KEY3 连接至 PC13 引脚KEY4 连接至 PB14 引脚。因此我们需要配置 GPIOA 、GPIOB和 GPIOC 这三个端口。GD32 官方提供了完善的固件库Firmware Library其中包含了丰富的库函数涵盖了时钟控制、GPIO 配置、中断管理、定时器、通信接口等所有外设功能。这些库函数经过精心设计和测试具有详细的注释和清晰的接口定义。在开发过程中我们无需从零开始编写底层驱动只需根据需求查找并使用相应的库函数即可这大大提高了开发效率和代码可靠性。我们无需从零开始编写底层驱动只需根据需求查找并使用相应的库函数即可这大大提高了开发效率和代码可靠性。1. 开启 GPIO 端口时钟在 GD32 微控制器中所有外设资源的时钟默认都是关闭的以降低功耗。因此在配置和使用任何外设如 GPIO之前必须先开启其对应的时钟。要开启 GPIO 端口的时钟我们需要查找相关的库函数。打开gd32f4xx_rcu.h头文件该文件包含了丰富的时钟控制函数声明。通过函数名可以大致判断其功能我们找到void rcu_periph_clock_enable(rcu_periph_enum periph)函数这正是我们需要的时钟使能函数。该函数接收一个枚举类型参数periph用于指定要开启时钟的外设。查看该枚举的定义可以看到其中定义了各种外设的时钟使能选项包括 GPIO、DMA、定时器等如图所示图中仅展示部分枚举值。2. GPIO 基础模式配置说明GD32F4 系列 GPIO 引脚模式配置分为两个核心配置阶段功能模式配置调用标准库函数指定引脚工作模式可选输入、通用输出、复用功能、模拟输入四类电平阻抗配置调用标准库函数配置引脚内部阻抗特性可选浮空、上拉电阻、下拉电阻三种。以 PA0 连接 KEY2 按键为例需求为检测按键按下时的低电平信号因此引脚需配置为输入模式。硬件设计规范在输入模式下通常需要根据电路设计选择内部上拉或下拉电阻。由于按键电路通常采用上拉电阻设计按键未按下时引脚为高电平按下时接地变为低电平因此需要开启内部上拉电阻以增强抗干扰能力。综上本例 PA0 需配置为输入模式 上拉电阻。式场景需根据引脚默认电平需求选择开启上拉或下拉电阻。综上本例 PD4 需配置为通用输出 浮空阻抗。该功能对应的库函数定义在gd32f4xx_gpio.h头文件中核心接口如下void gpio_mode_set(uint32_t gpio_periph, uint32_t mode, uint32_t pull_up_down, uint32_t pin);函数共 4 个入参释义gpio_periphGPIO 外设端口基地址可选配置参考如图所示mode引脚功能模式输入 / 输出 / 复用 / 模拟可选配置参考如图所示pull_up_down引脚内部上下拉阻抗配置浮空 / 上拉 / 下拉可选配置参考如图所示pin待配置的 GPIO 引脚位掩码可选配置参考。使用上述的二个函数进行void my_key_init(void)函数进行编写如图所示。#include gd32f4xx.h void my_key_init(void) { //KEY2 -- PA0 rcu_periph_clock_enable(RCU_GPIOA); gpio_mode_set(GPIOA,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_PIN_0); //KEY3 -- PC13 rcu_periph_clock_enable(RCU_GPIOC); gpio_mode_set(GPIOC,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_PIN_13); //KEY4 -- PB14 rcu_periph_clock_enable(RCU_GPIOB); gpio_mode_set(GPIOB,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_PIN_14); }3.4 头文件代码编写在嵌入式C语言开发中头文件.h文件扮演着至关重要的角色。它主要用于声明函数原型、宏定义、数据类型和全局变量为源文件.c文件提供接口规范。良好的头文件设计可以提高代码的可读性、可维护性和复用性。其内容如下#ifndef MY_KEY_H #define MY_KEY_H #include gd32f4xx.h void my_key_init(void); #endif下面对头文件中的每一部分进行详细解析防止重复包含的宏定义#ifndef MY_KEY_H和#define MY_KEY_H这是C/C中标准的头文件保护机制。当编译器第一次遇到这个头文件时MY_KEY_H宏尚未定义因此会执行#define MY_KEY_H并继续处理后续内容。如果同一个源文件多次包含了这个头文件第二次及以后遇到时由于MY_KEY_H已经定义#ifndef条件为假编译器会跳过整个头文件内容避免重复定义错误。#endif与#ifndef配对标记头文件保护区域的结束。必要的头文件包含#include gd32f4xx.h这是GD32F4系列微控制器的设备头文件包含了芯片的所有寄存器定义、外设地址映射和基本的类型定义。由于my_key_init函数中使用了GD32的库函数如rcu_periph_clock_enable、gpio_mode_set等必须包含这个头文件才能正确编译。函数声明void my_key_init(void);这是LED初始化函数的声明。它告诉编译器存在这样一个函数返回类型为void无返回值参数列表为void无参数。任何想要使用KEY驱动功能的源文件只需要包含my_key.h头文件就可以调用my_key_init()函数而无需关心函数的具体实现细节。头文件的使用方法在main.c文件中我们需要包含这个头文件才能使用其中声明的函数#include my_key.h int main(void) { // 初始化key相关GPIO引脚 my_key_init(); // 其他初始化代码... while(1) { // 主循环代码... } }通过这样的设计我们将KEY驱动的接口头文件与实现源文件分离符合模块化编程的原则。当其他模块需要使用KEY功能时只需要包含my_key.h头文件并调用my_key_init()函数即可无需了解底层GPIO配置的具体细节。3.5 main文件编写在完成KEY驱动模块的源文件和头文件编写后接下来需要编写主程序文件main.c。该文件是程序的入口点负责初始化系统并实现按键切换LED灯熄灭和点亮。1. 移植必要的源文件和头文件在开始编写main.c文件之前我们需要将上一章节编写的 LED 驱动文件移植到当前工程中。LED 驱动文件包括源文件my_led.c和头文件my_led.h它们包含了控制 LED 的 GPIO 初始化函数。步骤一复制文件到工程目录首先找到上一章节中创建的my_led.c和my_led.h文件将它们复制到当前工程的Hardware文件夹内。操作步骤如下打开文件资源管理器导航到上一章节的工程文件夹。找到Hardware文件夹中的my_led.c和my_led.h文件。复制这两个文件。导航到当前工程的Hardware文件夹。粘贴文件到该文件夹中。步骤二将文件添加到Keil工程文件复制完成后需要在Keil工程中添加这些文件以便编译器能够找到并编译它们。具体操作如下在Keil MDK中展开左侧的工程管理器找到Hardware分组。右键点击Hardware分组选择Add Existing Files to Group Hardware...。在弹出的文件选择对话框中导航到当前工程的Hardware文件夹。选择my_led.c和my_led.h文件按住Ctrl键可多选。点击Add按钮将文件添加到工程。添加完成后可以在Hardware分组下看到这两个文件。验证文件添加成功添加完成后可以点击编译按钮检查是否有错误。如果文件添加成功且路径正确编译应该能够通过。如果出现找不到文件的错误请检查文件是否确实复制到了正确的Hardware文件夹中。Keil工程中的文件路径设置是否正确。文件是否被正确添加到Hardware分组中。2. 包含必要的头文件在main.c文件中我们需要包含以下头文件#include gd32f4xx.h #include my_led.h #include my_key.hgd32f4xx.hGD32F4 系列微控制器的设备头文件包含所有外设寄存器定义和库函数声明。my_led.h自定义的 LED 驱动头文件声明了my_led_init()函数。my_key.h自定义的按键驱动头文件声明了my_key_init()函数。3. 编写 main() 函数main()函数是 C 程序的入口点其基本结构如下int main(void) { // 系统初始化 systick_config(); // 滴答定时器初始化 // 外设初始化 my_led_init(); // 初始化LED相关GPIO my_key_init(); // 初始化按键相关GPIO // 主循环 while(1) { // 应用程序逻辑 } }4. 初始化外设驱动在main()函数开始时需要调用初始化函数来配置所有使用的外设int main(void) { // 系统时钟和滴答定时器初始化 systick_config(); // 外设初始化 my_led_init(); // 初始化LED控制引脚 my_key_init(); // 初始化按键检测引脚 // 主循环 while(1) { // 按键检测和LED控制逻辑 } }5. 实现按键控制LED逻辑现在我们需要实现按键检测和LED控制逻辑。GD32 固件库提供了gpio_input_bit_get()函数来读取GPIO引脚的电平状态我们可以使用这个函数来检测按键是否被按下。完整的main.c文件代码如下#include gd32f4xx.h #include my_led.h #include my_key.h int main(void) { // 系统初始化 systick_config(); // 外设初始化 my_led_init(); // 初始化LED my_key_init(); // 初始化按键 // 主循环 while(1) { // 检测KEY2按键PA0 if(gpio_input_bit_get(GPIOA, GPIO_PIN_0) RESET) { // 按键按下LED1亮 gpio_bit_set(GPIOD, GPIO_PIN_4); // LED1亮 delay_1ms(20); // 消抖延时 } else { // 按键释放LED1灭 gpio_bit_reset(GPIOD, GPIO_PIN_4); // LED1灭 } // 检测KEY3按键PC13 if(gpio_input_bit_get(GPIOC, GPIO_PIN_13) RESET) { // 按键按下LED2亮 gpio_bit_set(GPIOD, GPIO_PIN_5); // LED2亮 delay_1ms(20); // 消抖延时 } else { // 按键释放LED2灭 gpio_bit_reset(GPIOD, GPIO_PIN_5); // LED2灭 } // 检测KEY4按键PB14 if(gpio_input_bit_get(GPIOB, GPIO_PIN_14) RESET) { // 按键按下LED3亮 gpio_bit_set(GPIOG, GPIO_PIN_3); // LED3亮 delay_1ms(20); // 消抖延时 } else { // 按键释放LED3灭 gpio_bit_reset(GPIOG, GPIO_PIN_3); // LED3灭 } } }代码解析头文件包含包含了必要的设备头文件和自定义驱动头文件。外设初始化my_led_init()函数配置了 PD4、PD5 和 PG3 引脚为推挽输出模式。my_key_init()函数配置了 PA0、PC13 和 PB14 引脚为输入模式并启用上拉电阻。主循环逻辑使用gpio_input_bit_get()函数检测每个按键引脚的电平状态。当检测到低电平RESET时表示按键被按下相应的LED点亮。当检测到高电平SET时表示按键被释放相应的LED熄灭。每次检测后添加 20ms 的延时用于按键消抖。按键与LED对应关系KEY2PA0控制 LED1PD4KEY3PC13控制 LED2PD5KEY4PB14控制 LED3PG36. 编译与下载编译工程点击 Keil MDK 工具栏中的编译按钮或按 F7检查是否有语法错误或警告。解决编译问题如果出现未定义delay_1ms()函数的错误需要确保已包含相应的延时库文件或自行实现一个简单的延时函数。连接开发板使用 USB 数据线连接开发板与电脑。下载程序点击下载按钮或按 F8将编译生成的二进制文件烧录到 GD32F470ZKT6 的 Flash 中。观察现象程序运行后按下不同的按键对应的LED灯应该点亮松开按键LED灯熄灭。7. 实验现象验证编译成功且无错误后点击烧录按钮将程序下载到开发板。程序运行后可以观察到以下现象按下 KEY2 按键LED1PD4点亮松开 KEY2LED1 熄灭。按下 KEY3 按键LED2PD5点亮松开 KEY3LED2 熄灭。按下 KEY4 按键LED3PG3点亮松开 KEY4LED3 熄灭。三个按键独立控制对应的LED互不影响。8. 扩展思考按键消抖优化当前的消抖方法使用简单的延时可以尝试使用状态机实现更可靠的按键检测。功能扩展尝试实现按键长按、双击等高级功能。LED控制模式修改代码实现按键控制LED的闪烁模式切换常亮、慢闪、快闪等。多按键组合实现多个按键同时按下时的组合功能。通过本节的实践读者应掌握如何将已有的驱动模块移植到新工程中以及如何编写主程序文件来实现按键检测和LED控制功能。这种模块化的编程方法有助于提高代码的复用性和可维护性。