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