RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)篇) 第3章 GPIO流水燈的前世今生

嵌入式大雜燴 ? 來(lái)源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2023-05-10 08:58 ? 次閱讀

開發(fā)環(huán)境:

MDK:Keil 5.30

開發(fā)板:GD32F207I-EVAL

MCU:GD32F207IK

上一章通過(guò)控制GPIO的高低電平實(shí)現(xiàn)了流水燈,但只是告訴了大家怎么做,如何實(shí)現(xiàn)流水燈,本文將深入剖析的GPIO流水燈的前生今世,深入研究流水燈的調(diào)用邏輯和數(shù)據(jù)結(jié)構(gòu)。

1 GPIO配置概述

前面一章大概講解GPIO的配置過(guò)程和核心的寄存器,當(dāng)然啦,關(guān)于GPIO的寄存器遠(yuǎn)不止我上一章列出來(lái)的,還有很多,具體請(qǐng)參看《GD32F20x_User_Manual》中GPIO相關(guān)的內(nèi)容吧。

16836413176280iij1n1apo

根據(jù)前面實(shí)現(xiàn)的GPIO流水燈,本文將其歸納如下:

F:\\File\\1 Hardware\\1-1 MCU\\1-1-5 GD32\\筆記\\GD32F2開發(fā)指南\\3 GPIO流水燈的前世今生\\附件\\GPIO流水燈流程.png

要想控制LED亮滅,就需要做以上三件事:使能時(shí)鐘,配置GPIO參數(shù),最后循環(huán)控制GPIO的高低電平就能實(shí)現(xiàn)流水燈的效果,GPIO的寄存器這里就不說(shuō)了,更多詳細(xì)的寄存器描述看官方手冊(cè)就行,下面先來(lái)看看GD32的時(shí)鐘。

2 GD32的時(shí)鐘系統(tǒng)

2.1 GD32的系統(tǒng)架構(gòu)

GD32的系統(tǒng)架構(gòu)比51單片機(jī)強(qiáng)大很多了。首先我們看看GD32的系統(tǒng)架構(gòu)圖:

168364131854943r6uyf009

GD32F20x系列器件是基于Arm? Cortex?-M3處理器的32位通用微控制器。 Arm? Cortex?-M3處理器包括三條AHB總線分別稱為I-CODE總線、 D-Code總線和系統(tǒng)總線。

下面我們具體講解一下圖中幾個(gè)總線的知識(shí):

ICode 總線:該總線將 M3 內(nèi)核指令總線和閃存指令接口相連,指令的預(yù)取在該總線上面完成。

DCode 總線:該總線將 M3 內(nèi)核的 DCode 總線與閃存存儲(chǔ)器的數(shù)據(jù)接口相連接,常量加載和調(diào)試訪問(wèn)在該總線上面完成。

③ 系統(tǒng)總線:該總線連接 M3 內(nèi)核的系統(tǒng)總線到總線矩陣,總線矩陣協(xié)調(diào)內(nèi)核和 DMA 間訪問(wèn)。

④ DMA 總線:該總線將 DMA 的 AHB 主控接口與總線矩陣相連,總線矩陣協(xié)調(diào) CPU 的DCode 和 DMA 到 SRAM,閃存和外設(shè)的訪問(wèn)。

⑤ 總線矩陣:總線矩陣協(xié)調(diào)內(nèi)核系統(tǒng)總線和 DMA 主控總線之間的訪問(wèn)仲裁,仲裁利用輪換算法。

⑥ AHB/APB 橋:這兩個(gè)橋在 AHB 和 2 個(gè) APB 總線間提供同步連接,APB1 操作速度限于60MHz,APB2 操作速度全速。

對(duì)于系統(tǒng)架構(gòu)的知識(shí),在剛開始學(xué)習(xí) GD32 的時(shí)候只需要一個(gè)大概的了解,大致知道是個(gè)什么情況即可。

2.2 GD32時(shí)鐘架構(gòu)

時(shí)鐘是整個(gè)處理器運(yùn)行的基礎(chǔ),時(shí)鐘信號(hào)推動(dòng)處理器內(nèi)各個(gè)部分執(zhí)行相應(yīng)的指令。時(shí)鐘系統(tǒng)就是CPU的脈搏,決定CPU速率,像人的心跳一樣 只有有了心跳,人才能做其他的事情,而單片機(jī)有了時(shí)鐘,才能夠運(yùn)行執(zhí)行指令,才能夠做其他的處理 (點(diǎn)燈,串口,ADC),時(shí)鐘的重要性不言而喻。

