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