一、gpio用途
General Purpose Input Output (通用輸入/輸出)簡稱為GPIO,或總線擴(kuò)展器,人們利用工業(yè)標(biāo)準(zhǔn)I2C、SMBus或SPI接口簡化了I/O口的擴(kuò)展。當(dāng)微控制器或芯片組沒有足夠的I/O端口,或當(dāng)系統(tǒng)需要采用遠(yuǎn)端串行通信或控制時(shí),GPIO產(chǎn)品能夠提供額外的控制和監(jiān)視功能。
每個(gè)GPIO端口可通過軟件分別配置成輸入或輸出。Maxim的GPIO產(chǎn)品線包括8端口至28端口的GPIO,提供推挽式輸出或漏極開路輸出。提供微型3mm x 3mm QFN封裝。
不同系統(tǒng)間的GPIO的確切作用不同。通用常有下面幾種:
1.輸出值可寫(高=1,低=0)。一些芯片也可以選擇驅(qū)動(dòng)這些值的方式,以便支持“線-或”或類似方案(開漏信號(hào)線)。
2.輸入值可讀(1,0)。一些芯片支持輸出管腳回讀,這在線或的情況下非常有用(以支持雙向信號(hào)線)。GPIO控制器可能具有一個(gè)輸入防故障/防反跳邏輯,有時(shí)還會(huì)有軟件控制。
3.輸入經(jīng)常被用作中斷信號(hào),通常是邊沿觸發(fā),但也有可能是電平觸發(fā)。這些中斷可以配置為系統(tǒng)喚醒事件,從而將系統(tǒng)從低功耗模式喚醒。
4.一個(gè)GPIO經(jīng)常被配置為輸入/輸出雙向,根據(jù)不同的產(chǎn)品單板需求,但也存在單向的情況。
5.大多是GPIO可以在獲取到spinlock自旋鎖時(shí)訪問,但那些通過串行總線訪問的通常不能如此操作(休眠的原因)。一些系統(tǒng)中會(huì)同時(shí)存在這兩種形式的GPIO。
6.在一個(gè)給定單板上,每個(gè)GPIO用于一個(gè)特定的目的,如監(jiān)控MMC/SD卡的插入/移除,檢查卡寫保護(hù)狀態(tài),驅(qū)動(dòng)LED,配置發(fā)送器,串行總線位拆,觸發(fā)一個(gè)硬件看門狗,觸發(fā)一個(gè)開關(guān)之類的。
二、gpio優(yōu)點(diǎn)
低功耗:GPIO具有更低的功率損耗(大約1μA,μC的工作電流則為100μA)。
集成IIC從機(jī)接口:GPIO內(nèi)置IIC從機(jī)接口,即使在待機(jī)模式下也能夠全速工作。
小封裝:GPIO器件提供最小的封裝尺寸 ― 3mm x 3mm QFN!
低成本:您不用為沒有使用的功能買單。
快速上市:不需要編寫額外的代碼、文檔,不需要任何維護(hù)工作。
靈活的燈光控制:內(nèi)置多路高分辨率的PWM輸出。
可預(yù)先確定響應(yīng)時(shí)間:縮短或確定外部事件與中斷之間的響應(yīng)時(shí)間。
更好的燈光效果:匹配的電流輸出確保均勻的顯示亮度。
布線簡單:僅需使用2條就可以組成IIC總線或3條組成SPI總線。
與ARM 的幾組GPIO引腳,功能相似,GPxCON 控制引腳功能,GPxDAT用于讀寫引腳數(shù)據(jù)。另外,GPxUP用于確定是否使用上拉電阻。 x為A,B,,H/J,
GPAUP 沒有上拉電阻。
三、GPIO系統(tǒng)結(jié)構(gòu)
如前面提醒的一樣,一個(gè)可選的實(shí)現(xiàn)結(jié)構(gòu)使得平臺(tái)支持不同種類的GPIO控制器使用同一個(gè)編程接口變得簡單。這個(gè)結(jié)構(gòu)稱為gpiolib。
作為一個(gè)調(diào)試目的,如果debugfs有效,一個(gè)/sys/kernel/debug/gpio文件在那里將被找到。它列出了所有的通過這個(gè)結(jié)構(gòu)注冊(cè)的GPIO控制器,和GPIO當(dāng)前的使用狀態(tài)。
Controller Drivers: gpio_chip
控制器驅(qū)動(dòng):gpio_chip
在這個(gè)架構(gòu)中,每個(gè)GPIO控制器被封裝為一個(gè)“gpio_chip”結(jié)構(gòu)體,此結(jié)構(gòu)體中包含了每個(gè)控制器的通用信息:
--確定GPIO方向的方法
--存取GPIO值的方法
--聲明方法是否休眠的flag
--可選的debugfs dump方法(展現(xiàn)附加的狀態(tài)如上拉配置等)
--用于診斷目的的標(biāo)簽
每個(gè)實(shí)例也有自己的私有數(shù)據(jù),可能來自device.platform_data:它的第一個(gè)GPIO和它暴露幾個(gè)GPIO.
實(shí)現(xiàn)一個(gè)gpio_chip的代碼應(yīng)該支持多個(gè)控制器的實(shí)例,可能使用驅(qū)動(dòng)模型。代碼會(huì)配置每個(gè)gpio_chip并且執(zhí)行g(shù)piochip_add()。移除一個(gè)GPIO控制器是少見的,使用gpio_remove()移除一個(gè)不再有效的GPIO控制器。
大多數(shù)時(shí)候,一個(gè)gpio_chip是一個(gè)實(shí)例獨(dú)有的結(jié)構(gòu),它的一些狀態(tài)值不暴露給GPIO接口,如編址、電源管理等等。編碼解碼器之類的芯片會(huì)有復(fù)雜的非GPIO狀態(tài)。
所有的debugfs dump 方式通常應(yīng)該忽略那些未作為GPIO請(qǐng)求的信號(hào)。他們可以使用gpiochip_is_requested(),此函數(shù)返回與GPIO相關(guān)的label或是NULL。
平臺(tái)支持
為了支持這個(gè)結(jié)構(gòu),一個(gè)平臺(tái)的Kconfig需要選擇ARCH_REQUIRE_GPIOLIB或是ARCH_WANT_OPTIONAL_GPIOLIB之一,且安排的它的《asm/gpio.h》包含《asm-generic/gpio.h》并且定義3個(gè)函數(shù)gpio_get_value(), gpio_set_value(), 和 gpio_cansleep()。
他也可以提供一個(gè)自定義的值:ARCH_NR_GPIOS,以便能更好的反映平臺(tái)實(shí)際使用的GPIO數(shù)目,并不浪費(fèi)靜態(tài)區(qū)域空間。(它應(yīng)該計(jì)數(shù) 內(nèi)建/SOC GPIO和GPIO擴(kuò)展器擴(kuò)展的數(shù)目)
ARCH_REQUIRE_GPIOLIB意味著此平臺(tái)上gpiolib代碼將永久編譯進(jìn)內(nèi)核
ARCH_WANT_OPTIONAL_GPIOLIB意味著gpiolib代碼默認(rèn)是關(guān)閉的,用于可以使能它并且將它可選的編譯進(jìn)內(nèi)核。
如果這些選項(xiàng)都未被選上,平臺(tái)不能通過GPIO-lib支持GPIO,這些代碼也不能被用戶使能。
那些函數(shù)瑣細(xì)的實(shí)現(xiàn)可以直接使用架構(gòu)代碼,它們經(jīng)常通過gpio_chip分配:
#define gpio_get_value__gpio_get_value
#define gpio_set_value__gpio_set_value
#define gpio_cansleep__gpio_cansleep
愛好者實(shí)現(xiàn)可以代替定義他們使用內(nèi)聯(lián)函數(shù),使用邏輯優(yōu)化存取特定的基于SOC的GPIO。例如,如果 引用的GPIO是常數(shù)“12”,getting或setting它的值可能只需要2個(gè)或3個(gè)指令,且從不休眠。如果這樣一個(gè)優(yōu)化是不可能的話,這些調(diào)用實(shí)現(xiàn)必須委托給架構(gòu)代碼,它會(huì)耗費(fèi)至少幾十個(gè)指令。為了位拆型I/O,這些指令的節(jié)約是有相當(dāng)大的意義的。
對(duì)于SOC來說,平臺(tái)特定的代碼為每個(gè)bank的片上GPIO定義和注冊(cè)了gpio_chip實(shí)例。那些GPIO應(yīng)該被編號(hào)和打上標(biāo)簽以匹配芯片廠商文檔,且直接匹配單板設(shè)計(jì)圖。他們可以從零開始一直到平臺(tái)特定的限制。這些GPIO通常集成到單板初始化過程中以使得它們總是有效的,從arch_initcall()到更早,它們總是可以為中斷服務(wù)。
板級(jí)支持
對(duì)于外部GPIO控制器(如I2C或SPI擴(kuò)展)、ASIC、多功能器件、FPGA或是CPLD,通常單板私有代碼例程注冊(cè)控制器器件且確定它們的驅(qū)動(dòng)使用什么GPIO號(hào)來調(diào)用gpiochip_add。它們的號(hào)碼經(jīng)常在平臺(tái)特定GPIO之后開始。
例如,單板setup代碼可以創(chuàng)建結(jié)構(gòu)標(biāo)示芯片想要暴露的GPIO的范圍,且使用platform_data傳遞它們到每個(gè)GPIO擴(kuò)展器芯片。這樣芯片驅(qū)動(dòng)的probe()歷程可以傳遞這些數(shù)據(jù)到gpiochip_add()。
初始化順序是很重要的。例如當(dāng)一個(gè)依賴于基于I2C的GPIO的設(shè)備,它的probe()例程應(yīng)該僅能在GPIO有效后調(diào)用。這意味著設(shè)備不能在GPIO可以工作之前注冊(cè)。一個(gè)解決這樣依賴的方法是在板級(jí)特定代碼中,對(duì)于這種gpio_chip控制器來提供setup()和teardown()回調(diào),這些板級(jí)特定的回調(diào)將注冊(cè)設(shè)備一旦所有的需要資源有效時(shí),并且在GPIO控制器無效時(shí)將它們移除。
用戶空間的Sysfs接口(可選)
使用gpiolib實(shí)現(xiàn)結(jié)構(gòu)的平臺(tái)可以選擇為GPIO配置一個(gè)sysfs用戶接口。這與debugfs接口不同,因?yàn)樗峁┝烁采wGPIO方向和值的控制而不只是顯示一個(gè)gpio狀態(tài)信息摘要。另外,它可以在產(chǎn)品系統(tǒng)中提供而不需要調(diào)試支持。
為系統(tǒng)給出對(duì)應(yīng)的硬件文檔,用戶空間可以知道例如GPIO#23控制著保護(hù)線,用于保護(hù)flash中的boot區(qū)域。系統(tǒng)升級(jí)程序可能需要臨時(shí)移除這個(gè)保護(hù),首先引入一個(gè)GPIO,然后改變它的輸出狀態(tài),接下來在重新使能寫保護(hù)之前升級(jí)代碼。通常用法中,GPIO#23將不會(huì)被觸碰,并且內(nèi)核也不需知道它的信息。
同樣依靠一個(gè)合適的硬件文檔,在一些系統(tǒng)用戶空間,GPIO可以被用于決定那些內(nèi)核并不關(guān)心的系統(tǒng)配置數(shù)據(jù)。對(duì)于一些任務(wù),簡單用戶空間GPIO驅(qū)動(dòng)是系統(tǒng)真正需要的
注意,針對(duì)通用“LED和按鈕”的標(biāo)準(zhǔn)內(nèi)核驅(qū)動(dòng)存在對(duì)應(yīng)的GPIO任務(wù)“l(fā)eds-gpio”和“gpio-keys”。使用它們代替直接與GPIO通話,它們集成在內(nèi)核架構(gòu)比你的用戶態(tài)代碼可能更好。
Paths in Sysfs
Sysfs路徑
There are three kinds of entry in /sys/class/gpio:
/sys/class/gpio有3個(gè)入口條目:
-控制接口用于用戶空間獲取GPIO控制
-GPIO自己
-GPIO控制器(“gpio_chip”實(shí)例)
這是對(duì)于標(biāo)準(zhǔn)文件的補(bǔ)充,包括“device”符號(hào)
控制接口是只寫的:
/sys/class/gpio/
“export” ————通過寫GPIO的號(hào)碼到此文件,用戶空間可以要求內(nèi)核導(dǎo)出一個(gè)GPIO的控制到用戶空間
例如:“echo 19 》 export”將創(chuàng)建一個(gè)GPIO #19的“gpio19”節(jié)點(diǎn)(假設(shè)內(nèi)核代碼未申請(qǐng)此GPIO號(hào))。
“unexport”————與“export”效果相反
例如:“echo 19 》 unexport”將移除一個(gè)由“export”文件導(dǎo)出的“gpio19”節(jié)點(diǎn)。
GPIO信號(hào)擁有如/sys/class/gpio/gpio42/(對(duì)應(yīng)于GPIO#42)的路徑,并且具有下列讀寫屬性:
/sys/class/gpio/gpioN/
“direction”————讀為“in”或是“out”。這個(gè)值通常可寫。寫“out”默認(rèn)初始化此值為低。為了確定無障礙操作,值“l(fā)ow”和“high”可以被寫入以配置GPIO的輸出初始化值。
注意這個(gè)屬性“將不存在”如果內(nèi)核不支持改變一個(gè)GPIO的方向,或者它不能被內(nèi)核代碼導(dǎo)出(不能顯式的允許用戶空間來重新配置GPIO的選項(xiàng)。)
“value”—————讀作“0”(低)或“1”(高)。如果GPIO被配置為一個(gè)輸出,這個(gè)值可寫;任何非零值均被視為高。
如果管腳可以被配置為中斷產(chǎn)生中斷管腳,且如果它已經(jīng)被配置為產(chǎn)生中斷(參考“edge”描述),你可以poll(2)此文件并且當(dāng)中斷觸發(fā)時(shí)poll(2)將返回。如果你使用了poll(2),設(shè)置POLLPRI和POLLERR事件。如果你使用select(2),在exceptfds中設(shè)置文件描述符。在poll(2)返回之后,有兩個(gè)選擇一是lseek(2)到sysfs文件的開始且讀新的值,另一個(gè)是關(guān)閉文件且重打開它來讀取新的值。(為何這樣設(shè)置?)
“edge”————讀作“none”、“rising”、“falling”或是“both”。寫這些字符串以選擇邊沿信號(hào),他將使得“value”文件上的poll(2)操作返回。
這個(gè)文件只在管腳可以配置為中斷產(chǎn)生輸入管腳時(shí)存在。
“active_low”————讀為0(false)或1(true)。寫任何非零值都會(huì)反轉(zhuǎn)讀或?qū)懙闹怠D壳昂秃髞淼膒oll(2)支持經(jīng)由edge屬性配置為“rising”或“falling”上升沿或下降沿將遵循這個(gè)設(shè)置。
GPIO控制器具有如/sys/class/gpio/gpiochip42/(針對(duì)控制器,實(shí)現(xiàn)GPIO開始于#42)的路徑,且具有下列制度屬性:
/sys/class/gpio/gpiochipN/
“base”————與N相等,是第一個(gè)被此芯片管理的GPIO
“l(fā)abel”————提供用于診斷(并不總是獨(dú)一無二的)
“ngpio”————管理的GPIO數(shù)(N到N+ngpio-1)
大多數(shù)情況下,單板文檔應(yīng)該提供GPIO的使用目的。雖然如此這些號(hào)碼并非總是固定的,一個(gè)子板上的GPIO可能與基礎(chǔ)板使用的不同。此種情況下,你可能需要使用gpiochip節(jié)點(diǎn)(可能與設(shè)計(jì)結(jié)合)來為每個(gè)信號(hào)決定正確的GPIO號(hào)碼。
從內(nèi)核代碼中導(dǎo)出
內(nèi)核代碼可以顯式管理那些使用gpio_request()申請(qǐng)的GPIO的導(dǎo)出
/* export the GPIO to userspace */
int gpio_export(unsigned gpio, bool direction_may_change);
/* reverse gpio_export() */
void gpio_unexport();
/* create a sysfs link to an exported GPIO node */
int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
/* change the polarity of a GPIO node in sysfs */
int gpio_sysfs_set_active_low(unsigned gpio, int value);
一個(gè)內(nèi)核驅(qū)動(dòng)申請(qǐng)一個(gè)GPIO后,它可以使用gpio_export()使得sysfs接口有效。驅(qū)動(dòng)可以控制信號(hào)方向是否可以改變。這使得驅(qū)動(dòng)可以防止用戶空間代碼不小心沖擊重要的系統(tǒng)狀態(tài)。
明確的exporting有助于調(diào)試(使得一些實(shí)驗(yàn)更簡單),或是提供一個(gè)總是可以使用的接口,適合于bsp文檔。
GPIO被導(dǎo)出后,gpio_export_link()允許在sysfs的任何地方創(chuàng)建GPIO sysfs節(jié)點(diǎn)的符號(hào)鏈接。驅(qū)動(dòng)可以用此在它們自己設(shè)備sysfs目錄下提供指定名字的接口(鏈接到GPIO節(jié)點(diǎn))
驅(qū)動(dòng)可以使用gpio_sysfs_set_active_low()隱藏GPIO在用戶空間和單板之間的線極性不同。這僅影響sysfs接口。極性變換可以在gpio_export()之前和之后完成,并且前面使能的poll(2) (支持上升沿或下降沿事件)將被重新配置為遵循此設(shè)置。
四、GPIO使用方法
要使用GPIO,系統(tǒng)首先要分配一個(gè)GPIO,使用gpio_request() 為系統(tǒng)分配一個(gè)GPIO。
接下來要做的一件事是標(biāo)示GPIO的方向,通常在使用GPIO建立一個(gè)platform_device時(shí)(位于單板的setup代碼中):
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
返回0標(biāo)示成功,或是一個(gè)負(fù)的errno錯(cuò)誤碼。它應(yīng)該被檢查,因?yàn)間et/set調(diào)用沒有錯(cuò)誤返回,且可能會(huì)有錯(cuò)誤配置。你通常應(yīng)該在線程上下文中使用這些調(diào)用。雖然如此,對(duì)于spinlock-safe的GPIO,在tasking使能之前使用也是可以的,作為一個(gè)早期的單板建立。
對(duì)于輸出GPIO,value參數(shù)提供了初始輸出值。這有助于避免系統(tǒng)啟動(dòng)過程中的信號(hào)干擾。
為了與GPIO早期的接口兼容,設(shè)置一個(gè)GPIO的方向,隱性要求申請(qǐng)GPIO。這個(gè)兼容性從可選的gpiolib架構(gòu)中移除了。
如果GPIO號(hào)碼無效或是指定的GPIO不能使用對(duì)應(yīng)模式操作的話,設(shè)置方向會(huì)失敗。依靠boot固件設(shè)置好GPIO的方向通常不是一個(gè)好主意,因?yàn)閎oot的功能可能沒有通過驗(yàn)證(除了boot linux)。(類似的,單板setup代碼可能需要將管腳復(fù)用為一個(gè)GPIO,和配置為合適的上拉/下拉。)
Spinlock-Safe GPIO訪問
-------------------------
大多數(shù)GPIO控制器可以使用內(nèi)存讀寫指令訪問。它們不需要休眠,且可以從內(nèi)部硬件中斷處理(非線程)和類似的上下文環(huán)境安全完成。
使用下列調(diào)用訪問這些GPIO,此時(shí)gpio_cansleep將總是返回錯(cuò)誤
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);
其中,value是一個(gè)布爾型參數(shù),零表示低,非零表示高。當(dāng)讀一個(gè)輸出管腳的值時(shí),返回的值應(yīng)該是在管腳上看到的值。。。這并不總是與指定輸出值相匹配的,因?yàn)榇嬖陂_漏信號(hào)和輸出延遲問題。
get/set調(diào)用沒有錯(cuò)誤返回,因?yàn)椤盁o效GPIO”應(yīng)該已經(jīng)由gpio_direction_*()提早報(bào)告了。雖然如此,并非所有的平臺(tái)都可以讀取輸出管腳的值,那些不能讀的應(yīng)該總是返回零。同時(shí),對(duì)那些可能導(dǎo)致睡眠的GPIO使用這些接口是一個(gè)錯(cuò)誤。
平臺(tái)的特定實(shí)現(xiàn)被鼓勵(lì)優(yōu)化這兩個(gè)調(diào)用以獲取GPIO值。在那些GPIO號(hào)碼是常量的情況下,它們通常只需一對(duì)指令(讀或?qū)懸粋€(gè)硬件寄存器)訪問,且不需要spinlock。這樣的優(yōu)化可以使位拆分應(yīng)用更有效率(在時(shí)間和空間上)(相比較于花費(fèi)一堆指令在子例程調(diào)用來說)。
評(píng)論