我們?cè)趯W(xué)習(xí)51單片機(jī)時(shí),其最小系統(tǒng)必有晶振電路,這塊電路就是單片機(jī)的時(shí)鐘來(lái)源,晶振的振蕩頻率直接影響單片機(jī)的處理速度。GD32相比51單片機(jī)就復(fù)雜得多,不僅是外設(shè)非常多,就連時(shí)鐘來(lái)源就有四個(gè)。但我們實(shí)際使用的時(shí)候只會(huì)用到有限的幾個(gè)外設(shè),使用任何外設(shè)都需要時(shí)鐘才能啟動(dòng),但并不是所有的外設(shè)都需要系統(tǒng)時(shí)鐘那么高的頻率,為了兼容不同速度的設(shè)備,有些高速,有些低速,如果都用高速時(shí)鐘,勢(shì)必造成浪費(fèi),而且,同一個(gè)電路,時(shí)鐘越快功耗越快,同時(shí)抗電磁干擾能力也就越弱,所以較為復(fù)雜的MCU都是采用多時(shí)鐘源的方法來(lái)解決這些問(wèn)題,因此便有了GD32的時(shí)鐘系統(tǒng)和時(shí)鐘樹。

1683641319046msyvu4bqni

GD32三個(gè)不同的時(shí)鐘源可以用來(lái)驅(qū)動(dòng)系統(tǒng)時(shí)鐘(CK_SYS):

● IRC8M晶振時(shí)鐘(高速內(nèi)部時(shí)鐘信號(hào))

● HXTAL晶振時(shí)鐘(高速外部時(shí)鐘信號(hào))

● PLL時(shí)鐘

GD32有兩個(gè)二級(jí)時(shí)鐘源:

● 40kHz的低速內(nèi)部IRC40K,它可以驅(qū)動(dòng)獨(dú)立看門狗,還可選擇地通過(guò)程序選擇驅(qū)動(dòng)RTC。 RTC用于從停機(jī)/待機(jī)模式下自動(dòng)喚醒系統(tǒng)。

● 32.768kHz的低速外部晶振LXTAL,可選擇它用來(lái)驅(qū)動(dòng)RTC。

每個(gè)時(shí)鐘源在不使用時(shí)都可以單獨(dú)被打開或關(guān)閉,這樣就可以優(yōu)化系統(tǒng)功耗。

2.3 GD32的時(shí)鐘系統(tǒng)

GD32 芯片為了實(shí)現(xiàn)低功耗,設(shè)計(jì)了一個(gè)功能完善但卻非常復(fù)雜的時(shí)鐘系統(tǒng)。普通的MCU 一般只要配置好 GPIO 的寄存器就可以使用了,但 GD32還有一個(gè)步驟,就是開啟外設(shè)時(shí)鐘。

C:\\Users\\BruceOu\\Desktop\\12313.png

在 GD32中,可分為五種時(shí)鐘源,為 IRC8M、HXTAL、IRC40K、LXTAL、PLL。從時(shí)鐘頻率來(lái)分可以分為高速時(shí)鐘源和低速時(shí)鐘源,其中 IRC8M, HXTAL以及 PLL 是高速時(shí)鐘,IRC40K和 LXTAL是低速時(shí)鐘。從來(lái)源可分為外部時(shí)鐘源和內(nèi)部時(shí)鐘源,外部時(shí)鐘源就是從外部通過(guò)接晶振的方式獲取時(shí)鐘源,其中 HXTAL和 LXTAL是外部時(shí)鐘源,其他的是內(nèi)部時(shí)鐘源。

下面我們看看 GD32 的 5 個(gè)時(shí)鐘源,我們講解順序是按圖中紅圈標(biāo)示的順序:

①IRC8M是__高速內(nèi)部時(shí)鐘__,RC 振蕩器,頻率為 8MHz。

②HXTAL是__高速外部時(shí)鐘__,可接石英/陶瓷諧振器,或者接外部時(shí)鐘源,頻率范圍為4MHz~32MHz。我們的開發(fā)板接的是 25M 的晶振。當(dāng)使用有源晶振時(shí),時(shí)鐘從 OSC_IN 引腳進(jìn)入, OSC_OUT 引腳懸空,當(dāng)選用無(wú)源晶振時(shí),時(shí)鐘從 OSC_IN 和 OSC_OUT 進(jìn)入,并且要配諧振電容。當(dāng)確定 PLL 時(shí)鐘來(lái)源的時(shí)候, HXTAL可以不分頻或者 2 分頻,這個(gè)由時(shí)鐘配置寄存器 CFG0 的位 17。

③IRC40K是__低速內(nèi)部時(shí)鐘__,RC 振蕩器,頻率為 40kHz。獨(dú)立看門狗的時(shí)鐘源只能是 IRC40K,同時(shí) IRC40K還可以作為 RTC 的時(shí)鐘源。

④LXTAL是__低速外部時(shí)鐘__,接頻率為 32.768kHz 的石英晶體。這個(gè)主要是 RTC 的時(shí)鐘源。

⑤PLL 為鎖相環(huán)倍頻輸出,其時(shí)鐘輸入源可選擇為 IRC8M、HXTAL。倍頻可選擇為2~32倍,但是其輸出頻率最大不得超過(guò) 120MHz。

圖中我們用 A~E 標(biāo)示我們要講解的地方。

A. OUT是 GD32 的一個(gè)時(shí)鐘輸出IO,它可以選擇一個(gè)時(shí)鐘信號(hào)輸出, 可以選擇為 PLL 輸出的 2 分頻、IRC8M、HXTAL、或者系統(tǒng)時(shí)鐘。這個(gè)時(shí)鐘可以用來(lái)給外部其他系統(tǒng)提供時(shí)鐘源。

