開發環境:
MDK:Keil 5.30
MCU:GD32F207IK
2.1 GPIO工作原理
熟悉單片機的朋友都知道,學習的第一個例程就是流水燈,要想實作流水燈,首先必須了解GPIO的工作原理。GPIO的基本結構如下圖所示。
GD32 的 IO 口可以由軟體配置成如下 8 種模式:
輸入模式
浮空輸入:浮空(floating)就是邏輯器件的輸入引腳即不接高電平,也不接低電平。由于邏輯器件的内部結構,當它輸入引腳懸空時,相當于該引腳接了高電平。一般實際運用時,引腳不建議懸空,易受幹擾。 通俗講就是讓管腳什麼都不接,浮空着。信号進入晶片内部後,既沒有接上拉電阻也沒有接下拉電阻,經由觸發器輸入。配置成這個模式後,用電壓變量引腳電壓為1點幾伏,這是個不确定值。由于其輸入阻抗比較大,一般把這種模式用于标準的通訊協定,比如IIC、USART的等。該模式是GD32複位之後的預設模式。
上拉輸入:上拉就是把電位拉高,比如拉到Vcc。上拉就是将不确定的信号通過一個電阻嵌位在高電平,電阻同時起限流作用,弱強隻是上拉電阻的阻值不同,沒有什麼嚴格區分。上拉輸入就是信号進入晶片後加了一個上拉電阻,再經過施密特觸發器轉換成0、1信号,讀取此時的引腳電平為高電平;
下拉輸入:就是把電壓拉低,拉到GND。與上拉原理相似。下拉輸入就是信号進入晶片後加了一個下拉電阻,再經過施密特觸發器轉換成0、1信号,讀取此時的引腳電平為低電平;
模拟輸入:信号進入後不經過上拉電阻或者下拉電阻,關閉施密特觸發器,經由另一線路把電壓信号傳送到片上外設子產品。模拟輸入是指傳統方式的輸入,數字輸入是輸入PCM數字信号,即0、1的二進制數字信号,通過數模轉換,轉換成模拟信号,經前級放大進入功率放大器,功率放大器還是模拟的。比如傳送給ADC子產品,由ADC采集電壓信号。是以可以了解為模拟輸入的信号是未經處理的信号,是原汁原味的信号。
輸出模式
開漏輸出:一般用在電平不比對的場合,如需要輸出5V的高電平。輸出端相當于三極管的集電極,要得到高電平狀态需要上拉電阻才行。适合于做電流型的驅動,其吸收電流的能力相對強(一般20mA以内)。
複用開漏輸出:可以了解為GPIO口被用作第二功能時的配置情況(即并非作為通用IO口使用)。端口必須配置成複用開漏功能輸出模式。
推挽式輸出:可以輸出高、低電平,連接配接數字器件;推挽結構一般是指兩個三極管分别受兩個互補信号的控制,總是在一個三極管導通的時候另一個截止。高低電平由IC的電源決定。推挽電路是兩個參數相同的三極管或MOSFET,以推挽方式存在于電路中,各負責正負半周的波形放大任務,電路工作時,兩隻對稱的功率開關管每次隻有一個導通,是以導通損耗小、效率高。輸出既可以向負載灌電流,也可以從負載抽取電流。推拉式輸出級既提高電路的負載能力,又提高開關速度。
推挽式複用輸出
2.2 I/O複用和重映射
2.2.1 I/O複用
GD32 有很多的内置外設,這些外設的外部引腳都是與 GPIO 複用的。也就是說,一個 GPIO如果可以複用為内置外設的功能引腳,那麼當這個 GPIO 作為内置外設使用的時候,就叫做複用。當I/O端口被配置為複用功能時:
● 在開漏或推挽式配置中,輸出緩沖器被打開
● 内置外設的信号驅動輸出緩沖器(複用功能輸出)
● 施密特觸發輸入被激活
● 弱上拉和下拉電阻被禁止
● 在每個APB2時鐘周期,出現在I/O腳上的資料被采樣到輸入資料寄存器
● 開漏模式時,讀輸入資料寄存器時可得到I/O口狀态
● 在推挽模式時,讀輸出資料寄存器時可得到最後一次寫的值
大家都知道,MCU 都有序列槽,GD32 有好幾個序列槽。比如說 GD32F207IK有 8個序列槽,我們可以查手冊知道,序列槽 0 的引腳對應的 IO 為 PA9,PA10.PA9, PA10 預設功能是 GPIO, 是以當PA9,PA10 引腳作為序列槽0的 TX,RX 引腳使用的時候,那就是端口複用。
引腳 | pin |
}USART0_TX | PA9 |
USART0_RX | PA10 |
關于序列槽的内容後面的章節會詳細講解。
2.2.2 I/O重映射
為了使不同器件封裝的外設 IO 功能數量達到最優,可以把一些複用功能重新映射到其他一些引腳上。 GD32 中有很多内置外設的輸入輸出引腳都具有重映射(remap)的功能。 我們知道每個内置外設都有若幹個輸入輸出引腳,一般這些引腳的輸出端口都是固定不變的,為了讓設計工程師可以更好地安排引腳的走向和功能,在 GD32中引入了外設引腳重映射的概念,即一個外設的引腳除了具有預設的端口外,還可以通過設定重映射寄存器的方式,把這個外設的引腳映射到其它的端口。
複用功能 | USART1_REMAP = 0 | USART1_REMAP = 1 |
USART0_TX | PA9 | PB6 |
USART0_RX | PA10 | PB7 |
從表中可以看出,預設情況下,序列槽 0複用的時候的引腳位 PA9、PA10,同時我們可以将 TX 和 RX 重新映射到管腳 PB6 和 PB7 上面去。是以重映射我們同樣要使能複用功能的時候講解的 2 個時鐘外,還要使能 AFIO 功能時鐘,然後要調用重映射函數。
2.3 GPIO流水燈硬體電路分析
發光二極管是屬于二極管的一種,具有二級管單向導電特性,即隻有在正向電壓(二極管的正極接正,負極接負)下才能導通發光。PF6引腳接發光二極管(LED1)的正極,是以PF6引腳輸出高電平LED1亮,PF6引腳輸出低電平LED1熄滅,其他LED同理。
值得注意的,不同的開發闆,LED連接配接的GPIO一般是不同的,請注意修改。
2.4 GPIO流水燈寄存器分析
要想真正掌握一款單片機,分析寄存器是必不可少,但是對于GD32來再說,GD已經将寄存器操作封裝成庫函數,開發者隻需要調用庫函數即可,對于初學者來說,隻需學會使用使用函數即可,對于沒有基礎的讀者朋友就不必細究每個寄存器,當學到一定程度,再來一探究竟吧,筆者再這裡隻是給出GPIO的寄存配置相關配置表,在後面的章節也是如此。好了,繼續進入正題吧。
每個GPIO端口都有兩個32位配置寄存器(GPIO_CTL0 ,GPIO_CTL1) ,兩個16位資料寄存器 (GPIO_ISTAT和GPIO_OCTL),一個32位置位寄存器(GPIO_BOP),一個16位複位寄存器(GPIO_BC),一個16位鎖定寄存器(GPIO_LOCK)。每個I/O端口位可以自由程式設計。
點亮LED,基本步驟是:配置寄存器;控制寄存器。庫開發隻是将傳統的配置方式程式設計函數,是的單片機開發變得簡單友善快捷。
我們常用的 IO 端口寄存器隻有 4 個: GPIO_CTL0、GPIO_CTL1、 GPIO_OCTL、 GPIO_BOP。其中GPIO_CTL0、GPIO_CTL1 控制着每個 IO 口的模式及輸出速率。
GPIO_CTL0、GPIO_CTL1類似,讀者朋友可以參看《GD32F10x_User_Manual_EN_Rev2.4》資料輸入輸出寄存器是将對應的IO口置位,進而進行資料的輸入與輸出。
2.5 GPIO 流水燈實作流程
筆者在上文已經分析了GPIO的原理及操作步驟,現在我們就來寫代碼吧。
GPIO是開發GD32最基本的配置,是以掌握GPIO的配置顯得尤為重要。要實作流水燈,一般步驟可以總結為如下:
1)GPIO 時鐘使能;
2)GPIO 端口模式設定;
3)初始化IO口;
4)編寫處理函數;
2.6 GPIO 流水燈實作
2.6.1 GPIO庫函數
GPIO庫函數相關的庫函數如下:
gpio_deinit 複位外設GPIO
gpio_afio_deinit 複位AFIO
gpio_init GPIO參數初始化
gpio_bit_set 置位引腳值
gpio_bit_reset 複位引腳值
gpio_bit_write 将特定的值寫入引腳
gpio_port_write 将特定的值寫入一組端口
gpio_input_bit_get 擷取引腳的輸入值
gpio_input_port_get 擷取一組端口的輸入值
gpio_output_bit_get 擷取引腳的輸出值
gpio_output_port_get 擷取一組端口的輸出值
gpio_pin_remap_config 配置GPIO引腳重映射
gpio_pin_remap1_config 配置GPIO引腳重映射1
gpio_exti_source_select 選擇哪個引腳作為EXTI源
gpio_ethernet_phy_select 以太網MII或RMII PHY選擇
gpio_event_output_config 配置事件輸出
gpio_event_output_enable 事件輸出使能
gpio_event_output_disable 事件輸出禁能
gpio_pin_lock 相應的引腳配置被鎖定
2.6.2流水燈代碼實作
核心代碼如下:
/* Includes*********************************************************************/
#include "gd32f20x.h"
/*簡單延時函數*/
void delay(uint32_t xms);
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
/* enable the LEDs GPIO clock */
rcu_periph_clock_enable(RCU_GPIOF);
/* configure LED1 GPIO port */
gpio_init(GPIOF, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
/* reset LED1 GPIO pin */
gpio_bit_reset(GPIOF, GPIO_PIN_6);
/* configure LED2 GPIO port */
gpio_init(GPIOF, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
/* reset LED2 GPIO pin */
gpio_bit_reset(GPIOF, GPIO_PIN_7);
/* configure LED3 GPIO port */
gpio_init(GPIOF, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
/* reset LED3 GPIO pin */
gpio_bit_reset(GPIOF, GPIO_PIN_8);
/* configure LED4 GPIO port */
gpio_init(GPIOF, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
/* reset LED4 GPIO pin */
gpio_bit_reset(GPIOF, GPIO_PIN_9);
while(1)
{
/* turn on LED1, turn off LED4 */
gpio_bit_set(GPIOF, GPIO_PIN_6);
gpio_bit_reset(GPIOF, GPIO_PIN_9);
/*delay about 500ms*/
delay(0xffffff);
/* turn on LED2, turn off LED1 */
gpio_bit_set(GPIOF, GPIO_PIN_7);
gpio_bit_reset(GPIOF, GPIO_PIN_6);
/*delay about 500ms*/
delay(0xffffff);
/* turn on LED3, turn off LED2 */
gpio_bit_set(GPIOF, GPIO_PIN_8);
gpio_bit_reset(GPIOF, GPIO_PIN_7);
/*delay about 500ms*/
delay(0xffffff);
/* turn on LED4, turn off LED3 */
gpio_bit_set(GPIOF, GPIO_PIN_9);
gpio_bit_reset(GPIOF, GPIO_PIN_8);
/*delay about 500ms*/
delay(0xffffff);
}
}
/**
* @brief 延時函數
* @param
xms 延時長度
* @retval None
*/
void delay( uint32_t xms)
{
//for(; nCount != 0; nCount--);(方法一)
while(xms--);//(方法二)
}
代碼還是比較簡單的,首先開啟GPIO的時鐘,然後對GPIO初始化,主要是設定模式和速率,然後就可以控制GPIO高低電平了。
2.7實驗現象
将編譯好的程式下載下傳到闆子中,可以看到四個LED燈不同地閃爍。
歡迎通路我的網站
BruceOu的哔哩哔哩
BruceOu的首頁
BruceOu的部落格
BruceOu的CSDN部落格
BruceOu的簡書
BruceOu的知乎
資源擷取方式
1.關注公衆号[嵌入式實驗樓]