本文以 立创·天空星开发板-GD32F407VET6-青春版 作为学习的板子,记录学习笔记。
立创·天空星开发板-GD32F407VE-GPIO
- 基础概念
- 三极管
- MOS管
- GPIO输出模式
- 输出线与
- GPIO输入模式
- GPIO点灯
基础概念
- GPIO,全称为“通用输入/输出”(General Purpose Input/Output),是计算机系统中用于与外部世界进行数字通信的一种接口标准。
- 它允许硬件和软件通过电信号来交换数据,控制外部设备或接收外部事件。
- GPIO通常用于连接各种外设,如按钮、LED灯、传感器、马达、继电器等,以便与计算机系统进行交互。GD32 支持的 GPIO 模式有如下八种:
模式 | 性质 |
---|---|
浮空输入 | 数字输入 |
上拉输入 | 数字输入 |
下拉输入 | 数字输入 |
模拟输入 | 模拟输入 |
开漏输出 | 数字输出 |
推挽输出 | 数字输出 |
复用开漏输出 | 数字输出 |
复用推挽输出 | 数字输出 |
三极管
总是记混 NPN 和 PNP 这两种型号的三极管,如下图所示:
特性描述:
- 电流关系: I E = I B + I C I_E = I_B + I_C IE=IB+IC
- 导通条件: NPN型的基极比发射极电压高0.7v,PNP型的基极比发射极电压低0.7v
- 设计原理图: 无论是 NPN 还是 NPN 型的三极管,耗电元器件都需要接在集电极
助记小技巧:
- 电路图中,箭头永远指向的 N 极,根据箭头可快速确认是 NPN 还是 PNP
- 电路图中,箭头对应的极比箭尾对应的极的电压要低
思维导图:
MOS管
MOS管有 NMOS 和 PMOS 两种类型。MOS管包含了三个极:
- 栅极(G),对应英文单词:Gate
- 漏极(D),对应英文单词:Drain
- 源极(S),对应英文单词:Source
MOS管的作用就是开关,通过栅极控制漏极和源极的导通。主要关注两个点:
- 控制:负责MOS管导通和截止,高电平导通还是低电平导通。
- 流向:是从漏极流向源极,还是从源极流向漏极。
MOS管和三级管主要区别:三极管导通有电流,而MOS管导通没有电流(有点像继电器)
一张图搞懂 MOS 管,如下所示:
如果 Input 为高电平,PMOS 断开,NMOS 导通,如果 GPIO_PIN_X 有上拉电阻,则电流可以顺利从 NMOS 的漏极(D)流向源极(S)。
如果 Input 为低电平,PMOS 导通,NMOS 断开,如果 GPIO_PIN_X 有下拉电阻,则电流可以顺利从 PMOS 的源极(S)流向漏极(D)。
特点描述:
- PMOS 的栅极(G)低通高断,导通时,电流方向是源极(S)流向漏极(D)
- PMOS 可以类比 PNP 类型的三极管
- NMOS 的栅极(G)高通低断,导通时,电流方向是漏极(D)流向源极(S)
- NMOS 可以类比 NPN 类型的三极管
思维导图:
GPIO输出模式
- 推挽输出
- 【推】寄存器控制输出高电平时,过非门后变低电平,PMOS导通,外部引脚为高,电流流出
- 【挽】寄存器控制输出低电平时,过非门后变高电平,NMOS导通,外部引脚为低,电流流入
- 开漏输出
- 寄存器控制输出低电平时,过非门后变高电平,NMOS导通,外部引脚为低,电流流入
- 寄存器控制输出高电平时,因为PMOS未接入,所以外部引脚断开。
- 高阻态
- 因为 PMOS和 NMOS 均未接入,无论寄存器输出高或者低,外部引脚始终断开。
- 复用开漏输出 和 复用推挽输出
- 不经过寄存器来输出高低电平,也就是下图中的
Alternate function output
部分
输出线与
- 推挽线与
推挽过程中,如果一方输出高,一方输出低,则会烧芯片。因此,推挽是不可以线与的。
2. 开漏线与
开漏过程中,无论双方输出高低电平,芯片都不会收到影响。I2C就是线与的一个实例。
GPIO输入模式
- 浮空输入
- 就是将模拟信号、上拉、下拉全部断开,只接收外部电路的输入信号。如下图红色线条所示:
- 上拉输入
- 过上拉电阻后,经由斯密特触发器,写入寄存器。如下图红色线条所示:
- 过斯密特后,也可做复用输入,不写入寄存器
- 下拉输入
- 过下拉电阻后,经由斯密特触发器,写入寄存器。如下图红色线条所示:
- 过斯密特后,可做复用输入,不写入寄存器
- 模拟输入
- 不经过斯密特触发器,直接读入。如下图红色线条所示:
GPIO点灯
我用 GPIO 封装了一个可以动态点亮多个 LED 的拓展驱动,针对 PD 端口, 可以动态增加 LED 灯的引脚。只需要修改 LED_PINS 数组的元素即可。
- ExtendedLED.h
#ifndef __EXTENDED_LED_H__
#define __EXTENDED_LED_H__#include "gd32f4xx.h"
#include "systick.h"#define LED_RCU RCU_GPIOD
#define LED_PORT GPIOD
#ifndef BIT
#define BIT(x) ((uint16_t)((uint16_t)0x01U<<(x)))
#endif// 声明需要针对的引脚, PDx(x=8...15)
static uint32_t LED_PINS[] = {BIT(8), BIT(9), BIT(10), BIT(11),BIT(12), BIT(13), BIT(14), BIT(15)
};/*!\brief 初始化 LED\param[in] none\param[out] none\retval none
*/
void extended_led_init();/*!\brief 熄灭所有 LED 灯\param[in] none\param[out] none\retval none
*/
void extended_led_turn_off_all();/*!\brief 点亮所有 LED 灯\param[in] none\param[out] none\retval none
*/
void extended_led_turn_on_all();/*!\brief 熄灭 LED 灯\param[in] index[int]: LED 灯在 LED_PINS 数组中的索引, -1 针对所有引脚\param[out] none\retval none
*/
void extended_led_turn_off(int index);/*!\brief 点亮 LED 灯\param[in] index[int]: LED 灯在 LED_PINS 数组中的索引, -1 针对所有引脚\param[out] none\retval none
*/
void extended_led_turn_on(int index);/*!\brief 运行多个共阳 LED 灯示例: extended_led_run(0, 1, 1); 效果为:从第一个灯开始依次亮起, 切灯过程不熄灭其他灯[流水灯]示例: extended_led_run(0, 1, 0); 效果为:从第一个灯开始依次亮起, 切灯过程会熄灭其他灯[跑马灯]示例: extended_led_run(0, 2, 1); 效果为:按 0.2.4.6 顺序依次亮起, 切灯过程不熄灭其他灯[流水灯]示例: extended_led_run(1, 2, 0); 效果为:按 1.3.5.7 顺序依次亮起, 切灯过程会熄灭其他灯[跑马灯]示例: extended_led_run(6, -2, 1); 效果为:按 6.4.2.0 顺序依次亮起, 切灯过程不熄灭其他灯[流水灯]示例: extended_led_run(7, -2, 0); 效果为:按 7.5.3.1 顺序依次亮起, 切灯过程会熄灭其他灯[跑马灯]\param[in] status [uint16_t]: 表示灯的状态, 可选值有 (亮:0) 或 (灭:1)\param[in] start [uint32_t]: 表示从哪个灯开始亮, 可选值有 0...\param[in] step [int16_t] : 表示切灯的时候跨越步长, 可选值有 0...\param[in] flowing [uint16_t]: 表示切灯的时候是否采用流水灯\param[out] none\retval none
*/
void extended_led_run(uint16_t status, uint32_t start, int16_t step, uint16_t flowing);#endif
- ExtendedLED.c
#include "ExtendedLED.h"// 获取 LED 灯的总数
static uint16_t pins_length(void) {return sizeof(LED_PINS) / sizeof(LED_PINS[0]);
}// 获取所有引脚
static uint16_t pins_all(void) {int length = 0, i = 0;length = pins_length();uint16_t pin = (uint16_t)0x00U;for(i = 0; i < length; i++) {pin |= LED_PINS[i];}return pin;
}/*!\brief 初始化 LED\param[in] none\param[out] none\retval none
*/
void extended_led_init() {rcu_periph_clock_enable(LED_RCU);gpio_mode_set(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pins_all());gpio_output_options_set(LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, pins_all());gpio_bit_set(GPIOD, pins_all());
}/*!\brief 熄灭所有 LED 灯\param[in] none\param[out] none\retval none
*/
void extended_led_turn_off_all() { extended_led_turn_off(-1); }/*!\brief 点亮所有 LED 灯\param[in] none\param[out] none\retval none
*/
void extended_led_turn_on_all() { extended_led_turn_on(-1); }/*!\brief 熄灭 LED 灯\param[in] index[int]: LED 灯在 LED_PINS 数组中的索引, -1 针对所有引脚\param[out] none\retval none
*/
void extended_led_turn_off(int index) {int length = pins_length();if (index >= length) {return;}if (index < 0) {gpio_bit_set(GPIOD, pins_all()); // 负数针对所有return;}gpio_bit_set(GPIOD, LED_PINS[index]);
}/*!\brief 点亮 LED 灯\param[in] index[int]: LED 灯在 LED_PINS 数组中的索引, -1 针对所有引脚\param[out] none\retval none
*/
void extended_led_turn_on(int index) {int length = pins_length();if (index >= length) {return;}if (index < 0) {gpio_bit_reset(GPIOD, pins_all()); // 负数针对所有return;}gpio_bit_reset(GPIOD, LED_PINS[index]);}/*!\brief 运行多个共阳 LED 灯示例: extended_led_run(0, 1, 1); 效果为:从第一个灯开始依次亮起, 切灯过程不熄灭其他灯[流水灯]示例: extended_led_run(0, 1, 0); 效果为:从第一个灯开始依次亮起, 切灯过程会熄灭其他灯[跑马灯]示例: extended_led_run(0, 2, 1); 效果为:按 0.2.4.6 顺序依次亮起, 切灯过程不熄灭其他灯[流水灯]示例: extended_led_run(1, 2, 0); 效果为:按 1.3.5.7 顺序依次亮起, 切灯过程会熄灭其他灯[跑马灯]示例: extended_led_run(6, -2, 1); 效果为:按 6.4.2.0 顺序依次亮起, 切灯过程不熄灭其他灯[流水灯]示例: extended_led_run(7, -2, 0); 效果为:按 7.5.3.1 顺序依次亮起, 切灯过程会熄灭其他灯[跑马灯]\param[in] status [uint16_t]: 表示灯的状态, 可选值有 (亮:0) 或 (灭:1)\param[in] start [uint32_t]: 表示从哪个灯开始亮, 可选值有 0...\param[in] step [int16_t] : 表示切灯的时候跨越步长, 可选值有 0...\param[in] flowing [uint16_t]: 表示切灯的时候是否采用流水灯\param[out] none\retval none
*/
void extended_led_run(uint16_t status, uint32_t start, int16_t step, uint16_t flowing) {int length = pins_length();int i = 0;if(start < 0 || start >= length) {start = 0;}if(start + step >= length) {start = length - 1 - (step - 1);}for(i = start; i < length && i > -1; i += step) {if(status) {// 熄灭指定索引的灯extended_led_turn_off(i);} else {// 点亮指定索引的灯if(!flowing) {// 如果不是流水灯, 每次切灯之前都需要关掉所有灯extended_led_turn_off(-1);delay_1ms(200);}extended_led_turn_on(i);}delay_1ms(200);}
}
- main.c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>#include "ExtendedLED.h"int main(void) {systick_config();extended_led_init();extended_led_run(0, 0, 1, 1);extended_led_run(1, 7, -1, 1);extended_led_run(0, 0, 1, 0);extended_led_turn_off_all();while(1) { }
}