B. 這里是 RTC 時(shí)鐘源,從圖上可以看出,RTC 的時(shí)鐘源可以選擇 IRC40K,以及HXTAL的 128 分頻。

C. 從圖中可以看出 C 處 USB 的時(shí)鐘是來(lái)自 PLL 時(shí)鐘源。 GD32 中有一個(gè)全速功能的 USB 模塊,其串行接口引擎需要一個(gè)頻率為 48MHz 的時(shí)鐘源。該時(shí)鐘源只能從 PLL 輸出端獲取,可以選擇為 1/1.5/2/2.5 分頻。

D. D 處就是 GD32 的系統(tǒng)時(shí)鐘 SYSCLK,它是供 GD32 中絕大部分部件工作的時(shí)鐘源。系統(tǒng)時(shí)鐘可選擇為 PLL 輸出、 IRC8M或者 HXTAL。系統(tǒng)時(shí)鐘最大頻率為 120MHz,當(dāng)然你也可以超頻,不過(guò)一般情況為了系統(tǒng)穩(wěn)定性是沒有必要冒風(fēng)險(xiǎn)去超頻的。

E. 這里的 E 處是指其他所有外設(shè)了。從時(shí)鐘圖上可以看出,其他所有外設(shè)的時(shí)鐘最終來(lái)源都是 SYSCLK。SYSCLK 通過(guò) AHB 分頻器分頻后送給各模塊使用。這些模塊包括:

①AHB 總線、內(nèi)核、內(nèi)存和 DMA 使用的 HCLK 時(shí)鐘。

②通過(guò) 8 分頻后送給 Cortex 的系統(tǒng)定時(shí)器時(shí)鐘,也就是 systick 了。

③直接送給 Cortex 的空閑運(yùn)行時(shí)鐘 FCLK。

④送給 APB1 分頻器。APB1 分頻器輸出一路供 APB1 外設(shè)使用(PCLK1,最大頻率 60MHz),另一路送給定時(shí)器(Timer)使用。

⑤送給 APB2 分頻器。APB2 分頻器分頻輸出一路供APB2外設(shè)使用(PCLK2,最大頻率 120MHz),另一路送給定時(shí)器(Timer)使用。

其中需要理解的是 APB1 和 APB2 的區(qū)別, APB1 上面連接的是低速外設(shè),包括電源接口、備份接口、 CAN、 USB、 I2C0、 I2C1、 UART1、 UART2 等等, APB2 上面連接的是高速外設(shè)包括 UART0、 SPI0、 Timer0、 ADC0、 ADC1、所有普通 IO 口(PA~PG)、第二功能 IO 口等。

不同的總線有不同的頻率,不同的外設(shè)掛在不同的總線下,為了更適合初學(xué)者查閱,筆者把常用的外設(shè)與總線的對(duì)應(yīng)關(guān)系總結(jié)如下:

F:\\File\\1 Hardware\\1-1 MCU\\1-1-5 GD32\\筆記\\GD32F2開發(fā)指南\\3 GPIO流水燈的前世今生\\附件\\GD32總線與外設(shè)連接關(guān)系.png

SystemInit()函數(shù)中設(shè)置的系統(tǒng)時(shí)鐘大小:

  • SYSCLK(系統(tǒng)時(shí)鐘) =120MHz
  • AHB 總線時(shí)鐘(使用 SYSCLK) =120MHz
  • APB1 總線時(shí)鐘(PCLK1) =60MHz
  • APB2 總線時(shí)鐘(PCLK2) =120MHz
  • PLL 時(shí)鐘 =120MHz

值得注意的是,GD32F207系列有多個(gè)PLL,具體參看源碼。

2.4 GD32的時(shí)鐘配置剖析

既然時(shí)鐘搞清楚了,接下來(lái)回到上一章的配置時(shí)鐘的代碼:

/*enable the LED clock*/
rcu_periph_clock_enable(RCU_GPIOF );

rcu_periph_clock_enable就是配置時(shí)鐘的函數(shù),函數(shù)原型如下:

