91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

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

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

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

掌握HAL API中面向?qū)ο笤O(shè)計(jì)的思想

冬至子 ? 來(lái)源:Researcher LC ? 作者:licedu ? 2023-06-26 17:25 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1. 初識(shí)HAL

ST 為開(kāi)發(fā)者提供了三種的開(kāi)發(fā)庫(kù):

  1. 標(biāo)準(zhǔn)外設(shè)庫(kù)(Standard Peripheral Library, SPL庫(kù))
  2. 硬件抽象層庫(kù)(Hardware Abstraction Layer,HAL庫(kù))
  3. 底層庫(kù)(Low-Layer,底層庫(kù))

其中,ST CubeMX軟件支持STM32全線產(chǎn)品的HAL和LL庫(kù);SPL已經(jīng)停更,部分芯片如STM32F7xx沒(méi)有推出SPL庫(kù)。

相比標(biāo)準(zhǔn)外設(shè)庫(kù),STM32 HAL庫(kù)擁有更好的抽象整合水平,HAL API(HAL Application Programming Interface,HAL應(yīng)用程序接口)集中關(guān)注各個(gè)外設(shè)(Peripheral)的公共函數(shù)功能,通過(guò)定義一套通用的、用戶友好的API函數(shù)接口,支持不同STM32系列產(chǎn)品之間的輕松移植。

以點(diǎn)亮LED的工程舉例。

1.首先配置MDK的代碼補(bǔ)全

Edit Configuration Text Completion Symbols after 3 Characters

圖片

2.代碼補(bǔ)全效果。

HAL庫(kù)函數(shù)都以HAL作為開(kāi)頭。打開(kāi)代碼自動(dòng)補(bǔ)全后,輸入HAL_GPIO即可彈出一系列支持的函數(shù),如下圖的Init(初始化)、LockPin(鎖引腳)、ReadPin(讀引腳)、TogglePin(翻轉(zhuǎn)引腳)等。

圖片

3.HAL支持哪些函數(shù)?

如下圖所示,點(diǎn)擊MDK左側(cè)工程欄下方的Functions,點(diǎn)開(kāi)對(duì)應(yīng)的hal_xx.c文件,即可顯示出所有的HAL庫(kù)函數(shù)。

圖片

ST的HAL庫(kù)通過(guò)高度抽象化,使用統(tǒng)一的HAL API對(duì)硬件進(jìn)行操作。無(wú)論是使用STM32F1系列、L4系列、F7系列、H7系列等,對(duì)GPIO的初始化、讀、寫(xiě)、翻轉(zhuǎn)操作都是如下的統(tǒng)一接口,極大地方便了開(kāi)發(fā)者將相同的代碼移植到不同的ST系列芯片中。

  • void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
  • GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  • void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
  • void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

CubeMX通過(guò)圖形化界面操作,配置各個(gè)引腳、外設(shè)的工作狀態(tài),自動(dòng)生成驅(qū)動(dòng)初始化代碼,方便用戶快速進(jìn)行底層功能部署,開(kāi)發(fā)者只關(guān)注CubeMX圖形化界面的配置,可以不關(guān)注寫(xiě)底層硬件寄存器,通過(guò)調(diào)用統(tǒng)一的HAL API實(shí)現(xiàn)外設(shè)各種功能,這是HAL的一個(gè)典型特點(diǎn)。

2. STM32 Manual

關(guān)于STM32L4系列的手冊(cè),可以在https://www.st.com/zh/microcontrollers-microprocessors/stm32l4-series.html下載相關(guān)手冊(cè)。

圖片

ST系列常見(jiàn)文檔的命名規(guī)則如下:

