本文導(dǎo)讀
一個(gè)硬件設(shè)備正常工作的前提是系統(tǒng)中存在對應(yīng)的驅(qū)動。AWorks提供了大量常用硬件設(shè)備的驅(qū)動,用戶通常不需要開發(fā)驅(qū)動。為了使讀者對設(shè)備驅(qū)動有一定的理解,本文介紹了設(shè)備驅(qū)動相關(guān)的基礎(chǔ)概念,展示了設(shè)備驅(qū)動在AWbus-lite中驅(qū)動設(shè)備正常工作的原理。
本文為《面向AWorks框架和接口的編程(上)》第三部分軟件篇——第13章——第3小節(jié):設(shè)備驅(qū)動。13.3 設(shè)備驅(qū)動
上面討論了如何通過Method機(jī)制獲得具體設(shè)備提供的LED服務(wù),系統(tǒng)能夠從某一設(shè)備獲得LED服務(wù)的前提是,該硬件具有提供LED服務(wù)的能力。一個(gè)硬件設(shè)備相關(guān)的功能,需要通過設(shè)備驅(qū)動才能體現(xiàn)出來,進(jìn)而為系統(tǒng)服務(wù)。為此,在LED設(shè)備驅(qū)動中,需要實(shí)現(xiàn)一個(gè)LED服務(wù),以供上層獲取。
13.3.1 基礎(chǔ)驅(qū)動信息
AWBus-lite對設(shè)備驅(qū)動進(jìn)行了高度的抽象,定義了驅(qū)動的基本結(jié)構(gòu),一個(gè)設(shè)備驅(qū)動相關(guān)的信息統(tǒng)一使用一個(gè)結(jié)構(gòu)體常量進(jìn)行描述。其中包含了諸多信息,比如:驅(qū)動名、驅(qū)動初始化入口、驅(qū)動提供的Method對象等。開發(fā)驅(qū)動的核心即完成一個(gè)結(jié)構(gòu)體常量的定義。
不同總線下的設(shè)備驅(qū)動需要提供的驅(qū)動信息可能不同,對應(yīng)的驅(qū)動信息類型也就不同。但無論什么總線下的設(shè)備驅(qū)動,其驅(qū)動信息類型均是從基礎(chǔ)驅(qū)動信息類型派生而來的,也就是說,無論什么設(shè)備驅(qū)動,都需要提供AWBus-lite定義的基礎(chǔ)驅(qū)動信息,即使需要擴(kuò)展其它驅(qū)動信息,也只能在基礎(chǔ)驅(qū)動信息的基礎(chǔ)上進(jìn)行擴(kuò)展。
由于所有設(shè)備驅(qū)動均會提供基礎(chǔ)驅(qū)動信息,因此,AWBus-lite可以方便的對所有驅(qū)動進(jìn)行統(tǒng)一的管理?;A(chǔ)驅(qū)動信息的類型為struct awbl_drvinfo,具體定義詳見程序清單13.19。
程序清單13.19 struct awbl_drvinfo類型定義(awbl_lite.h)
要實(shí)現(xiàn)一個(gè)驅(qū)動,需要定義一個(gè)該類型結(jié)構(gòu)體,并完成各個(gè)成員的賦值。下面,首先對各個(gè)成員的含義作簡要介紹。
1. AWBus-lite版本號
awb_ver表示該驅(qū)動支持的AWBus-lite版本號,當(dāng)前AWBus-lite的版本號為:AWBL_VER_1,其在awbus_lite.h文件中定義如下:
在新開發(fā)驅(qū)動時(shí),將awb_ver設(shè)置為AWBL_VER_1即可。
2. 總線ID
bus_id表示總線ID,該值由總線類型和設(shè)備類型兩部分組成。總線類型與設(shè)備描述中的總線類型概念一致,其表示了該驅(qū)動所驅(qū)動的設(shè)備掛在何種總線上,常見總線類型詳見表12.1。實(shí)際中,在進(jìn)行驅(qū)動和設(shè)備的匹配操作時(shí),只有當(dāng)驅(qū)動信息中的總線類型與設(shè)備描述中的總線類型一致時(shí),才會繼續(xù)判定設(shè)備名和驅(qū)動名是否一致,只有當(dāng)兩者完全相同時(shí),驅(qū)動和設(shè)備才會判定為匹配。設(shè)備類型表明該驅(qū)動對應(yīng)的設(shè)備是普通設(shè)備還是特殊設(shè)備(總線控制器),若是總線控制器,則其驅(qū)動的設(shè)備又會擴(kuò)展出另外一條總線。設(shè)備類型可能的取值詳見表13.4。
表13.4 設(shè)備類型宏定義
bus_id的值為總線類型和設(shè)備類型的或值(C語言的“|”運(yùn)算符)。特別地,若一個(gè)設(shè)備是普通設(shè)備,則設(shè)備類型可以省略,即
AWBL_DEVID_DEVICE宏可以被省略。
例如,對于使用GPIO直接控制的LED設(shè)備驅(qū)動,GPIO是一種片內(nèi)外設(shè),當(dāng)前并沒有將GPIO視為一種總線,在AWBus-lite中,由GPIO直接驅(qū)動的設(shè)備也視為掛在PLB上的一種設(shè)備。因而總線類型為:AWBL_BUSID_PLB,同時(shí),LED設(shè)備只是一個(gè)普通設(shè)備,并非總線控制器,因而設(shè)備類型為AWBL_DEVID_DEVICE。bus_id的值即為:
AWBL_BUSID_PLB | AWBL_DEVID_DEVICE
(或省略AWBL_DEVID_DEVICE,直接設(shè)定為
AWBL_BUSID_PLB)。
對于i.MX28x的I2C驅(qū)動,其驅(qū)動的設(shè)備是片內(nèi)外設(shè),掛在PLB總線上,因而總線類型為:AWBL_BUSID_PLB。同時(shí),i.MX28x 中的I2C設(shè)備又是一種總線控制器,可以擴(kuò)展出一條I2C總線,因而設(shè)備類型為:
AWBL_DEVID_BUSCTRL。bus_id的值即為:
AWBL_BUSID_PLB | AWBL_DEVID_BUSCTRL。
對于PCF85063設(shè)備驅(qū)動,其驅(qū)動的設(shè)備掛在I2C總線上,因而總線類型為:AWBL_BUSID_I2C。同時(shí),PCF85063是一個(gè)普通設(shè)備,并非總線控制器,因而設(shè)備類型為AWBL_DEVID_DEVICE。bus_id的值即為:
AWBL_BUSID_I2C | AWBL_DEVID_DEVICE
(或省略AWBL_DEVID_DEVICE,直接設(shè)定為
AWBL_BUSID_I2C)。
3. 驅(qū)動名
p_drvname表示該驅(qū)動的名字。在設(shè)備描述中,使用了“設(shè)備名”用來描述設(shè)備的名字,在系統(tǒng)啟動時(shí),會為每個(gè)設(shè)備尋找合適的驅(qū)動,當(dāng)驅(qū)動的總線類型和設(shè)備描述中的總線類型一致時(shí),將會把“驅(qū)動名”與“設(shè)備名”進(jìn)行比對,完全一致時(shí),將視為驅(qū)動與設(shè)備匹配,進(jìn)而將該驅(qū)動和對應(yīng)的設(shè)備進(jìn)行綁定。在AWBus-lite中,當(dāng)一個(gè)驅(qū)動和設(shè)備匹配后,驅(qū)動名和設(shè)備名勢必是完全一致的,因而在匹配后可以直接將驅(qū)動名作為設(shè)備名。
對于使用GPIO控制的LED設(shè)備驅(qū)動,其驅(qū)動名可以定義為:
4. 驅(qū)動入口點(diǎn)
設(shè)備在使用前,需要完成必要的初始化操作,例如,對于使用GPIO控制的LED設(shè)備,需要在初始時(shí)將引腳配置為輸出模式。作為設(shè)備驅(qū)動,必須提供相關(guān)的初始化函數(shù)以完成設(shè)備的初始化。驅(qū)動入口點(diǎn)p_busfuncs即用于提供初始化函數(shù)的入口,其為指向struct awbl_drvfuncs類型結(jié)構(gòu)體常量的指針,struct awbl_drvfuncs類型定義詳見程序清單13.20。
程序清單13.20 struct awbl_drvfuncs類型定義(awbus_lite.h)
其中,包含了3個(gè)函數(shù)指針,分別對應(yīng)了AWBus-lite中三個(gè)階段的初始化動作。由此可見,為了完成一個(gè)設(shè)備的初始化,驅(qū)動需要提供三個(gè)初始化函數(shù),分別用于完成設(shè)備不同階段的初始化,各階段對應(yīng)的初始化函數(shù)將在系統(tǒng)啟動過程中被依次調(diào)用。各階段初始化函數(shù)的類型是完全一樣的,均只有一個(gè)指向設(shè)備實(shí)例的指針作為形參,且均無返回值。即:
例如,對于使用GPIO控制的LED設(shè)備驅(qū)動,為了完成設(shè)備的初始化,需要提供3個(gè)初始化函數(shù),結(jié)構(gòu)性范例程序詳見程序清單13.21。
程序清單13.21 驅(qū)動初始化函數(shù)結(jié)構(gòu)性范例程序
其中,__g_awbl_drvfuncs_led_gpio的地址即可作為驅(qū)動入口點(diǎn)p_busfuncs的值。各初始化函數(shù)的實(shí)現(xiàn)將在后文進(jìn)行詳細(xì)介紹。
在系統(tǒng)啟動時(shí),將根據(jù)驅(qū)動信息中提供的驅(qū)動入口點(diǎn)信息,依次調(diào)用各階段對應(yīng)的初始化函數(shù),進(jìn)而完成一個(gè)設(shè)備的初始化。在調(diào)用各初始化函數(shù)時(shí),傳入形參p_dev的值為設(shè)備描述中p_dev的值,其本質(zhì)上指向了靜態(tài)定義的設(shè)備實(shí)例。
在AWBus-lite中,將設(shè)備的初始化分為了三個(gè)階段:第一階段、第二階段、第三階段。這樣的劃分有著極其重要的意義,各階段對應(yīng)的初始化函數(shù)被系統(tǒng)調(diào)用的時(shí)機(jī)并不相同。
-
第一階段
第一階段為設(shè)備初始化過程中最先進(jìn)入的階段,在該階段中,系統(tǒng)總中斷被關(guān)閉,OS內(nèi)核服務(wù)(如多任務(wù)管理)尚未提供,調(diào)試串口也尚未準(zhǔn)備就緒。作為設(shè)備驅(qū)動,只能處理一些設(shè)備相關(guān)的最基本、最簡單的操作,不可在本階段中使用常見的其它服務(wù),比如:執(zhí)行連接中斷、申請信號量、打印調(diào)試信息等操作。對于絕大部分普通設(shè)備驅(qū)動,該階段對應(yīng)的函數(shù)設(shè)置為空,不執(zhí)行任何操作。
-
第二階段
第二階段為設(shè)備初始化的主要階段,設(shè)備相關(guān)的絕大部分初始化操作均在該階段中完成,在第二階段中,系統(tǒng)相關(guān)的服務(wù)均已準(zhǔn)備就緒,比如:中斷、調(diào)試串口、信號量、多任務(wù)等??梢栽诘诙A段中使用這些服務(wù)。
-
第三階段
第三階段作為第二階段后的一個(gè)階段,系統(tǒng)相關(guān)的服務(wù)同樣已準(zhǔn)備就緒,比如:中斷、調(diào)試串口、信號量、多任務(wù)等。第三階段主要用于完成比較耗時(shí)的初始化操作,第三階段相關(guān)的操作將在一個(gè)單獨(dú)的任務(wù)中執(zhí)行,不會影響系統(tǒng)的整體啟動過程。
例如,在某一設(shè)備的初始化過程中,有一個(gè)特殊的操作需要一分鐘才能完成,為了不影響系統(tǒng)的啟動效率,可以將該耗時(shí)較長的操作放在第三階段中完成,如此一來,系統(tǒng)同樣可以快速啟動,進(jìn)而運(yùn)行至應(yīng)用程序入口,即aw_main()。否則,若將該操作放在第二階段中,則系統(tǒng)必須在第二階段初始化完成后才能啟動完成,接著才能運(yùn)行至應(yīng)用程序入口處,導(dǎo)致系統(tǒng)的整個(gè)啟動過程變慢。
系統(tǒng)啟動速度的快慢將直接影響用戶體驗(yàn),以數(shù)字示波器為例,傳統(tǒng)示波器的開機(jī)時(shí)間幾乎都在30秒甚至1分鐘以上,對于現(xiàn)場測試工程師來說,有可能會遺漏稍縱即逝的異常信號,幾乎所有的示波器廠商都對這個(gè)需求熟視無睹,而廣州致遠(yuǎn)電子有限公司的設(shè)計(jì)理念卻與眾不同,使用AWorks助力ZDS系列示波器,使用戶從按下電源到開始使用,整個(gè)過程僅需要十余秒,其開機(jī)時(shí)間擊敗了所有其他品牌的示波器。讓用戶從按下電源的那一刻起,就能感受到極致的體驗(yàn)。
在AWBus-lite中,巧妙的將一個(gè)設(shè)備的初始化過程分為了三個(gè)階段,可以使系統(tǒng)的啟動速度從結(jié)構(gòu)上得到優(yōu)化,在大多數(shù)中小系統(tǒng)中,系統(tǒng)啟動時(shí)間都小于1秒。
特別地,在一些設(shè)備的初始化過程中,可能并沒有耗時(shí)較長的操作,此時(shí),可以將第三階段對應(yīng)的函數(shù)設(shè)置為空,不執(zhí)行任何操作。
5. Method對象列表
每個(gè)設(shè)備都是為系統(tǒng)提供某種服務(wù)而存在的,其提供了相應(yīng)的服務(wù),才能被系統(tǒng)、用戶所使用。如LED設(shè)備可以為系統(tǒng)提供LED服務(wù),為了使系統(tǒng)能夠獲取到設(shè)備提供的LED服務(wù),需要提供相應(yīng)的Method對象,以指定獲取LED服務(wù)對應(yīng)的入口函數(shù)。定義一個(gè)Method對象的范例詳見程序清單13.22。
程序清單13.22 定義Method對象范例程序
一些特殊設(shè)備,可能可以為系統(tǒng)提供多種服務(wù),這時(shí),對應(yīng)驅(qū)動中將需要定義多個(gè)Method對象,每個(gè)Method對象用于獲取某一種服務(wù)。
為此,AWBus-lite中,將一個(gè)驅(qū)動定義的所有Method對象存放在一個(gè)列表中,并以AWBL_METHOD_END表示列表的結(jié)束。驅(qū)動信息p_methods即用于指向Method對象列表,范例詳見程序清單13.23。
程序清單13.23 定義Method對象列表范例程序
其中,__g_led_gpio_dev_methods即可作為驅(qū)動信息中p_methods的值。Method對象的具體定義將在后文詳細(xì)介紹。
6. 驅(qū)動探測函數(shù)
在基礎(chǔ)驅(qū)動信息中,pfunc_drv_probe是一個(gè)函數(shù)指針,用于指向一個(gè)探測函數(shù),用于探測驅(qū)動是否支持該設(shè)備。其類型為:
由此可見,其指向的函數(shù)是具有一個(gè)p_dev形參,返回值為布爾類型的函數(shù)。
p_dev是指向設(shè)備實(shí)例的指針,用于指定探測的設(shè)備。返回值為布爾類型,返回AW_TRUE時(shí),探測成功;否則,探測失敗。
前面提到過系統(tǒng)是如何判定設(shè)備與驅(qū)動是否匹配的。設(shè)備與驅(qū)動匹配的首要條件是設(shè)備描述中的總線類型與驅(qū)動信息中的總線類型一致。在總線類型一致的情況下,可以增加兩種額外的判定條件。
一種條件是針對該總線類型的,該總線類型下的設(shè)備和驅(qū)動,都必須滿足該條件。不同總線類型對該條件的定義可能不同,但對于絕大部分總線來說,其判定條件都是:驅(qū)動名和設(shè)備名是否相同。這也是前面提到系統(tǒng)中使用驅(qū)動名和設(shè)備名進(jìn)行匹配判定的原因。需要用戶注意的是,這種條件是與具體總線類型相關(guān)的,雖然絕大部分總線類型的判定條件都是驅(qū)動名和設(shè)備名是否相同,但也不排除可能出現(xiàn)某一類型的總線,其不要求設(shè)備名和驅(qū)動名一致,這種情況極為罕見,如果出現(xiàn),應(yīng)該對該類總線作出非常重要的特殊說明。
一種條件是針對某一特定驅(qū)動的,這種條件通過驅(qū)動提供探測函數(shù)來實(shí)現(xiàn)。若驅(qū)動不需要進(jìn)行額外的判斷,則將pfunc_drv_probe的值設(shè)置為NULL。若需要進(jìn)行額外的判斷,則應(yīng)提供一個(gè)有效的探測函數(shù),在探測函數(shù)的實(shí)現(xiàn)中,若判定驅(qū)動和設(shè)備匹配,能夠支持相應(yīng)的設(shè)備,則應(yīng)返回AW_TRUE,以告知AWBus-lite系統(tǒng),驅(qū)動和設(shè)備是匹配的,進(jìn)而將設(shè)備和驅(qū)動進(jìn)行綁定;若判定驅(qū)動和設(shè)備不匹配,驅(qū)動不支持該設(shè)備,則應(yīng)該返回AW_FALSE,以告知AWBus-lite系統(tǒng),驅(qū)動和設(shè)備不匹配。
通常情況下,對于絕大部分驅(qū)動而言,并不需要進(jìn)行的探測,此時(shí),需將pfunc_drv_probe的值設(shè)置為NULL?;诖耍ǔG闆r下,只要驅(qū)動與設(shè)備的總線類型和名字一致,均可視為驅(qū)動和設(shè)備匹配。
以上僅僅對基礎(chǔ)驅(qū)動信息中各成員的含義進(jìn)行了簡要的介紹,其中的初始化函數(shù),Method對象列表等均還未實(shí)際實(shí)現(xiàn),后文將以開發(fā)LED設(shè)備驅(qū)動為例,進(jìn)一步介紹設(shè)備類型的定義、設(shè)備信息類型的定義、初始化函數(shù)的實(shí)現(xiàn),Method對象的具體實(shí)現(xiàn)等。
13.3.2 實(shí)際驅(qū)動信息
在開發(fā)具體的設(shè)備驅(qū)動時(shí),需要明確該驅(qū)動所對應(yīng)的設(shè)備掛在何種總線上,不同總線下的設(shè)備對應(yīng)驅(qū)動可能需要提供不同的驅(qū)動信息,此時(shí),它們對應(yīng)的驅(qū)動信息類型也是不同的。無論何種總線下的設(shè)備驅(qū)動,它們的驅(qū)動信息類型都是從基礎(chǔ)驅(qū)動信息派生而來的,以便在基礎(chǔ)驅(qū)動信息的基礎(chǔ)上,擴(kuò)展一些特殊的總線相關(guān)的成員。但在實(shí)際中,常見的大多數(shù)總線下的設(shè)備驅(qū)動信息并沒有擴(kuò)展更多的成員,例如,PLB總線下的設(shè)備驅(qū)動,其對應(yīng)的驅(qū)動信息類型為awbl_plb_drvinfo_t,其定義詳見程序清單13.24。
程序清單13.24 awbl_plb_drvinfo_t類型定義(awbl_plb.h)
對于I2C總線下的設(shè)備驅(qū)動,其對應(yīng)的驅(qū)動信息類型為awbl_i2c_drvinfo_t,其定義詳見程序清單13.25。
程序清單13.25 awbl_i2c_drvinfo_t類型定義(awbl_i2cbus.h)
由此可見,這些總線下的設(shè)備驅(qū)動,并沒有擴(kuò)展額外的成員,均是對基礎(chǔ)驅(qū)動信息的簡單繼承。即使如此,出于結(jié)構(gòu)性考慮,為了便于后續(xù)擴(kuò)展,每種總線都單獨(dú)定義了相應(yīng)的驅(qū)動信息類型。用戶僅需了解到,開發(fā)不同總線下的設(shè)備驅(qū)動時(shí),它們對應(yīng)的驅(qū)動信息類型可能是不同的,需要查看總線對應(yīng)的實(shí)際驅(qū)動信息類型,以判斷在開發(fā)驅(qū)動時(shí),除了提供基礎(chǔ)驅(qū)動信息外,是否還需要提供其它額外的信息。
13.3.3 定義設(shè)備類型
通過前面對硬件設(shè)備列表的介紹可知,在硬件設(shè)備的描述中,需要使用驅(qū)動定義的具體設(shè)備類型定義一個(gè)設(shè)備實(shí)例,用于為設(shè)備分配必要的內(nèi)存空間,設(shè)備相關(guān)的狀態(tài)、變量、屬性等相關(guān)數(shù)據(jù)都可以存放在該設(shè)備實(shí)例中。
在AWBus-lite中,所有具體設(shè)備類型均是從基礎(chǔ)設(shè)備類型struct awbl_dev派生而來的,在定義設(shè)備類型時(shí),基礎(chǔ)設(shè)備類型的成員應(yīng)該作為設(shè)備類型的第一個(gè)成員,基于此,可以定義LED設(shè)備類型為:
顯然,要完成LED設(shè)備類型的定義,重點(diǎn)是考慮需要定義哪些其它成員。LED設(shè)備的核心功能是為系統(tǒng)提供LED服務(wù),回顧LED服務(wù)的具體類型定義,詳見程序清單13.26。
程序清單13.26 LED服務(wù)類型定義(awbl_led.h)
LED服務(wù)類型作為一個(gè)結(jié)構(gòu)體類型,顯然,需要占用一定的內(nèi)存空間,由于每個(gè)LED設(shè)備均能提供LED服務(wù),因此,可以將LED服務(wù)作為LED設(shè)備類型的一個(gè)成員。當(dāng)需要為系統(tǒng)上層提供LED服務(wù)時(shí),只需要將LED服務(wù)中的各個(gè)成員正確賦值,提交給上層即可?;诖耍梢愿翷ED設(shè)備類型的定義,詳見程序清單13.27。
程序清單13.27 LED設(shè)備類型的定義
當(dāng)前僅僅從LED的主要功能出發(fā),完成了LED設(shè)備類型的定義,若在開發(fā)過程中,發(fā)現(xiàn)需要在設(shè)備類型中增加新的成員,可以隨時(shí)動態(tài)添加。在設(shè)備描述中,如需定義一個(gè)設(shè)備實(shí)例,直接使用該類型定義一個(gè)設(shè)備實(shí)例即可,例如:
13.3.4 定義設(shè)備信息類型
通過前面對硬件設(shè)備列表的介紹可知,在硬件設(shè)備的描述中,需要提供硬件設(shè)備信息。硬件設(shè)備信息的具體類型同樣由相應(yīng)的驅(qū)動定義。
在使用LED設(shè)備時(shí),往往需要用戶提供一些必要的信息,例如,在使用GPIO控制LED時(shí),需要知道各個(gè)LED對應(yīng)的引腳信息,即使用哪些GPIO引腳控制相應(yīng)的LED。同時(shí),對于不同的硬件電路,點(diǎn)亮LED對應(yīng)的GPIO輸出電平可能是不同的,可能是輸出低電平點(diǎn)亮LED,也可能是輸出高電平點(diǎn)亮LED,此外,不同硬件設(shè)備中,LED的數(shù)目也可能存在差異,這些都是與具體硬件相關(guān)的。
為了便于對這些信息進(jìn)行修改、配置,可以定義一個(gè)信息結(jié)構(gòu)體類型,以包含所有需要由用戶提供的信息,即:
特別的,在部分平臺中,使用GPIO前,可能需要一些特殊的平臺相關(guān)的操作,比如:使能時(shí)鐘、申請GPIO的使用權(quán)、配置GPIO的特殊模式等。由于具體平臺相關(guān)的操作當(dāng)前并不能確定,為此,可以由用戶提供一個(gè)平臺初始化函數(shù),以在必要時(shí),通過該函數(shù)完成平臺相關(guān)的初始化操作?;诖耍谠O(shè)備信息類型中新增一個(gè)函數(shù)指針成員,用以指向用戶提供的平臺初始化函數(shù),即:
新增的pfn_plfm_init是一個(gè)函數(shù)指針,指向的函數(shù)是無參數(shù)、無返回值的函數(shù),用于完成在使用LED前需要完成的平臺相關(guān)的初始化操作。在一些情況下,可能不需要執(zhí)行任何平臺相關(guān)的初始化操作,則可以將其值設(shè)置為NULL。
上面主要基于硬件層面定義了設(shè)備信息中的各個(gè)成員,此外,LED主要的功能是提供LED服務(wù),在LED服務(wù)中,需要提供的一個(gè)重要信息是LED服務(wù)信息,其主要包含了LED設(shè)備中各個(gè)LED的編號信息,LED服務(wù)信息的定義詳見程序清單13.28。
程序清單13.28 LED服務(wù)信息類型定義
由此可見,LED服務(wù)信息中包含了起始編號和結(jié)束編號,用戶通過設(shè)定起始編號和結(jié)束編號,就可以為設(shè)備中的每個(gè)LED分配一個(gè)唯一ID。為了存放用戶分配的ID信息,可以在設(shè)備信息類型中新增一個(gè)LED服務(wù)信息成員,設(shè)備信息完整的定義詳見程序清單13.29。
程序清單13.29 LED設(shè)備信息類型完整定義
在EPC-AW280開發(fā)套件中,板載了兩個(gè)LED,標(biāo)識分別為RUN、Error,對應(yīng)的引腳分別為PIO2_6、PIO2_5,等效原理圖詳見圖13.2,由此可見,當(dāng)GPIO輸出低電平時(shí),對應(yīng)的LED點(diǎn)亮,GPIO輸出高電平時(shí),對應(yīng)的LED熄滅。若為兩個(gè)LED分配的ID號分別為0、1,則在設(shè)備描述中,LED設(shè)備信息的定義范例詳見程序清單13.30。
圖13.2 板載LED電路
程序清單13.30 LED設(shè)備信息定義范例
13.3.5 實(shí)現(xiàn)三個(gè)階段的初始化函數(shù)
在一個(gè)設(shè)備使用前,需要完成設(shè)備相關(guān)的初始化操作,例如,對于LED驅(qū)動,可能需要將GPIO設(shè)置為輸出模式等。在基礎(chǔ)驅(qū)動信息中,使用了驅(qū)動入口點(diǎn)指定驅(qū)動提供的初始化函數(shù),其結(jié)構(gòu)性代碼詳見程序清單13.21,共需實(shí)現(xiàn)3個(gè)初始化函數(shù),分別對應(yīng)3個(gè)階段。
需要特別注意的是,雖然各階段初始化函數(shù)的形參類型均為awbl_dev_t *,但實(shí)際上,在系統(tǒng)調(diào)用各初始化函數(shù)時(shí),傳入形參p_dev的值為設(shè)備描述中p_dev的值,其本質(zhì)上指向了靜態(tài)定義的設(shè)備實(shí)例。對于GPIO控制型LED設(shè)備,其設(shè)備實(shí)例的實(shí)際類型為struct awbl_led_gpio_dev(具體定義詳見程序清單13.27),在使用p_dev時(shí),可以將其強(qiáng)制轉(zhuǎn)換為指向?qū)嶋H設(shè)備實(shí)例的指針,以訪問設(shè)備實(shí)例中的各個(gè)成員。范例程序詳見程序清單13.31。
程序清單13.31 將p_dev轉(zhuǎn)換為指向?qū)嶋H設(shè)備實(shí)例的指針
在初始化時(shí),往往還需要獲得用戶為設(shè)備提供的相關(guān)信息,AWBus-lite提供了通過p_dev獲取硬件設(shè)備描述的宏:AWBL_DEVHCF_GET(),其返回值即為const struct awbl_devhcf *類型的指向硬件設(shè)備描述的指針,struct awbl_devhcf類型的定義詳見程序清單12.2,其中包含了設(shè)備名、設(shè)備單元號、所處總線、設(shè)備信息等常見的信息,使用范例詳見程序清單13.32。
程序清單13.32 AWBL_DEVHCF_GET()宏的使用范例程序
程序中,只要獲得了設(shè)備描述,即可通過指針獲得設(shè)備描述中的其它成員信息,但通常情況下,可能只需要獲得設(shè)備描述中某一個(gè)成員的信息,此時(shí),為了簡化獲取步驟,AWBus-lite提供了直接獲取設(shè)備描述中某一成員的輔助宏,詳見表13.5。
表13.5 獲取設(shè)備相關(guān)信息的輔助宏(awbus_lite.h)
特別地,在設(shè)備描述中,設(shè)備實(shí)例信息的類型為const void *,而實(shí)際上,p_devinfo指向的是具體設(shè)備信息,對于GPIO控制型LED設(shè)備,其設(shè)備信息的實(shí)際類型為
struct awbl_led_gpio_param(具體定義詳見程序清單13.29),因此,在使用設(shè)備實(shí)例信息時(shí),可以將其強(qiáng)制轉(zhuǎn)換為指向?qū)嶋H設(shè)備信息的指針,以便訪問實(shí)際設(shè)備信息中的成員。例如:
1. 第一階段初始化函數(shù)實(shí)現(xiàn)
第一階段通常無需作任何操作,該階段對應(yīng)的始化函數(shù)往往為空,詳見程序清單13.33。
程序清單13.33 第一階段初始化函數(shù)實(shí)現(xiàn)范例
2. 第二階段初始化函數(shù)實(shí)現(xiàn)
第二階段為初始化設(shè)備的主要階段,可以在該階段中完成GPIO模式設(shè)置、初始電平設(shè)置等初始化相關(guān)操作,范例程序詳見程序清單13.34。
程序清單13.34 第二階段初始化函數(shù)實(shí)現(xiàn)范例
程序中,將所有LED對應(yīng)的引腳設(shè)置為了輸出模式,并將初始電平設(shè)置為了設(shè)備信息中active_low的值,以使LED初始處于熄滅狀態(tài)。例如,active_low的值為1,則表示引腳輸出低電平時(shí)點(diǎn)亮LED,而初始時(shí),將輸出電平設(shè)置為了
active_low的值,即高電平,從而熄滅了LED。為了更清楚的理解這個(gè)關(guān)系,可以列舉出active_low的值和GPIO輸出電平對LED狀態(tài)的影響,詳見表13.6。由此可見,當(dāng)active_low的值和GPIO輸出電平相同時(shí),LED熄滅,否則,LED點(diǎn)亮。因此,初始時(shí),將GPIO輸出電平設(shè)置為active_low的值,確保了LED初始處于熄滅狀態(tài)。
表13.6 LED狀態(tài)的影響因素
3. 第三階段初始化函數(shù)實(shí)現(xiàn)
第三階段通常用于耗時(shí)較長的初始化操作,由于LED設(shè)備初始化中,并沒有任何比較復(fù)雜、耗時(shí)的操作,因此,第三階段無需作任務(wù)處理,設(shè)定為空即可,詳見程序清單13.35。
程序清單13.35 第三階段初始化函數(shù)實(shí)現(xiàn)范例
實(shí)現(xiàn)了各階段初始化函數(shù)后,可以定義一個(gè)struct awbl_drvfuncs類型的常量,以將所有初始化函數(shù)整合在一起,作為基礎(chǔ)驅(qū)動信息中驅(qū)動入口點(diǎn)的值。詳見程序清單13.36。
程序清單13.36 定義struct awbl_drvfuncs類型的常量
其中,__g_awbl_drvfuncs_led_gpio即可作為驅(qū)動入口點(diǎn)p_busfuncs的值。
為了簡化程序,可以在定義
struct awbl_drvfuncs類型的常量時(shí),將空函數(shù)對應(yīng)的指針直接設(shè)定為NULL。例如,在程序清單13.36中,由于第一階段和第三階段對應(yīng)的初始化函數(shù)是空函數(shù),沒有作任何操作,因此,可以將pfunc_dev_init1和pfunc_dev_connect的值設(shè)置為NULL,更新后的struct awbl_drvfuncs類型常量定義詳見程序清單13.37。
程序清單13.37 更新struct awbl_drvfuncs類型的常量定義
13.3.6 實(shí)現(xiàn)LED服務(wù)
LED設(shè)備的主要功能是為系統(tǒng)提供LED服務(wù),在向系統(tǒng)提供LED服務(wù)前,需要實(shí)現(xiàn)一個(gè)LED服務(wù),回顧LED服務(wù)類型的定義,詳見程序清單13.38。
程序清單13.38 LED服務(wù)類型定義(awbl_led.h)
在LED設(shè)備實(shí)例中,具有一個(gè)
struct awbl_led_service 類型的led_serv成員(詳見程序清單13.27)。實(shí)現(xiàn)LED服務(wù)的核心工作就是完成led_serv中各成員的賦值。
1. p_next成員賦值
在LED服務(wù)中,p_next用于系統(tǒng)組織多個(gè)LED服務(wù),使它們以鏈表的形式串接起來,便于統(tǒng)一管理。對于單個(gè)LED設(shè)備來講,其僅能提供一個(gè)LED服務(wù),p_next的值設(shè)置應(yīng)設(shè)置為NULL。設(shè)置范例詳見程序清單13.39。
程序清單13.39 LED服務(wù)中p_next成員賦值范例
2. p_servinfo成員賦值
在LED服務(wù)中,p_servinfo用于指向LED服務(wù)信息,通過LED設(shè)備信息類型的定義可知,LED服務(wù)信息由用戶提供,因此,只需將p_servinfo指向設(shè)備信息中的LED服務(wù)信息,設(shè)置范例詳見程序清單13.40。
程序清單13.40 LED服務(wù)中p_servinfo成員賦值范例
3. p_servfuncs成員賦值
為了屏蔽底層硬件的差異性,系統(tǒng)為LED設(shè)備定義了兩個(gè)抽象方法,回顧
struct awbl_led_servfuncs類型的定義,詳見程序清單13.41。
程序清單13.41 struct awbl_led_servfuncs類型的定義(awbl_led.h)
在LED服務(wù)中,p_servfuncs即為指向各抽象方法的具體實(shí)現(xiàn)列表。為了完成p_servfuncs成員的賦值,首先需要實(shí)現(xiàn)操作LED設(shè)備的的兩個(gè)抽象方法,然后將它們整合到一個(gè)
struct awbl_led_servfuncs類型的結(jié)構(gòu)體常量中,抽象方法的實(shí)現(xiàn)詳見程序清單13.42。
程序清單13.42 LED抽象方法的實(shí)現(xiàn)
在各個(gè)抽象方法的實(shí)現(xiàn)中,都將參數(shù)p_cookie直接視為了指向設(shè)備的指針,為了便于使用,將p_cookie的類型從void *強(qiáng)制轉(zhuǎn)換為了struct awbl_led_gpio_dev *。實(shí)際中,p_cookie的值是由驅(qū)動自身決定的,在下一小節(jié)p_cookie成員的賦值中將作進(jìn)一步介紹。
各函數(shù)完成的主要功能是根據(jù)需要控制LED對應(yīng)引腳的輸出電平,主要分為兩個(gè)步驟:根據(jù)LED的ID得到引腳索引;通過引腳索引,控制相應(yīng)引腳的輸出電平。
LED對應(yīng)的引腳在設(shè)備信息的引腳數(shù)組中,數(shù)組的起始索引為0,但LED的ID是從LED服務(wù)信息中指定的起始編號開始的,為了通過LED的ID獲得其對應(yīng)引腳在數(shù)組中的索引,應(yīng)使用ID號減去起始編號,即:
在__led_gpio_set()函數(shù)的實(shí)現(xiàn)中,其需要根據(jù)參數(shù)on的值決定是否點(diǎn)亮LED,點(diǎn)亮LED的電平與設(shè)備信息中的active_low有關(guān), 為了更清楚的理解這個(gè)關(guān)系,可以列舉出參數(shù)on和active_low的值對GPIO輸出電平的影響,詳見表13.7。例如,當(dāng)active_low為0時(shí),表示GPIO輸出高電平時(shí)點(diǎn)亮LED,此時(shí),若on為1,表示需要點(diǎn)亮LED,則GPIO應(yīng)輸出高電平;若on為0,表示需要熄滅LED,則GPIO應(yīng)輸出低電平。
表13.7 GPIO輸出與active_low和on值的關(guān)系
由此可見,當(dāng)active_low和on相同時(shí),GPIO應(yīng)該輸出“0”,而當(dāng)active_low和on值不同時(shí),GPIO應(yīng)輸出“1”。即:“相同為0,相異為1”,這恰好是一種異或關(guān)系,因此,在設(shè)置GPIO輸出電平時(shí),直接將active_low和on的異或值作為GPIO的輸出電平,即:
在__led_gpio_toggle()函數(shù)的實(shí)現(xiàn)中,僅需翻轉(zhuǎn)GPIO輸出電平接口,與設(shè)備信息中active_low的值無關(guān),即:
至此,實(shí)現(xiàn)了LED服務(wù)中的兩個(gè)抽象方法,并存放在了__g_led_servfuncs常量中,該常量的地址即可直接作為LED服務(wù)中p_servfuncs的值,詳見程序清單13.43。
程序清單13.43 p_servfuncs成員的賦值
4. p_cookie成員賦值
在LED服務(wù)中,p_cookie用于系統(tǒng)在調(diào)用設(shè)備實(shí)現(xiàn)的抽象方法時(shí),“原封不動”的傳遞給各個(gè)抽象方法的p_cookie參數(shù)。這樣一來,傳入抽象方法中的p_cookie與LED服務(wù)中的p_cookie是完全相同的,換句話說,驅(qū)動為LED服務(wù)中的p_cookie設(shè)置了什么值,那么,在系統(tǒng)通過LED服務(wù)調(diào)用驅(qū)動實(shí)現(xiàn)的抽象方法時(shí),傳入p_cookie參數(shù)的值也為該值。
通常情況下,p_cookie都起到一個(gè)p_this的作用,用于指向設(shè)備自身,為此,直接將LED服務(wù)中p_cookie的設(shè)置為p_this,詳見程序清單13.44。
程序清單13.44 p_cookie成員的賦值
也正因?yàn)槿绱?,在程序清?3.42所示的LED抽象方法的實(shí)現(xiàn)中,可以直接將p_cookie強(qiáng)制轉(zhuǎn)換為指向設(shè)備自身的指針。
至此,清楚了LED服務(wù)中各成員應(yīng)該設(shè)置的具體值,可以選擇在初始化函數(shù)中完成各成員的賦值,比如將相關(guān)的賦值語句添加到第二階段初始化函數(shù)中。也可以選擇在系統(tǒng)獲取LED服務(wù)時(shí),再進(jìn)行相關(guān)成員的賦值。顯然,在系統(tǒng)獲取LED服務(wù)時(shí)再進(jìn)行相關(guān)成員的賦值,這種方式更優(yōu),因?yàn)槿粝到y(tǒng)不獲取LED服務(wù),那么就不會進(jìn)行相關(guān)成員的賦值,避免了不必要的操作,下面將對這種方法作進(jìn)一步介紹。
13.3.7 定義Method對象
通過前面對Method機(jī)制的介紹可知,為了使LED設(shè)備可以向系統(tǒng)提供LED服務(wù),需要定義Method對象,一個(gè)Method對象由Method類型標(biāo)識和一個(gè)入口函數(shù)構(gòu)成。已知獲取LED服務(wù)的Method類型為:awbl_ledserv_get。因此,定義Method對象的關(guān)鍵在于實(shí)現(xiàn)一個(gè)用于系統(tǒng)獲取LED服務(wù)的入口函數(shù),范例程序詳見程序清單13.45。
程序清單13.45 獲取LED服務(wù)的入口函數(shù)實(shí)現(xiàn)范例
基于此,可以完成一個(gè)Method對象的定義,即:
為了便于管理,一個(gè)驅(qū)動提供的所有Method對象應(yīng)該存放在一個(gè)列表中,由于LED設(shè)備僅能提供LED服務(wù),因此,Method對象列表中僅包含一個(gè)用于獲取LED服務(wù)的Method對象,詳見程序清單13.46。
程序清單13.46 LED設(shè)備驅(qū)動Method對象列表定義
其中,__g_led_gpio_dev_methods即可作為基礎(chǔ)驅(qū)動信息中p_methods的值。
13.3.8 注冊驅(qū)動
通過前面的介紹,LED設(shè)備驅(qū)動相關(guān)的函數(shù)均已實(shí)現(xiàn),驅(qū)動已經(jīng)基本開發(fā)完成,基于此,可以按照AWbus-lite的定義,使用相應(yīng)驅(qū)動信息結(jié)構(gòu)體類型完成一個(gè)驅(qū)動信息的定義,用于完整的描述LED驅(qū)動。
由GPIO直接控制的LED設(shè)備掛在PLB總線上,PLB總線上的所有設(shè)備驅(qū)動對應(yīng)的信息結(jié)構(gòu)體類型為awbl_plb_drvinfo_t,其是直接從基礎(chǔ)驅(qū)動信息類型派生而來的,其定義詳見程序清單13.47。
程序清單13.47 awbl_plb_drvinfo_t類型定義(awbl_plb.h)
由此可見,其并未擴(kuò)展任何其它新的成員,和基礎(chǔ)驅(qū)動信息是完全一樣的,可以定義用于描述LED驅(qū)動的信息常量,詳見程序清單13.48。
程序清單13.48 定義描述LED驅(qū)動的信息常量
完成描述驅(qū)動的信息常量定義后,還需要將驅(qū)動注冊到系統(tǒng)中,以便被系統(tǒng)中相應(yīng)的設(shè)備所使用,AWBus-lite提供了驅(qū)動注冊函數(shù),用于向系統(tǒng)中注冊一個(gè)驅(qū)動,其函數(shù)原型為:
其中,p_drvinfo指向待注冊的驅(qū)動信息,返回值為標(biāo)準(zhǔn)的錯誤號,若返回AW_OK,則表示驅(qū)動注冊成功;若返回-AW_ENOSPC,則表示內(nèi)存空間不足,驅(qū)動注冊失敗;若返回-AW_ENOTSUP,則表示AWbus-lite不支持該驅(qū)動的版本,往往是由于驅(qū)動信息中的awb_ver版本號設(shè)置有誤引起的。例如,注冊LED驅(qū)動的范例程序詳見程序清單13.49。
程序清單13.49 注冊LED驅(qū)動范例程序
由于__g_drvinfo_led_gpio的類型是從基礎(chǔ)驅(qū)動信息類型派生而來的,兩者類型并不完全相同,為了避免警告,可以將__g_drvinfo_led_gpio的地址轉(zhuǎn)換為struct awbl_drvinfo *類型。
顯然,是否注冊驅(qū)動應(yīng)該是由用戶決定的,只有當(dāng)需要使用某一設(shè)備時(shí),才應(yīng)將相應(yīng)的驅(qū)動注冊到系統(tǒng)中。為了使用戶可以在需要使用LED驅(qū)動時(shí)再注冊驅(qū)動,比較容易想到的方法可能是將__g_drvinfo_led_gpio作為一個(gè)對外開放的全局變量,引出到驅(qū)動文件外部,當(dāng)用戶需要使用LED驅(qū)動時(shí),再使用程序清單13.49所示的程序進(jìn)行注冊。但是,__g_drvinfo_led_gpio作為一個(gè)結(jié)構(gòu)體常量,可以看作驅(qū)動的一個(gè)數(shù)據(jù),在面向?qū)ο蟮木幊讨?,作為一種良好的編程習(xí)慣,應(yīng)該盡可能避免將數(shù)據(jù)直接引出到對象外部供其它模塊使用,對數(shù)據(jù)的操作都應(yīng)該通過相關(guān)的接口實(shí)現(xiàn)。將過多的數(shù)據(jù)作為全局變量引出到文件外部,將嚴(yán)重破壞系統(tǒng)的可維護(hù)性。同時(shí),對于用戶來說,其并不需要訪問驅(qū)動信息中的相關(guān)成員,將整個(gè)驅(qū)動信息開放給用戶也是不必要的。
由于驅(qū)動信息僅用于在注冊驅(qū)動時(shí)使用,為此,可以提供一個(gè)用于注冊LED驅(qū)動的專用函數(shù),其實(shí)現(xiàn)詳見程序清單13.50。
程序清單13.50 注冊LED驅(qū)動的專用函數(shù)
如此一來,驅(qū)動信息僅在內(nèi)部被訪問,做到了很好的“封裝”。當(dāng)用戶需要使用LED驅(qū)動時(shí),直接調(diào)用awbl_led_gpio_drv_register()函數(shù)即可。
通常情況下,并不需要由用戶手動調(diào)用該函數(shù),而是將該函數(shù)的調(diào)用放在模板工程下的
aw_prj_config.c文件中,具體位于
awbl_group_init()函數(shù)中,該函數(shù)在系統(tǒng)啟動時(shí)會被自動調(diào)用,從而使系統(tǒng)在啟動時(shí)自動調(diào)用awbl_led_gpio_drv_register()函數(shù)完成驅(qū)動的注冊,詳見程序清單13.51。
程序清單13.51 系統(tǒng)啟動時(shí)自動注冊驅(qū)動的原理
由此可見,用戶可以通過是否定義
AW_DRV_AWBL_GPIO_LED宏來確定是否注冊LED驅(qū)動,在aw_prj_params.h工程配置文件中,只要使能了LED設(shè)備,即定義了AW_DEV_GPIO_LED宏,則表示要使用GPIO驅(qū)動型LED,此時(shí),將自動完成
AW_DRV_AWBL_GPIO_LED的定義,以此確保當(dāng)使用LED時(shí),相應(yīng)驅(qū)動會在系統(tǒng)啟動過程中被自動注冊。核心的原理性程序詳見程序清單13.52。
程序清單13.52 自動定義AW_DRV_AWBL_GPIO_LED宏的原理(aw_prj_params.h)
-
設(shè)備驅(qū)動
+關(guān)注
關(guān)注
0文章
68瀏覽量
10872 -
致遠(yuǎn)電子
+關(guān)注
關(guān)注
13文章
405瀏覽量
31259 -
AWorks
+關(guān)注
關(guān)注
1文章
16瀏覽量
5680
原文標(biāo)題:AWorks軟件篇 — 深入理解 AWbus-lite(設(shè)備驅(qū)動基礎(chǔ)概念)
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論