天天看點

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

開發環境:

MDK:Keil 5.30

MCU:GD32F207IK

2.1 GPIO工作原理

熟悉單片機的朋友都知道,學習的第一個例程就是流水燈,要想實作流水燈,首先必須了解GPIO的工作原理。GPIO的基本結構如下圖所示。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

GD32 的 IO 口可以由軟體配置成如下 8 種模式:

 輸入模式

 浮空輸入:浮空(floating)就是邏輯器件的輸入引腳即不接高電平,也不接低電平。由于邏輯器件的内部結構,當它輸入引腳懸空時,相當于該引腳接了高電平。一般實際運用時,引腳不建議懸空,易受幹擾。 通俗講就是讓管腳什麼都不接,浮空着。信号進入晶片内部後,既沒有接上拉電阻也沒有接下拉電阻,經由觸發器輸入。配置成這個模式後,用電壓變量引腳電壓為1點幾伏,這是個不确定值。由于其輸入阻抗比較大,一般把這種模式用于标準的通訊協定,比如IIC、USART的等。該模式是GD32複位之後的預設模式。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 上拉輸入:上拉就是把電位拉高,比如拉到Vcc。上拉就是将不确定的信号通過一個電阻嵌位在高電平,電阻同時起限流作用,弱強隻是上拉電阻的阻值不同,沒有什麼嚴格區分。上拉輸入就是信号進入晶片後加了一個上拉電阻,再經過施密特觸發器轉換成0、1信号,讀取此時的引腳電平為高電平;

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 下拉輸入:就是把電壓拉低,拉到GND。與上拉原理相似。下拉輸入就是信号進入晶片後加了一個下拉電阻,再經過施密特觸發器轉換成0、1信号,讀取此時的引腳電平為低電平;

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 模拟輸入:信号進入後不經過上拉電阻或者下拉電阻,關閉施密特觸發器,經由另一線路把電壓信号傳送到片上外設子產品。模拟輸入是指傳統方式的輸入,數字輸入是輸入PCM數字信号,即0、1的二進制數字信号,通過數模轉換,轉換成模拟信号,經前級放大進入功率放大器,功率放大器還是模拟的。比如傳送給ADC子產品,由ADC采集電壓信号。是以可以了解為模拟輸入的信号是未經處理的信号,是原汁原味的信号。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 輸出模式

 開漏輸出:一般用在電平不比對的場合,如需要輸出5V的高電平。輸出端相當于三極管的集電極,要得到高電平狀态需要上拉電阻才行。适合于做電流型的驅動,其吸收電流的能力相對強(一般20mA以内)。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 複用開漏輸出:可以了解為GPIO口被用作第二功能時的配置情況(即并非作為通用IO口使用)。端口必須配置成複用開漏功能輸出模式。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 推挽式輸出:可以輸出高、低電平,連接配接數字器件;推挽結構一般是指兩個三極管分别受兩個互補信号的控制,總是在一個三極管導通的時候另一個截止。高低電平由IC的電源決定。推挽電路是兩個參數相同的三極管或MOSFET,以推挽方式存在于電路中,各負責正負半周的波形放大任務,電路工作時,兩隻對稱的功率開關管每次隻有一個導通,是以導通損耗小、效率高。輸出既可以向負載灌電流,也可以從負載抽取電流。推拉式輸出級既提高電路的負載能力,又提高開關速度。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

 推挽式複用輸出

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

2.2 I/O複用和重映射

2.2.1 I/O複用

GD32 有很多的内置外設,這些外設的外部引腳都是與 GPIO 複用的。也就是說,一個 GPIO如果可以複用為内置外設的功能引腳,那麼當這個 GPIO 作為内置外設使用的時候,就叫做複用。當I/O端口被配置為複用功能時:

● 在開漏或推挽式配置中,輸出緩沖器被打開

● 内置外設的信号驅動輸出緩沖器(複用功能輸出)

● 施密特觸發輸入被激活

● 弱上拉和下拉電阻被禁止

● 在每個APB2時鐘周期,出現在I/O腳上的資料被采樣到輸入資料寄存器

● 開漏模式時,讀輸入資料寄存器時可得到I/O口狀态

● 在推挽模式時,讀輸出資料寄存器時可得到最後一次寫的值

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

大家都知道,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同理。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

值得注意的,不同的開發闆,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 口的模式及輸出速率。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

GPIO_CTL0、GPIO_CTL1類似,讀者朋友可以參看《GD32F10x_User_Manual_EN_Rev2.4》資料輸入輸出寄存器是将對應的IO口置位,進而進行資料的輸入與輸出。

《嵌入式 – GD32開發實戰指南》第2章 初識GPIO流水燈

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.關注公衆号[嵌入式實驗樓]