1.AN, Application Note ,應(yīng)用手冊(cè)。一般是一些相對(duì)復(fù)雜、精細(xì)、精巧的應(yīng)用原理與結(jié)果介紹,閱讀門(mén)檻較高,建議熟悉芯片、熟悉嵌入式系統(tǒng)后,再根據(jù)具體開(kāi)發(fā)工作需求進(jìn)行查找與閱讀。
2.DS, Data Sheet ,規(guī)格書(shū)。芯片手冊(cè),說(shuō)明芯片容量、芯片時(shí)序、芯片封裝等情況的文檔,一般用于硬件選型階段。
3.UM, User Manual ,用戶手冊(cè),為開(kāi)發(fā)者提供HAL庫(kù)使用說(shuō)明、硬件使用說(shuō)明等情況的文檔,開(kāi)發(fā)階段可以作為參考書(shū)。瀏覽https://www.st.com/zh/embedded-software/stm32cubel4.html可以找到STM32L4系列的HAL庫(kù)UM手冊(cè)。本課程要求下載UM1884 Description of STM32L4/L4+ HAL and low-layer drivers.pdf手冊(cè)。建議將該手冊(cè)作為參考書(shū),有需要時(shí)再查閱,不要通讀,以后該文件簡(jiǎn)稱為UM1884.pdf文件。
RM, Reference Manual ,參考手冊(cè)。說(shuō)明芯片內(nèi)部寄存器如何配置的手冊(cè),本課程要求下載RM0394_STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx advanced Arm?-based 32-bit MCUs.pdf文件,對(duì)應(yīng)例程逐步深入了解。以后該文件簡(jiǎn)稱為RM0394.pdf。
4.PM, Programming Manual ,編程手冊(cè),針對(duì)具體芯片,一般是RISC匯編指令的解讀,不推薦給初學(xué)者。
5.TN, Technical Note ,技術(shù)手冊(cè),一般是一些芯片規(guī)格、封裝、PCB制版、Toolchains等軟硬件方面的雜項(xiàng)技術(shù)要點(diǎn)和進(jìn)一步解讀,不推薦給初學(xué)者。

3. 熟悉GPIO HAL Driver

STM32L431RCT6芯片有GPIOA~GPIOE、GPIOH等6個(gè)IO口,其中,每個(gè)IO口都有16個(gè)引腳,從GPIOx的PIN0 ~ PIN15。

在第一個(gè)EVB MX+的GPIO例程中,我們翻轉(zhuǎn)GPIOC的引腳13,實(shí)現(xiàn)LED的點(diǎn)亮和熄滅。

HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);

/* 其函數(shù)原型為 */
/**
  * @brief  Toggle the specified GPIO pin.
  * @param  GPIOx where x can be (A..H) to select the GPIO peripheral for STM32L4 family
  * @param  GPIO_Pin specifies the pin to be toggled.
  * @retval None
  */
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

我們依次認(rèn)識(shí)GPIOCGPIO_PIN_13,從HAL庫(kù)的數(shù)據(jù)結(jié)構(gòu)、操作原理、STM32的GPIO結(jié)構(gòu)的角度,來(lái)逐步深入了解。GPIO是最基礎(chǔ)的內(nèi)容,掌握了GPIO的HAL操作原理,也就理解了USART、SPI、ADC、IIC等更復(fù)雜外設(shè)的HAL庫(kù)工作原理。

3.1 回顧指針

3.1.1 內(nèi)存中的數(shù)據(jù)與數(shù)據(jù)類型

圖片

計(jì)算機(jī)的內(nèi)存,可以簡(jiǎn)單看作一條長(zhǎng)街上的一行房子,每一個(gè)房子內(nèi)能容納數(shù)據(jù),并且每一個(gè)房子具有獨(dú)一無(wú)二的編號(hào)。

圖片

  • 上圖中,每一個(gè)格子表示1個(gè)字節(jié),一個(gè)字節(jié)的無(wú)符號(hào)數(shù)的表示范圍是
  • 為了存儲(chǔ)更大的數(shù),我們也可以將4個(gè)字節(jié)看作一個(gè)單元,在32位計(jì)算機(jī)中,4個(gè)字節(jié)即一個(gè)字word

圖片

計(jì)算機(jī)中所有的數(shù)據(jù)都必須放在內(nèi)存中,不同類型的數(shù)據(jù)占用的字節(jié)數(shù)不一樣。如 int 占用 4 個(gè)字節(jié),char 占用 1 個(gè)字節(jié)。為了正確地訪問(wèn)這些數(shù)據(jù),必須為每個(gè)字節(jié)都編上號(hào)碼,就像門(mén)牌號(hào)、身份證號(hào)一樣,每個(gè)字節(jié)的編號(hào)是唯一的,根據(jù)編號(hào)可以準(zhǔn)確地找到某個(gè)字節(jié)。

我們將內(nèi)存中字節(jié)的編號(hào)稱為地址(Address)。地址從 0 開(kāi)始依次增加。對(duì)于32位環(huán)境,程序能夠使用的內(nèi)存為 4GB,最小的地址為0x00000000,最大的地址為0XFFFFFFFF

下圖是 4G 內(nèi)存中每個(gè)字的編號(hào)(以十進(jìn)制表示):

