周立功教授新書《面向AMetal框架與接口的編程(上)》,對AMetal框架進行了詳細介紹,通過閱讀這本書,你可以學到高度復用的軟件設計原則和面向接口編程的開發思想,聚焦自己的“核心域”,改變自己的編程思維,實現企業和個人的共同進步。
第八章為深入理解AMetal,本文內容為8.3 蜂鳴器接口和8.4 溫度采集接口。
8.3 蜂鳴器接口
>>> 8.3.1 定義接口
1. 接口命名
由于操作的對象是蜂鳴器(buzzer),因此,接口命名以“am_buzzer_”作為前綴。對于蜂鳴器,基本的操作是打開和關閉蜂鳴器,可定義相應的兩個接口名為:
-
am_buzzer_on
-
am_buzzer_off
特別地,在一些應用場合,還需要類似蜂鳴器“嘀一聲”這樣的操作,即鳴叫一定的時間后自動停止。可以定義其接口名為:
-
am_buzzer_beep
-
am_buzzer_beep_async
這里定義了兩個接口,都是用于蜂鳴器鳴叫指定的時間,二者的區別在于函數返回的時機不同。am_buzzer_beep 會等待鳴叫結束后返回,am_buzzer_beep_async 不會等待,函數立即返回,蜂鳴器鳴叫指定時間后自動停止。
顯然,對于am_buzzer_beep_async 接口,在最開始的蜂鳴器接口設計中,很可能是不會想到的,該接口是在大量實際應用中得出的,由于在一些特殊的應用場景,不希望程序被阻塞,因此,需要提供am_buzzer_beep_async 這樣的異步接口。
2. 接口參數
在LED 通用接口的設計中,由于在一個系統中,可能存在多個LED,這就必須使用某種方法區分不同的LED,如使用了唯一ID 號led_id 表示來區分系統中的多個LED。按照這種邏輯,是否也需要一個buzzer_id 來區分不同的蜂鳴器呢?
蜂鳴器的功能單一,是一種發聲器件,在一個具體應用中,發聲器件往往只有一個,沒有必要使用多個蜂鳴器。因此,蜂鳴器可以看做系統的一個單實例設備,基于此,也就無需使用類似于buzzer_id 這樣的參數來區分多個蜂鳴器了,對于打開和關閉蜂鳴器的接口,則無需任何參數,即:
-
am_buzzer_on(void);
-
am_buzzer_off(void);
特別地,對于am_buzzer_beep 和am_buzzer_beep_async 接口,雖無需參數來區分多個蜂鳴器,但由于其功能是鳴叫一定的時間,因此,還需要一個用于指定鳴叫時長的參數。
-
am_buzzer_beep(uint32_t ms);
-
am_buzzer_beep_async (uint32_t ms);
其中,ms 用于指定鳴叫時長,單位為毫秒。
3. 返回值
接口無特殊說明,直接將所有接口的返回值定義為int 類型的標準錯誤號。基于此,蜂鳴器控制接口的完整定義詳見表8.5。
表8.5 蜂鳴器通用接口(am_buzzer.h)
其對應的類圖詳見圖8.8。
圖8.8 蜂鳴器接口類圖
>>> 8.3.2 實現接口
1. 抽象的蜂鳴器設備類
蜂鳴器共計4 個通用接口,其中,am_buzzer_beep()和am_buzzer_beep_async()接口可以直接基于am_buzzer_on()和am_buzzer_off()接口實現,am_buzzer_beep()的實現詳見程序清單8.24。
程序清單8.24 am_buzzer_beep()的實現
程序中,首先使用am_buzzer_on()打開蜂鳴器,若打開蜂鳴器失敗(返回值為負數),則直接返回相應的錯誤號,若打開成功,則使用am_mdelay()延時指定的時間,最后關閉蜂鳴器。對于am_buzzer_beep_async()接口,其需要立即返回,不能在函數內部直接使用延時函數,可以基于軟件定時器實現,范例程序詳見程序清單8.25。
程序清單8.25 am_buzzer_beep_async()的范例程序
程序中,首先使用am_buzzer_on()打開蜂鳴器,若打開蜂鳴器失敗(返回值為負數),則直接返回相應的錯誤號,若打開成功,則啟動軟件定時器,定時時間為指定的鳴叫時間,啟動定時器后,函數立即返回。軟件定時器定時時間到后,需要調用自定義回調函數__beep_timer_callback(),在回調函數中,關閉了軟件定時器和蜂鳴器,鳴叫結束。
顯然,軟件定時器在使用前,需要初始化,以將__beep_timer_callback()函數作為其定時時間到后的回調函數,如:
初始化語句放在哪里呢?這里僅僅展示了使用軟件定時器實現am_buzzer_beep_async()函數的范例,后文再介紹初始化軟件定時器的合適時機。
由于am_buzzer_beep()和am_buzzer_beep_async()接口可以直接基于am_buzzer_on()和am_buzzer_off()實現,因此實現蜂鳴器接口的核心是實現am_buzzer_on()和am_buzzer_off()接口,按照LED 或HC595 的設計方法,可以抽象對應的兩個方法。即:
雖然按照這種設計方法是完全可行的,但是考慮到on 和off 是一組相互對稱的接口,功能是同屬一類的,具有很大的相似性,因此,可以僅抽象一個方法,使用一個布爾類型的參數區分操作是打開還是關閉,比如:
可見,定義抽象方法并不一定是原封不動的按照接口定義抽象方法,可以作適當的調整,只要基于抽象方法,能夠實現通用接口即可。
雖然只有一個抽象方法,但是為了保證結構的統一,也為了方便后續擴展(如新增抽象方法等),往往還是將抽象方法放到一個虛函數表中。即:
類似地,將抽象方法和p_cookie 定義在一起,即為抽象的蜂鳴器設備。如:
在前面實現am_buzzer_beep_async()接口時,使用到了軟件定時器,顯然,軟件定時器是用于實現一個蜂鳴器鳴叫功能的,是與蜂鳴器設備相關的,其不應定義為全局變量,取而代之的是,直接定義在抽象設備結構體中,即:
抽象設備中定義的抽象方法需要由具體的蜂鳴器設備來完成,am_buzzer_on()和am_buzzer_off()接口則可以直接基于抽象方法實現。
在定義蜂鳴器接口時,由于蜂鳴器是單實例設備(系統中只有一個),因此沒有在接口中定義區分蜂鳴器對象的參數,如ID 號或者句柄參數等,那么,在實現接口時,如何找到相應的設備呢?由于在系統中只有一個蜂鳴器設備,因此,可以直接使用一個全局變量來指向蜂鳴器設備,am_buzzer_on()和am_buzzer_off()的實現詳見程序清單8.26。
程序清單8.26 am_buzzer_on 和am_buzzer_off ()的范例程序
其中,__gp_buzzer_dev 是指向蜂鳴器設備的指針,初始沒有任何有效的蜂鳴器設備,因此初始值為NULL。顯然,要正常使用蜂鳴器,就必須使__gp_buzzer_dev 指向有效的蜂鳴器設備,這就需要由具體蜂鳴器設備實現pfn_buzzer_set 抽象方法。
為了完成__gp_buzzer_dev 的賦值,需要定義一個設備注冊接口,用于向系統中注冊一個有效蜂鳴器設備:
其中,為了方便向系統中添加一個蜂鳴器設備時,避免直接操作蜂鳴器設備的各個成員,將需要賦值的成員通過參數傳遞給接口函數。其實現詳見程序清單8.27。
程序清單8.27 向系統中添加蜂鳴器設備
該程序首先判定參數的有效性,然后完成了抽象設備中抽象方法和p_cookie 賦值,接著給全局變量__gp_buzzer_dev 的賦值,使其指向有效的蜂鳴器設備,最后,初始化了抽象設備中的軟件定時器,便于實現異步的蜂鳴器鳴叫接口,由此可見,軟件定時器的初始化操作是在添加一個蜂鳴器設備時完成的。
顯然,接下來,就需要基于抽象的蜂鳴器設備派生具體的蜂鳴器設備,在具體的蜂鳴器設備中,完成抽象方法pfn_buzzer_set 的實現,并使用am_buzzer_dev_register()接口向系統中添加一個蜂鳴器設備,使得用戶可以使用蜂鳴器通用接口操作到具體有效的蜂鳴器。
為了便于查閱,如程序清單8.28 所示展示了蜂鳴器設備接口文件(am_buzzer_dev.h)的內容。其對應的類圖詳見圖8.9。
程序清單8.28 am_buzzer_dev.h 文件內容
圖8.9 抽象的蜂鳴器設備類
2. 具體的蜂鳴器設備類
以使用PWM 輸出控制蜂鳴器發聲為例,簡述具體蜂鳴器設備的實現方法。首先應該基于抽象設備類派生一個具體的設備類,其類圖詳見圖8.10,可直接定義具體的蜂鳴器設備類,如:
圖8.10 具體的蜂鳴器設備類
am_buzzer_pwm_dev_t 即為具體的蜂鳴器設備類。具有該類型后,即可使用該類型定義一個具體的蜂鳴器設備實例,即:
特別地,由于蜂鳴器是單實例設備,不能夠使用該類型定義多個實例,因此,可以直接在具體設備實現的文件內部定義一個蜂鳴器設備實例,無需用戶使用該類型自定義設備實例。基于此,am_buzzer_pwm_dev_t 類型無需開放給用戶,可以直接定義在.c 文件中,由于am_buzzer_pwm_dev_t 類型無需開放給用戶,僅內部使用,因此可以修改類型名為雙下劃線“__”開頭,如在am_buzzer_pwm.c 文件中定義設備類型以及對應的設備實例如下:
在使用PWM 輸出控制蜂鳴器時,需要知道PWM 的句柄,通道號等相關信息,這些信息需要保存在設備中,因此更新設備類型的定義如下:
顯然,這些成員需要初始化后才能使用,定義初始化函數的原型為:
其中,pwm_handle 為標準的PWM 服務句柄,chan 為PWM 通道號,duty_ns 和period_ns分別指定了輸出PWM 波形的脈寬和周期,決定了蜂鳴器鳴叫的響度和頻率,比如,AM824-Core 板載的蜂鳴器。
若使用SCT 輸出PWM,由于PIO0_24 對應SCT 的通道1,因此初始話函數的調用形式如下:
程序中,使用了am_lpc82x_sct0_pwm_inst_init()函數獲取到了PWM 句柄,使用了通道1,并設定輸出PWM 的周期為400000ns,即 2.5KHz (1000000000 / 400000),脈寬恰好為周期的一半,即輸出PWM 的占空比為50%。初始化函數的實現范例詳見程序清單8.29。
程序清單8.29 初始化函數實現范例
該程序首先判定了參數的有效性,然后完成了設備實例中相關成員的賦值,接著調用了am_buzzer_dev_register()函數,將蜂鳴器設備添加到系統中,最后配置了PWM 輸出通道的脈寬和周期。添加設備時,將p_funcs 賦值為&__g_buzzer_pwm_drv_funcs,p_cookie 賦值為具體設備的地址,即p_cookie 指向了設備自身。__g_buzzer_pwm_drv_funcs 中包含了抽象方法的具體實現,完整定義詳見程序清單8.30。
程序清單8.30 抽象方法的實現
為了便于查閱,如程序清單8.31 所示展示了蜂鳴器設備接口文件(am_buzzer_pwm.h)的內容。
程序清單8.31 am_buzzer_pwm.h 文件內容
由此可見,與其它具體設備的接口文件(詳見程序清單8.14、程序清單8.17 和程序清單8.23)相比,不同的是,其沒有包含具體設備類型的定義,初始化接口的第一個參數,也不是指向具體設備的指針。這是由于蜂鳴器是單實例設備,系統中最多只能定義一個,因此直接在實現文件的內部完成了設備實例的定義,相關類型無需開放給用戶。同理,由于是單實例設備,初始化函數初始化的必然是文件內部定義的設備實例,無需額外使用指向設備的指針指定要初始化的設備。
至此,詳細介紹了LED 通用接口、HC595 接口和蜂鳴器接口,它們代表了AMetal 中典型的3 種類型的設備接口。
-
LED 通用接口:使用唯一ID 區分不同設備;
-
HC595 接口:使用句柄區分不同的設備,句柄本質上是指向設備的指針;
-
蜂鳴器接口:不使用任何參數區分不同設備,是一種單實例設備。
8.4 溫度采集接口
>>> 8.4.1 定義接口
1. 接口命名
由于操作的對象是溫度(temperature),為了縮短接口名,將temperature 縮寫為temp,因此,接口命名以“am_temp_”作為前綴。對于溫度采集,主要的操作就是讀取當前溫度。可定義接口名為:
-
am_temp_read
2. 接口參數
顯然,一個系統中可能存在多個溫度傳感器,可以簡單的使用句柄來區分不同的溫度傳感器,因此第一個參數的類型定義為溫度傳感器句柄,和HC595 設備類似,其應該定義為指向抽象溫度設備的指針,假定抽象溫度設備的類型為am_temp_dev_t,則handle 的類型可以定義為:
讀取溫度接口的核心功能是返回當前的溫度值,首先需要定義溫度值的類型,然后再確定溫度值的返回方式:通過返回值返回或通過出口參數返回。
通常使用1 位小數表示溫度值,比如,37.5℃,由此可見,溫度值需要使用小數表示,但要求的精度并不高,往往只會精確到小數點后一位,因此溫度值可以使用float 類型表示。
由于AMetal 運行的實際硬件平臺往往是以低端的Cortex-M0、Cortex-M0+和Cortex-M3等作為內核的芯片,這些芯片沒有硬件浮點運算單元,浮點運算的效率很低。因此,AMetal平臺中,不建議使用浮點類型,據此,可以使用整數表示溫度值,同時,為了保證一定的精度,使用整數表示擴大1000 倍后的溫度值。如實際溫度為37.5 度,則使用整數37500 表示。使用這種方法巧妙的避免了使用浮點類型,但也能保證實際溫度的精度為小數點后三位。由于溫度可能存在負值,因此,使用有符號的32 位數據來表示溫度值,即溫度值的類型定義為int32_t。
在通用接口中,返回值往往定義為int 類型的錯誤號,且使用負數表示出錯,顯然,如果使用返回值直接返回溫度,用戶將無法區分溫度為負數和讀取溫度出錯的情況。為此,使用一個輸出參數,用以返回溫度值,即定義一個int32_t 類型的指針作為輸出參數:
-
am_temp_read(am_temp_handle_t handle, int32_t *p_temp)
其中,handle 為溫度傳感器的句柄,p_temp 為輸出參數,用于返回當前的溫度值,其表示的溫度值為實際溫度值的1000 倍。
3. 返回值
接口無特殊說明,直接將所有接口的返回值定義為int 類型的標準錯誤號。基于此,完整的讀取溫度接口的原型為:
其對應的類圖詳見圖8.11。
圖8.11 溫度采集接口
>>> 8.4.2 實現接口
1. 抽象的溫度采集設備類
根據讀取溫度接口,可以定義相應的抽象方法,并將其存放在一個虛函數表中:
類似地,將抽象方法和p_cookie 定義在一起,即為抽象的溫度采集設備。比如:
顯然,具體的溫度采集設備直接從抽象的溫度采集設備派生,然后由具體的溫度采集設備根據實際的硬件,實現讀取溫度的抽象方法。
在讀取溫度接口中,使用了handle 作為第一個參數,其本質上是指向設備的指針,讀取溫度接口可以直接調用抽象方法實現,詳見程序清單8.32。
程序清單8.32 讀取溫度接口實現
在接口實現中,沒有與硬件相關的實現代碼,僅僅是簡單的調用了抽象方法。抽象方法需要由具體的溫度采集設備來實現。類似地,由于讀取溫度接口的實現非常簡單,往往將其實現直接以內聯函數的形式存放在.h 文件中。
為便于查閱,如程序清單8.33 所示展示了抽象溫度采集設備接口文件(am_temp.h)的內容,其包含了抽象溫度采集設備相關的抽象方法定義、類型定義和接口實現,對應的類圖詳見圖8.12。
圖8.12 抽象的溫度采集設備類
程序清單8.33 am_temp.h 文件內容
2. 具體的溫度采集設備類
以使用LM75B 溫度傳感器實現溫度采集為例,簡述具體溫度采集設備的實現方法。首先應該基于抽象設備類派生一個具體的設備類,其類圖詳見圖8.13,可直接定義具體的溫度采集設備類。比如:
圖8.13 具體的溫度采集設備類
am_temp_lm75_t 為具體的溫度采集設備類,具有該類型后,即可使用該類型定義一個具體的溫度采集設備實例:
LM75B 是標準的I2C 從機器件,需要知道LM75B 的從機地址,才能使用I2C 總線讀取LM75B 中的溫度數據。由于從機地址與LM75 外部引腳電平相關,因此LM75 的地址信息需要由用戶根據實際硬件電路設置。將需要由用戶提供的設備相關信息存放到一個新的設備信息結構體類型中。比如:
當使用AM824-Core 上板載的LM75B 時,LM75B 的7 位I2C 從機地址為1001A2A1A0,由于A0、A1、A2 均與地連接為低電平,因此可得板載LM75B 的7 位從機地址為1001000,即:0x48。基于此,板載LM75B 對應的設備實例信息可以定義如下:
同理,在設備類中需要維持一個指向設備信息的指針。此外,由于使用I2C 接口從LM75B中讀取溫度數據時,LM75B 相當于是一個I2C 從設備,為了使用I2C 接口與之通信,需要為LM75B 定義一個與之對應的I2C 從設備,新增兩個成員,完整的溫度采集設備定義即為:
顯然,在使用I2C 接口從LM75B 中讀取溫度之前,需要完成設備中各成員的賦值,這些工作通常在驅動的初始化函數中完成,定義初始化函數的原型為:
-
p_lm75 為指向am_temp_lm75_t 類型實例的指針;
-
p_devinfo 為指向am_temp_lm75_info_t 類型實例信息的指針。
handle 為I2C 句柄,便于使用I2C 接口讀取溫度數據,初始化函數的返回值即為溫度采集設備句柄,其調用形式如下:
返回值即為溫度采集設備的句柄,可以作為溫度采集接口的第一個參數(handle)的實參,初始化函數的實現范例詳見程序清單8.34。
程序清單8.34 初始化函數實現范例
該程序首先建立了標準的I2C 從設備,便于后續使用I2C 接口讀取數據,然后初始化了p_info 成員,接著完成了抽象溫度采集設備中p_funcs 和p_cookie 的賦值,最后返回設備地址作為用戶操作溫度采集設備的句柄。pfuncs 賦值為了&__g_temp_lm75_drv_funcs,其中包含了讀取溫度抽象方法的具體實現,完整定義詳見程序清單8.35。
程序清單8.35 抽象方法的實現
在讀取溫度的實現函數__temp_lm75_read()中,首先使用I2C 接口從LM75B 中讀取出當前的實際溫度值,詳見程序清單8.35(6);接著對數據進行簡單處理,兩字節數據整合為一個16 位有符號的溫度值temp,詳見程序清單8.35(10 ~ 11);最后,確認p_temp 指針有效后,將temp 乘以125,再除以32,最終的結果作為輸出的溫度值。
為什么將temp 乘以125,然后再除以32 呢?這是因為LM75B 中直接讀取的數據時實際溫度值的256倍,即:實際溫度= temp / 256。
而溫度采集接口需要返回的溫度值是實際溫度的1000 倍,即:
* p _ temp=實際溫度*1000 = temp/ 256*1000 = temp*1000/ 256
化簡可得:
為了便于查閱,如程序清單8.36 所示展示了具體溫度采集設備(LM75B)接口文件(am_temp_lm75.h)的內容。
程序清單8.36 am_temp_lm75.h 文件內容
-
蜂鳴器
+關注
關注
12文章
892瀏覽量
45990 -
周立功
+關注
關注
38文章
130瀏覽量
37664 -
LM75B
+關注
關注
0文章
5瀏覽量
9629 -
致遠電子
+關注
關注
13文章
406瀏覽量
31323
原文標題:周立功:深入理解AMetal——蜂鳴器接口和溫度采集接口
文章出處:【微信號:Zlgmcu7890,微信公眾號:周立功單片機】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論