前言
一提 GPIO 可能會(huì)讓很多人覺(jué)得不屑,這么簡(jiǎn)單的東西有什么可說(shuō)的,也就是一個(gè)拉低拉高,誰(shuí)不會(huì)呢。
今天我們不講推挽開(kāi)漏、不提上拉下拉。大家來(lái)頭腦風(fēng)暴一下 GPIO 相關(guān)的幾個(gè)問(wèn)題。筆者說(shuō)的不一定對(duì),僅代表個(gè)人的一點(diǎn)兒小想法。
提出兩個(gè)問(wèn)題
有兩個(gè)問(wèn)題,需要考慮一下:
1. pin 設(shè)備驅(qū)動(dòng)框架存在的必要性。
2. gpio 底層驅(qū)動(dòng)使用官方提供的 api 還是直接寄存器操作?
在單片機(jī)里,pin 引腳操作往往很簡(jiǎn)單,寫(xiě)一個(gè)寄存器就成。但是在一個(gè)操作系統(tǒng)里,為了方便移植、便于閱讀,對(duì) pin 進(jìn)行封裝,在不同芯片上使用同一套 api 也還是有必要的。而這也正是一個(gè)操作系統(tǒng)的職責(zé)之一。
第二個(gè)問(wèn)題,留著大家自己想吧。
pin 驅(qū)動(dòng)框架
我們大家都知道,在我們使用 pin 的時(shí)候,沒(méi)有誰(shuí)先用 `rt_device_find` 查找 pin 設(shè)備,然后使用用 `rt_device_open` `rt_device_read` `rt_device_write` 去控制芯片引腳。而都是直接調(diào)用的 `rt_pin_xxx` c 函數(shù)簇。
pin 驅(qū)動(dòng)框架,先把所有的 gpio 看作一個(gè)設(shè)備進(jìn)行注冊(cè),然后提供了三個(gè) `rt_pin_mode` `rt_pin_read` `rt_pin_write` c 函數(shù),而不是 `rt_device_xxx` api 去訪問(wèn)某個(gè) pin。
`rt_pin_mode` `rt_pin_read` `rt_pin_write` 這一套函數(shù),可以不用考慮當(dāng)前使用的是什么芯片,不用考慮芯片廠商提供的外設(shè)驅(qū)動(dòng)庫(kù) api 是怎么寫(xiě)的。但是,真的是這樣嗎?
可以把驅(qū)動(dòng)框架刪掉,`rt_pin_xxx` 函數(shù)直接對(duì)接底層驅(qū)動(dòng)嗎?
當(dāng)筆者閱讀模擬 iic 驅(qū)動(dòng)源碼時(shí),看到在控制 SCL SDA 高低電平切換時(shí)使用的 `rt_pin_write` 操作,一時(shí)間腦子一陣暈眩。為什么?我們先捋順一下拉低 SDA 的函數(shù)調(diào)用過(guò)程。
以 `SDA_L` 為例
1. `SDA_L` 宏是 `ops->set_sda(ops->data, val)` 通過(guò)操作符指針調(diào)用底層接口,
2. `set_sda` 調(diào)用 `rt_pin_write`
3. `rt_pin_write` 通過(guò) pin 設(shè)備執(zhí)行 `_hw_pin.ops->pin_write` 調(diào)用的 pin 底層接口
4. 在 stm32 平臺(tái)上 `_hw_pin.ops->pin_write` 等于調(diào)用 `stm32_pin_write` 函數(shù)
5. `stm32_pin_write` 調(diào)用 `HAL_GPIO_WritePin`
6. `HAL_GPIO_WritePin` 函數(shù)寫(xiě)寄存器。
彎彎繞繞,想控制 pin 引腳電平變化還真是煞費(fèi)苦心了。
可以壓縮上述調(diào)用過(guò)程嗎?
`SDA_L` 宏直接定義調(diào)用 `stm32_pin_write` ,`stm32_pin_write` 內(nèi)部直接操作寄存器。
soft iic 驅(qū)動(dòng)
軟件模擬 iic 驅(qū)動(dòng)需要軟件代碼控制 SCL SDA 兩根線時(shí)序,如前所述,拉低 SDA 線的過(guò)程被繁冗化了。
裸機(jī)能達(dá)到的 iic 時(shí)鐘速度,在使用 rt-thread 的模擬 iic 時(shí)根本達(dá)不到,在多級(jí)指針和函數(shù)調(diào)用過(guò)程中,效率被極大降低了。
有沒(méi)有一種策略,使 `SDA_L` 宏直接定義成 `stm32_pin_write` 或者 `gd32_pin_write` 等等。
筆者嘗試把 i2c-bit-ops.c 文件和 drv_soft_i2c.c 兩個(gè)文件進(jìn)行合并,省掉了一級(jí) `struct rt_i2c_bit_ops`,然后 `SDA_L` 也不使用 `rt_pin_write` 又跳過(guò)了多次指針調(diào)用。目前感覺(jué)良好。
GET_PIN
有哪位能告訴大家, `rt_pin_write(17, PIN_HIGH)` 這句代碼有明確的語(yǔ)義嗎?
函數(shù)調(diào)用中的第一個(gè)參數(shù)值 “17” 表示了什么?
可能啥也不代表。
首先,它肯定不是芯片引腳編號(hào)。
大多數(shù)芯片,GPIO 編碼采用的類(lèi)似如下方式:
- 以端口編碼,一顆芯片上的 GPIO 可以分成若干個(gè)端口,用字母 A B C ... 命名(也有 1 2 3 編號(hào)命名的,比如 RA6M4)。我們稱之為 PA PB PC ...
- 每個(gè)端口有8/16個(gè) io 。分別編碼 0-7 或者 0-15。有些芯片上的某個(gè)端口只有 15 個(gè) io ,那就只有 0-14 有效。我們稱之為 PA0 PA15
為了不使用魔數(shù) “17” ,這種模棱兩可,含義不明的寫(xiě)法,rt-thread 針對(duì)每種芯片要求定義一個(gè) `GET_PIN` 宏,它可以從一種直觀的引腳編號(hào)寫(xiě)法中返回一個(gè)數(shù)字。比如 `GET_PIN(G, 1)` 的結(jié)果是 97。
使用 `GET_PIN` `rt_pin_write(17, PIN_HIGH)` 可以寫(xiě)成 `rt_pin_write(GET_PIN(B, 1), PIN_HIGH)` ,這樣是不是更直觀了?
但是,有一種情況,不允許我們用 `GET_PIN` 。那就是在 menuconfig 或者 RT-Studio 的 Settings 里配置模擬 iic 兩個(gè)引腳號(hào)的時(shí)候。它只支持輸入數(shù)字,這個(gè)時(shí)候我們必須知道 `GET_PIN` 的數(shù)學(xué)含義,心算把 `GET_PIN(B, 1)` 轉(zhuǎn)成 17 。
`GET_PIN` 的數(shù)學(xué)含義是確定的嗎?是放之四海而皆準(zhǔn)的嗎?在每一款芯片上可以使用同一個(gè)數(shù)學(xué)公式演算嗎?
**這個(gè)可以是,但實(shí)際卻不是**。
另類(lèi)的 AB32 RA6M4 N32
我們?nèi)匀灰?17 這個(gè)編號(hào)為例,下面來(lái)看看 AB32 RA6M4 上面它分別代表哪個(gè) GPIO 。
AB32 上應(yīng)該是 PE4。
AB32 版 `GET_PIN` 是這樣的:
#define __AB32_PORT(port) GPIO##port
#define __AB32_GET_PIN_A(PIN) PIN
#define __AB32_GET_PIN_B(PIN) 8 + PIN
#define __AB32_GET_PIN_E(PIN) 13 + PIN
#define __AB32_GET_PIN_F(PIN) 21 + PIN
幾個(gè)端口不通用,各自為戰(zhàn)
RA6M4 上不存在。因?yàn)?RA6M4 的 P100 對(duì)應(yīng)的是 256 ;P015 對(duì)應(yīng)的是 15 。沒(méi)有 17 這個(gè)編號(hào)。
AB32 版 `GET_PIN` 未實(shí)現(xiàn)。
還有 N32,上面筆者說(shuō)了句“它肯定不是芯片引腳編號(hào)”。但是,我又發(fā)現(xiàn)在 N32 的drv_gpio.c 中,定義成了芯片引腳號(hào)。打臉了...
N32 版 `GET_PIN` 也未實(shí)現(xiàn)。
還有其它芯片是上述三種情況之外的嗎?歡迎大家講出來(lái)。
明確的應(yīng)用層語(yǔ)義
不失一般性,假設(shè)可能存在某芯片端口編號(hào)不是連續(xù)的,中間缺失端口B。同時(shí)端口 A 也只有 12 個(gè) io。我們把所有的端口和 io 進(jìn)行排序編號(hào)。PA0 是 0 號(hào)、PA1 是 1 號(hào) ... PA11 是 11 號(hào)。那么,PC0 編號(hào)是多少?12嗎?
假如有一同系列芯片,它是有端口 B 的。那么 PB0 編號(hào)該定義成多少合適呢?也是 12 嗎?
> 或者,干脆我們就假定所有的芯片端口都是連續(xù)無(wú)缺失的,每個(gè)端口也是滿滿當(dāng)當(dāng) 16 個(gè) io 。這樣 PB0 總是 16,PC0 總是 32。
從理論上講,所有的芯片 gpio 編號(hào)系統(tǒng)是可以用一個(gè)公式實(shí)現(xiàn)的,這個(gè)公式可以在 rt-thread 使用寶典(2022-0516更新)中找到。
RA6M4 上,應(yīng)用程序?qū)涌梢允褂?17 表示 P101,因?yàn)樗亩丝诰幪?hào)從 0 開(kāi)始;
N32 上,應(yīng)用程序?qū)涌梢允褂?17 表示 PB01,因?yàn)樗亩丝诰幪?hào)從 A 開(kāi)始;
STM32 上,應(yīng)用程序?qū)涌梢允褂?17 表示 PB01,因?yàn)樗亩丝诰幪?hào)從 A 開(kāi)始;
AB32 上,應(yīng)用程序?qū)涌梢允褂?17 表示 PB01,因?yàn)樗亩丝诰幪?hào)從 A 開(kāi)始
無(wú)論用的哪家芯片,無(wú)論是哪個(gè)系列芯片,無(wú)論是哪款型號(hào),它有多少引腳。我們希望 17 這個(gè)值能對(duì)應(yīng)一個(gè)明確的引腳名。不會(huì)因?yàn)槟诚盗行酒心匙有吞?hào)因?yàn)槠渲心硞€(gè)端口 io 數(shù)量少一個(gè)導(dǎo)致后面所有 io 的編號(hào)都變了。又或者同樣的 PB01 在不同子型號(hào)不同封裝下的編號(hào)也不一樣。
結(jié)束語(yǔ)
大家有什么意見(jiàn)和想法,一塊兒聊聊啊。
> 把不同芯片的差異性進(jìn)行封裝,提供給應(yīng)用層語(yǔ)義明確的接口,是一個(gè)操作系統(tǒng)的職責(zé)之一。
復(fù)雜事情簡(jiǎn)單化,簡(jiǎn)單的事情保留那一點(diǎn)兒純粹。這也是封裝的基本原則。
寫(xiě)應(yīng)用程序代碼時(shí),我們不想關(guān)心底層的實(shí)現(xiàn),這是另一個(gè)操作系統(tǒng)的職責(zé)之一。
審核編輯:湯梓紅
-
GPIO
+關(guān)注
關(guān)注
16文章
1196瀏覽量
51934 -
PIN
+關(guān)注
關(guān)注
1文章
303瀏覽量
24239 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1274瀏覽量
39941 -
驅(qū)動(dòng)框架
+關(guān)注
關(guān)注
0文章
14瀏覽量
4032
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論