圖片

舉個(gè)簡(jiǎn)單例子:下圖表明計(jì)算機(jī)中, 5個(gè)連續(xù)的字單元中的存儲(chǔ)內(nèi)容。

圖片

  • 不得不說(shuō),如果直接通過(guò)地址編號(hào)去讀取/修改這些數(shù)據(jù),是一件讓人為難的事情 ;
  • 高級(jí)語(yǔ)言提供了解決方案,支持通過(guò)變量名進(jìn)行訪問(wèn);
  • 通過(guò)變量名來(lái)訪問(wèn)變量,對(duì)于開(kāi)發(fā)者非常友好。但是要時(shí)刻記住計(jì)算機(jī)硬件依然是通過(guò)地址來(lái)訪問(wèn)內(nèi)存單元(Hardware still accesses memory locations using addresses)。

下圖和代碼表示通過(guò)變量名訪問(wèn)內(nèi)存:

圖片

int a = 112, b = -1;
float c = 3.14;
int *d = &a;
float *e = &c;

在上述代碼中,變量d和e是指針,它們不是int和float類型,而分別是(int *)和(float *)類型,它們是變量,也存儲(chǔ)在內(nèi)存中。在變量d中,可以存儲(chǔ)int類型變量的地址,在變量e中,可以存儲(chǔ)float類型變量的地址。

通過(guò)前面的圖,我們已經(jīng)知道,變量a存儲(chǔ)在地址編號(hào)為100的格子中。如果需要將變量a的數(shù)值修改為200,則下面語(yǔ)句互相完全等價(jià):

a = 200;
*d = 200; /*變量d之前的*,是指針變量的解引用操作符,derefrence,返回存儲(chǔ)在指針地址中的值*/
*( (int *)(100) ) = 200;
  • 第三條語(yǔ)句是典型的C語(yǔ)言Cast,即類型轉(zhuǎn)換。
  • 第三條語(yǔ)句將無(wú)符號(hào)數(shù)100強(qiáng)制轉(zhuǎn)換成了(int *)的指針,然后在編號(hào)為100的地址中寫(xiě)入數(shù)據(jù)200。
  • 但是,務(wù)必要注意,這種寫(xiě)法很危險(xiǎn)。我們?cè)诰幾g程序之后,一般并不知道某個(gè)變量在內(nèi)存中的存放地址,通過(guò)直接地址編號(hào)進(jìn)行數(shù)據(jù)操作,很容易造成程序崩潰。
  • 但是,ST HAL庫(kù)對(duì)內(nèi)部寄存器操作,卻主動(dòng)采用了這種看似危險(xiǎn)的做法。后文會(huì)清晰說(shuō)明原因。

3.1.2 指針是變量

假設(shè)聲明的變量被依次存放在0x20000000UL地址開(kāi)始的單元格內(nèi)。

unsignedint  a    = 0xFFFFFFFF; /*無(wú)符號(hào)數(shù)據(jù),4294967295*/
signedint    b    = -1;         /*有符號(hào)數(shù),-1*/
unsignedint  c    = 0xFFFFFFFD; /*無(wú)符號(hào)數(shù)據(jù),4294967293*/
signedint    d    = -2;         /*有符號(hào)數(shù),-2*/
unsignedint *pa   = &a;         /*指針變量pa指向a,即,將a的地址賦值給變量pa*/
unsignedint **ppa = &pa;        /*指針變量ppa指向pa,即,將pa的地址賦值給變量ppa*/
typedefstruct{
    unsignedint a;
    signedint b;
    unsignedint c;
    signedint   d;
}User_Typedef; /*自定義某個(gè)數(shù)據(jù)類型,將其命名為User_Typedef*/

User_Typedef data     = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFD,0xFFFFFFFD};
User_Typedef *pdata   = &data;  /*指針變量pdata指向data*/
User_Typedef **ppdata = &pdata; /*指針變量ppdata指向pdata*/

在C語(yǔ)言中,字節(jié)對(duì)齊的情況下,結(jié)構(gòu)體所占用的內(nèi)存是連續(xù)的,且每個(gè)成員也是連續(xù)存放的。在本例中,結(jié)構(gòu)體變量data中的各個(gè)成員data.a、data.b、data.c、data.d的內(nèi)存地址是連續(xù)的。因此,雖然兩段代碼表面上完全不同,但是程序編譯和運(yùn)行后,數(shù)據(jù)在內(nèi)存中的分布完全相同。

