嵌入式架構到底有多重要?看完驚呆了

嵌入式架構有多重要?

要做到嵌入式應用的代碼邏輯清晰,且避免重複的造輪子,沒有好的應用架構怎麼行?

如果沒有好的架構,移植將會是一件很痛苦的事情。

如果沒有好的架構,複用是最大的難題,沒法更大限度的複用原有的代碼。

接下來嵌入式ARM便和大家分享一下,

嵌入式架構那些事兒……

01

嵌入式系統的基本架構

 

嵌入式系統一般由軟件和硬件兩個部分組成,基中嵌入式處理器、存儲器和外部設備構成整個系統的硬件基礎。嵌入式系統的軟件部分可以分爲三個層次,分別是系統軟件、支撐軟件和應用軟件,其中系統軟件和支撐軟件是基礎,應用軟件是最能體現整個嵌入式系統的特點和功能部分。

 

硬件架構

 

嵌入式系統的核心部件是各種類型的嵌入式處理器

 

(1)嵌入式微處理器:在功能上跟普通微處理器基本一致,但是它具有體積小、功耗低、成本低及可靠性高的優點。

 

(2)嵌入式微控制器:雙稱單片機,一般以某一種微處理器內核爲核心,整個計算機系統都集成到一塊芯片中,與嵌入式微處理器相比,最大特點是單片化。

 

(3)嵌入式數字信號處理器:一種專門用於信號處理的處理器,DSP(Digital Signal Processor)是芯片內部採用程序和數據分開的結構,具有專門的硬件乘法器,廣泛採用流水線操作,提供特殊的DSP指令。

 

(4)嵌入式片上系統:一種在一塊芯片上集成很多功能模塊的複雜系統,在大量生產時,生產成本也遠遠低於單片部件組成的電路板系統。

 

 

軟件架構

 


大多數人蔘與的是嵌入式軟件設計,更多的是接觸的是上層軟件系統部分,可以分爲兩大類型嵌入式軟件應用工程師以及嵌入式驅動工程師。

 


前者主要負責 linux APP 設計,負責應用層業務開發,主要具備如下幾個專業技能:

 

1.熟悉網絡編程、TCP/IP協議、IIC、SPI協議
2.熟悉多線程管理、進程間通信、文件IO操作
3.瞭解基本的shell編程
4.熟悉數據庫操作
5.瞭解QT或者Android

後者負責驅動開發,更加涉及底層。

 

1.熟悉uboot和Linux內核,完成Linux內核裁剪定製以及系統的固件更新
2.熟悉Linux驅動模型
3.熟悉ARM架構
4.熟悉基本的電路原理

 

02
嵌入式程序設計思路

 

現在的小朋友都愛玩搭積木的遊戲,一個模塊一個模塊的拼裝起來,快速組成各種不同的模型。現在的產品設計也很少從零開始。大都複用現有成熟的模塊,專注於某個擅長領域。

 

我的嵌入式應用架構思路來源與此,即功能模塊設計與分層。

 

把API分爲驅動層和應用層API,而不是所有程序都調用驅動層API。(整個應用中都調用驅動層API會導致應用中驅動調用隨處可見,無法移植和最大限度的複用)

 

先把一個應用進行功能模塊劃分,並對整體結構進行分層,然後設計出功能獨立的各個模塊(如算法模塊,文件庫模塊,通信庫模塊),在模塊之上開放公共接口。

 

驅動層提供出公共接口供上層調用。各個功能模塊可以獨立編譯(如算法模塊純ANSI C,可在任意平臺複用),或者調用驅動層接口(文件庫模塊調用了驅動讀寫Flash),總而言之,言而總之,封裝出各個功能獨立的可複用的功能模塊。

 

總體分:硬件驅動層-->功能模塊層-->應用接口層-->業務邏輯層-->應用層

 

總體結構示意框圖:

 

 

應用層,爲程序的總體的運行框架,組織調用業務邏輯。可以用某種嵌入式操作系統實現幾種任務 。如定時任務,卡處理任務,菜單任務,通信任務。

 

業務邏輯層,如CPU卡處理,交通部卡處理,銀聯卡處理,M1卡處理,通信記錄上傳,黑名單下載,票價參數下載等。

 

應用接口層,提供公共的api接口供應用接口供上層調用。這些接口也可由下層的功能模塊開放出來,應用接口層負責彙總。

 

功能模塊層,可以封裝不同的功能模塊。如算法庫,文件庫,通信庫,銀聯庫,向上提供應用接口層的接口,向下調用驅動接口。

 

硬件驅動層,由各個驅動模塊組成,向上提供統一的接口。

 

