当前位置: 首页 > news >正文

掌握GPIO基于GD32F407VE的天空星的输入输出控制

掌握GPIO基于GD32F407VE的天空星的输入输出控制

1. GPIO输出模式编程实践

1.1 LED控制基础

以GD32F407VE天空星为例,实现LED闪烁功能:

硬件连接:

  • LED正极 → PB2(通过限流电阻)
  • LED负极 → GND

代码实现步骤:

/*********************************************************** @brief   输出配置 * @param 	rcu  	时钟端口  	RCU_GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	port  	引脚端口  	GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	pin  	引脚  	  	GPIO_PIN_x(x=0..15)* @param 	otype  	输出模式  	GPIO_OTYPE_PP 推挽; GPIO_OTYPE_OD 开漏* @return  **********************************************************/
void GPIO_output(rcu_periph_enum rcu, uint32_t port, uint32_t pin, uint8_t otype) {// 时钟初始化rcu_periph_clock_enable(rcu);// GPIO模式:输出gpio_mode_set(port, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, pin);// 输出选项设置gpio_output_options_set(port, otype, GPIO_OSPEED_MAX, pin);
}void GPIO_config() {// =============== PB2 推挽输出GPIO_output(RCU_GPIOB, GPIOB, GPIO_PIN_2, GPIO_OTYPE_PP);// ============== 底板  有别的引脚也要配置输出
}

1.2 输出模式选择技巧

推挽输出 vs 开漏输出:

模式优点缺点适用场景
推挽输出驱动能力强、高低电平都有主动驱动不能线与连接LED、继电器、电机控制
开漏输出支持线与、电平转换灵活需要外接上拉电阻、高电平驱动能力弱I²C总线、多设备总线

2. GPIO输入模式编程实践

2.1 按键检测基础

2.1.1 核心板按键(PA0)

电路特性:

  • 按下时:高电平(连接VCC)
  • 抬起时:低电平(外部下拉电阻)
/*********************************************************** @brief   输出配置 * @param 	rcu  	时钟端口  	RCU_GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	port  	引脚端口  	GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	pin  	引脚  	  	GPIO_PIN_x(x=0..15)* @param 	otype  	输出模式  	GPIO_OTYPE_PP 推挽; GPIO_OTYPE_OD 开漏* @return  **********************************************************/
void GPIO_output(rcu_periph_enum rcu, uint32_t port, uint32_t pin, uint8_t otype) {// 时钟初始化rcu_periph_clock_enable(rcu);// GPIO模式:输出gpio_mode_set(port, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, pin);// 输出选项设置gpio_output_options_set(port, otype, GPIO_OSPEED_MAX, pin);
}/*********************************************************** @brief   输入配置 * @param 	rcu  			时钟端口  	RCU_GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	port  			引脚端口  	GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	pin  			引脚  	  	GPIO_PIN_x(x=0..15)* @param 	pull_up_down  	上下拉模式  	GPIO_PUPD_NONE 浮空GPIO_PUPD_PULLUP 上拉GPIO_PUPD_PULLDOWN 下拉* @return  **********************************************************/
void GPIO_input(rcu_periph_enum rcu, uint32_t port, uint32_t pin, uint32_t pull_up_down) {// 时钟初始化rcu_periph_clock_enable(rcu);// GPIO模式:输入gpio_mode_set(port, GPIO_MODE_INPUT, pull_up_down, pin);
}void GPIO_config() {// =============== PB2 推挽输出GPIO_output(RCU_GPIOB, GPIOB, GPIO_PIN_2, GPIO_OTYPE_PP);// ============== PA0  浮空输入     外围电路已经下拉了,芯片内部浮空即可GPIO_input(RCU_GPIOA, GPIOA, GPIO_PIN_0, GPIO_PUPD_NONE);
}#define  	KEY0	GPIOA, GPIO_PIN_0
#define   	DOWN0	SET
#define   	UP0		RESET
#define     PB2		GPIOB, GPIO_PIN_2// 上一步状态
FlagStatus last_state0 = UP0;  // 有分号int main(void) {GPIO_config();// 系统滴答定时器初始化systick_config();while(1) {// ==================核心板  PA0=================FlagStatus cur_state0 = gpio_input_bit_get(KEY0);if (last_state0 == UP0 && cur_state0 == DOWN0) {// 上一次是抬起,当前是按下,按下才有效last_state0 = DOWN0; // 保存状态gpio_bit_set(PB2);  // 高电平亮} else if (last_state0 == DOWN0 && cur_state0 == UP0) {// 上一次是按下,当前是抬起,抬起才有效last_state0 = UP0; // 保存状态gpio_bit_reset(PB2);  // 低电平灭}delay_1ms(20);}}
2.1.2 扩展板按键(PC0)