值得指出的是,結(jié)構(gòu)體指針中,存放的數(shù)據(jù)是結(jié)構(gòu)體變量第一個(gè)成員的地址。在本例中,data.a的地址,即0x20000000被賦值給了結(jié)構(gòu)體指針pdata。而pdata存放在編號(hào)為0x20000010的內(nèi)存地址中,所以該地址中存放的數(shù)據(jù)是0x20000000。

圖片

圖片

從上面的程序中可以看出:

  • C語(yǔ)言是強(qiáng)類型語(yǔ)言,不僅要聲明變量,還要關(guān)注變量類型。a和b的內(nèi)存地址中存放的數(shù)據(jù)其實(shí)是一樣的,但是因?yàn)轭愋筒煌?,所以程序?qū)?shù)據(jù)的理解完全不同。
  • 指針也是變量,所以也需要存儲(chǔ)在某個(gè)內(nèi)存地址中。指針并不特殊,(Type *)類型的指針變量中,只能存儲(chǔ)Type類型變量的地址。此處的Type,適用于C語(yǔ)言的基礎(chǔ)類型數(shù)據(jù)、結(jié)構(gòu)體、聯(lián)合體、函數(shù)等各種類型。
  • 在32位環(huán)境中,一個(gè)指針變量占用4個(gè)字節(jié)的存儲(chǔ)空間,無(wú)論該指針是何種類型。

在第二段代碼中,可以用如下方式訪問(wèn)結(jié)構(gòu)體中的各個(gè)成員,第5~7行完全等價(jià)。

User_Typedef data;/*data中的成員還沒(méi)有初始化*/
User_Typedef *pdata   = &data;  /*指針變量,pdata指向data*/
User_Typedef **ppdata = &pdata; /*指針變量,ppdata指向pdata*/

data.a       = 0xFFFFFFFF;
pdata- >a     = 0xFFFFFFFF;
(*ppdata)- >a = 0xFFFFFFFF;

3.2 初識(shí)GPIOx

在GPIOC上點(diǎn)擊右鍵,選擇Go To Definition of 'GPIOC'

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOH               ((GPIO_TypeDef *) GPIOH_BASE)

目前,先不管GPIO_TypeDef這種自定義的結(jié)構(gòu)體中含有哪些成員,但是我們可以清楚地知道,GPIOx是一個(gè)自定義的GPIO_TypeDef *類型的指針,通過(guò)GPIOx->member的方式,可以直接訪問(wèn)到各個(gè)成員。

進(jìn)一步在GPIOC_BASE上點(diǎn)擊右鍵,依次得到:

#define GPIOA_BASE            (AHB2PERIPH_BASE + 0x0000UL)
#define GPIOB_BASE            (AHB2PERIPH_BASE + 0x0400UL)
#define GPIOC_BASE            (AHB2PERIPH_BASE + 0x0800UL)
#define GPIOD_BASE            (AHB2PERIPH_BASE + 0x0C00UL)
#define GPIOE_BASE            (AHB2PERIPH_BASE + 0x1000UL)
#define GPIOH_BASE            (AHB2PERIPH_BASE + 0x1C00UL)

#define AHB2PERIPH_BASE       (PERIPH_BASE + 0x08000000UL)

#define PERIPH_BASE           (0x40000000UL)

通過(guò)換算,GPIOA、GPIOB、GPIOC等實(shí)際上等價(jià)于:

#define GPIOA               ((GPIO_TypeDef *) (0x40800000UL))
#define GPIOB               ((GPIO_TypeDef *) (0x40800400UL))
#define GPIOC               ((GPIO_TypeDef *) (0x40800800UL))

結(jié)合C語(yǔ)言存儲(chǔ)結(jié)構(gòu)體變量的特點(diǎn),我們可以得出推論:以GPIOC為例,從地址0x40800800UL開(kāi)始,是一段連續(xù)地址空間,這段連續(xù)的空間可以完整存儲(chǔ)GPIO_TypeDef類型的數(shù)據(jù)。但是,這一段連續(xù)地址空間到底占用了多少字節(jié)?我們還需要深入了解自定義結(jié)構(gòu)體GPIO_TypeDef。

3.3 深入了解GPIO_TypeDef