遵循一些約定:

 

1.每個模塊提供出的接口要統一,後續只能增,不能改原來的接口。

2.模塊與模塊之間相互獨立,互不影響,不能相互調用,只能調用它下層的接口。

3.由模塊構成層,層與層之間不能跨級調用。如在應用層中不能看到直接調用驅動層的代碼。

4.模塊中又可以繼續分層,如接口層,驅動層,硬件層。

 

如果驅動變動了,或者換不同平臺,只需更改驅動層,應用層不受影響。
如果功能模塊變動了,只需升級功能功能模塊,其他的模塊不受影響,應用層也不受影響。

 

按照這種邏輯設計好之後,主要的工作就是在業務邏輯層。應用層則爲程序的總體流程和框架,主要調用業務邏輯層實現不同的功能。

 

我們現在的代碼結構,基本是按這個思路來的。

 

硬件驅動層-->功能模塊層-->應用接口層-->業務邏輯層-->應用層。

 

看看以下兩種風格的代碼,你更喜歡哪個。

 

 

另一種風格:

 

 

同樣是保存參數,非要拆成 AlgCRC16  ,WritePraFlash( (unsigned char *)&NetPra , NETPRA_ADDR , sizeof(_NetPra) )兩步嗎?

 

還有AH_Para_Verify這個,在應用層中真是多餘啊,檢測失敗又從Flash讀取。關於參數,一開機就應該檢測合法性了。

 

 

既然都是要保存參數,就應該做個封裝,如上圖所示,把系統用到的不同參數做個規劃。應用層調用APP_Open_UseFile 或者APP_Read_UseFile,

而不是直接的去讀寫Flash。

 

來看看赫赫有名的谷歌的android架構,雖然很複雜,但從框圖上看,也像是搭積木,各個功能模塊獨立,層次分明。最低層建立在linux Kernel基礎上,然後是各個組件庫libraries,再往上是應用框架和應用。

 

 

以NC_FileLib,文件庫模塊爲例,如果要用在其他平臺,如EH0918手持機設備,只需要移植幾個硬件層接口即可。

休息一下,推薦一篇文章《怎樣混好嵌入式/MCU/ARM/DSP這一行?

 

03
一個引以爲戒的實例


一、錯誤的示範

 

近公司新招了一個做嵌入式軟件開發的同事,該同事是從上海的某一個上市公司出來的,因爲我們這邊人手不夠,因此把他安排了去負責一個新產品的研發,前期讓他負責加速度計、NB-IOT、舵機、外置Flash的功能測試,測試完成之後,準備讓他做一個該產品的概要設計。然後他花了2個星期的時間,給我們寫出來一個概要設計,說實話,我看到這個概要設計,我就覺得是剛畢業的大學生寫的。

版本一的架構設計

 

2.1系統體系結構
系統分爲兩層:硬件驅動層、應用層。
2.1.1硬件驅動層
硬件驅動層包含板載硬件資源正常運行所需的所有驅動程序。
1)MCU初始化
2)I2C數據存取
3)SPI數據讀取
4)加速度計初始化
5)藍牙模塊啓動
6)BC95模塊啓動
7)485通訊模塊啓動
2.2.2應用層
1)Mcu運行模式切換
2)震動及傾斜
3)數據解析
4)開/關鎖
5)數據發送
6)歷史數據保存

 

看到版本一的架構設計之後,說實話,我還是第一次見到這樣來寫架構設計的,居然是以序號來寫的,這個讓別人讀起來,特別的彆扭。  

版本二的架構設計

 

 

看到版本二的架構設計之後,雖然頗感欣慰,但是想到達到我們所要求的,還要很大的一段距離,該架構設計,主要有以下幾點問題:

1.對架構的理解還不是很清晰,既然是做架構設計,那就應該從整體來看,而不是僅僅只是侷限於一個模塊,或者功能裏面。

2.還是每個層次的理解也還不是很清晰,比如講MCU的初始化,歸於硬件驅動層裏面。MCU的初始化,嚴格意義上來說,是屬於流程的一部分了,而不是驅動。比如電腦的開啓啓動,把這個歸於硬件的驅動裏面,肯定是屬於牛頭不對馬嘴的。

3.還有就是各個模塊的啓動,也是不能屬於硬件驅動層的,也都是業務流程的一部分了,都不應該屬於驅動層的一部分

4.還有就是總線數據的讀寫,雖然驅動的作用也就是讀寫,但是數據總線的讀寫不能寫成硬件驅動

5.應用層的系統參數初始化,也還是屬於流程