/*!
    \\brief      enable the peripherals clock
    \\param[in]  periph: RCU peripherals, refer to rcu_periph_enum
                only one parameter can be selected which is shown as below:
      \\arg        RCU_GPIOx (x=A,B,C,D,E,F,G,H,I): GPIO ports clock
      \\arg        RCU_AF : alternate function clock
      \\arg        RCU_CRC: CRC clock
      \\arg        RCU_DMAx (x=0,1): DMA clock
      \\arg        RCU_ENET: ENET clock
      \\arg        RCU_ENETTX: ENETTX clock
      \\arg        RCU_ENETRX: ENETRX clock
      \\arg        RCU_USBFS: USBFS clock
      \\arg        RCU_EXMC: EXMC clock
      \\arg        RCU_TIMERx (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13): TIMER clock
      \\arg        RCU_WWDGT: WWDGT clock
      \\arg        RCU_SPIx (x=0,1,2): SPI clock
      \\arg        RCU_USARTx (x=0,1,2,5): USART clock
      \\arg        RCU_UARTx (x=3,4,6,7): UART clock
      \\arg        RCU_I2Cx (x=0,1,2): I2C clock
      \\arg        RCU_CANx (x=0,1): CAN clock
      \\arg        RCU_PMU: PMU clock
      \\arg        RCU_DAC: DAC clock
      \\arg        RCU_RTC: RTC clock
      \\arg        RCU_ADCx (x=0,1,2): ADC clock
      \\arg        RCU_SDIO: SDIO clock
      \\arg        RCU_BKPI: BKP interface clock
      \\arg        RCU_TLI: TLI clock
      \\arg        RCU_DCI: DCI clock
      \\arg        RCU_CAU: CAU clock
      \\arg        RCU_HAU: HAU clock
      \\arg        RCU_TRNG: TRNG clock
    \\param[out] none
    \\retval     none
*/
void rcu_periph_clock_enable(rcu_periph_enum periph)
{
    RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));
}

整個(gè)函數(shù)就一個(gè)參數(shù),其參數(shù)就是具體的外設(shè)時(shí)鐘,整個(gè)函數(shù)很簡(jiǎn)單,就是打開具體的外設(shè)時(shí)鐘。

參數(shù)periph傳入值是通過(guò)宏來(lái)定義的,這樣的好處也是便于移植,如果換了MCU,架構(gòu)一樣,只需要就該底層驅(qū)動(dòng)就行,不需要更改上層應(yīng)用,這樣就提高了開發(fā)效率。言歸正傳,我們傳入的RCU_GPIOC定義如下。

16836413204081p9c0x672i

RCU_GPIOF是一個(gè)枚舉類型。我們繼續(xù)追溯以上的宏。

/* constants definitions */
/* define the peripheral clock enable bit position and its register index offset */
#define RCU_REGIDX_BIT(regidx, bitpos)      (((uint32_t)(regidx) << 6) | (uint32_t)(bitpos))
#define RCU_REG_VAL(periph)                 (REG32(RCU + ((uint32_t)(periph) >> 6)))
#define RCU_BIT_POS(val)                    ((uint32_t)(val) & 0x1FU)

#define APB2EN_REG_OFFSET               0x18U                     /*!< APB2 enable register offset */
#define BIT(x)                       ((uint32_t)((uint32_t)0x01U<<(x)))
/* RCU definitions */
#define RCU                             RCU_BASE
#define RCU_BASE              (AHB1_BUS_BASE + 0x00009000U)  /*!< RCU base address                                */
#define AHB1_BUS_BASE         ((uint32_t)0x40018000U)        /*!< ahb1 base address                               */

以上宏定義就是整個(gè)時(shí)鐘初始化相關(guān)的宏定義了,將其帶入函數(shù)中。RCU的基地址就是0x40018000+0x9000。可以從GD32參考手冊(cè)中獲取。

AHB1總線的基地址是0x40018000。

RCU偏移是0x9000。

1683641320891so54frpua3

RCU_REG_VAL(RCU_GPIOF)最終的結(jié)果是0x40021018。

宏定義BIT就是獲取GPIO具體的使能位。

BIT(RCU_BIT_POS(RCU_GPIOF))最終的結(jié)果就是0x64。

最終的函數(shù)替換后如下:

0x40021018 |= 0x64;

都是宏定義直接替換就行,還是比較簡(jiǎn)單。

這里需要注意RCU_REGIDX_BIT宏定義。

#define RCU_REGIDX_BIT(regidx, bitpos)      (((uint32_t)(regidx) << 6) | (uint32_t)(bitpos))

該宏定義就是將要配置的寄存器偏移和bit位綁定在一起,然后再通過(guò)以下宏定義分開偏移和bit位。

#define RCU_REG_VAL(periph)                 (REG32(RCU + ((uint32_t)(periph) >> 6)))
#define RCU_BIT_POS(val)                    ((uint32_t)(val) & 0x1FU)

RCU的APB2使能寄存器如下:

1683641321195lnosma58zk

1683641321454ds8h5a7sen

這里配置GPIOF的時(shí)鐘,需要將第7位置1,因此轉(zhuǎn)換成10進(jìn)制就是64,和代碼就匹配起來(lái)了。

GD32的固件庫(kù)和STM32的固件庫(kù)還是有一些差別的,但是不管如何,最終都是配置的寄存器,只是STM32通過(guò)結(jié)構(gòu)體對(duì)外設(shè)進(jìn)行了封裝,GD32是通過(guò)宏定義直接替換,偏向于直接操作寄存器。

3 GD32的地址映射

我們先看看51 單片機(jī)中是怎么做的,51 單片機(jī)開發(fā)中會(huì)引用一個(gè) reg51.h 的頭文件,51單片機(jī)是通過(guò)以下方式將名字和寄存器聯(lián)系起來(lái)的:

sfr P0 =0x80;

