第五章為深入淺出AMetal,本文內容為5.1 接口與實現。
本章導讀:
對于初學者來說,要想實現一個溫度采集是很難的,但AMetal 可以做到。AMetal 構建了一套抽象度更高的標準化接口,封裝了各種MCU 底層的變化,為應用軟件提供了更穩定的抽象服務,延長了軟件系統的生命周期。因此無論你選擇什么MCU,只要支持AMetal,開發者無需閱讀用戶手冊,甚至不需要知道什么是AMetal,就可以高度復用原有的代碼。
盡管你已經得心應手地使用AMetal 編寫了很多的程序,但還是想深入了解更多的接口是如何實現的?那么我們不妨從這里開始AMetal 的神奇之旅!
5.1 接口與實現
>>> 5.1.1 GPIO 接口函數
AMetal 提供了操作GPIO 的標準接口函數,所有GPIO 的標準接口函數原型位于ametal_am824_core_1.00\ametal\common\interface\am_gpio.h 文件中,其中包括宏定義和提供給用戶操作GPIO 的函數原型的聲明。即:
-
配置引腳功能和模式:int am_gpio_pin_cfg(int pin, uint32_t flags)
-
獲取引腳電平:int am_gpio_get(int pin)
-
設置引腳電平:int am_gpio_set(int pin, int value)
-
翻轉引腳電平:int am_gpio_toggle(int pin)
1. 配置引腳功能和模式
其中的pin 為引腳編號,格式為PIOx_x,比如,PIO0_0 用于指定配置相應引腳。在GPIO標準接口層中,所有函數的第一個參數均為pin,用于指定具體操作的引腳,相關的宏定義在ametal_easy_arm_lpc8xx\ametal\lpc8xx\drivers\include\lpc8xx_pin.h 中定義。
flags 為配置標志,由“通用功能 | 通用模式 | 平臺功能 | 平臺模式”(‘|’就是C 語言中的按位或)組成。通用功能和模式在am_gpio.h 文件中定義,是從標準接口層抽象出來的GPIO 最通用的功能和模式,格式為AM_GPIO_*。通用功能相關宏定義與含義詳見表5.1,通用模式相關宏定義與含義詳見表5.2。
表5.1 引腳通用功能
表5.2 引腳通用模式
平臺功能和模式與具體芯片相關,會隨著芯片的不同而不同,平臺功能和模式相關的宏定義在lpc8xx_pin.h 文件中定義,芯片引腳的復用功能和一些特殊的模式都定義這個文件中,格式為PIO*_*_*,比如,PIO0_4_UART0_TX,PIO0_4 的串口0 發送。
如果需要找到PIO0_0 相關的平臺功能和平臺模式,可以打開lpc8xx_pin.h 這個文件,找到PIO0_0為前綴的宏定義,PIO0_0 相關的平臺功能詳見表5.3,平臺模式詳見表5.4。
表5.3 PIO0_0 平臺功能
表5.4 PIO0_0 平臺模式
在這里,讀者可能會問,為什么要將功能分為通用功能和平臺功能呢?各自相關的宏存放在各自的文件中,文件數目多了,會不會使用起來更加復雜呢?
通用功能定義在標準接口層中,不會隨芯片的改變而改變。而GPIO 復用功能等,會隨著芯片的不同而不同,這些功能是由具體芯片決定的,因此必須放在平臺定義的文件中。如果這部分也放到標準接口層文件中,就不能保證所有芯片標準接口的一致性,從而也就失去了標準接口的意義。這樣分開使用讓使用者更清楚,哪些代碼是全部使用標準接口層實現的,全部使用標準接口層的代碼與具體芯片是無關的,是可跨平臺復用的。
如果返回AM_OK,說明配置成功;如果返回- AM_ENOTSUP,說明配置的功能不支持,配置失敗,配置引腳為GPIO 功能詳見程序清單5.1。
程序清單5.1 配置管腳為GPIO 功能
程序清單5.2 配置引腳為AD 模擬輸入功能
配置引腳為UART 功能詳見程序清單5.3。
程序清單5.3 配置引腳為UART 功能
2. 獲取引腳電平
其中的pin 為引腳編號,格式為PIOx_x,比如PIO0_0,用于獲取引腳的電平狀態。使用范例詳見程序清單5.4。
程序清單5.4 am_gpio_get()范例程序
3. 設置引腳電平
其中的pin 為引腳編號,格式為PIOx_x。比如,PIO0_0 用于設置PIO0.0 引腳的電平。Value 為設置的引腳狀態,0-低電平,1-高電平。如果返回AM_OK,說明操作成功,使用范例詳見程序清單5.5。
程序清單5.5 am_gpio_set()范例程序
4. 翻轉引腳電平
翻轉 GPIO 引腳的輸出電平,如果GPIO 當前輸出低電平,當調用該函數后,GPIO 翻轉輸出高電平,反之則翻轉為低電平。
其中的Pin 為引腳編號,格式為PIOx_x。比如,PIO0_0 用于翻轉PIO0.0 引腳的電平狀態。如果返回AM_OK,說明操作成功,使用范例詳見程序清單5.6。
程序清單5.6 am_gpio_toggle()范例程序
5. 范例
顯然,控制LED0 點亮或熄滅是通過GPIO 輸出0 或1 實現的,因此需要先調用am_gpio_pin_cfg()函數將GPIO 配置為輸出模式,并初始化為高電平,確保初始時LED0 處于確定的熄滅狀態,接著調用am_gpio_set()函數,使PIO0_20 輸出低電平點亮LED0,其相應的代碼詳見程序清單5.7。
程序清單5.7 點亮LED 范例程序
LED 不停地閃爍就是讓一個I/O 不斷翻轉的過程,詳見程序清單5.8。
程序清單5.8 單個LED 閃爍范例程序(1)
AMetal 針對I/O 提供了引腳電平翻轉函數am_gpio_toggle(),詳見程序清單5.9。
程序清單5.9 單個LED 閃爍范例程序(2)
假如使蜂鳴器發出1KHz 頻率的聲音,1KHz 頻率對應的周期為:T=1/1000(s)=1(ms),由于一個周期是低電平(接通)時間和高電平(斷開)時間的總和,因此在一個周期內,高、低電平保持的的時間分別為500us。由此可見,要使蜂鳴器不間斷地發聲,只要以500us 的時間間隔不斷的翻轉引腳的輸出電平即可,詳見程序清單5.10。
程序清單5.10 蜂鳴器發聲范例程序
>>> 5.1.2 LED 接口與實現
下面將使用這些程序設計的概念實現通用函數接口,比如,家用電器的某個動作完成時,或工業現場數據采集的上下限報警,都會通過LED 提醒操作者。顯然LED 驅動軟件應用非常廣泛,完全有必要編寫一個通用函數作為接口以便復用。編寫通用函數應該建立一個“.h”文件和一個“.c”文件,.h 文件用于提供接口,告知調用者提供了哪些接口,.c 文件用于實現各個接口函數,所以需要建立一個led.c 文件和led.h 文件,并將.c 文件添加到工程中。
顯然LED 只有點亮、熄滅和翻轉3 種操作,如果我們不在乎抽象性的話,則可以直接調用AMetal 函數實現。抽象的方法在操作LED 的實現代碼和使用操作LED 的代碼之間添加一個函數層,創建一個定義明確的接口,正確的抽象性是將對象的實現和它的接口分離。即將操作LED 的方法“聲明”函數原型如下:
其中的led_id 對應的LED 編號,為了方便調用者以后不用再查看原理圖,則將LED 與GPIO 的對應關系定義在一個數組中,其相應的代碼詳見程序清單5.11。
程序清單5.11 定義LED 對應的GPIO 口
那么調用者只要將索引號傳入數組即可。由于I/O 口的數量只有8 個,則led_id 的有效值是0 ~ 7,所以需要判定led_id 是否合法防止數組越界,其相應的代碼詳見程序清單5.12。
程序清單5.12 通用接口函數(led.c)的實現(1)
編程到這里貌似已經很完善了,但LED 還是不能工作,因為還沒有將GPIO 配置為輸出模式,其相應的代碼詳見程序清單5.13。
程序清單5.13 添加初始化函數
這里并沒有簡單地將GPIO 初始化為輸出,而是在配置為輸出模式的同時,初始化GPIO為高電平,以保證LED 處于熄滅狀態。此時編程完畢,則將相關的函數接口聲明封裝到led.h文件中,詳見程序清單5.14。當后續需要調用時,只需要 #include "led.h"就可以了。
程序清單5.14 在led.h 中添加函數聲明
在實際的使用中,接口函數都應添加詳細的描述,告訴調用者應該如何調用這些接口。為了方便調用,可以在led.h 中將LED 的編號與實際數組中的索引號的對應關系使用宏定義出來。那么在調用LED 接口函數時,就不再需要關心led_id 的具體數值,直接使用宏即可,其相應的代碼詳見程序清單5.15。
程序清單5.15 LED_ID 的定義
此時,如果要點亮LED0,則調用led_on(LED0)即可。這個接口是否已經做到很通用了呢?雖然LED 對應的GPIO 信息中包含了I/O 信息,卻沒有包括對應的電平信息。如果僅僅看數組,而不看硬件原理圖,還是不知道點亮或熄滅LED 究竟是高電平還是低電平?
由于LED 對應的管腳信息和相應的電平信息分別屬于不同的數據類型,顯然只有使用結構體,才能將不同類型的數據放在一起作為一個整體來對待。同時注意在聲明結構體時給出typedef 定義,且在定義的類型名稱后面追加標簽,比如,led_info,其相應的數據結構詳見程序清單5.16(3)~ (8)通用接口函數的實現。
程序清單5.16 通用接口函數(led.c)的實現(2)
如果我們需要改變處理數據的方法,則只需要在一個地方進行修改就可以了,而不必改動程序中所有直接訪問數據的地方。正確的封裝機制,不僅鼓勵而且強迫隱藏實現細節。它使你的代碼更可靠,而且更容易維護。文件led.h 僅包含了相應的接口函數的聲明,而在led.c中對它們進行定義,實際上用戶是看不到led.c 的。
在實際的應用中,用戶使用LED 有兩種情況,絕大部分情況都是使用AM824-Core 板載的兩個LED,但在流水燈實驗中,使用的是MiniPort-LED 上的8 個LED,它們對應的引腳是不同的,基于此,可以在led.h 文件中定義一個宏USE_MINIPORT_LED,默認值為0,使用板載LED,為1 時使用MiniPort-LED。引腳信息數組g_led_info 的定義修改如下:
顯然,根據抽象定義的接口操作對象,將極大地減少了子系統實現之間的相互依賴關系,也產生了可復用的程序設計的原則:只針對接口編程而不是針對實現編程。因為針對接口編程的組件不需要知道對象的具體類型和實現,只需要知道抽象類定義了哪些接口,從而減少了實現上的依賴關系。
實際上,這些接口并不妨礙你將一個對象和其它對象一起使用,因而對象只能通過接口來訪問,所以并不會破壞封裝性。
>>> 5.1.3 I/O 接口與中斷
GPIO 觸發部分主要是使GPIO 工作在中斷狀態的相關操作接口,詳見表5.5。
表5.5 GPIO 觸發相關接口函數
1. 配置引腳觸發條件函數
配置引腳觸發函數原型如下:
其中的pin 為引腳編號,格式為PIOx_x,比如PIO0_0,配置相應引腳的觸發條件。Flag為觸發條件,所有可選的觸發條件詳見表5.6。
表5.6 GPIO 觸發條件配置宏
注意,這些觸發條件并不一定每個GPIO 口都支持,當配置觸發條件時,應檢測返回值,確保相應引腳支持所配置的觸發條件。細心的人可能會發現,這里的參數flag 為單數形式,而am_gpio_pin_cfg()函數的參數flag 為復數形式。當參數為單數形式時,則表明只能從可選宏中選擇一個具體的宏值作為實參;當參數為復數形式時,則表明可以選多個宏值的或值(C 語言中的“|”運算符)作為實參。
如果返回AM_OK,說明配置成功,如果返回-AM_ENOTSUP,說明引腳不支持該觸發條件,配置失敗,使用范例詳見程序清單5.17。
程序清單5.17 am_gpio_trigger_cfg ()范例程序
2. 連接引腳觸發回調函數
連接一個回調函數到觸發引腳,當相應引腳觸發事件產生時,則會調用本函數連接的回調函數。其函數原型為:
其中的pin 為引腳編號,格式為PIOx_x,比如PIO0_0,將函數與相應引腳關聯。pfn_callback 為回調函數,類型為am_pfnvoid_t (void (*)(void *) ),即無返回值,參數為void*型的函數。p_arg 為回調函數的參數為void *型,這個參數就是當回調函數調用時,傳遞給回調函數的參數。如果返回AM_OK,說明連接成功,使用范例詳見程序清單5.18。
程序清單5.18 am_gpio_trigger_connect()范例程序
3. 斷開引腳觸發回調函數
與am_gpio_trigger_connect()函數的功能相反,當不需要使用一個引腳中斷時,應該斷開引腳與回調函數的連接;或者當需要將一個引腳的回調函數重新連接到另外一個函數時,應該先斷開當前連接的回調函數,再重新連接到新的回調函數。其函數原型為:
其中的Pin 為引腳編號,格式為PIOx_x,比如PIO0_0,斷開相應引腳的連接函數;pfn_callback 為回調函數,應該與連接函數對應的回調函數一致;p_arg 為回調函數的參數為void *型,應該與連接函數對應的回調函數參數一致。如果返回AM_OK,說明斷開連接成功,使用范例詳見程序清單5.19。
程序清單5.19 am_gpio_trigger_disconnect()范例程序
4. 打開引腳觸發
打開引腳觸發,只有打開引腳觸發后,引腳觸發才開始工作。在打開引腳觸發之前,應該確保正確連接了回調函數并設置了相應的觸發條件。其函數原型為:
其中的pin 為引腳編號,格式為PIOx_x,比如PIO0_0,打開相應引腳的觸發。如果返回AM_OK,說明打開成功,使用范例詳見程序清單5.20。
程序清單5.20 am_gpio_trigger_on ()范例程序
5. 關閉引腳觸發
關閉后,引腳觸發將停止工作,即相應觸發條件滿足后,不會調用引腳相應的回調函數。如需引腳觸發繼續工作,可以使用am_gpio_trigger_on()重新打開引腳觸發。其函數原型為:
其中的pin 為引腳編號,格式為PIOx_x,比如,PIO0_0,關閉相應引腳的觸發。如果返回AM_OK,說明關閉引腳觸發成功,使用范例詳見程序清單5.21。
程序清單5.21 am_gpio_trigger_off()范例程序
原文標題:周立功:深入淺出AMetal——接口與實現
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論