本文以 立創·天空星開發板-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) { }
}