6.數據的解析和數據的發生,都是屬於通信功能裏面的,不應該單獨獨立出來,屬於單個的應用。

 

二、更改版基本框架圖

 

(1)架構設計的目的

1.應用的代碼邏輯清晰,且避免重複造輪子。
2.如果沒有好的架構,移植將會是一件很痛苦的事情,因此一個好的架構設計,方便軟件的移植。
3.最大限度地複用。
4.高內聚低耦合。 

(2)設計思路

如何把硬件的驅動和一個功能封裝成一個個的模塊,然後可以像小朋友搭積木一個,一個個模塊可以快速的拼接起來,組成一個個不同的模型。

我們的嵌入式架構思路也是來源於此,即功能模塊化設計、分層設計。

這個設計和WEB開發的MVC模式類似,都是注重分層設計。

模塊化設計:將收集到的需求,進行歸類,總結和分析,將這些需求概括爲一個個單獨的功能,每一個功能,做成一個單獨的功能模塊。

分層設計一句話不好直接表達,其主要體現在一下幾方面:

1.功能模塊對外調用的模塊封裝成一個個API,將底層驅動做個API以供功能模塊調用。(各個功能模塊可以獨立編譯(如通信模塊純ANSI C,可在任意平臺複用),或者調用驅動層接口(日誌庫模塊調用了驅動讀寫Flash),總而言之,言而總之,封裝出各個功能獨立的可複用的功能模塊。)
2.API分爲驅動層API和應用層API,而不是所有程序都調用驅動層API。(整個應用中都調用驅動層API會導致應用中驅動調用隨處可見,無法移植和最大限度的複用)

總體分 硬件驅動層-->功能模塊層-->業務邏輯層-->應用層

總體結構示意框圖:

 

 

說明:

1.層與層之間不能跨層調用。
2.模塊與模塊各自獨立,無依賴關係。
3.模塊提供統一的接口供上層調用,模塊的內外接口分明。
4.模塊的功能只能增,不能改。
5.各個功能模塊層也還可以進行繼續分層,比如接口層、驅動層、硬件層。

(3)模塊層次說明

 

硬件驅動層

硬件驅動層包含板載硬件資源正常運行所需的所有驅動程序並提供API給功能模塊調用。

 

功能模塊層

功能模塊層包括實現具體功能的函數,通過調用驅動層API實現相應功能,同時提供可調用的API給業務邏輯層。

 

業務邏輯層

業務邏輯層包括產品整體功能的各個業務流程,通過調用功能模塊層的API實現。

 

應用層

應用層將各個業務邏輯進行整合調用,完成整個產品的功能。

(4)優勢

如果驅動變動了,或者換不同平臺,只需更改驅動層,應用層不受影響。

如果功能模塊變動了,只需升級相應的功能模塊,其他的模塊不受影響,應用層也不受影響。

按照這種邏輯設計好之後,主要的工作就是在業務邏輯層。應用層則爲程序的總體流程和框架,主要調用業務邏輯層實現不同的功能。

 

04
給嵌入式代碼也來個分層

 

一、遇到的問題


代碼結構也會有缺陷:


(1)開發效率低:每次使用片內的某一資源(例如定時器等),筆者都要去查詢CC2430中文手冊,比較eggache~

(2)代碼重複較多:每個實驗源碼中,諸如 xtal_init ,led_init 等初始化函數每次都要編寫

(3)不易修改:代碼中的業務邏輯與SFR的操作混在一起,可讀性較差,修改起來也費力


正是由於以上問題,筆者決定暫停了該系列博文的續寫,抽出時間來思考一下解決辦法。
 

二、由網站分層引起的思考


筆者在學習嵌入式編程之前,曾有過 ASP.NET 網站開發經驗,對其分層理論也有所實踐,下面簡單提一下:


一般的有一定複雜度的網站可分爲以下三層:
 

(1)數據接入層(DAL):負責與數據庫的交互,供業務邏輯層調用

(2)業務邏輯層(BLL):調用數據接入層以獲取數據,併爲具體的業務需求提供支持

(3)用戶界面層(UIL):負責呈現最終的用戶界面


總之,分層以後,大大提高了代碼的複用性與擴展性。


那麼在嵌入式開發中,能否也利用分層的思想,來提高開發效率,增強其可維護性與可擴展性呢?下面,是一些筆者思考後的淺見。

 

三、嵌入式項目也來分個層


當然不能照搬ASP.NET 的具體分層思想,具體問題得具體分析嘛~