sfr 也是一種擴(kuò)充數(shù)據(jù)類型,占用一個(gè)內(nèi)存單元,值域?yàn)?0~255。利用它可以訪問(wèn) 51 單片機(jī)內(nèi)部的所有特殊功能寄存器。如用 sfr P1 = 0x90 這一句定義 P1 為 P1 端口在片內(nèi)的寄存器。然后我們往地址為 0x80 的寄存器設(shè)值的方法是: P0=value;通過(guò)改變value的值來(lái)控制單片機(jī)。

16836413217748u4ellyv1o

所謂地址映射,就是將芯片上的存儲(chǔ)器甚至 I/O 等資源與地址建立一一對(duì)應(yīng)的關(guān)系。如果某地址對(duì)應(yīng)著某寄存器,我們就可以運(yùn)用 C 語(yǔ)言的指針來(lái)尋址并修改這個(gè)地址上的內(nèi)容,從而實(shí)現(xiàn)修改該寄存器的內(nèi)容。Cortex-M的地址映射也是類似的。Cortex-M有 32 根地址線,所以它的尋址空間大小為 2 32 bit=4 GB。ARM 公司設(shè)計(jì)時(shí),預(yù)先把這 4 GB 的尋址空間大致地分配好了。它把從 0x40000000 至 0x5FFFFFFF( 512 MB)的地址分配給片上外設(shè)。通過(guò)把片上外設(shè)的寄存器映射到這個(gè)地址區(qū),就可以簡(jiǎn)單地以訪問(wèn)內(nèi)存的方式,訪問(wèn)這些外設(shè)的寄存器,從而控制外設(shè)的工作。這樣,片上外設(shè)可以使用 C 語(yǔ)言來(lái)操作。

gd32f10x.h 這個(gè)文件中重要的內(nèi)容就是把 GD32 的所有寄存器進(jìn)行地址映射。如同51 單片機(jī)的 頭文件一樣,gd32f10x.h 像一個(gè)大表格,我們?cè)谑褂玫臅r(shí)候就是通過(guò)宏定義進(jìn)行類似查表的操作,但是這樣操作會(huì)很麻煩,而且32位的MCU寄存器很多,非常不方便。于是就有了現(xiàn)在的固件庫(kù)。

在這里我們以流水燈中的 GPIOF為例進(jìn)行剖析,如果是其他的 IO 端口,則改成相應(yīng)的地址即可。在這個(gè)文件中一系列宏實(shí)現(xiàn)了地址映射。

#define APB2_BUS_BASE         ((uint32_t)0x40010000U)        /*!< apb2 base address                */
#define GPIO_BASE             (APB2_BUS_BASE + 0x00000800U)  /*!< GPIO base address                */

這幾個(gè)宏定義是從文件中的幾個(gè)部分抽離出來(lái)的,具體的內(nèi)容讀者可參考gd32f10x.h 源碼。

宏APB2_BUS_BASE指向的地址為 0x40010000。這個(gè) APB2_BUS_BASE宏是什么地址呢?GD32 不同的外設(shè)是掛載在不同的總線上的。GD32 芯片有 AHB 總線、APB2總線和 APB1 總線,掛載在這些總線上的外設(shè)有特定的地址范圍。其中像 GPIO、串口 1、ADC 及部分定時(shí)器是掛載在稱為 APB2 的總線上,掛載到APB2 總 線上的外設(shè)地址空間是從0x40010000 至 0x40017FFF地址。這里的第一個(gè)地址,也就是 0x40010000,稱為 APB2_BUS_BASE(APB2 總線外設(shè)基地址)。

而 APB2 總線基地址相對(duì)于外設(shè)基地址的偏移量為 0x10000 個(gè)地址,即為 APB2 相對(duì)外設(shè)基地址的偏移地址。

最后到了宏 GPIO_BASE,宏展開為 APB2_BUS_BASE加上偏移量 0x1400得到了 GPIO端口的寄存器組的基地址。

在gd32f20x_gpio.h 文件,我們還可以發(fā)現(xiàn)有關(guān)各個(gè) GPIO 基地址的宏。

/* GPIOx(x=A,B,C,D,E,F,G,H,I) definitions */
#define GPIOA                      (GPIO_BASE + 0x00000000U)       /*!< GPIOA bsae address */
#define GPIOB                      (GPIO_BASE + 0x00000400U)       /*!< GPIOB bsae address */
#define GPIOC                      (GPIO_BASE + 0x00000800U)       /*!< GPIOC bsae address */
#define GPIOD                      (GPIO_BASE + 0x00000C00U)       /*!< GPIOD bsae address */
#define GPIOE                      (GPIO_BASE + 0x00001000U)       /*!< GPIOE bsae address */
#define GPIOF                      (GPIO_BASE + 0x00001400U)       /*!< GPIOF bsae address */
#define GPIOG                      (GPIO_BASE + 0x00001800U)       /*!< GPIOG bsae address */
#define GPIOH                      (GPIO_BASE + 0x00006C00U)       /*!< GPIOH bsae address */
#define GPIOI                      (GPIO_BASE + 0x00007000U)       /*!< GPIOI bsae address */

