MCU資源受限 怎么面對編程的挑戰(zhàn)
心有多大,舞臺就有多大,夢想有多大,你的世界就有多大。
本文引用地址:http://www.ex-cimer.com/article/201905/400449.htm這句濃濃的雞湯實在是太“唯心主義”了。因為,如果MCU資源非常有限,軟件工程師的心再大,也沒有舞臺施展自己的斧鉞鉤叉。
在廣闊的人生舞臺上才能大有作為,面對廣袤的世界,大開大合地書寫自己的人生,這種揮灑、寫意的疏闊沒有人不喜歡??墒牵绻阋谝粋€資源受限的MCU上編程,就像是在一個空間狹小的舞臺上演話劇,你需要反復(fù)思量,才能應(yīng)對空間的局促,半分揮灑就會讓劇情漫出舞臺之外。為了利用好每一寸空間,你需要反復(fù)斟酌、再斟酌,調(diào)整、再調(diào)整,哪里還有半分寫意可得?!
說實話,這種感覺實在壞透了。在一次產(chǎn)品開發(fā)中,筆者就切身體會過這種地獄般的局促和捉襟見肘,現(xiàn)在想來,還有幾絲寒意漫上心頭......
1
順利的開始等于成功的一半。筆者開始開發(fā)中控鎖模塊時,就有這種美好的感覺。因為,從功能上來看,中控鎖相當(dāng)于車身控制器的一個子模塊,筆者有車身控制器的開發(fā)經(jīng)驗,把它刀劈斧砍,擇菜式地弄出來個中控鎖,還不是小菜一碟?
事實證明,這還真是小菜一碟。因為我們這款用在某個國產(chǎn)車上的中控鎖的功能實在太簡單了,就是三大塊:機械鑰匙解鎖、閉鎖+遙控鑰匙解鎖、閉鎖+車身防盜功能,所謂車身防盜,就是車門被撬開時用轉(zhuǎn)向燈和喇叭報警。
在以往開發(fā)車身控制器的歷史積累中,開關(guān)檢測模塊、定時器管理模塊、遙控接收解析模塊都是現(xiàn)成的,無非是從之前的MCU上搬過來,稍微移植一下即可。開發(fā)難度,自然是沒有的,所以深諳內(nèi)情的領(lǐng)導(dǎo)把整個產(chǎn)品的開發(fā)周期定為三個月。
在這三個月的時間里,硬件開發(fā)和軟件開發(fā)是同步進(jìn)行的,首要的工作自然是MCU的選型。
所有成功決定的背后都是對若干關(guān)鍵因素的權(quán)衡,MCU的選型也不例外,MCU的資源、成本、性能、生命周期、供貨能力都是必須考慮的因素。其中,資源是最為軟件工程師看重的指標(biāo)。
最初,領(lǐng)導(dǎo)并沒有插手MCU選型的工作,這項任務(wù)落在了我和硬件工程師張工的頭上。我個人比較青睞于飛思卡爾的單片機,因為我之前從事的所有產(chǎn)品的開發(fā)都用飛思卡爾系列,代碼移植起來比較方便,可是張工卻傾向于微芯的一顆MCU。反復(fù)論戰(zhàn)之后,報領(lǐng)導(dǎo)裁決,領(lǐng)導(dǎo)決定使用微芯方案,選用的MCU是微芯的PIC16F1509,原因無它,便宜!
這個芯片內(nèi)置512字節(jié)RAM,8K字節(jié)程序Flash,和之前用過的MCU比起來確實袖珍得可以。最初打眼一看,RAM和Flash的數(shù)量好像有點少,資源到底夠用不夠用,說實話我心里是沒譜的,帶著這種忐忑的心情,我開始了代碼的移植工作。
雖然之前沒有用過微芯的產(chǎn)品,但是畢竟中控鎖的功能太簡單,而這顆MCU也太小巧玲瓏了,所以移植工作進(jìn)行地順風(fēng)順?biāo)?,在硬件電路板還沒有拿到手之前,我就完成了初步編程工作。板子到手后,一頓操作猛如虎,調(diào)試、運行、修改三板斧下來,最終軟件基本定型時,RAM用了300個字節(jié)左右,F(xiàn)lash更是用了不到4K。
當(dāng)然,這并不能說明我水平有多高,只能說明這個產(chǎn)品實在太簡單了。
2
我本以為,這將是我有史以來用過的資源最少的一款MCU,不曾想,這個記錄很快就被我自己打破了。
項目啟動兩個月后,軟硬件開發(fā)工作就結(jié)束了,提前了原計劃整整一個月的時間。在隨后召開的項目會議上,我?guī)е鴳浛嗨继鸬男腋8?,向領(lǐng)導(dǎo)匯報了代碼移植、調(diào)試工作,并表達(dá)了最初對MCU資源的忐忑之情。
透過厚厚的鏡片,領(lǐng)導(dǎo)滴溜溜地轉(zhuǎn)了一下眼睛,詢問了一下RAM和Flash的具體使用量,老實、不帶任何戒心的我如實地告知了領(lǐng)導(dǎo)具體數(shù)字。那一刻我還不知道,正是這種誠實讓我在接下來的一個月里度過了一段不堪回首、雞飛狗跳的日子。
從我這里得知RAM、Flash的具體使用量之后,領(lǐng)導(dǎo)歪了歪憨態(tài)可掬的脖子,把頭轉(zhuǎn)向張工,問起PIC16F1508的資源來??粗鴱埞こ了嫉臉幼樱倚闹谢鸸怆婇W,瞬間‘領(lǐng)會了’領(lǐng)導(dǎo)的意圖。領(lǐng)導(dǎo)做硬件出身,MCU選型也是他平時主抓的業(yè)務(wù)工作之一,他無非是想知道pin to pin兼容、成本更低的PIC16F1508能不能替換更貴的PIC16F1509罷了。
不等張工發(fā)言,我已經(jīng)在內(nèi)心里盤算了一下:‘1508的Flash是4K字節(jié),程序空間是夠用的,但是它的RAM是256字節(jié),明顯不夠用?!荒钪链?,我舒了一口氣,給張工眨了眨眼睛,于是,張工如實地告知了領(lǐng)導(dǎo)1508的資源。
我本以為事情到此就結(jié)束了,卻不曾想,領(lǐng)導(dǎo)又把他那扎煞在硬硬的衣領(lǐng)里面的脖子朝我轉(zhuǎn)過來,向我交待了一個匪夷所思的任務(wù):
把代碼改改,把RAM使用量降到256字節(jié)以下,用PIC16F1508能便宜一塊多吶!
領(lǐng)導(dǎo)接著笑言:“項目合作方要求降成本,我都答應(yīng)人家了,正好不知道從哪里下手呢!”
3
代碼基本定型,這時要精簡掉將近20%左右的RAM使用量,腫么辦?
筆者首先想到的是那些取值范圍有限的全局變量,這些變量之前都是用uint8_t類型定義的,在這種定義下,MCU會用一個8位的字節(jié)來存儲它。這對于一個實際上當(dāng)成布爾量使用的變量來說絕對是一種浪費,顯然,用一個‘位’來存儲它更節(jié)約空間。還有那些取值范圍不超過7的,都可以用3位存儲,不超過15的,可以用4位存儲,以此類推。
具體實現(xiàn)方式也很簡單,像MCU的硬件寄存器定義一樣,定義一個位域形式的結(jié)構(gòu)體,把這些變量以結(jié)構(gòu)體中位域形式的成員變量的形式來表示。通過這番操作,筆者居然省掉了整整20來個字節(jié)的RAM空間。
當(dāng)然這也不是沒有代價的,那些“干脆利落”的變量名稱不見了,結(jié)構(gòu)體.成員變量形式的“麻煩啰嗦”的名稱倒是隨處可見,散布在源代碼各個角落,甚是扎眼。
革命尚未成功,同志仍需努力,要把RAM使用量降到256字節(jié)以下,還需要繼續(xù)精簡。我的目光在各個源文件之間流連不斷,內(nèi)心不斷地做著權(quán)衡和取舍。我深深地知道:要省RAM,必須修改某些模塊的實現(xiàn)方式,而這必然要付出犧牲代碼閱讀性的代價。
主動絞殺閱讀上的美感,這種感覺實在是難受極了。
目光所及之處,正是定時器管理模塊的“軟件定時器結(jié)構(gòu)體”,這里有個成員變量是“定時回調(diào)函數(shù)”,這是一個16位指針變量,指向回調(diào)函數(shù)首地址。定時器啟動時,把回調(diào)函數(shù)首地址賦給該變量,等計時滿時,定時器管理模塊會“自動”調(diào)用該回調(diào)函數(shù),這是一種多么優(yōu)美的設(shè)計!當(dāng)然,優(yōu)美的代價是每一個軟件定時器節(jié)點都需要安排兩個字節(jié)存儲該回調(diào)函數(shù)首地址,10個定時器節(jié)點就對應(yīng)整整20個字節(jié)。
當(dāng)斷不斷反受其亂,我懷著無比悲壯的心情修改了定時器管理模塊的設(shè)計,把這個成員變量刪掉,代之以一個表示定時超時的標(biāo)志flag,這個flag用一個位變量表示即可,通過和軟件定時器結(jié)構(gòu)體中臨近成員變量(正好也是一個位變量)的結(jié)合,這個flag實際上不會引入任何額外的存儲需求。在定時器管理模塊設(shè)計中,通過查詢超時標(biāo)志flag,“手動”調(diào)用相應(yīng)的回調(diào)函數(shù),一樣可以滿足程序功能要求。
通過這種方式,我又節(jié)省了20個字節(jié)。目標(biāo)越來越近了,我也越來越欲減乏術(shù)了。
4
這一天,距離256個字節(jié)的目標(biāo)只剩下10個字節(jié)了,我默默地躺在床上,盯著頭頂?shù)奶旎ò澹镁脽o法入眠。
這是最孤寂的時刻,在靜靜的午夜,宇宙向它的聆聽者展示著廣漠的荒涼。
我不禁想起了大劉在《三體》中寫過的智子工程-將一個9維空間的質(zhì)子展開為2維,然后在這個展開后變得無比巨大的2維質(zhì)子上蝕刻微觀集成電路。。。我要是能把RAM展開就好了!
RAM展開?還能展開到Flash里面去嗎?這時,‘RAM不夠,F(xiàn)lash來湊’的八字真言奇跡般地出現(xiàn)在我眼前,我隱約覺得哪里有些變量實際上只取一些固定值了,那么,把這些變量直接定義成常量,它就不消耗RAM空間了,轉(zhuǎn)而去消耗Flash空間了!對,明天就這么干。
果不其然,第二天,我就在遙控鑰匙的解析接收程序里找到了一個四字節(jié)的數(shù)組變量,它在實際運行中只取常量值。改掉它之后,距離目標(biāo)只剩下6個字節(jié)了!
勝利在望,但是接下來的每一步都將無比艱難。為了完成領(lǐng)導(dǎo)安排的任務(wù),我做好了重寫所有代碼的準(zhǔn)備,拼了。
然后,救星突然降臨了。領(lǐng)導(dǎo)過來詢問了RAM精簡的情況后,沉思片刻后,悠悠地說了一句:
算了,RAM空間必須得有20-30%的余量,要不以后功能升級怎么辦?
喜從天降,竟是這么令人猝不及防,我張著空洞洞的嘴巴,把沖到嗓子眼想罵人的話生生咽了下去,配合著領(lǐng)導(dǎo),說了一句總結(jié)陳詞:
MCU資源受限,編程成了巨大的挑戰(zhàn)?。?/p>
領(lǐng)導(dǎo)卻不置可否,優(yōu)雅地一個轉(zhuǎn)身,留給我一個高大的背影,慢慢踱開了去。他的背影竟而越變越大,壓得我有些窘慌,似乎要擠出我藏在棉衣下的那個‘小’來!
評論