認(rèn)識(shí)GPIO_TypeDef,等于認(rèn)識(shí)了ST HAL中所有外設(shè)的xxx_TypeDef。在GPIO_TypeDef上點(diǎn)擊右鍵,選擇Go To Definition of 'GPIO_TypeDef',它是一個(gè)結(jié)構(gòu)體,包括MODER、OTYPER等成員,每個(gè)成員都是uint32_t類型(無(wú)符號(hào)32位整型),__IO表示volatile。每個(gè)成員的作用見(jiàn)下圖的注釋部分,翻譯成中文分別是模式寄存器、輸出模式寄存器、輸出速度寄存器、上拉-下拉寄存器、輸入數(shù)據(jù)寄存器、輸出數(shù)據(jù)寄存器、置位-復(fù)位寄存器、鎖定配置寄存器、復(fù)用功能寄存器、Bit復(fù)位寄存器。

圖片

在RM0394.pdf的274 ~ 275頁(yè),有GPIOx的寄存器布局圖,其中x表示A ~ E,H

圖片

圖片

結(jié)合GPIOx的地址和寄存器布局圖,可以得到推論:

  • 如果要設(shè)置GPIOx的各個(gè)引腳模式,需要向GPIOx的MODER寄存器中寫(xiě)入相應(yīng)數(shù)值;
  • 如果要設(shè)置GPIOx的各個(gè)引腳輸出模式,需要向GPIOx的OTYPER寄存器中寫(xiě)入相應(yīng)數(shù)值;
  • GPIOA MODER的地址是0x40800000UL,GPIOA OTYPER的地址是0x40800004UL;
  • GPIOB MODER的地址是0x40800400UL,GPIOB OTYPER的地址是0x40800404UL;
  • GPIOC MODER的地址是0x40800800UL,GPIOC OTYPER的地址是0x40800804UL。

顯然,對(duì)于GPIOA ~ GPIOH,所有寄存器的布局是相同的,寄存器地址依次偏移4個(gè)字節(jié),圖示如下:

圖片

  • 圖中,每個(gè)地址都是32位的,每個(gè)地址中能容納的數(shù)據(jù)也是32位。
  • 地址0x40800000UL中寫(xiě)入一個(gè)32位的數(shù)據(jù),等價(jià)于向GPIOA的MODER寄存器中寫(xiě)入一個(gè)32位的數(shù)據(jù),顯然,地址編號(hào)不如寄存器名稱方便。
  • 在C語(yǔ)言中,字節(jié)對(duì)齊的情況下,結(jié)構(gòu)體所占用的內(nèi)存是連續(xù)的,且每個(gè)成員也是連續(xù)存放的。利用C語(yǔ)言的特性,HAL庫(kù)中聲明了一個(gè)自定義的結(jié)構(gòu)體GPIO_TypeDef,該結(jié)構(gòu)體的各個(gè)成員嚴(yán)格按照STM32L4xx系列的GPIOx各寄存器順序進(jìn)行排序,且每個(gè)成員都能容納(存儲(chǔ))一個(gè)32位的數(shù)據(jù)。
  • 在STM32中,還有諸如USART、IIC、SPI、CAN、ADC等各種不同的外設(shè),自然也就有對(duì)應(yīng)的xxx_Typedef的自定義結(jié)構(gòu)體類型。下圖給出了USART_TypeDef的結(jié)構(gòu)體定義,我們無(wú)需查看手冊(cè)就知道在STM32處理器中,控制USART外設(shè)工作需要向CR1、CR2等系列寄存器寫(xiě)入符合芯片RM手冊(cè)中規(guī)定的數(shù)據(jù)即可。USART_TypeDef的聲明如下圖所示:

圖片

3.4 進(jìn)一步了解GPIOx

#define GPIOC   ((GPIO_TypeDef *) (0x40800800UL))

define是一個(gè)宏,表示GPIOC等價(jià)于((GPIO_TypeDef *) (0x40800800UL))。因此,GPIOC本質(zhì)上是GPIO_TypeDef *類型的指針。

Q&A

Q1: 如何對(duì)GPIOA的MODER寄存器執(zhí)行寫(xiě)操作?如何對(duì)GPIOC的OTYPER寄存器執(zhí)行寫(xiě)操作?

A1: ->是C語(yǔ)言中的指向結(jié)構(gòu)體成員運(yùn)算符,用于使用指向某種結(jié)構(gòu)的指針來(lái)訪問(wèn)結(jié)構(gòu)內(nèi)的成員。使用GPIOA->MODE = 0x1234; GPIOC->OTYPER= 0x789A;即可完成GPIOA和GPIOC對(duì)應(yīng)寄存器的數(shù)據(jù)寫(xiě)入。