除了 GPIOF寄存器組的地址,還有 GPIOA、GPIOB等地址,并且這些地址是不一樣的。前面提到,每組 GPIO 都對(duì)應(yīng)著獨(dú)立的一組寄存器,查看 GD32 的數(shù)據(jù)手冊(cè)。

注意到這個(gè)說(shuō)明中有一個(gè)偏移地址:0x1400,這里的偏移地址是相對(duì)哪個(gè)地址的偏移呢?下面進(jìn)行舉例說(shuō)明。

4 固件庫(kù)對(duì)寄存器的封裝

GD的工程師用結(jié)構(gòu)體的形式封裝了寄存器組,在gd32f20x_gpio.h文件定義的。

/* GPIOx(x=A,B,C,D,E,F,G,H,I) definitions */
#define GPIOA                      (GPIO_BASE + 0x00000000U)       /*!< GPIOA bsae address */
#define GPIOB                      (GPIO_BASE + 0x00000400U)       /*!< GPIOB bsae address */
#define GPIOC                      (GPIO_BASE + 0x00000800U)       /*!< GPIOC bsae address */
#define GPIOD                      (GPIO_BASE + 0x00000C00U)       /*!< GPIOD bsae address */
#define GPIOE                      (GPIO_BASE + 0x00001000U)       /*!< GPIOE bsae address */
#define GPIOF                      (GPIO_BASE + 0x00001400U)       /*!< GPIOF bsae address */
#define GPIOG                      (GPIO_BASE + 0x00001800U)       /*!< GPIOG bsae address */
#define GPIOH                      (GPIO_BASE + 0x00006C00U)       /*!< GPIOH bsae address */
#define GPIOI                      (GPIO_BASE + 0x00007000U)       /*!< GPIOI bsae address */

有了這些宏,我們就可以定位到具體的寄存器地址,gd32f10x_gpio.h 文件中定義了以下類型的宏定義。

/* GPIO registers definitions */
#define GPIO_CTL0(gpiox)           REG32((gpiox) + 0x00000000U)    /*!< GPIO port control register 0 */
#define GPIO_CTL1(gpiox)           REG32((gpiox) + 0x00000004U)    /*!< GPIO port control register 1 */
#define GPIO_ISTAT(gpiox)          REG32((gpiox) + 0x00000008U)    /*!< GPIO port input status register */
#define GPIO_OCTL(gpiox)           REG32((gpiox) + 0x0000000CU)    /*!< GPIO port output control register */
#define GPIO_BOP(gpiox)            REG32((gpiox) + 0x00000010U)    /*!< GPIO port bit operation register */
#define GPIO_BC(gpiox)             REG32((gpiox) + 0x00000014U)    /*!< GPIO bit clear register */
#define GPIO_LOCK(gpiox)           REG32((gpiox) + 0x00000018U)    /*!< GPIO port configuration lock register */

這里定義了 7 個(gè)宏定義,兩個(gè)宏之間是4 個(gè)字節(jié)地址的偏移量。

0x010偏移量正是 GPIOx_BOP寄存器相對(duì)于所在寄存器組的偏移地址。

168364132205391k72qmavp

通過(guò)類似的方式,我們就可以給具體的寄存器寫上適當(dāng)?shù)膮?shù)以控制 GD32 了。

這樣我們就可以通過(guò)庫(kù)函數(shù)實(shí)現(xiàn)了GPIO的初始化了。

