我是老溫,一名熱愛學(xué)習(xí)的嵌入式工程師,關(guān)注我,一起變得更加優(yōu)秀!
在如今的嵌入式軟硬件技術(shù)開發(fā)領(lǐng)域,幾乎每一位工程師都會(huì)大談模塊化設(shè)計(jì),硬件工程師在設(shè)計(jì)原理圖的時(shí)候,電源要模塊化,核心板要模塊化,功能電路要模塊化。軟件工程師在coding的時(shí)候,CPU初始化要模塊化,IIC代碼要模塊化,RTC代碼要模塊化,等等。
于是,當(dāng)大家對模塊化設(shè)計(jì)的概念和思想有了普遍共識后,我們在寫單片機(jī)代碼的時(shí)候,(大多數(shù)工程師極有可能)是以下面這種代碼形式去組織產(chǎn)品的功能模塊代碼。
void main(void)
{
gpio_init(); //初始化GPIO
iic_init(); //初始化IIC總線
rtc_init(); //初始化RTC
while(1)
{
function_code_1(); //功能代碼1
function_code_2(); //功能代碼2
????????function_code_3(); //功能代碼3
}
}
這看上去似乎是非常合情合理合邏輯的,先把需要用到的硬件接口都初始化完成,然后在 while(1) 循環(huán)里面不斷地輪詢,處理我們的業(yè)務(wù)邏輯。
關(guān)于源代碼文件的存放,可能是 iic.c 和 iic.h ,gpio.c 和 gpio.h 都放在MCU的外設(shè)驅(qū)動(dòng)文件夾,功能模塊源代碼放在另一個(gè)文件夾。
(幾乎所有的單片機(jī)入門教程都是這么教我們的,外設(shè)驅(qū)動(dòng)放一塊,業(yè)務(wù)邏輯放一塊,這看上去很模塊化呀,沒啥毛病~)
然而,當(dāng)我們對這個(gè)世界的客觀事物理解越來越深刻的時(shí)候,才發(fā)現(xiàn)這個(gè)世界的大多數(shù)事物,都是以“模塊”的形式存在著。
我是一個(gè)模塊(職場嗎嘍),給公司當(dāng)牛馬;公司是一個(gè)模塊,給行業(yè)提供解決方案或產(chǎn)品;行業(yè)是一個(gè)模塊,給產(chǎn)業(yè)鏈提供完善的行業(yè)支持;產(chǎn)業(yè)鏈?zhǔn)且粋€(gè)模塊,給社會(huì)主義經(jīng)濟(jì)建設(shè)提供可靠的資源支撐;社會(huì)主義是。。。扯遠(yuǎn)了:)
于是,我們上面的單片機(jī)偽代碼,比如它是一個(gè)給廣州塔按時(shí)點(diǎn)亮流水燈的控制器產(chǎn)品,用模塊化的思想就可以優(yōu)化為以下的形式;
void main(void)
{
????light_module_init();????//燈光模塊,里面包含GPIO接口初始化
????storage_module_init();??//存儲模塊,里面包含IIC初始化,存儲設(shè)備初始化
????clock_module_init();????//時(shí)鐘模塊,里面包含RTC額初始化。
while(1)
{
????????light_module.handler();??//處理燈光模塊的邏輯,比如定時(shí)點(diǎn)燈
????????storage_module.handler();??//處理存儲器邏輯,比如定時(shí)保存配置數(shù)據(jù)
????????clock_module.handler();????//處理時(shí)鐘或鬧鐘,比如發(fā)出定時(shí)或計(jì)時(shí)信號
}
}
從以上代碼可以看出,我們把MCU外設(shè)驅(qū)動(dòng)的初始化,放在模塊的初始化函數(shù)里面進(jìn)行處理了,主循環(huán)while(1)里面也是調(diào)用了模塊提供的事務(wù)處理函數(shù)。各個(gè)模塊分工明確,只掃自家門前雪,不管他人瓦上霜。
模塊之間可以通過約定的接口進(jìn)行數(shù)據(jù)的交互和通信,并且嚴(yán)格禁止跨模塊使用全局變量,模塊的接口頭文件禁止放入 “extern xxx變量” 的代碼,一旦放入這種代碼,大概率是會(huì)被下一位接坑者“友好問候”的?。
模塊化之后,事情一下子變得簡單起來了,當(dāng)我們發(fā)現(xiàn)塔上的燈不亮了,就去檢查一下 light_module 和電源總閘唄,當(dāng)我們發(fā)現(xiàn)定時(shí)時(shí)間不準(zhǔn)了,就去檢查一下時(shí)鐘模塊唄。懷疑硬件驅(qū)動(dòng)有問題的,就去看看 xxx_module_init 的代碼,懷疑業(yè)務(wù)邏輯的,就去看看 xxx_module_handler 代碼。
模塊化設(shè)計(jì)是嵌入式系統(tǒng)設(shè)計(jì)中的一種重要方法論,但通常這種方法論描述得過于抽象,難以理解,以下是模塊化設(shè)計(jì)的核心哲學(xué)思想,我們可以嘗試通俗地理解這些思想背后的目標(biāo)。
1、高內(nèi)聚、低耦合、接口明確
模塊內(nèi)部的各個(gè)變量各個(gè)函數(shù),都給咱統(tǒng)一管理起來,不對外的函數(shù)要static,不想讓模塊使用者知道的內(nèi)容,就不要放在接口頭文件,對外接口要規(guī)范,提供明確的接口使用指導(dǎo)。
2、可重用性、可擴(kuò)展性、可維護(hù)性
模塊源碼被使用時(shí),可以輕松復(fù)制粘貼,模塊使用者只需遵循接口規(guī)范,通過正確的輸入即可獲得正確的輸出。模塊作者給模塊擴(kuò)展功能時(shí),不需要重寫所有代碼。模塊設(shè)計(jì)者在維護(hù)模塊某個(gè)功能時(shí),只需專注某個(gè)局部,而不是模塊全局。
3、單一職責(zé)原則
一個(gè)模塊只負(fù)責(zé)一項(xiàng)任務(wù)(或者一項(xiàng)功能),其他不屬于自己分內(nèi)的事情,就別管了(也管不了),各家只掃門前雪就行了!
4、分層架構(gòu)、配置管理
模塊化的內(nèi)在設(shè)計(jì)可以采用分層架構(gòu),每一層負(fù)責(zé)處理不同的抽象級別。也就是說,模塊內(nèi)部之間也要有層次感,模塊內(nèi)部也不是一鍋亂燉的東西,最起碼的層次階級還是要有的,并且還要提供適當(dāng)?shù)呐渲梦募ɑ驍?shù)據(jù)結(jié)構(gòu))來控制模塊的功能與行為。
5、測試與驗(yàn)證
哪里出問題了,直接找出問題的模塊就行,電源出問題了,一開始該不會(huì)去懷疑網(wǎng)絡(luò)通信不了而導(dǎo)致電源出問題吧?!模塊化設(shè)計(jì)的軟件,使得對單個(gè)模塊進(jìn)行測試驗(yàn)證或者找問題變得更加容易,有助于提高軟件整體質(zhì)量。
通過遵循這些設(shè)計(jì)哲學(xué),嵌入式軟件的開發(fā)可以更加高效,最后,需要補(bǔ)充的一點(diǎn)就是,如果產(chǎn)品的功能邏輯肉眼可見的簡單,就不用扯啥模塊化了,用最直接簡單粗暴的方式來操作硬件,完成功能開發(fā),怎么方便怎么來,別整啥模塊化給自己制造麻煩了。
模塊化思想,是適用于對中大型的產(chǎn)品業(yè)務(wù)軟件進(jìn)行規(guī)劃設(shè)計(jì)的,并不具備普適性用途,也不是放之四海而皆準(zhǔn)的真理,工程師們還是需要具體問題具體分析,按需使用。