周立功教授新書《面向AMetal框架與接口的編程(上)》,對AMetal框架進行了詳細介紹,通過閱讀這本書,你可以學到高度復用的軟件設(shè)計原則和面向接口編程的開發(fā)思想,聚焦自己的“核心域”,改變自己的編程思維,實現(xiàn)企業(yè)和個人的共同進步。經(jīng)周立功教授授權(quán),即日起,致遠電子公眾號將對該書內(nèi)容進行連載,愿共勉之。
第四章為面向接口的編程,本文內(nèi)容包括:4.1 平臺技術(shù)、4.2 開關(guān)量信號。
本章導讀:
在結(jié)構(gòu)化程序設(shè)計中,由于高層模塊依賴底層模塊,通常一個變化會引出另外的問題發(fā)生改變,則變化的代價就會急劇上升。所以在引入接口時,一個重要的經(jīng)濟考量是軟件的不可預測性,因為需求和技術(shù)都在以不可預測的方式變化,其目的就是為了降低依賴。
4.1 平臺技術(shù)
>>> 4.1.1 創(chuàng)新的窘境
雖然嵌入式系統(tǒng)和通用計算機系統(tǒng)同源,由于應用領(lǐng)域和研發(fā)人員的不同,嵌入式系統(tǒng)很早就走向了相對獨立的發(fā)展道路。通用計算機軟件幫助人們解決了各種繁雜的問題,隨著需求的提升,所面臨的問題越來越復雜,軟件領(lǐng)域的大師們對這些問題進行了深入研究和實踐,于是誕生了科學的軟件工程理論,無需多言通用計算機軟件的發(fā)展是我們有目共睹的。
再回過頭來看嵌入式系統(tǒng)的發(fā)展,其需求相對來說較為簡單,比如,通過熱電阻傳感器測溫、上下限報警與繼電器的動作,因此嵌入式系統(tǒng)的應用開發(fā)似乎沒有必要使用復雜的軟件工程方法,于是通用計算機系統(tǒng)和嵌入式系統(tǒng)走上了不同的發(fā)展道路。
當嵌入式系統(tǒng)發(fā)展到今天,所面對的問題也日益變得復雜起來,而編程模式卻沒有多大的進步,這就是所面臨的困境。相信大家都或多或少地感覺到了,嵌入式系統(tǒng)行業(yè)的環(huán)境已經(jīng)開始發(fā)生了根本的改變,智能硬件和工業(yè)互聯(lián)網(wǎng)等都讓人始料不及,危機感油然而生。
盡管企業(yè)投入巨資不遺余力地組建了龐大的開發(fā)團隊,當產(chǎn)品開發(fā)完成后,從原材料BOM 與制造成本角度來看,毛利還算不錯。當扣除研發(fā)投入和合理的營銷成本后,企業(yè)的利潤所剩無幾,即便這樣員工依然還是感到不滿意,這就是傳統(tǒng)企業(yè)管理者的窘境。
雖然ZLG 投入了大量的人力資源,但重復勞動所造成的損耗以億元計。上千種MCU,由于缺乏平臺化的技術(shù),即便相同的外圍器件,幾乎都要重新編寫相應的代碼和文檔并進行測試,所有的應用軟件很難做到完美地復用。
在開發(fā)同一系列高中低三個層次的產(chǎn)品時,通常會遇到這樣的問題,主芯片可能使用ARM9、雙核A9 和DSP,其操作系統(tǒng)分別為μC/OS-II、Linux 和SysBIOS。不僅驅(qū)動代碼不兼容,而且應用層代碼也不一樣,顯然浪費在這上面的開銷是不可想象的。
>>> 4.1.2 AWorks
在MCU 產(chǎn)業(yè)快速發(fā)展的今天,不同的MCU 外設(shè)的差異千差萬別。AWorks 對同一類外設(shè)進行了抽象,并設(shè)計了相應的標準接口與對應的中間層,使得不同廠商、型號的MCU 外設(shè)都能以標準的接口操作。
對于上層操作系統(tǒng)而言,對各個外設(shè)都需要編寫驅(qū)動。在編寫特定操作系統(tǒng)下的驅(qū)動時,必須熟悉特定的驅(qū)動平臺與操作系統(tǒng)調(diào)用,以至于程序員不得不花費大量的時間學習與此相關(guān)的知識。對于同一個外設(shè)而言,如果要支持多個操作系統(tǒng),則需要編寫多個驅(qū)動,其實驅(qū)動底層對硬件的操作是有相通之處的,AWorks 的出現(xiàn)便是解決這里提到的問題。
1. AWorks 的特點
如圖4.1 所示是AWorks 的標識符,這是ZLG 經(jīng)過十多年時間積累開發(fā)的IoT 物聯(lián)網(wǎng)生態(tài)系統(tǒng),成功地應用到ZLG 的示波器、功率計、功率分析儀、電壓監(jiān)測儀、電能質(zhì)量分析儀、數(shù)據(jù)記錄儀與工業(yè)通訊等系列高性能儀器和工業(yè)IoT 產(chǎn)品中。
圖4.1 AWorks
雖然AWorks 內(nèi)嵌了操作系統(tǒng),但AWorks 中的操作系統(tǒng)如同一個驅(qū)動代碼一樣,僅僅是一個可以根據(jù)需要任意更換的組件。操作系統(tǒng)適配器直接駐留在操作系統(tǒng)接口之上,主要用于屏蔽各類操作系統(tǒng)和硬件接口的差異,從而增強了AWorks 的可移植性和可維護性,詳見圖4.2。
圖4.2 AMetal 與AWorks 的關(guān)系
AWorks 的核心是制定了統(tǒng)一的接口規(guī)范,可以“按需定制”提供各種各樣的高質(zhì)量組件,以及圖形化配置工具,則開發(fā)者無需關(guān)心與MCU、外圍器件、OS有關(guān)的技術(shù)細節(jié)。如果要對底層硬件外設(shè)進行操作,也可以直接調(diào)用底層接口。
其目的是不依賴于具體的底層硬件,實現(xiàn)“一次編程、終生使用”和“跨平臺”,通過高度復用應用軟件組件,從而將程序員從苦海中釋放出來,使之聚焦于更有競爭力的需求分析、算法和用戶體驗等業(yè)務邏輯上。行業(yè)合作伙伴可以在該平臺上開發(fā)各種應用,通過有線接入和無線接入收集、管理和處理數(shù)據(jù)。
由于AWorks 提供的所有組件均使用靜態(tài)內(nèi)存,不使用malloc()等動態(tài)內(nèi)存分配函數(shù),徹底避免了內(nèi)存泄漏。且程序編譯完成后,即可知道系統(tǒng)運行需要占用多大的內(nèi)存。AWorks是輕量級的實時系統(tǒng),所有組件對初始化都進行了優(yōu)化,系統(tǒng)能以極短的時間啟動,因此在絕大部分項目中,啟動時間<1s。顯然系統(tǒng)運行時的實時性,確定性得到了最大的保障。
2. AMetal 的特點
AMetal 是AWorks 的子集,它是以API 的形式提供的,但不依賴AWorks。即:
-
將外設(shè)操作標準化,無需再次開發(fā)上層軟件、驅(qū)動;
-
AMetal 作為AWorks 標準化的外設(shè)功能補充,可以直接調(diào)用;
-
可作為獨立發(fā)布的軟件包。
其特點為:
-
能獨立運行的AMetal,提供工程模板與demo,在此基礎(chǔ)上開發(fā)應用程序;
-
不依賴操作系統(tǒng)服務,將外設(shè)的所有特性開放出來;
-
封裝時將效率和變化部分放在第一位,用戶不看手冊也能使用。
AMetal 在嵌入式軟件開發(fā)中的位置詳見圖4.3,它位于底層硬件和上層軟件之間,可以被應用程序與操作系統(tǒng)直接調(diào)用。AMetal 包括兩部分,即AMetal API 和AMetal 組件。
圖4.3 AMetal 在嵌入式軟件開發(fā)中位置
AMetal API 重在抽象MCU 本身的功能部件,比如,GPIO、SPI、UART、I2C 等,程序員再也不用查看芯片手冊就能編寫外圍器件驅(qū)動程序。AMetal 組件重在抽象外圍擴展接口器件,比如,E2PROM、SPI NOR Flash、PCF85063 RTC、zigbee、BLE、鍵盤掃描與LED顯示器電路等,程序員再也不用看原理圖和編寫外圍器件驅(qū)動程序。
AMetal 提供的不僅僅是某個MCU 的軟件包,而是一套接口規(guī)范,因此只要遵循AMetal 標準化接口函數(shù)規(guī)約,無論所用的MCU 是ARM還是DSP 或其它的內(nèi)核,則應用軟件均可實現(xiàn)復用。
如圖4.4 所示的AMetal 框架分為硬件層、驅(qū)動層和標準接口層,上層軟件根據(jù)需求調(diào)用合適的API 接口。三層都有對應的頭文件和用戶配置文件,供用戶引用相應的接口。雖然各大半導體公司針對各自的MCU,提供了類似AMetal 這樣的裸機API,但每個新的MCU,其API 的差異很大,因此很難做到復用應用軟件。
圖4.4 AMetal 框架
這里以GPIO 為例,給出相應的的文件結(jié)構(gòu)圖,詳見圖4.5,AMetal 其相應的文件有:HW 層文件、驅(qū)動層文件和用戶配置文件。通常情況下,HW 層提供了直接操作硬件寄存器的接口,接口實現(xiàn)簡潔,往往以內(nèi)聯(lián)函數(shù)的形式放在.h 文件中,因此,HW 層通常只包含.h文件,但當某些硬件功能設(shè)置較為復雜時,也會提供對應的非內(nèi)聯(lián)函數(shù),存放在.c文件中。驅(qū)動層作為中間層,其使用HW 層接口,實現(xiàn)了標準接口層中定義的接口,以便用戶使用標準API 訪問GPIO。用戶配置文件完成了相應驅(qū)動的配置,如引腳數(shù)目等。
圖4.5 GPIO 文件結(jié)構(gòu)
標準接口層對常見外設(shè)的操作進行了抽象,提出了一套標準API 接口,可以保證在不同的硬件上,標準API 的行為都是一樣的。用戶使用一個GPIO 的過程:先調(diào)用驅(qū)動初始化函數(shù),后續(xù)在編寫應用程序時僅需直接調(diào)用標準接口函數(shù)即可。可見,應用程序基于標準API 實現(xiàn)的,標準API 與硬件平臺無關(guān),使得應用程序可以輕松的在不同的硬件平臺上運行。
4.2 開關(guān)量信號
>>> 4.2.1 I/O 輸入輸出
嵌入式系統(tǒng)的主要功能就是要實現(xiàn)對現(xiàn)實事件的監(jiān)控,如同沒有配置顯示器、打印機或鍵盤的臺式計算機一樣,嵌入式系統(tǒng)必須具備輸入輸出(I/O)數(shù)字信號和模擬信號的能力。LPC824 系列MCU 可用的GPIO 數(shù)目,具體取決于芯片的封裝類型,詳見表4.1。
表4.1 GPIO 引腳
其特性為:
(1)每個GPIO 引腳均可通過軟件配置為輸入或輸出;
(2)復位時所有GPIO引腳默認為輸入;
(3)引腳中斷寄存器允許單獨設(shè)置;
(4)可以獨立配置每個引腳的置高和置低。
當LPC824 系列MCU 的I/O 口作為數(shù)字功能時,可配置為上拉/下拉、開漏和遲滯模式。在輸出模式下,無論配置為哪種模式,I/O 口都可輸出高/低電平。在輸入模式下,且引腳懸空時,I/O 口設(shè)置為不同模式時,其情況為:
(1)設(shè)置為高阻模式時,讀取引腳的電平狀態(tài)不確定;
(2)設(shè)置為上拉模式時,讀取引腳的電平狀態(tài)為高電平;
(3)設(shè)置為下拉模式時,讀取引腳的電平狀態(tài)為低電平;
(4)設(shè)置為中繼模式時,如果引腳配置為輸入且不被外部驅(qū)動,那么它可以令輸入引腳保持上一種已知狀態(tài)。
在默認情況下,除I2C 總線P0_10 和P0_11 沒有可編程的上拉/下拉電阻和中繼模式外,其它所有GPIO 的上拉電阻都被使能。在默認情況下,每個GPIO 僅分配給一個且僅一個外部引腳,外部引腳隨后便由其固定引腳GPIO 功能來標識。但通過開關(guān)矩陣可將內(nèi)部功能分配給除電源和接地引腳外的任意外部引腳,這些功能稱為可轉(zhuǎn)移功能。而晶體振蕩器引腳(XTALIN/XTALOUT)或模擬比較器輸入等某些功能僅可分配給具有適當電氣特性的特定外部引腳,這些功能稱為固定引腳功能。如果某個固定引腳功能未被使用,則可將其替換為任意可轉(zhuǎn)移功能。對于固定引腳模擬功能,開關(guān)矩陣可使能模擬輸入或輸出并禁用數(shù)字端口。
>>> 4.2.2 新建工程
在編程之前,必須先建立工程,然后才能將程序下載到開發(fā)板上運行。由于AMetal 已經(jīng)提供了模板工程,所以“新建工程”只需拷貝一下即可。
模板工程就是位于projects_keil5applications 目錄下的 template 文件夾。新建工程即將該文件夾重新復制一份,命名為led_blinking。接著打開led_blinking 文件夾,將工程文件命名為led_blinking,然后雙擊led_blinking.uvprojx 工程文件打開工程,更詳細的操作詳見配套開發(fā)資料中的《快速入門手冊》。
打開工程后,雖然在工程視圖的左側(cè)有很多分組(user_config 和user_code 等),每個分組下都有相應的文件。但先不用理會,只需要關(guān)心user_code 分組下的main.c 文件,就在該文件中的am_main()函數(shù)中添加應用程序。當MCU 無事可做時,不能讓它閑下來,因為am_main()函數(shù)結(jié)束標志整個應用程序結(jié)束,從而導致MCU 跑飛。因此,am_main()函數(shù)中通常都存在一個while(1)死循環(huán)。當工程建好后,即可編程了。
>>> 4.2.3 輸出控制
如圖4.6 所示為AM824-Core 板載的2 個LED發(fā)光二極管,與MCU 的I/O 引腳通過J9 和J10 相連,其中的LED0 通過J9 與MCU 的PIO0_20 相連,LED1 通過J10 與MCU 的PIO0_21 相連。當I/O 輸出低電平0 時,由于LED 陽極加了3.3V 電壓(高電平1),因而形成了電位差,所以有電流流動,則LED 發(fā)光二極管導通,即LED 發(fā)光;當I/O 輸出高電平1 時,由于無法形成電位差,則LED二極管不導通,即LED 熄滅。
圖4.6 板載LED 電路
電阻R3、R4 的作用是防止產(chǎn)生過電流而燒壞LED,這是由電源電壓和LED 的額定電流決定的LED 電學特性而接入的,當LED 的電壓超過1.5V 時,電流將急劇增加,所以必須避免出現(xiàn)這樣的情況。在數(shù)字電路中,當輸出為高電位時,則電流流到負載上;當輸出為低電位時,則從負載一側(cè)吸入電流。前者的電流叫作源電流,后者叫作吸收電流。顯然LED只有點亮、熄滅和翻轉(zhuǎn)3 種操作,可以直接調(diào)用接口函數(shù)實現(xiàn),led.h 接口文件的內(nèi)容詳見程序清單4.1(各接口具體的實現(xiàn)在第4 章中會詳細介紹)。
程序清單4.1 led.h 接口
其中,led_id 是LED 的編號,AM824-Core 板載LED 的編號分別為0 和1。事實上程序設(shè)計的依賴倒置原則不一定要包含函數(shù)指針或抽象類型數(shù)據(jù),當調(diào)用led_on()和led_off()點亮或熄滅LED 時,就使用了依賴倒置原則。它本來可以與I/O 的內(nèi)存映射直接交互,卻將直接訪問抽象成了接口。那么針對接口的編程不妨就從點亮LED 開始,詳見程序清單4.2。
程序清單4.2 點亮LED 范例程序
LED 閃爍就是讓LED 不停的亮滅,因為計算機指令的執(zhí)行速度非常快,其執(zhí)行時間是微秒級的,所以在微秒之間點亮和熄滅LED,眼睛是看不到閃爍現(xiàn)象的。如果想讓人眼看到LED 閃爍,就必須將LED 點亮和熄滅的停頓時間擴大近秒級別。如何實現(xiàn)停頓呢?點亮LED 后,先不要熄滅LED,而是先延時一會兒,讓“點亮LED 后,再保持一段時間”,然后再熄滅LED。在實驗之前,我們需要延時函數(shù),它在C 語言中是怎么實現(xiàn)呢?很簡單,就是讓MCU 執(zhí)行一些沒有任何實際意義的空循環(huán)指令,進而等效于延時。延時范例程序詳見程序清單4.3,該程序中,MCU 大約就要執(zhí)行1000000 條空指令。
程序清單4.3 延時范例程序
這樣的延時函數(shù)好用嗎?由于MCU 運行速率不同,因而導致實際的延時結(jié)果不同。如果要求不同的延時時間,則又需要不同的延時函數(shù)。顯然,該延時函數(shù)不僅通用性太差,而且延時時間也不精確。幸運的是,AMetal 提供了使用定時器實現(xiàn)的高精度標準延時函數(shù),主要包含了2 個延時函數(shù),其函數(shù)原型(am_delay.h)如下:
按照前面的思路,在點亮燈之后,延時一段時間,讓“亮燈狀態(tài)”保持一段時間,再熄滅LED 燈,再延時一段時間,讓“熄滅狀態(tài)”保持一段時間,詳見程序清單4.4。
程序清單4.4 單個LED 閃爍范例程序(1)
其實LED 不停地閃爍就是讓一個I/O 不斷翻轉(zhuǎn)的過程,而AMetal 軟件包針對LED 提供了翻轉(zhuǎn)LED 狀態(tài)的函數(shù)led_toggle(),優(yōu)化后的代碼詳見程序清單4.5。
程序清單4.5 單個LED 閃爍范例程序(2)
MiniPort-LED 模塊集成了8 個LED,均為低電平有效,分別通過PIO0_8~PIO0_15 控制,其中的R1~R8 為LED 的限流電阻,詳見圖4.7。
圖4.7 LED 模塊電路
LED 模塊通過MiniPort B(排母)與AM824-Core相連,同時將其余不使用的I/O 通過MiniPort A(排針)引出,實現(xiàn)模塊的橫向堆疊,其對應AM824-Core 的MiniPort 接口J4 的功能定義詳見圖4.8。
圖4.8 LED 模塊實物與接口定義圖
下面將以LED 流水燈為例說明通用函數(shù)接口的應用開發(fā)方法。人們時常看到戶外動畫廣告,一會兒從左到右顯示,一會兒又從右到左顯示,這就是流水燈效果。現(xiàn)在用LED 流水燈來模擬戶外動畫廣告,使LED 順時針旋轉(zhuǎn)循環(huán)流動。假設(shè)上電初始化,將所有的GPIO 都配置為輸出,且初始化為高電平,即所有的LED 全部熄滅。首先點亮LED0,延時后熄滅LED0;接著點亮LED1,延時后熄滅LED1......;然后點亮LED7,延時后熄滅LED7;接著點亮LED0,延時后熄滅LED0……,周而復始形成了循環(huán)的圓環(huán),詳見程序清單4.6。
程序清單4.6 LED 流水燈范例程序(1)
流水燈實驗使用的是MiniPort-LED 的8 個LED,其對應的控制引腳與AM824-Core 上兩個LED 對應的控制引腳是不同的。當使用MiniPort-LED 時,需要將led.h 文件中的宏USE_MINIPORT_LED 對應的值修改為1,表明使用MiniPort-LED。該宏的默認值為0,使用AM824-Core 板載的兩個LED 燈。
顯然,LED 流水燈是一個典型的“首尾相接”算法,循環(huán)隊列與環(huán)形緩沖區(qū)等都屬于同一類問題,這是一種常用的軟件設(shè)計模式,優(yōu)化后的代碼詳見程序清單4.7。
程序清單4.7 LED 流水燈范例程序(2)
如圖4.9 所示為無源蜂鳴器電路原理圖,只要短接J7_1 與J7_2,則蜂鳴器接入PIO0_24。當PIO0_24 輸出低電平時,則三極管導通,向蜂鳴器供電;當PIO0_24 輸出高電平時,則三極管截止,停止向蜂鳴器供電。因此只需要輪流切換PIO0_24 的電平狀態(tài),就可以控制蜂鳴器的“通電”和“斷電”,即以一定的頻率翻轉(zhuǎn)PIO0_24 的輸出電平。其實接通和斷開“一段時間”的總和就是蜂鳴器的振蕩周期,再稍作轉(zhuǎn)換就能夠得到確定的音頻脈沖頻率參數(shù)。從而產(chǎn)生機械振動音,只要頻率在人耳聽覺范圍內(nèi),即可聽到蜂鳴器發(fā)聲。
圖4.9 蜂鳴器電路圖
顯然,通過翻轉(zhuǎn)引腳電平和延時,也可以讓蜂鳴器發(fā)出固定頻率的聲音。假如使蜂鳴器發(fā)出1KHz 頻率的聲音,1KHz 頻率對應的周期為:T=1/1000(s)=1(ms),由于一個周期是低電平(接通)時間和高電平(斷開)時間的總和,因此在一個周期內(nèi),高、低電平保持的的時間分別為500us。由此可見,要使蜂鳴器不間斷地發(fā)聲,只要以500us 的時間間隔不斷的翻轉(zhuǎn)引腳的輸出電平即可。當需要控制蜂鳴器時,可直接調(diào)用蜂鳴器接口,buzzer.h文件內(nèi)容詳見程序清單4.8 buzzer.h 接口。
程序清單4.8 buzzer.h 接口
buzzer_init()會將發(fā)聲頻率設(shè)置為默認值:1KHz。如需修改發(fā)聲頻率為其它值,如:2.5KHz,則應調(diào)用發(fā)聲頻率設(shè)置函數(shù),即“buzzer_freq_set(2500);”,詳見程序清單4.9。
程序清單4.9 蜂鳴器發(fā)聲范例程序
-
編程
+關(guān)注
關(guān)注
88文章
3627瀏覽量
93809
原文標題:周立功:面向接口的編程——平臺技術(shù)、開關(guān)量信號
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論