/*!
    \\brief      GPIO parameter initialization
    \\param[in]  gpio_periph: GPIOx(x = A,B,C,D,E,F,G,H,I)
    \\param[in]  mode: gpio pin mode
                only one parameter can be selected which is shown as below:
      \\arg        GPIO_MODE_AIN: analog input mode
      \\arg        GPIO_MODE_IN_FLOATING: floating input mode
      \\arg        GPIO_MODE_IPD: pull-down input mode
      \\arg        GPIO_MODE_IPU: pull-up input mode
      \\arg        GPIO_MODE_OUT_OD: GPIO output with open-drain
      \\arg        GPIO_MODE_OUT_PP: GPIO output with push-pull
      \\arg        GPIO_MODE_AF_OD: AFIO output with open-drain
      \\arg        GPIO_MODE_AF_PP: AFIO output with push-pull
    \\param[in]  speed: gpio output max speed value
                only one parameter can be selected which is shown as below:
      \\arg        GPIO_OSPEED_10MHZ: output max speed 10MHz
      \\arg        GPIO_OSPEED_2MHZ: output max speed 2MHz
      \\arg        GPIO_OSPEED_50MHZ: output max speed 50MHz
    \\param[in]  pin: GPIO pin
                one or more parameters can be selected which are shown as below:
      \\arg        GPIO_PIN_x(x=0..15), GPIO_PIN_ALL
    \\param[out] none
    \\retval     none
*/
void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)
{
    uint16_t i;
    uint32_t temp_mode = 0U;
    uint32_t reg = 0U;

    /* GPIO mode configuration */
    temp_mode = (uint32_t)(mode & ((uint32_t)0x0FU));

    /* GPIO speed configuration */
    if(((uint32_t)0x00U) != ((uint32_t)mode & ((uint32_t)0x10U))) {
        /* output mode max speed: 10MHz, 2MHz, 50MHz */
        temp_mode |= (uint32_t)speed;
    }

    /* configure the eight low port pins with GPIO_CTL0 */
    for(i = 0U; i < 8U; i++) {
        if((1U << i) & pin) {
            reg = GPIO_CTL0(gpio_periph);
            /* clear the specified pin mode bits */
            reg &= ~GPIO_MODE_MASK(i);
            /* set the specified pin mode bits */
            reg |= GPIO_MODE_SET(i, temp_mode);

            /* set IPD or IPU */
            if(GPIO_MODE_IPD == mode) {
                /* reset the corresponding OCTL bit */
                GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin);
            } else {
                /* set the corresponding OCTL bit */
                if(GPIO_MODE_IPU == mode) {
                    GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin);
                }
            }
            /* set GPIO_CTL0 register */
            GPIO_CTL0(gpio_periph) = reg;
        }
    }
    /* configure the eight high port pins with GPIO_CTL1 */
    for(i = 8U; i < 16U; i++) {
        if((1U << i) & pin) {
            reg = GPIO_CTL1(gpio_periph);
            /* clear the specified pin mode bits */
            reg &= ~GPIO_MODE_MASK(i - 8U);
            /* set the specified pin mode bits */
            reg |= GPIO_MODE_SET(i - 8U, temp_mode);

            /* set IPD or IPU */
            if(GPIO_MODE_IPD == mode) {
                /* reset the corresponding OCTL bit */
                GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin);
            } else {
                /* set the corresponding OCTL bit */
                if(GPIO_MODE_IPU == mode) {
                    GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin);
                }
            }
            /* set GPIO_CTL1 register */
            GPIO_CTL1(gpio_periph) = reg;
        }
    }
}


然后再main函數(shù)中調(diào)用gpio\\_init\\(\\)函數(shù)接口對(duì)GPIO初始化了。

通過(guò)對(duì)時(shí)鐘和GPIO的分析,我想大家已經(jīng)對(duì)固件的邏輯有了一定的認(rèn)識(shí),從本質(zhì)上講,都是在配置寄存器,只是地址和值不同罷了,而固件庫(kù)就是對(duì)寄存器配置的封裝,便于開發(fā)者調(diào)用。