电路特性:

  • 按下时:低电平(接地)
  • 抬起时:高电平(内部上拉)
// 扩展板按键初始化
void ext_key_init(void)
{// 使能GPIOC时钟rcu_periph_clock_enable(RCU_GPIOC);// 配置PC0为上拉输入gpio_mode_set(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_0);
}// 扩展板按键检测
uint8_t ext_key_get_state(void)
{return gpio_input_bit_get(GPIOC, GPIO_PIN_0);
}

2.2 完整的按键控制LED实例

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
/*********************
任务目标:  核心板PA0 控制  PB2灯
PB2 高电平亮,PB2 低电平灭
PA0 按下是高,抬起是低需求:按下  灯亮,抬起灯灭
**********************//*********************************************************** @brief   输出配置 * @param 	rcu  	时钟端口  	RCU_GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	port  	引脚端口  	GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	pin  	引脚  	  	GPIO_PIN_x(x=0..15)* @param 	otype  	输出模式  	GPIO_OTYPE_PP 推挽; GPIO_OTYPE_OD 开漏* @return  **********************************************************/
static inline void GPIO_output(rcu_periph_enum rcu, uint32_t port, uint32_t pin, uint8_t otype) {// 时钟初始化rcu_periph_clock_enable(rcu);// GPIO模式:输出gpio_mode_set(port, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, pin);// 输出选项设置gpio_output_options_set(port, otype, GPIO_OSPEED_MAX, pin);
}/*********************************************************** @brief   输入配置 * @param 	rcu  			时钟端口  	RCU_GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	port  			引脚端口  	GPIOx(x = A,B,C,D,E,F,G,H,I)* @param 	pin  			引脚  	  	GPIO_PIN_x(x=0..15)* @param 	pull_up_down  	上下拉模式  	GPIO_PUPD_NONE 浮空GPIO_PUPD_PULLUP 上拉GPIO_PUPD_PULLDOWN 下拉* @return  **********************************************************/
static inline void GPIO_input(rcu_periph_enum rcu, uint32_t port, uint32_t pin, uint32_t pull_up_down) {// 时钟初始化rcu_periph_clock_enable(rcu);// GPIO模式:输入gpio_mode_set(port, GPIO_MODE_INPUT, pull_up_down, pin);
}void GPIO_config() {// =============== PB2 推挽输出GPIO_output(RCU_GPIOB, GPIOB, GPIO_PIN_2, GPIO_OTYPE_PP);// ============== PA0  浮空输入     外围电路已经下拉了,芯片内部浮空即可GPIO_input(RCU_GPIOA, GPIOA, GPIO_PIN_0, GPIO_PUPD_NONE);// ============= 独立按钮,  上拉输入, 外围电路没有做上下拉处理,交给芯片内部处理GPIO_input(RCU_GPIOC, GPIOC, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3, GPIO_PUPD_PULLUP);}#define  	KEY0	GPIOA, GPIO_PIN_0
#define   	DOWN0	SET
#define   	UP0		RESET
#define     PB2		GPIOB, GPIO_PIN_2// =================== 独立按键
#define  	KEY1	GPIOC, GPIO_PIN_0
#define  	KEY2	GPIOC, GPIO_PIN_1
#define  	KEY3	GPIOC, GPIO_PIN_2
#define  	KEY4	GPIOC, GPIO_PIN_3#define   	DOWN	RESET
#define   	UP		SET// 上一步状态
FlagStatus last_state0 = UP0;  // 有分号
// 独立按键
FlagStatus last_state1 = UP;
FlagStatus last_state2 = UP;
FlagStatus last_state3 = UP;
FlagStatus last_state4 = UP;int main(void) {GPIO_config();// 系统滴答定时器初始化systick_config();while(1) {// ==================核心板  PA0=================FlagStatus cur_state0 = gpio_input_bit_get(KEY0);// 上一次 和 当前 不同if (last_state0 != cur_state0) {last_state0 = cur_state0; // 保存状态// 里面在单独判断按下和抬起if (cur_state0 == DOWN0) {gpio_bit_set(PB2);  // 高电平亮} else {gpio_bit_reset(PB2);  // 低电平灭}}// ==================独立按键1=================FlagStatus cur_state1 = gpio_input_bit_get(KEY1);// 上一次 和 当前 不同if (last_state1 != cur_state1) {last_state1 = cur_state1; // 保存状态// 里面在单独判断按下和抬起if (cur_state1 == DOWN) {gpio_bit_set(PB2);  // 高电平亮} else {gpio_bit_reset(PB2);  // 低电平灭}}delay_1ms(20);}}

4. 按键消抖

4.1 软件消抖实现

4.1.1 简单延时消抖
// 简单延时消抖
uint8_t key_scan_debounce_simple(GPIO_TypeDef* GPIOx, uint32_t pin)
{if(gpio_input_bit_get(GPIOx, pin) == SET) { // 检测到按键按下delay_ms(20); // 等待20msif(gpio_input_bit_get(GPIOx, pin) == SET) { // 再次确认return 1;}}return 0;
}
4.2.2 状态机消抖(推荐)
// 按键状态机消抖
typedef enum {KEY_STATE_RELEASED,      // 按键释放状态KEY_STATE_DEBOUNCE,      // 消抖状态KEY_STATE_PRESSED,       // 按键按下状态KEY_STATE_LONG_PRESS     // 长按状态
} key_state_t;typedef struct {GPIO_TypeDef* gpio;uint32_t pin;key_state_t state;uint32_t press_time;uint8_t last_phys_state;
} key_t;// 按键处理函数
key_state_t key_process(key_t* key)
{uint8_t current_phys_state = gpio_input_bit_get(key->gpio, key->pin);uint32_t current_time = get_system_tick();switch(key->state) {case KEY_STATE_RELEASED:if(current_phys_state != key->last_phys_state) {key->state = KEY_STATE_DEBOUNCE;key->press_time = current_time;}break;case KEY_STATE_DEBOUNCE:if(current_time - key->press_time > 20) { // 20ms消抖时间if(current_phys_state != key->last_phys_state) {if(current_phys_state == SET) { // 按键按下key->state = KEY_STATE_PRESSED;key->last_phys_state = current_phys_state;return KEY_STATE_PRESSED;}}key->state = KEY_STATE_RELEASED;}break;case KEY_STATE_PRESSED:if(current_phys_state != key->last_phys_state) {key->state = KEY_STATE_DEBOUNCE;key->press_time = current_time;} else if(current_time - key->press_time > 1000) { // 长按1秒key->state = KEY_STATE_LONG_PRESS;return KEY_STATE_LONG_PRESS;}break;case KEY_STATE_LONG_PRESS:if(current_phys_state != key->last_phys_state) {key->state = KEY_STATE_DEBOUNCE;key->press_time = current_time;}break;}key->last_phys_state = current_phys_state;return key->state;
}
4.2.3 使用状态机的完整示例
// 初始化按键结构
key_t core_key = {.gpio = GPIOA,.pin = GPIO_PIN_0,.state = KEY_STATE_RELEASED,.last_phys_state = 0
};key_t ext_key = {.gpio = GPIOC,.pin = GPIO_PIN_0,.state = KEY_STATE_RELEASED,.last_phys_state = 1  // 扩展板按键默认高电平
};// 使用状态机处理按键
void advanced_key_demo(void)
{led_init();key_init();ext_key_init();while(1) {key_state_t core_state = key_process(&core_key);key_state_t ext_state = key_process(&ext_key);// 处理核心板按键事件switch(core_state) {case KEY_STATE_PRESSED:led_toggle(); // 按下切换LED状态break;case KEY_STATE_LONG_PRESS:gpio_bit_set(GPIOB, GPIO_PIN_2); // 长按点亮LEDbreak;default:break;}// 处理扩展板按键事件switch(ext_state) {case KEY_STATE_PRESSED:led_toggle(); // 按下切换LED状态break;default:break;}delay_ms(5); // 5ms扫描周期}
}

5. 输入模式选择指南

5.1 不同输入模式的应用场景

输入模式内部电路适用场景注意事项
浮空输入无上拉下拉外部电路已处理电平必须确保外部有确定电平
上拉输入内部上拉电阻按钮到地、开漏输出默认高电平,按下为低
下拉输入内部下拉电阻按钮到VCC、推挽输出默认低电平,按下为高
模拟输入直连ADC模拟信号采集不能用于数字信号

5.2 实际应用建议

  1. 按钮输入

    • 如果按钮连接VCC → 使用下拉输入
    • 如果按钮连接GND → 使用上拉输入
    • 如果外部已有电阻 → 使用浮空输入
  2. 传感器输入

    • 数字传感器 → 根据传感器输出特性选择
    • 模拟传感器 → 必须使用模拟输入
  3. 通信接口

    • I²C → 开漏输出 + 上拉输入
    • UART → 推挽输出 + 浮空输入

6. 函数注释翻译

/* 函数声明 */
/* 重置 GPIO端口 */
void gpio_deinit(uint32_t gpio_periph);
/* 设置GPIO模式 */
void gpio_mode_set(uint32_t gpio_periph, uint32_t mode, uint32_t pull_up_down, uint32_t pin);
/* 设置GPIO输出类型和速度 */
void gpio_output_options_set(uint32_t gpio_periph, uint8_t otype, uint32_t speed, uint32_t pin);/* 设置GPIO引脚 */
void gpio_bit_set(uint32_t gpio_periph, uint32_t pin);
/* 重置GPIO引脚 */
void gpio_bit_reset(uint32_t gpio_periph, uint32_t pin);
/* 向指定GPIO引脚写入数据 */
void gpio_bit_write(uint32_t gpio_periph, uint32_t pin, bit_status bit_value);
/* 向指定GPIO端口写入数据 */
void gpio_port_write(uint32_t gpio_periph, uint16_t data);/* 获取GPIO引脚输入状态 */
FlagStatus gpio_input_bit_get(uint32_t gpio_periph, uint32_t pin);
/* 获取GPIO端口输入状态 */
uint16_t gpio_input_port_get(uint32_t gpio_periph);
/* 获取GPIO引脚输出状态 */
FlagStatus gpio_output_bit_get(uint32_t gpio_periph, uint32_t pin);
/* 获取GPIO端口输出状态 */
uint16_t gpio_output_port_get(uint32_t gpio_periph);/* 设置GPIO复用功能 */
void gpio_af_set(uint32_t gpio_periph, uint32_t alt_func_num, uint32_t pin);
/* 锁定GPIO引脚 */
void gpio_pin_lock(uint32_t gpio_periph, uint32_t pin);/* 翻转GPIO引脚状态 */
void gpio_bit_toggle(uint32_t gpio_periph, uint32_t pin);
/* 翻转GPIO端口状态 */
void gpio_port_toggle(uint32_t gpio_periph);

函数作用解释

  1. gpio_deinit

    • 作用:将指定的GPIO端口恢复到复位状态(默认状态)
    • 说明:用于初始化或重置整个GPIO端口的配置,清除之前的所有设置
  2. gpio_mode_set

    • 作用:配置GPIO引脚的工作模式
    • 参数说明:
      • mode:指定模式(输入/输出/复用功能/模拟模式等)
      • pull_up_down:上拉/下拉电阻配置
      • pin:指定要配置的引脚
    • 说明:GPIO的核心配置函数,决定引脚的基本工作方式
  3. gpio_output_options_set

    • 作用:配置GPIO输出引脚的特性
    • 参数说明:
      • otype:输出类型(推挽/开漏)
      • speed:输出速度(如低速/中速/高速)
      • pin:指定要配置的引脚
    • 说明:仅对配置为输出模式的引脚有效
  4. gpio_bit_set

    • 作用:将指定GPIO引脚设置为高电平
    • 说明:快速将引脚置为高电平(1)
  5. gpio_bit_reset

    • 作用:将指定GPIO引脚设置为低电平
    • 说明:快速将引脚置为低电平(0)
  6. gpio_bit_write

    • 作用:向指定GPIO引脚写入特定电平值
    • 参数说明:
      • bit_value:要写入的值(高/低电平)
    • 说明:比set/reset更灵活,可以根据参数值决定写入高或低电平
  7. gpio_port_write

    • 作用:向整个GPIO端口写入16位数据
    • 说明:一次性配置端口的所有引脚状态(16位对应16个引脚)
  8. gpio_input_bit_get

    • 作用:读取指定GPIO引脚的输入状态
    • 返回值:引脚的当前输入电平(高/低)
    • 说明:用于检测外部输入信号
  9. gpio_input_port_get

    • 作用:读取整个GPIO端口的输入状态
    • 返回值:16位数据,每一位对应一个引脚的输入状态
    • 说明:一次性获取所有引脚的输入状态
  10. gpio_output_bit_get

    • 作用:获取指定GPIO引脚的当前输出状态
    • 返回值:引脚当前的输出电平(高/低)
    • 说明:查询之前设置的输出状态
  11. gpio_output_port_get

    • 作用:获取整个GPIO端口的当前输出状态
    • 返回值:16位数据,每一位对应一个引脚的输出状态
    • 说明:查询整个端口的输出配置
  12. gpio_af_set

    • 作用:配置GPIO引脚的复用功能
    • 参数说明:
      • alt_func_num:复用功能编号(如UART、SPI等外设功能)
    • 说明:用于将引脚分配给特定的外设功能,而不是作为普通GPIO使用
  13. gpio_pin_lock

    • 作用:锁定指定GPIO引脚的配置
    • 说明:锁定后,引脚配置无法更改,直到下次复位,防止意外修改
  14. gpio_bit_toggle

    • 作用:翻转指定GPIO引脚的状态
    • 说明:如果当前是高电平则变为低电平,反之亦然(常用于LED闪烁等场景)
  15. gpio_port_toggle

    • 作用:翻转整个GPIO端口所有引脚的状态
    • 说明:一次性翻转端口所有引脚的电平状态
http://www.dtcms.com/a/453721.html

相关文章:

  • 九【Python新手入门指南】极速搭建Python开发环境s
  • 四川成都企业高端网站建设一站式的手机网站制作
  • 【LeetCode热题100(36/100)】二叉树的中序遍历
  • 企业建立网站的必要性画网页前端界面的软件
  • Docker 基础命令的 6 大核心模块
  • 十大购物网站产品50个关键词
  • Kiln AI:重新定义AI系统构建的全栈开源平台深度解析
  • 测试epoll、io_uring的百万连接、建连、qps,以及qps客户端的实现
  • 做游戏女角色去衣的网站网站名 注册
  • Rust中所有权和作用域及生命周期
  • 外贸网站啥需要掌握在自己手里中企动力手机邮政登录
  • 二维码制作网站有哪些618网络营销策划方案
  • 【论文学习】2025年图像处理顶会论文
  • 【MyBatis】——执行过程
  • 修改配置文件之后,重启edge浏览器收藏夹消失怎么办?
  • 网站推广优化外包公司个人网站设计开题报告
  • 组播实验-IGMP、IGMP Snooping及PIM-DM协议
  • 企业seo网站推广公司wordpress 下载受限
  • 第十二篇:std::shared_ptr和std::weak_ptr:共享所有权与解决循环引用
  • 第十九章:千变万化,随心而动——State的状态艺术
  • Harmony中EventHub实现发布订阅
  • 高效学习方法——知识关联性
  • 教育类网站素材总部在上海的世界500强企业
  • CCF编程能力等级认证GESP—C++6级—20250927
  • libopenssl-1_0_0-devel-1.0.2p RPM 包安装教程(openSUSE/SLES x86_64)​
  • 网站开发栏目需求1仪征网站建设公司哪家好
  • FK 外键上需要创建index 避免 主表update时 的lock
  • 三剑合璧:C++11 lambda、variadic template 与 wrapper 的协奏
  • 空间智能找文献方向
  • 儒枫网网站建设惠州 企业网站建设