Q2: (0x40800800UL)是一個(gè)整形數(shù)據(jù),也能轉(zhuǎn)化為指針嗎?

A2: 通過(guò)前文,已經(jīng)知道GPIOx的所有寄存器在STM32的內(nèi)存中,是連續(xù)存放的。而C語(yǔ)言的結(jié)構(gòu)體在字節(jié)對(duì)齊的情況下,內(nèi)部成員也是連續(xù)存放的,且結(jié)構(gòu)體指針指向結(jié)構(gòu)體第一個(gè)成員的地址。利用這個(gè)特點(diǎn),將數(shù)據(jù)0x40800800UL強(qiáng)制轉(zhuǎn)換為(GPIO_TypeDef *)類型的指針,那么,從0x40800800UL到0x40800828UL地址段,每4個(gè)字節(jié)就對(duì)應(yīng)GPIOx中的一個(gè)寄存器,完美構(gòu)建了軟件與硬件的溝通橋梁。

Q3: 如果不用宏表示GPIOC,那么GPIOC->OTYPER = 0x1234應(yīng)該用什么形式實(shí)現(xiàn)?

A3: ( (GPIO_TypeDef *) (0x40800800UL) )->OTYPER = 0x1234;,意味著,程序?qū)⒃L問(wèn)0x40800800UL開(kāi)始的地址空間內(nèi)的OTYPER成員,即將32位的十六進(jìn)制數(shù)據(jù)0x1234寫(xiě)入地址0x40800804UL。顯然,這種寫(xiě)法很難看,不如GPIOC->OTYPER 直觀。

3.5 HAL API的設(shè)計(jì)

在C語(yǔ)言中,指針是最核心的內(nèi)容,也是難點(diǎn)。通過(guò)前文分析,我們已經(jīng)知道指針只是變量而已,并不復(fù)雜,HAL庫(kù)中所用的指針很簡(jiǎn)單。

現(xiàn)在對(duì)比兩種不同方式設(shè)計(jì)的HAL_GPIO_TogglePin函數(shù),其中,方式1是ST HAL官方庫(kù)的正確設(shè)計(jì),方式2是不合理方案。

/* 方式1:HAL庫(kù)官方方案*/
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
/* 方式2:不合理方案*/    
GPIO_TypeDef HAL_GPIO_TogglePin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin)

/* 方式1:HAL庫(kù)官方方案進(jìn)行函數(shù)調(diào)用*/
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
/* 方式2:不合理方案進(jìn)行函數(shù)調(diào)用*/
*(GPIOC) = HAL_GPIO_TogglePin(*(GPIOC), GPIO_PIN_13);

C 語(yǔ)言使用傳值調(diào)用方法來(lái)傳遞參數(shù),即將形參的值復(fù)制給實(shí)參。在發(fā)生函數(shù)調(diào)用時(shí),形參的存放地址空間來(lái)源于堆棧。

方式1:HAL庫(kù)官方方案進(jìn)行函數(shù)調(diào)用:

  • 第一個(gè)實(shí)參的值,GPIOC,即0x40800800UL 被復(fù)制給了形參GPIOx,占用4個(gè)字節(jié);
  • 第二個(gè)實(shí)參的值,GPIO_PIN_13,被復(fù)制給了形參GPIO_Pin,占用4個(gè)字節(jié)。
  • 堆棧在形參上的開(kāi)銷至少是8個(gè)字節(jié)。
  • 傳遞指針GPIOC的值給了臨時(shí)變量GPIOx,臨時(shí)變量GPIOx存放的具體地址不明,但是,可直接通過(guò)GPIOx->MODE = xx的方式,即( (GPIO_TypeDef *) (0x40800800UL) )->MODE = xx,以地址訪問(wèn)的形式直接修改了GPIOC MODE寄存器所對(duì)應(yīng)的內(nèi)存,從而成功修改寄存器的值。