值得注意的是,GD32的固件庫(kù)并沒有使用結(jié)構(gòu)體來(lái)對(duì)寄存器組進(jìn)行封裝,全程用的宏定義,這點(diǎn)和STM32有很大的不同。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • mcu
    mcu
    +關(guān)注

    關(guān)注

    146

    文章

    17123

    瀏覽量

    350975
  • 流水燈
    +關(guān)注

    關(guān)注

    21

    文章

    432

    瀏覽量

    59692
  • 開發(fā)板
    +關(guān)注

    關(guān)注

    25

    文章

    5032

    瀏覽量

    97371
  • GPIO
    +關(guān)注

    關(guān)注

    16

    文章

    1204

    瀏覽量

    52051
  • GD32
    +關(guān)注

    關(guān)注

    7

    文章

    403

    瀏覽量

    24326
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 2 初始GPIO流水燈

    熟悉單片機(jī)的朋友都知道,學(xué)習(xí)的第一個(gè)例程就是流水燈,要想實(shí)現(xiàn)流水燈,首先必須了解GPIO的工作原理。GPIO的基本結(jié)構(gòu)如下圖所示。
    的頭像 發(fā)表于 05-08 09:03 ?7770次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>2<b class='flag-5'>章</b> 初始<b class='flag-5'>GPIO</b><b class='flag-5'>流水燈</b>

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 1 開發(fā)環(huán)境搭建

    設(shè)計(jì)的GD32F207I-EVAL開發(fā)板。 GD32F207I-EVAL開發(fā)板使用 GD32F207IK作為主控制器,主頻120MHz、集成
    的頭像 發(fā)表于 05-07 23:35 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>1<b class='flag-5'>章</b> <b class='flag-5'>開發(fā)</b>環(huán)境搭建

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 7 定時(shí)器

    系統(tǒng)滴答定時(shí)器一般用來(lái)提供“心跳”作用,而GD32定時(shí)器最基本功能也是定時(shí),可以設(shè)置不同時(shí)間長(zhǎng)度的定時(shí)。定時(shí)器除了最基本的定時(shí)功能外,定時(shí)器與GPIO有掛鉤使得它可以發(fā)揮強(qiáng)大的作用,比如可以輸出
    的頭像 發(fā)表于 05-11 09:00 ?1.2w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>7<b class='flag-5'>章</b> 定時(shí)器

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 8 定時(shí)器

    開發(fā)環(huán)境: MDK:Keil 5.30 開發(fā)板:GD32F207I-EVAL MCU:GD32F207IK 1 PWM輸出的工作原理 脈沖寬度調(diào)制(PWM) ,是英文“Pulse Wi
    的頭像 發(fā)表于 05-12 22:14 ?7926次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>8<b class='flag-5'>章</b> 定時(shí)器

    ARM Cortex-M學(xué)習(xí)筆記:GPIO流水燈前世今生

    上一通過(guò)控制GPIO的高低電平實(shí)現(xiàn)了流水燈,但只是告訴了大家怎么做,如何實(shí)現(xiàn)流水燈,本文將深入剖析的GPIO
    的頭像 發(fā)表于 05-15 14:44 ?2640次閱讀
    ARM Cortex-M學(xué)習(xí)筆記:<b class='flag-5'>GPIO</b><b class='flag-5'>流水燈</b>的<b class='flag-5'>前世</b><b class='flag-5'>今生</b>

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 14 內(nèi)部溫度傳感器

    GD32 有一個(gè)內(nèi)部的溫度傳感器,可以用來(lái)測(cè)量 CPU 及周圍的溫度(TA)。該溫度傳感器在內(nèi)部和 ADCx_IN16 輸入通道相連接,此通道把傳感器輸出的電壓轉(zhuǎn)換成數(shù)字值。溫度傳感器模擬輸入
    的頭像 發(fā)表于 05-17 08:58 ?5335次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>14<b class='flag-5'>章</b> 內(nèi)部溫度傳感器

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 16 RTC

    開發(fā)環(huán)境: MDK:Keil 5.30 開發(fā)板:GD32F207I-EVAL MCU:GD32F207IK 1 RTC工作原理 1.1 RTC簡(jiǎn)介
    的頭像 發(fā)表于 05-18 22:14 ?7151次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>16<b class='flag-5'>章</b> RTC

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 17 看門狗

    開發(fā)環(huán)境: MDK:Keil 5.30 開發(fā)板:GD32F207I-EVAL MCU:GD32F207IK GD32 有兩個(gè)看門狗, 一個(gè)是
    的頭像 發(fā)表于 06-03 16:00 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>17<b class='flag-5'>章</b> 看門狗

    【圖書分享】《STM32庫(kù)開發(fā)實(shí)戰(zhàn)指南

    GPIO入門之流水燈 4 深入分析流水燈例程 5
    發(fā)表于 03-13 17:01

    好書分享】入門經(jīng)典:STM32庫(kù)開發(fā)實(shí)戰(zhàn)指南

    相關(guān)專業(yè)的教材,也適合作為從事嵌入式領(lǐng)域科技工作者的參考書。前言 第一部分 庫(kù)開發(fā)初級(jí) 1 為什么學(xué)習(xí)STM32 2
    發(fā)表于 06-24 20:53

    GD32 MCU原理及固件庫(kù)開發(fā)指南》+讀后感

    。 2介紹GD32 MCU快速入門與開發(fā)平臺(tái)搭建的方法,包括對(duì)軟硬件開發(fā)平臺(tái)、調(diào)試工具、GD32
    發(fā)表于 06-06 21:52

    GPIO-流水燈的前后今生

    主要是STM32系列的GPIO-流水燈的前后今生進(jìn)行詳細(xì)的講解,需要的自行下載
    發(fā)表于 06-17 16:48 ?8次下載

    《嵌入式-STM32開發(fā)指南》第二部分 基礎(chǔ) -1 GPIO流水燈(HAL庫(kù))

    在閱讀本文之前,先看看標(biāo)準(zhǔn)固件庫(kù)的內(nèi)容?!肚度胧?STM32開發(fā)指南》第二部分 基礎(chǔ) -1 GPIO1.1
    發(fā)表于 12-05 13:21 ?10次下載
    《嵌入式-STM32<b class='flag-5'>開發(fā)指南</b>》第二部分 基礎(chǔ)<b class='flag-5'>篇</b> -<b class='flag-5'>第</b>1<b class='flag-5'>章</b> <b class='flag-5'>GPIO</b><b class='flag-5'>流水燈</b>(HAL庫(kù))

    GD32開發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)) 19 程序加密

    GD32通過(guò)讀取芯片唯一ID號(hào)來(lái)實(shí)現(xiàn)程序的保護(hù),防止被抄襲。96位的產(chǎn)品唯一身份標(biāo)識(shí)所提供的參考號(hào)碼對(duì)任意一個(gè)GD32微控制器
    的頭像 發(fā)表于 05-20 09:10 ?4138次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>指南</b>(基礎(chǔ)<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>19<b class='flag-5'>章</b> 程序加密

    GD32 MCU入門教程】GD32 MCU GPIO 結(jié)構(gòu)與使用注意事項(xiàng)

    本文是專門為基于GD32 MCU開發(fā)的工程設(shè)計(jì)人員提供,主要介紹了GPIO的功能配置、內(nèi)部結(jié)構(gòu)以及在不同場(chǎng)景使用時(shí)的注意事項(xiàng),旨在幫助GD32 MCU
    的頭像 發(fā)表于 09-07 10:34 ?719次閱讀
    【<b class='flag-5'>GD32</b> MCU入門教程】<b class='flag-5'>GD32</b> MCU <b class='flag-5'>GPIO</b> 結(jié)構(gòu)與使用注意事項(xiàng)
    RM新时代网站-首页