第八章為深入理解AMetal,本文內(nèi)容為8.1 LED 通用接口。
第八章導(dǎo)讀
面向通用接口的編程使得應(yīng)用程序與具體硬件無關(guān),可以很容易地實(shí)現(xiàn)跨平臺復(fù)用。但究其本質(zhì)如何,具體是怎樣實(shí)現(xiàn)的呢?
8.1 LED 通用接口
本節(jié)將以LED 通用接口為例,詳細(xì)介紹通用接口的設(shè)計(jì)方法。
>>> 8.1.1 定義接口
合理的接口應(yīng)該是易閱讀的、職責(zé)明確的,下面將從接口的命名、參數(shù)和返回值三個(gè)方面闡述在AMetal 中定義接口的一般方法。
1. 接口命名
在AMetal 中,所有通用接口均以“am_”開頭,緊接著是操作對象的名字,對于LED控制接口來說,所有接口應(yīng)該以“am_led_”為前綴。
當(dāng)接口的前綴定義好之后,需要考慮定義哪些功能性接口,然后根據(jù)功能完善接口名。對于LED 來說,核心的操作是控制LED 的狀態(tài),點(diǎn)亮或熄滅LED,因此需要提供一個(gè)設(shè)置(set)LED 狀態(tài)的函數(shù),比如:
-
am_led_set
顯然,通過該接口可以設(shè)置LED 的狀態(tài),為了區(qū)分是點(diǎn)亮還是熄滅LED,需要通過一個(gè)參數(shù)指定具體的操作。
在大多數(shù)應(yīng)用場合中,可能需要頻繁地開燈和關(guān)燈操作,每次開關(guān)燈都需要通過傳遞參數(shù)給am_led_set()接口實(shí)現(xiàn)開燈和關(guān)燈,這樣做會非常繁瑣。因此可以為常用的開燈和關(guān)燈操作定義專用的接口,也就不再需要額外參數(shù)區(qū)分具體的操作。比如,使用on 和off 分別表示開燈和關(guān)燈,則定義開燈和關(guān)燈的接口名為:
-
am_led_on
-
am_led_off
在一些特殊的應(yīng)用場合種,比如,LED 閃爍,其可能并不關(guān)心具體的操作是開燈還是關(guān)燈,它僅僅需要LED 的狀態(tài)發(fā)生翻轉(zhuǎn)。此時(shí),可以定義一個(gè)用于翻轉(zhuǎn)(toggle)LED 狀態(tài)的接口,其接口名為:
-
am_led_toggle
2. 接口參數(shù)
在AMetal 中,通用接口的第一個(gè)參數(shù)表示要操作的具體對象。顯然,一個(gè)系統(tǒng)可能有多個(gè)LED,為了確定操作的LED,最簡單的方法是為每個(gè)LED 分配一個(gè)唯一編號,即ID號,然后通過ID 號確定需要操作的LED。ID 號是一個(gè)從0 開始的整數(shù),其類型為int,基于此,所有接口的第一個(gè)參數(shù)定義為int 類型的led_id。
對于am_led_set 接口來說,除使用led_id 確定需要控制的LED 外,還需要使用一個(gè)參數(shù)區(qū)分是點(diǎn)亮LED 還是熄滅LED。由于是二選一的操作,因此該參數(shù)的類型使用布爾類型:am_bool_t。當(dāng)值為真(AM_TRUE)時(shí),則點(diǎn)亮LED;當(dāng)值為假(AM_FALSE)時(shí),則熄滅LED?;诖?,包含參數(shù)的am_led_set 接口函數(shù)原型為(還未定義返回值):
對于am_led_on、am_led_off 和am_led_toggle 接口來說,它們的職責(zé)單一,僅僅需要指定控制的LED,即可完成點(diǎn)亮、熄滅或翻轉(zhuǎn)操作,無需額外的其它參數(shù)。因此對于這類接口,參數(shù)僅僅只需要led_id。其函數(shù)原型如下:
實(shí)際上,在AMetal 通用接口的第一個(gè)參數(shù)中,除使用ID 號表示操作的具體對象外,還可能直接使用指向具體對象的指針,或者表示具體對象的一個(gè)句柄來表示,它們的作用在本質(zhì)上是完全一樣的。
3. 返回值
對于用戶來說,調(diào)用通用接口后,應(yīng)該可以獲取本次執(zhí)行的結(jié)果,成功還是失敗,或一些其它的有用信息。比如,當(dāng)調(diào)用接口時(shí),如果指定的led_id 超過有效范圍時(shí),由于沒有與led_id 對應(yīng)的LED 設(shè)備,操作必定會失敗,此時(shí)必須返回錯(cuò)誤,告知用戶操作失敗,且失敗的原因是led_id 不在有效范圍內(nèi),無與之對應(yīng)的LED 設(shè)備。
在AMetal 中,通過返回值返回接口執(zhí)行的結(jié)果,其類型為int,返回值的含義為:若返回值為AM_OK,則表示操作成功;若返回值為負(fù)數(shù),則表示操作失敗,失敗原因可根據(jù)返回值,查找am_errno.h 文件中定義的宏,根據(jù)宏的含義確定失敗的原因;若返回值為正數(shù),其含義與具體接口相關(guān),由具體接口定義,無特殊說明時(shí),表明不會返回正數(shù)。
AM_OK 在am_common.h 文件中定義,其定義如下:
錯(cuò)誤號在am_errno.h 文件中定義,幾個(gè)常見錯(cuò)誤號的定義詳見表8.1。比如,在調(diào)用LED 通用接口時(shí),若led_id 不在有效范圍內(nèi),則該led_id沒有對應(yīng)的LED 設(shè)備,此時(shí)接口應(yīng)該返回-AM_ENODEV。注意:M_ENODEV 的前面有一個(gè)負(fù)號,以返回負(fù)值。
表8.1 常見錯(cuò)誤號定義(am_errno.h)
基于此,將所有LED 控制接口的返回值定義為int,LED 控制接口的完整定義詳見表8.2,其對應(yīng)的類圖詳見圖8.1。
表8.2 LED 通用接口(am_led.h)
圖8.1 LED 對應(yīng)的類圖
>>> 8.1.2 實(shí)現(xiàn)接口
當(dāng)完成接口定義后,還需要提供相應(yīng)的驅(qū)動實(shí)現(xiàn)這些接口,才能使用這些接口操作LED。
1. 實(shí)現(xiàn)接口初探
LED 有4 個(gè)通用接口函數(shù),其中的am_led_on()和am_led_off()接口是基于am_led_set()接口實(shí)現(xiàn)的,詳見程序清單8.1。
程序清單8.1 am_led_on()和am_led_off()接口的實(shí)現(xiàn)
實(shí)現(xiàn)接口的核心是實(shí)現(xiàn)am_led_set()和am_led_toggle()接口,通用接口在于屏蔽底層的差異性,即無論底層硬件如何變化,用戶都可以調(diào)用通用接口操作LED。但對于不同的硬件電路,比如,GPIO 和HC595 控制LED 的硬件電路,設(shè)置LED 狀態(tài)和LED 翻轉(zhuǎn)的具體實(shí)現(xiàn)是不同的。下面以設(shè)置LED 狀態(tài)的具體實(shí)現(xiàn)為例進(jìn)行詳細(xì)說明。
對于GPIO 控制LED 的硬件電路來說,當(dāng)使用GPIO 控制AM824-Core 的兩個(gè)板載LED時(shí),LED0 通過J9 與MCU 的PIO0_20 相連,LED1 通過J10 與MCU 的PIO0_21 相連。使用短路冒將J9 和J10 短路后即可使用PIO0_20 和PIO0_21 控制LED0 和LED1,當(dāng)引腳輸出低電平時(shí),則點(diǎn)亮LED;當(dāng)引腳輸出高電平時(shí),則熄滅LED,直接使用GPIO 通用接口實(shí)現(xiàn)am_led_set()接口詳見程序清單8.2。
程序清單8.2 am_led_set()的實(shí)現(xiàn)(GPIO 控制LED)
對于HC595 控制LED 的硬件電路來說,當(dāng)將MiniPort-595 和MiniPort-LED 聯(lián)合使用時(shí),通過控制HC595 的輸出,可以達(dá)到控制LED 點(diǎn)亮和熄滅的效果,當(dāng)相應(yīng)引腳輸出低電平時(shí),則點(diǎn)亮LED;當(dāng)相應(yīng)輸出高電平時(shí),則熄滅LED,直接使用HC595 通用接口實(shí)現(xiàn)am_led_set()接口詳見程序清單8.3。
程序清單8.3 am_led_set()的實(shí)現(xiàn)(HC595 控制LED)
在實(shí)際的應(yīng)用中,__g_hc595_handle 需要賦值后才能使用。通過程序清單8.2 和程序清單8.3 比較發(fā)現(xiàn),它們設(shè)置LED 狀態(tài)的實(shí)現(xiàn)是完全不同的。顯然,在同一個(gè)應(yīng)用中,一個(gè)接口的實(shí)現(xiàn)代碼只能有一份,因此程序清單8.2 和程序清單8.3 所示的實(shí)現(xiàn)是不能在一個(gè)應(yīng)用程序中共存的。在這種情況下,要么選擇使用GPIO 控制LED,要么使用HC595 控制LED。
2. 抽象的LED 設(shè)備類
在使用不同方式控制LED 時(shí),雖然它們對應(yīng)am_led_set()和am_led_toggle()的實(shí)現(xiàn)方法不同,但它們要實(shí)現(xiàn)的功能卻是一樣的,這是它們的共性:均要實(shí)現(xiàn)設(shè)置LED 狀態(tài)和翻轉(zhuǎn)LED 狀態(tài)的功能。由于一個(gè)接口的實(shí)現(xiàn)代碼只能有一份,因此它們的實(shí)現(xiàn)不能直接作為通用接口的實(shí)現(xiàn)代碼。為此,可以對它們的共性進(jìn)行抽象,即抽象為如下兩個(gè)方法:
相對通用接口來說,抽象方法多了一個(gè)p_cookie 參數(shù)。在面向?qū)ο蟮木幊讨?,對象中的方法都能通過隱形指針p_this 訪問對象自身,引用自身的一些私有數(shù)據(jù)。而在C 語言中則需要顯式的聲明,這里的p_cookie 就有相同的作用。
為了節(jié)省內(nèi)存空間,將所有抽象方法放在一個(gè)結(jié)構(gòu)體中,形成一個(gè)虛函數(shù)表,比如:
這里定義了一個(gè)虛函數(shù)表,包含了兩個(gè)方法,分別用于設(shè)置LED 的狀態(tài)和翻轉(zhuǎn)LED。針對不同的硬件設(shè)備,都可以根據(jù)自身特性實(shí)現(xiàn)這兩個(gè)方法。GPIO 控制LED 的偽代碼詳見程序清單8.4,HC595 控制LED 的偽代碼詳見程序清單8.5。
程序清單8.4 抽象方法的實(shí)現(xiàn)(GPIO 控制LED)
程序清單8.5 抽象方法的實(shí)現(xiàn)(HC595 控制LED)
顯然,__g_led_gpio_drv_funcs 和__g_led_hc595_drv_funcs 分別是使用GPIO 和HC595控制LED 的一種具體實(shí)現(xiàn),它們在形式上是兩個(gè)不同的結(jié)構(gòu)體常量,在同一系統(tǒng)中是可以共存的。當(dāng)有了針對不同硬件的驅(qū)動后,在am_led_set()接口的實(shí)現(xiàn)中,就需要根據(jù)實(shí)際情況找到對應(yīng)的驅(qū)動,然后調(diào)用其中實(shí)現(xiàn)的pfn_led_set 方法。在調(diào)用pfn_led_set()方法時(shí),由于該方法的第一個(gè)參數(shù)為p_cookie,p_cookie 代表了具體的對象,實(shí)際上驅(qū)動函數(shù)和p_cookie一起唯一地確定了一個(gè)具體的LED 設(shè)備?;诖丝梢詫Ⅱ?qū)動函數(shù)和p_cookie 定義在一起,形成一個(gè)新的LED 設(shè)備類型。即:
其中,p_funcs 為指向驅(qū)動虛函數(shù)表的指針,比如,指向__g_led_gpio_drv_funcs 或__g_led_hc595_drv_funcs,p_cookie 為指向設(shè)備的指針,即傳遞給驅(qū)動函數(shù)的第一個(gè)參數(shù)。
此時(shí),在am_led_set()接口的實(shí)現(xiàn)中,無需完成真實(shí)的設(shè)置LED 狀態(tài)的操作,僅需調(diào)用設(shè)備中的pfn_led_set 方法即可,其范例程序詳見程序清單8.6。
程序清單8.6 am_led_set()實(shí)現(xiàn)(1)
假定LED 設(shè)備為全局變量__gp_led_dev 指向的設(shè)備,展示了pfn_led_set 方法的調(diào)用形式。而實(shí)際上LED 設(shè)備往往不止一個(gè),比如,用GPIO 控制LED 的設(shè)備和使用HC595 控制LED 的設(shè)備,就需要在系統(tǒng)中管理多個(gè)LED 設(shè)備。由于它們的具體數(shù)目無法確定,因此需要使用單向鏈表進(jìn)行動態(tài)管理。在am_led_dev_t 中增加一個(gè)p_next 成員,用于指向下一個(gè)設(shè)備。即:
此時(shí),系統(tǒng)中的多個(gè)LED 設(shè)備使用鏈表的形式管理。那么在通用接口的實(shí)現(xiàn)中,如何確定該使用哪個(gè)LED 設(shè)備呢?在定義通用接口時(shí),使用了led_id 區(qū)分不同LED,若將一個(gè)LED 設(shè)備和該設(shè)備對應(yīng)的led_id 綁定在一起,則在通用接口的實(shí)現(xiàn)中,就可以根據(jù)led_id找到對應(yīng)的LED 設(shè)備,然后使用驅(qū)動中提供的相應(yīng)方法完成LED 的操作。
顯然,一個(gè)LED 設(shè)備可能包含多個(gè)LED,在AM824-Core 中,GPIO 控制了2 個(gè)LED,HC595 控制了8 個(gè)LED。如果兩個(gè)設(shè)備同時(shí)使用,則整個(gè)系統(tǒng)中共有10 個(gè)LED,編號為0~9。一般來說,一個(gè)設(shè)備中的所有LED 編號是連續(xù)的,比如,兩個(gè)LED 設(shè)備的編號分別為 0~1,2~9。如需獲得一個(gè)LED 設(shè)備中所有LED 的編號,僅需知道LED 的起始編號和結(jié)束編號即可,為此定義LED 設(shè)備對應(yīng)的led_id 信息為:
在設(shè)備中新增指向LED 信息的p_info 指針,便于在通用接口實(shí)現(xiàn)中根據(jù)led_id 查找到對應(yīng)的LED 設(shè)備,即:
基于此,am_led_set()函數(shù)的實(shí)現(xiàn)詳見程序清單8.7。
程序清單8.7 am_led_set()實(shí)現(xiàn)(2)
其中,__led_dev_find_with_id()的作用就是遍歷設(shè)備鏈表,與各個(gè)設(shè)備中的ID 信息一一比對,以找到led_id 對應(yīng)的LED 設(shè)備,其實(shí)現(xiàn)詳見程序清單8.8。
程序清單8.8 查找指定led_id 的LED 設(shè)備
其中,__gp_head 是一個(gè)全局變量,初始為NULL,表示初始時(shí)系統(tǒng)中無任何LED 設(shè)備。同理,可得到am_led_toggle()接口的實(shí)現(xiàn),詳見程序清單8.9。
程序清單8.9 am_led_toggle()實(shí)現(xiàn)
至此,實(shí)現(xiàn)了所有通用接口。由于當(dāng)前沒有任何LED 設(shè)備,因此__led_dev_find_with_id()為NULL,通用接口的返回值也始終為-AM_ENODEV。
為了使通用接口能夠操作到具體有效的LED,就必須向系統(tǒng)中添加一個(gè)有效的LED 設(shè)備。根據(jù)LED 設(shè)備類型的定義,添加一個(gè)設(shè)備時(shí),需要完成p_funcs、p_cookie 和p_info 的正確賦值,這些成員的賦值需要具體的LED 設(shè)備對象來完成,如GPIO 控制LED 的設(shè)備。為此,可以為驅(qū)動提供一個(gè)添加LED 設(shè)備的接口。比如:
其中,為了方便驅(qū)動直接添加一個(gè)設(shè)備,避免直接操作LED 設(shè)備的各個(gè)成員,將需要賦值的成員通過參數(shù)傳遞給接口函數(shù),其實(shí)現(xiàn)詳見程序清單8.10。
程序清單8.10 向系統(tǒng)中添加LED 設(shè)備
該程序首先通過判斷新設(shè)備的起始LED 編號和結(jié)束LED 編號是否已經(jīng)存在于系統(tǒng)之中來判斷ID 是否是有效范圍,確保添加的各個(gè)LED 設(shè)備的ID 不沖突,即保證了LED 編號的唯一性,然后將設(shè)備中的各個(gè)成員賦值,最后通過程序清單8.10的21~22 行共計(jì)2 行代碼將新設(shè)備添加到鏈表首部。
顯然,接下來需要在具體的LED 設(shè)備驅(qū)動實(shí)現(xiàn)中,使用am_led_dev_add()接口向系統(tǒng)中添加設(shè)備,使得用戶可以使用LED 通用接口操作到具體有效的LED。
在上述分析的過程中,定義了LED 設(shè)備類,在其中完成了LED 通用接口的實(shí)現(xiàn),可以用類圖來表示這個(gè)關(guān)系,詳見圖8.2。
圖8.2 抽象的LED 設(shè)備類
LED 設(shè)備中存在抽象方法pfn_led_set 和pfn_led_toggle,這兩個(gè)抽象方法是以虛函數(shù)表的形式存在LED 設(shè)備類中的。由于存在抽象方法,因此LED 設(shè)備類是一個(gè)抽象類,它本身不能夠直接實(shí)例化,必須由其派生的具體類實(shí)現(xiàn)這兩個(gè)抽象方法。為了便于查閱,如程序清單8.11 所示展示了LED 設(shè)備的接口文件am_led_dev.h 的內(nèi)容。
程序清單8.11 am_led_dev.h 文件內(nèi)容
3. 具體的LED 設(shè)備類
前面定義的抽象LED 設(shè)備類中包含了兩個(gè)抽象方法:pfn_led_set 和pfn_led_toggle。為了使用戶可以通過LED 通用接口操作LED,就必須根據(jù)實(shí)際硬件連接,實(shí)現(xiàn)兩個(gè)抽象方法,然后將具體設(shè)備添加到系統(tǒng)設(shè)備鏈表中。
下面分別以GPIO 控制LED 的驅(qū)動實(shí)現(xiàn)和HC595 控制LED 的驅(qū)動實(shí)現(xiàn)為例,闡述LED設(shè)備驅(qū)動開發(fā)的一般方法,如果后續(xù)有其它類型的LED 控制電路,可以按照此方法添加自定義的LED 驅(qū)動。
(1) GPIO 控制LED 的驅(qū)動實(shí)現(xiàn)
具體LED 設(shè)備的核心功能是實(shí)現(xiàn)抽象設(shè)備類中定義的方法,首先應(yīng)該基于抽象設(shè)備類派生一個(gè)具體的設(shè)備類,其類圖詳見圖8.3。
圖8.3 具體設(shè)備類(GPIO)
可直接定義具體的LED 設(shè)備類。比如:
am_led_gpio_dev_t 即為具體的LED 設(shè)備類。具有該類型后,即可使用該類型定義一個(gè)具體的LED 設(shè)備實(shí)例:
在使用GPIO 控制LED 時(shí),需要知道對應(yīng)的引腳信息和LED 點(diǎn)亮的電平信息,為了便于修改配置,這些信息往往由用戶傳遞給驅(qū)動。此外,還需要提供LED 設(shè)備的ID 信息,包含起始ID 和結(jié)束ID,以確定的為設(shè)備中的每個(gè)LED 分配一個(gè)唯一ID?;诖耍梢詫⑿枰捎脩籼峁┑脑O(shè)備相關(guān)信息存放到一個(gè)新的結(jié)構(gòu)體類型中,將其作為需要由用戶提供的設(shè)備信息。即:
對于AM824_Core 的兩個(gè)板載LED 來說,若編號為0~1,則可以使用該類型定義其對應(yīng)的設(shè)備實(shí)例信息如下
為了便于通過設(shè)備直接找到對應(yīng)的設(shè)備信息,在設(shè)備類中往往直接維持一個(gè)指向設(shè)備信息的指針。即:
顯然,在使用GPIO 控制LED 前,引腳需要初始化為輸出模式,此外,在完成初始化后,還需要將具體的LED 設(shè)備添加到系統(tǒng)中,便于使用通用接口操作LED。這些工作通常在驅(qū)動的初始化函數(shù)中完成,初始化函數(shù)的原型為:
其中,p_dev 指向am_led_gpio_dev_t 類型的設(shè)備,p_info 為指向am_led_gpio_info_t 類型實(shí)例信息的指針,其調(diào)用形式如下:
初始化函數(shù)的的實(shí)現(xiàn)詳見程序清單8.12。
程序清單8.12 初始化函數(shù)實(shí)現(xiàn)(GPIO 控制LED)
程序中,首先通過LED 的起始編號和結(jié)束編號,得到了LED 的數(shù)目,由于GPIO 的引腳數(shù)目與LED 數(shù)目相等,因此,也就得到了GPIO 引腳的數(shù)目。然后將所有引腳配置為了輸出模式,并根據(jù)是否為低電平點(diǎn)亮,初始時(shí)使所有LED 處于熄滅狀態(tài)。最后,通過am_led_dev_add()函數(shù),將具體的LED 設(shè)備添加到了系統(tǒng)之中。
在添加LED 設(shè)備時(shí),LED 的 ID 信息直接使用了設(shè)備信息中的ID 信息,抽象方法的實(shí)現(xiàn)使用了__g_led_gpio_drv_funcs 中實(shí)現(xiàn)的方法(其定義詳見程序清單8.4),p_cookie 直接設(shè)置為了指向設(shè)備自身的指針,正因?yàn)槿绱?,在抽象方法的?shí)現(xiàn)中,p_cookie 參數(shù)即為指向設(shè)備自身的指針,可以通過p_cookie 得到具體設(shè)備相關(guān)的信息,如GPIO 信息等,進(jìn)而實(shí)現(xiàn)LED 的相關(guān)操作,完善程序清單8.4 中實(shí)現(xiàn)的抽象方法,詳見程序清單8.13。
程序清單8.13 抽象方法的實(shí)現(xiàn)(GPIO 控制LED)
在抽象方法的實(shí)現(xiàn)中,首先通過類型強(qiáng)制轉(zhuǎn)換將p_cookie 轉(zhuǎn)換為指向具體設(shè)備的指針。然后通過它找到相應(yīng)的引腳信息,進(jìn)而實(shí)現(xiàn)LED 的相關(guān)操作。在設(shè)置LED 狀態(tài)的實(shí)現(xiàn)中,巧妙的使用了“異或(^)”預(yù)算。因?yàn)?a target="_blank">active_low 的值與實(shí)際的點(diǎn)亮電平是恰好相反的,即若active_low 為AM_TRUE,表明輸出低電平點(diǎn)亮,反之,輸出高電平點(diǎn)亮。state 及active_low 的值都將影響本次GPIO 的輸出電平,GPIO 的輸出電平與state 和active_low 的真值表詳見表8.3。由此可見,當(dāng)state 與active_low 相同時(shí),則GPIO 輸出0;當(dāng)state 與active_low 不同時(shí),則GPIO 輸出1,恰好是異或關(guān)系。
表8.3 GPIO 輸出增值表
為了便于查閱,如程序清單8.14 所示展示了LED 設(shè)備接口文件am_led_gpio.h 的內(nèi)容。
程序清單8.14 am_led_gpio.h 文件內(nèi)容
(2) HC595 控制LED 的驅(qū)動實(shí)現(xiàn)
同樣,首先基于抽象設(shè)備類派生一個(gè)具體的設(shè)備類,其類圖詳見圖8.4,可直接定義具體的LED 設(shè)備類:
am_led_hc595_dev_t 為具體的LED 設(shè)備類,當(dāng)具有該類型后,即可使用該類型定義一個(gè)具體的LED 設(shè)備實(shí)例:
圖8.4 具體設(shè)備類(HC595)
在使用HC595 控制LED 時(shí),需要知道LED 和HC595相關(guān)的信息,如LED 點(diǎn)亮的電平信息和HC595 的數(shù)目。雖然MiniPort-595 只有一個(gè)HC595,但作為一個(gè)通用的驅(qū)動,應(yīng)考慮到這些基礎(chǔ)的擴(kuò)展,以便驅(qū)動可以盡可能的支持更多的硬件電路。
特別地,HC595 的每次輸出都是完整的輸出,如對于單個(gè)HC595,其每次輸出都只能輸出完整的8 位數(shù)據(jù),不能單獨(dú)輸出1 位數(shù)據(jù),而LED 的控制又是對單個(gè)LED 進(jìn)行的,因此,為了在控制一個(gè)LED 時(shí),不影響到其它LED,必須使其他位的輸出保持不變,這就需要實(shí)時(shí)保存當(dāng)前的輸出,為了保存當(dāng)前所有HC595 的輸出信息,需要用戶提供一個(gè)緩沖區(qū),緩沖區(qū)的大小與HC595 的個(gè)數(shù)相等。
此外,還需要提供包含起始ID 和結(jié)束ID 的ID 信息?;诖耍梢詫⑿枰捎脩籼峁┑脑O(shè)備相關(guān)信息存放到一個(gè)新的結(jié)構(gòu)體類型中,將其作為需要由用戶提供的設(shè)備信息:
對于使用MiniPort-595 和MiniPort-LED 聯(lián)合使用的情況,共計(jì)8 個(gè)LED,若分配的編號為2~9,則可以使用該類型定義其對應(yīng)的設(shè)備實(shí)例信息如下:
同理,在設(shè)備類中需要維持一個(gè)指向設(shè)備信息的指針。此外,由于使用HC595 驅(qū)動LED時(shí),需要使用HC595 的句柄handle 來傳輸數(shù)據(jù),因此,用戶還需要提供一個(gè)595 的句柄。handle 需要保存到設(shè)備中:
注意,由于句柄往往需要通過動態(tài)的調(diào)用實(shí)例初始化函數(shù)獲得,比如,HC595 的句柄可通過如下語句獲得:
而設(shè)備信息往往在系統(tǒng)啟動后不會改變,可以定義為常量,因此,handle 往往由用戶單獨(dú)提供,不存放在設(shè)備信息中。
顯然,在使用GPIO 控制LED 前,需要完成設(shè)備中各成員的賦值,并熄滅所有LED,此外,在初始化完成后,還需要將具體的LED 設(shè)備添加到系統(tǒng)中。這些工作通常在驅(qū)動的初始化函數(shù)中完成,初始化函數(shù)的原型為:
其中, p_dev 為指向am_led_hc595_dev_t 類型實(shí)例的指針, p_info 為指向am_led_hc595_info_t 類型實(shí)例信息的指針,其調(diào)用形式如下:
初始化函數(shù)的的實(shí)現(xiàn)詳見程序清單8.15。
程序清單8.15 初始化函數(shù)實(shí)現(xiàn)(HC595 控制LED)
首先將緩存中的值設(shè)置為使所有LED 熄滅的值,然后使用am_hc595_send()將緩存中的值輸出,使所有LED 處于熄滅狀態(tài)。最后,通過am_led_dev_add()函數(shù),將具體的LED 設(shè)備添加到了系統(tǒng)之中。
在添加LED 設(shè)備時(shí),LED 的 ID 信息直接使用了設(shè)備信息中的ID 信息,抽象方法的實(shí)現(xiàn)使用了__g_led_hc595_drv_funcs 中實(shí)現(xiàn)的方法(其定義詳見程序清單8.5),p_cookie 直接設(shè)置為了指向設(shè)備自身的指針,正因?yàn)槿绱?,在抽象方法的?shí)現(xiàn)中,p_cookie 參數(shù)即為指向設(shè)備自身的指針,可以通過p_cookie 得到具體設(shè)備相關(guān)的信息,如HC595 句柄,HC595 緩存等,進(jìn)而實(shí)現(xiàn)LED 的相關(guān)操作,完善程序清單8.5 中實(shí)現(xiàn)的抽象方法詳見程序清單8.16。
程序清單8.16 抽象方法的實(shí)現(xiàn)(HC595 控制LED)
在抽象方法的實(shí)現(xiàn)中,首先通過類型強(qiáng)制轉(zhuǎn)換將p_cookie 轉(zhuǎn)換為指向具體設(shè)備的指針。然后通過它找到相關(guān)的信息,進(jìn)而實(shí)現(xiàn)LED 的相關(guān)操作。為了便于查閱,如程序清單8.17所示展示了LED 設(shè)備接口文件am_led_hc595.h 的內(nèi)容。
程序清單8.17 am_led_hc595.h 文件內(nèi)容
-
led
+關(guān)注
關(guān)注
242文章
23379瀏覽量
663460 -
單片機(jī)設(shè)計(jì)
+關(guān)注
關(guān)注
5文章
612瀏覽量
21261 -
ametal_led
+關(guān)注
關(guān)注
0文章
1瀏覽量
1707
原文標(biāo)題:周立功:深入理解AMetal——LED 通用接口
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
面向AMetal框架與接口的編程原理和技巧
![面向<b class='flag-5'>AMetal</b>框架與<b class='flag-5'>接口</b>的編程原理和技巧](https://file1.elecfans.com//web2/M00/A6/D6/wKgZomUMQMqABbZUAAAQhkg0PDk320.jpg)
面向ametal框架與接口的編程ametal uart總線
![面向<b class='flag-5'>ametal</b>框架與<b class='flag-5'>接口</b>的編程<b class='flag-5'>ametal</b> uart總線](https://file1.elecfans.com//web2/M00/A6/DC/wKgZomUMQO6AY9XzAADwFg-Lxkk551.png)
一文讀懂ametal_hc595接口的學(xué)習(xí)要點(diǎn)
![一文讀懂<b class='flag-5'>ametal</b>_hc595<b class='flag-5'>接口</b>的學(xué)習(xí)要點(diǎn)](https://file1.elecfans.com//web2/M00/A6/F8/wKgZomUMQYqAUr5GAAEVyt9xuQk987.png)
靈動微電子MM32 MCU正式入駐AMetal平臺
靈動微課堂 (第122講) | 基于MM32 MCU的OS移植與應(yīng)用:AMetal平臺新建工程
靈動微課堂 (第123講) | 基于MM32 MCU的OS移植與應(yīng)用:AMetal LED燈控制
靈動微課堂 (第123講) | 基于MM32 MCU的OS移植與應(yīng)用:AMetal LED燈控制
深入淺出AMetal之接口與實(shí)現(xiàn)
![深入淺出<b class='flag-5'>AMetal</b>之<b class='flag-5'>接口</b>與實(shí)現(xiàn)](https://file1.elecfans.com//web2/M00/A6/D8/wKgZomUMQNKABehgAADx_GIcmt0827.png)
AMetal開發(fā)通用外設(shè)的流程與規(guī)范
![<b class='flag-5'>AMetal</b>開發(fā)<b class='flag-5'>通用</b>外設(shè)的流程與規(guī)范](https://file.elecfans.com/web2/M00/3F/AC/pYYBAGJpBUOAJ2tyAAByiK27rQ8986.png)
AMetal適配:以TIM為例來展示如何開發(fā)通用驅(qū)動外設(shè)代碼
![<b class='flag-5'>AMetal</b>適配:以TIM為例來展示如何開發(fā)<b class='flag-5'>通用</b>驅(qū)動外設(shè)代碼](https://file.elecfans.com//web2/M00/43/35/pYYBAGJ8zqSAFLtUAAEZC_YKIIg009.png)
評論