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

RT-Thread驅(qū)動開發(fā)指南進階篇-動手驅(qū)動先楫未適配的外設(shè)LCD

【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集
如何在RT-Thread中使用通用GPIO設(shè)備驅(qū)動從而操作GPIO呢
RT-Thread上的CAN總線介紹以及驅(qū)動編寫
簡要分析Thread的通用GPIO設(shè)備驅(qū)動
RT-Thread Studio驅(qū)動SD卡

【RT-Thread開源作品秀】基于RT-Thread的星務(wù)平臺研究

《RT-Thread設(shè)備驅(qū)動開發(fā)指南》基礎(chǔ)篇--以先楫bsp的hwtimer設(shè)備為例

【好書推薦】RT-Thread設(shè)備驅(qū)動開發(fā)指南

評論