方式2:不合理方案進(jìn)行函數(shù)調(diào)用:

  • 第一個(gè)實(shí)參的值,*GPIOC,即從0x40800800UL到0x40800828UL地址空間內(nèi)的所有數(shù)據(jù),被復(fù)制給了形參GPIOx,合計(jì)占用44字節(jié);
  • 第二個(gè)實(shí)參的值,GPIO_PIN_13,被復(fù)制給了形參GPIO_Pin,占用4個(gè)字節(jié)。
  • 堆棧在形參上的開(kāi)銷至少是48個(gè)字節(jié)。
  • 由于GPIOx是個(gè)GPIO_TypeDef類型的臨時(shí)變量,存放的具體地址不明,即使在程序中使用GPIOx.MODE修改了GPIOx成員MODE的數(shù)值,也不會(huì)真正影響GPIOC->MODEGPIOC->MODE表示地址0x40800800UL,而GPIOx.MODE肯定不存放在該地址,修改GPIOx.MODE中存放的數(shù)值,自然不可能影響到內(nèi)存地址0x40800800UL,必須通過(guò)函數(shù)返回值進(jìn)行賦值,而這又會(huì)帶來(lái)一系列堆棧開(kāi)銷。

1.jpg

綜上,對(duì)比兩種設(shè)計(jì)方法,毫無(wú)疑問(wèn)是HAL庫(kù)提供的方式1效果更加,更加高效,占用內(nèi)存更少。HAL庫(kù)中,都是通過(guò)傳遞指針來(lái)進(jìn)行API函數(shù)設(shè)計(jì)的。