首先,嵌入式開發的核心就是芯片,它提供固定的片內資源共開發者使用。而且它具有一個很重要的特點就是,不隨項目的需求變動而變動。所以應將其作爲最底層,爲上層提供基礎支持。我們將其命名爲 硬件抽象層(Hardware Abstract Layer)。  


芯片有了當然還不夠,通常我們會在片外擴展一些功能模塊來滿足具體的項目需求,例如:傳感器、鍵盤、LCD屏等。這一層的特點是,隨項目的變動而以模塊爲單位動態增減。這一層的運作需要芯片內部資源的支持,所以應處於硬件抽象層之上,併爲上層調用。我們將其命名爲 功能模塊層(Functional Module Layer)。

  
OK,現在原材料都準備齊了:芯片+擴展模塊,接下來就要開始真正的加工了:我們需要靈活調用之前兩層所提供的接口,實現具體的項目需求。我們將其命名爲應用程序層(Application Layer)。

  
圖文:


(1)硬件抽象層(HAL)

  
實現對片內資源 (如定時器、ADC、中斷、I/O等) 的通用配置,隱藏具體的SFR操作細節,爲上層提供簡單清晰的調用接口。


嵌入式開發的核心就是芯片,它提供固定的片內資源(常用的有I/O,ISR,TIMER等,稍微好點的還有ADC,SPI等硬件資源,不需要芯片外圍ADC採集芯片或模擬SPI)共開發者使用。而且它具有一個很重要的特點就是,不隨項目的新增需求變動而變動。所以應將其作爲最底層,爲上層提供基礎支持。

(2)硬件驅動層(HDL)     

嵌入式開發基本都會使用片外資源,如AT24C02,W25Q128等常見的外圍EEPROM芯片,需要SPI通信(硬件SPI或I/O模擬的SPI)發送相應指令驅動該芯片,實現該芯片能正常工作。因此驅動這部分的API函數實現程序即爲硬件驅動層。即使換了MCU,也只需將調用過硬件抽象層的API函數替換即可。

(3)功能模塊層(FML)

  
通過調用 HAL,實現項目中所涉及到的各片外功能模塊,隱藏具體的模塊操作細節,併爲上層提供簡單清晰的調用接口。


硬件抽象層和驅動層主要就是爲功能模塊層提供的,實現該項目需要的功能,比如KEY、LED和EEPROM等功能,其中LEY、LED基本調用硬件抽象層的API函數(更復雜的可能通過片外芯片獲取/控制等,因此可能也需要使用硬件驅動層),EEPROM調用硬件驅動層的API函數,即使EEPROM芯片更換(AT24C02或W25Q128等),也不影響EEPROM之前編寫含的功能代碼程序(前提是AT24C02,W25Q128提供的API函數提供的是統一標準)。

(4)應用程序層(APL)

  
通過調用 HAL 與 FML,實現最終的應用功能。

負責的就是功能模塊的使用和之間的邏輯關係處理等等,比如用戶交互界面應用程序可能需要KEY、LED、LCD等。

四、硬件抽象層和硬件驅動層的主要區別

硬件抽象層使用的芯片內本身的資源(芯片手冊都有介紹),而硬件驅動層使用的是芯片本身不存在的資源,而且需要編寫相應代碼才能實現的資源。

比如正點原子STM32中CAN使用的TJA1050芯片,CAN屬於STM32的片內資源,TJA1050屬於片外資源,但由於TJA1050不需要額外的代碼就能通過STM32中CAN本身提供API函數正常 工作;因此可以認爲TJA1050不屬於硬件驅動層,而若使用TJA1041,則需要編寫額外代碼才能使正常工作才能使STM32中CAN本身提供API函數正常工作,因此可以將TJA1041歸爲硬件驅動層。

若不要分這麼細,可以將硬件抽象層和硬件驅動層統一歸爲硬件抽象層。
         
五、功能模塊層和硬件抽象層、硬件驅動層的主要區別

功能模塊層是按照項目需求提取出來的功能,需要硬件抽象層和硬件驅動層的硬件支持才能實現,功能模塊層根據項目的功能需求改變而改變,而硬件抽象層和硬件驅動層則是項目需求書中的功耗等硬件相關的需求變動而改變,當然,若子功能的增加而硬件不支持,則也需更換硬件驅動。

比如項目中的數據儲存功能,硬件支持有AT24C02、W25Q128和芯片本身的FLASH,都可以支持數據儲存功能,即使後期因爲功耗或節約成本等問題,硬件的更換也不影響數據儲存功能的實現(前提規劃好標準規範的API函數定義)且避免了重寫該功能代碼所帶來的各種問題,保證了該功能的穩定性。
 

分層結構示意圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章