4. 小結(jié)

  1. HAL的精髓在于Abstract抽象。
  2. STM32的RM、UM手冊(cè)是基礎(chǔ),AN手冊(cè)是進(jìn)階。
  3. 指針到底是什么?指針是變量。
  4. 指向int指針和指向結(jié)構(gòu)體的指針的相同點(diǎn)在于,在32位環(huán)境中占用4個(gè)字節(jié);不同點(diǎn)是存儲(chǔ)不同類型變量的地址。
  5. HAL的GPIO_TypeDef之類的xxx_TypeDef是嚴(yán)格與RM手冊(cè)中的寄存器分布一一對(duì)應(yīng)的。
  6. HAL庫(kù)通過(guò)封裝xxx_TypeDef類型的指針,利用C語(yǔ)言的結(jié)構(gòu)體實(shí)現(xiàn)了典型的面向?qū)ο缶幊痰乃悸贰?/li>
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(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)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    183

    文章

    7644

    瀏覽量

    145605
  • GPIO
    +關(guān)注

    關(guān)注

    16

    文章

    1328

    瀏覽量

    56231
  • STM32L4
    +關(guān)注

    關(guān)注

    1

    文章

    42

    瀏覽量

    10032
  • HAL庫(kù)
    +關(guān)注

    關(guān)注

    1

    文章

    121

    瀏覽量

    7634
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Linux內(nèi)核面向對(duì)象編程的實(shí)現(xiàn)

    面向對(duì)象編程(OOP),是一種設(shè)計(jì)思想或者架構(gòu)風(fēng)格。OO語(yǔ)言之父Alan Kay,Smalltalk的發(fā)明人,在談到OOP時(shí)是這樣說(shuō)的。
    發(fā)表于 07-21 14:51 ?1034次閱讀

    面向對(duì)象思想讓裸編程帶上靈魂

    告訴我:寫(xiě)好的程序不是如何去完成代碼,而是如何去組織代碼,是如何組織,不是組織。上位機(jī)面向對(duì)象的編程思想,就是一個(gè)非常可取的思想。
    發(fā)表于 11-24 10:00

    談?wù)?b class='flag-5'>面向對(duì)象編程

    工業(yè)控制系統(tǒng)的PLC程序也可以采用這種設(shè)計(jì)思想,雖然我們無(wú)法實(shí)現(xiàn)面向對(duì)象的很多優(yōu)秀特點(diǎn)如“繼承”,甚至于它根本就不具備面向
    發(fā)表于 09-08 07:47

    面向對(duì)象編程語(yǔ)言的特點(diǎn)

    工業(yè)控制系統(tǒng)的PLC程序也可以采用這種設(shè)計(jì)思想,雖然我們無(wú)法實(shí)現(xiàn)面向對(duì)象的很多優(yōu)秀特點(diǎn)如“繼承”,甚至于它根本就不具備面向
    發(fā)表于 09-08 07:44

    面向對(duì)象編程的基本概念及其特點(diǎn)

    面向對(duì)象編程是計(jì)算機(jī)高級(jí)語(yǔ)言的一種先進(jìn)的編程模式,在工業(yè)控制系統(tǒng)的PLC程序也可以采用這種設(shè)計(jì)思想,雖然我們無(wú)法實(shí)現(xiàn)面向
    發(fā)表于 09-09 06:33

    面向對(duì)象編程介紹

    5.項(xiàng)目案例: 棧與隊(duì)列的封裝5.1 棧的封裝5.2 隊(duì)列的封裝一、面向對(duì)象編程介紹1.面向過(guò)程編程“面向過(guò)程”(Procedure Oriented)是一種以過(guò)程為中心的編程
    發(fā)表于 12-13 07:22

    面向對(duì)象編程練習(xí)

    實(shí)驗(yàn) 3 面向對(duì)象編程練習(xí) 一、實(shí)驗(yàn)?zāi)康?    通過(guò)編程和上機(jī)實(shí)驗(yàn)理解 Java 語(yǔ)言是如何體現(xiàn)面向對(duì)象編程基本思想
    發(fā)表于 09-23 18:57 ?3408次閱讀

    面向對(duì)象的程序設(shè)計(jì)(C++)

    面向對(duì)象的程序設(shè)計(jì)(C++).面向對(duì)象的基本思想 C++對(duì)C的非面向
    發(fā)表于 03-22 14:40 ?0次下載

    面向過(guò)程和面向對(duì)象有什么區(qū)別

    面向過(guò)程(pop)和面向對(duì)象(oop)是什么  1. pop(Process-oriented programming)的縮寫(xiě),“面向過(guò)程”是一種是事件為中心的編程
    發(fā)表于 05-13 18:12 ?2次下載
    <b class='flag-5'>面向</b>過(guò)程和<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b>有什么區(qū)別

    在單片機(jī)引入面向對(duì)象思想

    的代碼也無(wú)法使用,得重新敲,代碼重用度不高,編程效率低下,代碼無(wú)法積累。而且感覺(jué)寫(xiě)這個(gè)代碼沒(méi)有思想,沒(méi)有靈魂,沒(méi)有框架,只是一個(gè)一個(gè)功能代碼的堆砌,很空泛。 那么這個(gè)時(shí)候,你也許應(yīng)該在單片機(jī)引入面向
    的頭像 發(fā)表于 11-01 11:36 ?2443次閱讀

    C/C++之面向對(duì)象編程思想1

    C++作為一門(mén)在C和Java之間的語(yǔ)言,其既可以使用C語(yǔ)言中的高效指針,又繼承了Java面向對(duì)象編程思想,在去年編程語(yǔ)言排行榜上更是首次超過(guò)Java,進(jìn)入前三。
    的頭像 發(fā)表于 03-30 15:14 ?1228次閱讀
    C/C++之<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b>編程<b class='flag-5'>思想</b>1

    C/C++之面向對(duì)象編程思想2

    C++作為一門(mén)在C和Java之間的語(yǔ)言,其既可以使用C語(yǔ)言中的高效指針,又繼承了Java面向對(duì)象編程思想,在去年編程語(yǔ)言排行榜上更是首次超過(guò)Java,進(jìn)入前三。
    的頭像 發(fā)表于 03-30 15:14 ?1191次閱讀
    C/C++之<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b>編程<b class='flag-5'>思想</b>2

    C/C++之面向對(duì)象編程思想3

    C++作為一門(mén)在C和Java之間的語(yǔ)言,其既可以使用C語(yǔ)言中的高效指針,又繼承了Java面向對(duì)象編程思想,在去年編程語(yǔ)言排行榜上更是首次超過(guò)Java,進(jìn)入前三。
    的頭像 發(fā)表于 03-30 15:16 ?1178次閱讀
    C/C++之<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b>編程<b class='flag-5'>思想</b>3

    面向對(duì)象思想封裝IIC、AT24C64驅(qū)動(dòng)

    使用面向對(duì)象的編程思想封裝IIC驅(qū)動(dòng),將IIC的屬性和操作封裝成一個(gè)庫(kù),在需要?jiǎng)?chuàng)建一個(gè)IIC設(shè)備時(shí)只需要實(shí)例化一個(gè)IIC對(duì)象即可,本文是基于STM32和
    的頭像 發(fā)表于 10-08 15:35 ?1776次閱讀

    淺談C語(yǔ)言面向對(duì)象編程思想

    C語(yǔ)言是一種面向過(guò)程的語(yǔ)言,但是也可以用結(jié)構(gòu)體和函數(shù)指針來(lái)模擬面向對(duì)象的特性,比如封裝、繼承和多態(tài)。
    發(fā)表于 11-02 12:27 ?1945次閱讀