[導(dǎo)讀]星標(biāo)「嵌入式大雜燴」,一起進(jìn)步?。ㄒ唬┞憔幊淌鞘裁矗肯嚷暶饕粋€概念,裸編程,我創(chuàng)造的名詞,指的是在裸機(jī)上編寫程序,裸機(jī),在單片機(jī)領(lǐng)域就是指帶著硬件的單片機(jī)控制系統(tǒng),不要想歪咯。在裸機(jī)上編程,就猶如在一片荒地上開墾,任何一鋤頭下去,都會碰到硬生生的石頭,要說做這有什么味?拓荒者追求...
(一)裸編程是什么?
先聲明一個概念,裸編程,我創(chuàng)造的名詞
,指的是在裸機(jī)上編寫程序,裸機(jī),在單片機(jī)領(lǐng)域就是指帶著硬件的單片機(jī)控制系統(tǒng),不要想歪咯。
在裸機(jī)上編程,就猶如在一片荒地上開墾,任何一鋤頭下去,都會碰到硬生生的石頭,要說做這有什么味?拓荒者追求的是來年的綠洲。而我們這些開墾裸機(jī)的所謂的工程師們追求的是什么?我們當(dāng)然追求的是完成一個任務(wù)。
我們一般都自稱是高級知識分子,那么我們在拓荒的過程中應(yīng)該想些什么?當(dāng)然不是想著如何把任務(wù)完成,而應(yīng)該首先想著我們在想些什么。繞了是不?繞了就對了,這一繞就繞出了思想。思想是一個簡單的人在一個復(fù)雜的環(huán)境里做任何事情的統(tǒng)帥,它影響著一個拓荒者人生的每一個細(xì)節(jié),當(dāng)然也包括裸編程本身。
當(dāng)一個人拿著鋤頭,一鋤又一鋤,汗滴腳下土的時候,我們能知道他們在想什么嗎?當(dāng)然這不好說,如果自己去鋤就知道了。但是大抵也差不多,隨便舉幾個吧:這太陽他娘的怎么這么毒?這石頭他娘的咋這么多?這地種什么最好?這還有多少天能搞完?這樣干太慢了,要是有臺機(jī)械搞多好。當(dāng)然這只是一部分,任何人可以想出很多想法來。
那么當(dāng)我們在裸機(jī)上拓荒的時候,我們該想些什么?也許我們一般的想法是:先把一個簡單的功能做了,先把一個重要的功能做了,今天終于把這個功能調(diào)試好了明天可以做下一個功能了,這個為什么不是我想象的那樣的結(jié)果?真是莫名其妙!也等等一下吧。
如果拿來一個任務(wù),搭好測試平臺就開始做程序,想著一個功能一個功能的湊完,然后就自我陶醉著成功的喜悅,那這樣做程序,基本就叫做沒思想。有思想的做程序,是不能一下去就堆積源碼的,因為那樣只會讓一堆生硬的數(shù)字怯生生的擠在一起,不管他們有沒有多余,有沒有矛盾。所以寫源碼之前,是要想想如何寫的。也許很多人在寫之前都想過類似的問題,比如把任務(wù)模塊化后再組織程序。但是這樣的想法只是任務(wù)上的事情,而并不是裸編程時的思想,裸編程的思想,應(yīng)該是在組織任務(wù)模塊過程中及編寫裸程序時影響源碼組織的指導(dǎo)思想,它直接決定著源碼的質(zhì)量。
一個數(shù)據(jù)結(jié)構(gòu),一個模塊形成,一個單片機(jī)的指令,一個硬指令的運行機(jī)制,一個口線的驅(qū)動方式,一個中斷的順序,一個跳變的延遲,一個代碼的位置,一個邏輯的組織,一個模塊與模塊之間的生(運行時的狀態(tài))死(不運行時的狀態(tài))關(guān)系等等,都是裸程序思想的組成部分。
這似乎很瑣碎,但是裸程序原本就如此,它不同于上位機(jī)程序,有一個強(qiáng)大完善的操作系統(tǒng)支持。單片機(jī)里不可能植入操作系統(tǒng),那樣做就變味了,可不要有人跳出來說,某某某單片機(jī)就有操作系統(tǒng)了。裸程序就應(yīng)該是建立在赤裸裸的硬件基礎(chǔ)上的程序,只有有用的功能才有代碼,裸程序的質(zhì)量也許經(jīng)常在應(yīng)用中感覺不出來,也許你做和他做都能實現(xiàn)功能,但是好的裸程序有良好的可擴(kuò)充性、可維護(hù)性,系統(tǒng)具有高穩(wěn)定性和高性能。
而追求這種高品位的技術(shù)境界,就必須要有好的思想來指導(dǎo)。是不是看著有些迷糊?別說看得迷糊,我說都說迷糊了,總的來說,就是把一個優(yōu)秀的靈魂,植入你的源碼中,讓你的源碼具有一個優(yōu)良的思想。
(二)裸編程具體做法
前文說到裸編程要有思想,也許還不夠具體,接下來就是要具體說裸編程的思想的具體做法。
沒有思想的裸程序就如一副人體骨架,有個人形,但沒有人樣,骨骼之間的關(guān)節(jié)都是靠膠水或拉線連接起來的,生硬而呆板。如果給骨架包上皮肉,加上靈魂,我們就會驚嘆:??!這是帥哥,這是美女!因為骨架活了。
裸程序也一樣,如果按傳統(tǒng)的思維方式說這樣就足夠了,那么裸程序就形如骨架,通常只是一些功能的粗糙堆砌,也只會叫后人看了說這程序垃圾,而后人再做也未必能跳出這個圈子,那么后后人看了又叫這程序垃圾,如此下去,代代相傳,傳了什么?傳了一個總被叫垃圾的東西:無思想的裸程序。
我做了程序好多年,也思考了編程好多年,不斷的經(jīng)驗積累告訴我:寫好的程序不是如何去完成代碼,而是如何去組織代碼。上位機(jī)中面向?qū)ο蟮木幊趟枷耄褪且粋€非??扇〉乃枷搿?/span>
面向?qū)ο蟮木幊趟枷朐谏衔粰C(jī)中是有一個非常豐富的開發(fā)包和功能強(qiáng)大的操作系統(tǒng)支持的,裸編程如何引入這樣的思想呢?也許很多人會覺得不可能。
其實,沒有什么是不可能的。再復(fù)雜的思想,最終都會歸結(jié)到匯編,歸結(jié)到裸程序,我們的單片機(jī)程序,正是一種裸程序。只是在單片機(jī)編程時和微機(jī)編程時我們站在開發(fā)平臺上的高度不一樣,而已!
對這個高度的理解,也許很多人很困惑,因為我們平時很少注意它們,那么這里我就舉個其他的例子來說明,盡管和裸編程好象不很相關(guān),但是這個例子里的高度概念十分清晰。
我們知道網(wǎng)絡(luò)傳輸標(biāo)準(zhǔn)層次有七層:應(yīng)用層、表示層、會話層、傳輸層、網(wǎng)絡(luò)層、鏈路層、物理層,這么多層做什么用?也許理解這樣分層的概念也十分辛苦,但是理解這樣分層的思想,就容易多了,而且這也是我們硬件工程師們最應(yīng)該借鑒的思想,讓我們的硬件設(shè)計更具有標(biāo)準(zhǔn)性和前瞻性。
這個七層的思想從根本上講就是將一個網(wǎng)絡(luò)傳輸產(chǎn)品細(xì)化,讓不同的制造商選擇一個適合自己的層次開發(fā)自己的產(chǎn)品,層次不一樣,他們所選擇的開發(fā)基礎(chǔ)和開發(fā)內(nèi)容就不一樣,高一層開發(fā)者繼承低層開發(fā)者的成果,從而節(jié)省社會資源,提高社會生產(chǎn)力。對這個指導(dǎo)思想我就不贅述了,各位自己去理解,這里要說的是,微機(jī)上的面向?qū)ο缶幊趟枷刖褪侨缤趹?yīng)用層上實現(xiàn)的思想,而裸程序的面向?qū)ο笏枷雱t如同在鏈路層上實現(xiàn)的思想,他下面沒有軟件開發(fā)包,只有物理構(gòu)架。但是在應(yīng)用層上實現(xiàn)的思想,最終都要翻譯到物理構(gòu)架上。
看懂了上面的例子,就一定明白,裸程序的面向?qū)ο笏枷?,是可以實現(xiàn)的,只是難度要大得多,理解要難得多。但是這不要緊,這正是軟件水平的表現(xiàn),你喜歡技術(shù),又何懼之?其實也不會難到哪里去,只是把做事情的方式稍微改變一下而已。
傳統(tǒng)上我們都喜歡用功能來劃分模塊,細(xì)分任務(wù),面向?qū)ο笏枷氩贿@樣。面向?qū)ο笏枷雱t是先從一個任務(wù)中找出對象,在對象中攙雜些模塊等來實現(xiàn)功能的。這就是兩種風(fēng)格截然不同的地方。比如我們要讓我們的單片機(jī)把顯示信息輸出到顯示器,那么傳統(tǒng)的分析方法是信息格式化、格式化數(shù)據(jù)送顯示器顯示,似乎這樣也就足夠了,不同的顯示器用不同的送顯示程序或者程序段,配置不同的變量,能共的共起來,不能共的分開。
但是面向?qū)ο蟮乃枷氩皇沁@樣做的,而是首先把顯示器當(dāng)作一個對象,該對象具有一些功能和一些變量屬性,不同的顯示器在對象中使用相同的代碼標(biāo)識,如函數(shù)指針(C語言中),這樣對于任何一個不同的顯示器,在調(diào)用時都使用同樣的代碼。也許有人說,傳統(tǒng)的做法這樣也可以做呀,為什么要弄得羅里吧唆的呢?其實不然,使用了正確的思想的好處在前頭已經(jīng)說了好多了,如果還模糊就上去再看一次。
說了那么多理論,現(xiàn)在就說些具體的做法吧。以KeilC為編譯環(huán)境來說說一個對象具體組織的一些做法。首先是找出對象,如顯示器,這就是一個典型的對象。其次是分析一個活對象所應(yīng)具有的基本特征,即屬性與動作。顯示器的屬性如:類型代號、亮度、對比度、顯存等,動作如:初始化、內(nèi)容刷新和顯示、開啟和關(guān)閉、內(nèi)容閃爍等花樣顯示等。這樣分也比較容易理解,下面是對于代碼的組織上,要注意對象的獨立性與完整性,首先把顯示器對象單獨放在一個文檔上,屬于對象特有的變量與對象的定義放在一起,要區(qū)分公有變量與私有變量的定義方式,對于私有變量要考慮臨時變量與永久變量的安排,這些安排都是對變量生命期的嚴(yán)格確定,這樣可以節(jié)省內(nèi)存,避免混亂。如某一個函數(shù)要使用一個變量,函數(shù)在調(diào)用完了就退出了,而有一個變量只有它使用,卻要保存每一次調(diào)用函數(shù)所產(chǎn)生的結(jié)果,這樣的變量怎么定義呢?很多人會直接定義一個全局變量,但是一個好的做法是把這個變量定義成該函數(shù)的局部變量,但是定義成靜態(tài)的,那么這樣這個變量對其他代碼就是透明的,完全不可能會被誤修改,而且代碼分類性好,便于將來的維護(hù)。用函數(shù)指針來統(tǒng)一不同類型的顯示器不同的處理方式,也是一個很好的處理辦法,那樣可以讓具體處理方式千差萬別的顯示器都能用一個統(tǒng)一的對象,但是函數(shù)指針要慎重使用。
好了,說長了我就頭暈,不說了,思想的精華,不必有一樣的形態(tài),不同的人會有不同的理解,我只希望能給大家的程序生涯拋磚引玉,我就覺得很有成就感了。
(三)準(zhǔn)備工作
本文在此引用一個例子。在引入例子之前,我們要做一些準(zhǔn)備工作,然后一步一步地走向例子里去。就以前面帖子提到過的顯示器控制為例。
顯示器就是一個對象。無論它是功能多么復(fù)雜的顯示器,或者功能多么簡單的顯示器,對于需要顯示信息的調(diào)用者來說,都并不重要,也就是說對于需要使用顯示器的主體來講,他只管顯示信息,不管顯示器的千差萬別,只要顯示器提供了某功能,它可以調(diào)用就行,調(diào)用前當(dāng)然要遵守顯示器的數(shù)據(jù)傳遞規(guī)則,但是不必考慮不同的顯示器所產(chǎn)生的傳遞規(guī)則的差異。也就是說,對于調(diào)用者來說,永遠(yuǎn)不會希望有多條規(guī)則來讓自己的調(diào)用代碼變復(fù)雜。
因此,我們首先需要構(gòu)造一個相對獨立的代碼段,也就是顯示器對象,以下都以KeilC作為裸程序的編譯環(huán)境。正如很多人說的,KeilC并不是OOP語言,那怎么做?正是因為我們認(rèn)為KeilC不能做,所以我才把這種思想拿出來與大家探討,讓我們的程序變得更精彩,更有技術(shù)含量。
形成一個獨立代碼段,最好的辦法就是在主工程目錄下建立一個子目錄,如DISPLAY,然后再在DISPLAY目錄下建立一個文檔,如DISPLAY.H,然后把DISPLAY\DISPLAY.H文檔#include到一個恰當(dāng)?shù)奈恢蒙?,這樣,一個獨立的面向?qū)ο蟮拇a段就初步形成了,以后維護(hù)代碼的時候,你永遠(yuǎn)不要考慮調(diào)用者是什么感受,只要維護(hù)這個文檔,就可以保證你對顯示器的不斷更新了。
很多人也許會說,這算什么OOP?大家先別著急,這才是個開始,下面才是組織代碼的具體過程。
對于一個顯示器,我們必須要有顯示要求,我們才會去定制它,如果連使用要求都提不出來,就不要去讓人為你做顯示器。所以我們首先要明確我們要的顯示器必須要做什么。由于是單片機(jī)控制的顯示器,我們不能想象成微機(jī)顯示器那樣,一個大的顯存,可以顯示多少頁,顯示多少色,滿屏滿屏的傳遞數(shù)據(jù),如果這樣想了,就是犯了盲目例比的錯誤,說明對問題沒研究透。對于單片機(jī)控制的顯示器,我們考慮能顯示單個字符、單行顯示,就基本足夠了。所以我們可以定義下列兩個對象功能:
dispShowAChar();//顯示一個字符
dispShowALine();//顯示一行字符
由于是單片機(jī)的裸系統(tǒng),所以我們作為一個軟件設(shè)計者,我們一定要清楚,我們所面對的顯示器,經(jīng)常是沒有CPU的,所以我們一定要明白,我們這兩個函數(shù),實質(zhì)上都做些什么。很顯然,這兩個函數(shù)是不能長期占有CPU的,否則我們的程序?qū)⑹裁炊疾荒茏?,專去顯示了,成了顯示器的一個處理芯片,所以這兩個函數(shù)運行完后是肯定要退出來的,而顯示不能中斷呀,所以必須要有一個代碼段一直存在于活動代碼中而且不能影響其他的功能。做過上位機(jī)程序的人應(yīng)該能看出來,這段代碼就是線程。裸編程中我們也用這個概念。
我們的顯示器對象正需要一個一直活動的線程,來完成單片機(jī)系統(tǒng)對顯示功能的解釋和執(zhí)行,因此dispShowAChar()和dispShowALine()實質(zhì)上是不能直接去做顯示工作的,它倆最合適的工作,就是去按指定的格式去設(shè)置顯示內(nèi)容,這樣我們在使用的時候就不必在這兩個函數(shù)里設(shè)置復(fù)雜的代碼和嵌套調(diào)用關(guān)系,因為那樣一定會浪費很多的代碼,調(diào)用多了也會讓單片機(jī)運行效率降低,硬件資源消耗增加,嚴(yán)重的可能會造成堆棧溢出最后還不曉得為什么。讓我們也為這個活動線程也先命個名吧:
dispMainThread();//按指定的要求執(zhí)行顯示功能
//指定的要求包括顏色信息、閃爍、游動等等
程序分析下去,引出的概念也就會越來越多,這里所說的多線程概念以后有機(jī)會再說,單片機(jī)里的多線程也是一個復(fù)雜羅嗦的處理問題,現(xiàn)在介紹還為時過早。只是我感覺一不小心又說長了,具體下文繼續(xù)展開。
(四)展開思想
對于對象能力的定義,我們一般可以從重要的入手,然后慢慢地展開,把所需要的其他能力逐漸歸納為函數(shù),從而把面向?qū)ο蟮乃枷氚l(fā)展下去。上文我們提到了三個函數(shù)是怎么來的,還沒有涉及到函數(shù)的任何實質(zhì),那么本帖就探討一下這三個函數(shù)的實質(zhì)性規(guī)劃與設(shè)計。
有了功能要求,我們就要實現(xiàn)它,在裸程序中,實現(xiàn)它的一個首要任務(wù),就是要進(jìn)行數(shù)據(jù)傳遞方式的設(shè)計。很顯然我們必須要有一個顯示區(qū)域,來存放我們所要顯示的內(nèi)容,以及顯示內(nèi)容的顯示屬性,我們還要規(guī)劃這個顯示區(qū)域到底要顯示多少多少字符或者是點陣。但是由于我們事先并不知道我們的顯示設(shè)備一次會提供多少顯示量,所以我們可以把顯示區(qū)域的內(nèi)存,也就是顯存,定義得大一點,以至任何一款符合設(shè)計要求的顯示器都能得到滿足,這樣的做法在裸編程中其實還是比較實用的,因為裸編程中我們很少去申請動態(tài)的空間,程序設(shè)計完,所有的變量位置皆已確定,行就行,不行編譯就過不去,所以我們可以通常選擇一些內(nèi)存資源比較豐富的新款單片機(jī)。
但是這樣的做法也有一個弊端,比如當(dāng)我們預(yù)先估計不足而導(dǎo)致數(shù)據(jù)空間不夠的時候,我們就得從頭來改這個顯存的大小,從而導(dǎo)致整個顯示程序都要相應(yīng)的產(chǎn)生一些變動,這還不是最糟糕的,最糟糕的是當(dāng)一款新的顯示器因為新的功能需求而導(dǎo)致數(shù)據(jù)結(jié)構(gòu)需要發(fā)生變化的時候,我們就崩潰了,前期的工作可能改動就非常大,甚至于都要重新過一遍,也就是重寫重調(diào),這么痛苦的事情,我是最討厭的了。
所以我們要盡量避免這類事情發(fā)生,這里對面向?qū)ο蟮乃枷?,就頗為需求了。這個時候,我們就要引入一個新的概念,那就是對象的兒子,子對象。前面討論的,其實都只是一個抽象的對象,沒有任何具體的樣子,而只是籠統(tǒng)的規(guī)劃了所有的顯示器必須具有什么能力,而對于每一個具體的顯示器來說,還沒有任何具體的設(shè)計,在這里,每一個具體的顯示器,就是顯示器對象的子對象,他們形態(tài)各異,但是都必須能完成規(guī)定的功能。以傳統(tǒng)的OOP語言理論來說,這里就產(chǎn)生了一個繼承的關(guān)系,但是在裸程序思想里,我并不贊成引入這個概念,因為傳統(tǒng)的OOP語言里的繼承,純粹是一個語法上的邏輯關(guān)系,繼承關(guān)系明確,而裸程序中的這個思想,并沒有任何語法支持,繼承關(guān)系就非常微弱了,還不如說是歸類與概括。但無論是什么關(guān)系,我還是不想就這種一目了然的關(guān)系弄個新名詞來,讓看的人費解。
既然引入了子對象,我們能看出這種做法有什么實際意義嗎?也許有經(jīng)驗的資深程序員能看出來。我們在做父對象數(shù)據(jù)設(shè)計的時候,我們并不規(guī)定具體的數(shù)據(jù)格式和顯存大小,而是一股腦兒地全推給子對象自己去搞,父對象什么都不管。哈哈!這樣做事情真是很簡單吧?不是我的事情我不管,不要說我偷懶,因為站在父對象的角度講,這是最明智的做法,因為管不了,所以不管。
到這里也許就會產(chǎn)生更多的疑問了,一個對象什么都不管,那作為調(diào)用者怎么使用這個對象呢?你想用它,它什么都不管,這怎么行呀?別著急,父對象不管的,是那些具體的事情,抽象的事情,還是管的,要不然它就沒有理由存在了。你抱怨了,說明你在思考,既然思考了,就把思考的問題提出來,提出來的,就是我們設(shè)計父對象的依據(jù)。提問題,我想這比搞程序要簡單得多,比如:顯示器能顯示多少乘多少的字符?顏色是多色還是單色?顯示模式是否支持預(yù)定的方式(如移動、閃爍等)?工作模式是圖像還是字符?等等,這里附加說明一下,對于顯示模式,我們這里都以字符顯示為例,既然是面向?qū)ο蟮乃枷?,相信擴(kuò)充出圖像顯示模式,還是很容易的事情。
有問題出來了,我們就繼續(xù)為它添加代碼好了。
dispGetMaxCol();//取一行最多有多少列
dispGetMaxRow();//取顯示器一共有多少行
dispGetMaxColors();//取顯示器最多有多少色
dispSetShowMode();//設(shè)置顯示的方式,對于不支持的顯示方式就自動轉(zhuǎn)為正常顯示
dispSetWorkMode();//設(shè)置工作模式,如果沒有的模式就返回0,支持的就返回1
對于這些函數(shù)的定義,各人可以根據(jù)自己的習(xí)慣來設(shè)置,我只是臨時弄了這個例子,未必就是最好的,我的目的是重在說明思想。我也害怕把程序弄得龐大了,出本書都嫌厚。
似乎加了這些函數(shù)之后,我們根本就沒看到顯示數(shù)據(jù)的具體形式,和前面的函數(shù)一起,都并沒有什么明確的說法。這種感覺很正確,我們確實沒有對顯存做任何定義,但是似乎功能卻都已經(jīng)定義了,其實也確實是定義了,而且將來我們就這樣用,而且也不用怕,程序一定會寫完的。
(五)數(shù)據(jù)傳遞與程序邏輯是同等重要的
繼續(xù)上面討論的問題。前面我們提到,為了使用dispShowAChar()、dispShowALine()、dispMainThread()這三個函數(shù),我們又引出五個新的函數(shù)來,這些新的函數(shù)最主要的目標(biāo),就是要實現(xiàn)調(diào)用者與被調(diào)用者之間數(shù)據(jù)的傳遞。
對于程序設(shè)計來講,數(shù)據(jù)傳遞與程序邏輯有著同樣重要的地位,前者經(jīng)常在最后會形成一種協(xié)議,后者則經(jīng)常表現(xiàn)為各種算法。
在裸程序中,我們的思想應(yīng)該主要是表現(xiàn)為一種靈魂,而不能如C 那樣,去追求語法的完美,所以對于參數(shù)的傳遞,我們不能去追求語法上的完美,而是不拘一格用傳遞。除了函數(shù)可以傳遞數(shù)據(jù)外,直接調(diào)用值也是一種很快捷的方式,但是調(diào)用不能隨便說調(diào)就調(diào),而是也要學(xué)習(xí)C 上語法的習(xí)慣,盡量不能讓一些專用的變量名稱,出現(xiàn)在與專用變量無關(guān)的程序體中。例如,我們的設(shè)計中規(guī)定,我們這套裸系統(tǒng)對顯示器最多支持65536色,那么我們就會用一個16位的無符號整數(shù)來保存這個指標(biāo)。為了簡化以后的說明,我們先定義兩個數(shù)據(jù)類型:
typedefunsignedintUINT;
typedefunsignedcharUCH;
如果我們用函數(shù)來傳遞這項數(shù)據(jù),我們可以用如下的方式:
#defineMonitor01_MaxColors0xFFFF
對于顏色調(diào)用函數(shù)則定義如下:
UINTdispGetMaxColors()
{
returnMonitor01_MaxColors;
}
很顯然,如果另一個顯示器是個單色顯示器,則顏色調(diào)用函數(shù)只需要改為下列形式就可以了:
#defineMonitor02_MaxColors0x0001
UINTdispGetMaxColors()
{
returnMonitor02_MaxColors;
}
之前有人提到過,用數(shù)組,這可以解決很多問題。說得一點沒錯!上面的例子我們忽略了一個問題,那就是同一個函數(shù)名要去做很多不同函數(shù)所做的事情,而我們卻沒有在函數(shù)體內(nèi)使用switch(),這顯然是不對的。要真正實現(xiàn)不同顯示器的共同屬性MaxColors的傳遞,我們必須要添加switch()以區(qū)分不同的顯示器類型。那么這里我們就需要引入一個新的父對象屬性以指代它的第幾號兒子:
UCHMonitorType=0;//顯示器類型,最多支持256種顯示器
并在初始化的時候,為該屬性初始化為0,以作為缺省類型顯示器的代號。以下命名我們就說一個約定,以讓代碼更具有規(guī)范的模樣:父對象的接口函數(shù)用小寫的disp打頭,變量用Monitor打頭,宏數(shù)據(jù)用Monitor開頭并且內(nèi)部至少有一個下劃線,宏函數(shù)則用全大寫字母組成。那么不用數(shù)組的情況下,上面的代碼將會變成如下形式:
#defineMonitor_000
#defineMonitor_011
#defineMonitor_022
UINTdispGetMaxColors()
{//以下用多出口,但這并不會破壞什么,為節(jié)約代碼,完全可以使用
switch(MonitorType)
{
caseMonitor_01:returnMonitor01_MaxColors;
caseMonitor_02:returnMonitor02_MaxColors;
}
returnMonitor00_MaxColors;//缺省則返回默認(rèn)顯示器
}
這樣的形式很顯然是太冗長了,盡管非常結(jié)構(gòu)化,但是一般在優(yōu)化程序的時候我們還是可能會廢棄它的,所以這里就提到了數(shù)組的使用。既然是數(shù)組,那么它自然不能屬于某一個子對象,而是應(yīng)該在父對象中定義的,盡管這樣做我們每次在添加新顯示器的時候我們比如在父對象中添加難以理解的新的數(shù)據(jù),但是為了節(jié)省代碼,我們還是能忍受這樣的痛苦。如果改用數(shù)組,則上面的代碼將改變?yōu)槿缦滦问剑?/span>
#defineMax_Monitor_Types3***
#defineMonitor00_MaxColors1
UINTcodeMonitorMaxColorsArray[Max_Monitor_Types]=
{Monitor00_MaxColors,//缺省為單色
Monitor01_MaxColors,
Monitor02_MaxColors,
};***
打***的語句將是未來擴(kuò)充時不斷需要修改的句子。那么上面的函數(shù)就簡單了
UINTdispGetMaxColors()
{
returnMonitorMaxColorsArray[MonitorType];
}
甚至有人還可以用宏函數(shù)來節(jié)省運行時間,只要修改一下調(diào)用規(guī)則就可以了:
#defineDISPGETMAXCOLORS(c)c=MonitorMaxColorsArray[MonitorType];
也許當(dāng)我們寫成如上代碼的時候,我們的每一次改進(jìn),都會讓我們欣喜,我們的代碼又優(yōu)化了。但是可惜的是,這種沒有思想的優(yōu)化會在不遠(yuǎn)的將來,給我們帶來麻煩。我覺得我的記憶力很不好,也許一分鐘前的事情我都會想不起來,這種在將來擴(kuò)充中的上竄下跳地修改會讓我覺得暈眩!
所以,在工程化的工作中,我們需要把父對象與子對象盡量隔離開來,減少關(guān)聯(lián)性的修改量,這也是面向?qū)ο笏枷氲闹匾饬x之所在,對于這一改動,我將在下帖中闡述。
(六)父對象接口函數(shù)與子對象功能剝離
上文我們說到dispGetMaxColors()的一些設(shè)計思路,我們有很多很好的辦法來實現(xiàn)它,但是我們有沒有更好的管理辦法來實現(xiàn)它,這才是我們要站在更高層次看問題的焦點,是更重要的。這也就是一個從傳統(tǒng)思維到面向?qū)ο笏季S的一個重要的轉(zhuǎn)折。
要想把這個函數(shù)轉(zhuǎn)變?yōu)槊嫦驅(qū)ο蟮倪壿嫿Y(jié)構(gòu),我們也要先做些準(zhǔn)備工作。
第一說參數(shù)傳遞的思想。盡量減少參數(shù)傳遞,這是尊重C51系列8位單片機(jī)硬件現(xiàn)狀的一項重要措施,記著,不要抱怨C51檔次低,資源少,而是要在尊重和熱愛C51的前提下,我們才有熱情來發(fā)展我們的裸程序面向?qū)ο笏枷氲?,也就是說,無論我們面臨的系統(tǒng)有多簡陋,我們都有策略,來實現(xiàn)復(fù)雜的功能,而且從發(fā)展的眼光來看,產(chǎn)品的升級,并不是盲目的升級我的CPU,因為那樣只會讓產(chǎn)品設(shè)計者智商下降,所以我覺得C51的特色就是應(yīng)該在簡潔,越來越簡潔,而不是越來越復(fù)雜。所以我希望我們把思想升級作為升級產(chǎn)品的一個發(fā)展方向。傳遞參數(shù)要減少指針、浮點等類型的數(shù)據(jù)傳遞,盡量以UCH與UINT為主,而且參數(shù)的數(shù)量不要太多,最理想的上限是2個,如果實在多了,就使用共享緩沖區(qū),或者全局變量。最好是不要傳遞參數(shù)。
本函數(shù)就利用了MonitorType省略了一個參數(shù)傳遞。
第二是我們要讓父對象的接口函數(shù)與具體的子對象的多種可能性的功能實現(xiàn)剝離,這里我們就需要使用函數(shù)指針。函數(shù)指針也許我們一般用得少,但是其實并不是很復(fù)雜。先看我們函數(shù)的形式:
UINTdispGetMaxColors(void);
為該函數(shù)定義一個指針類型,只需做如下定義,就可以了:
typedefUINT(*dGMC)(void);
那么對于父對象中的dispGetMaxColors()函數(shù),我們就只需要轉(zhuǎn)換定義一個函數(shù)指針,在創(chuàng)建父對象的時候為它提供一個子對象對應(yīng)功能調(diào)用的入口地址,就足夠了。所以對于這個函數(shù)的實體將只在子對象中出現(xiàn),而在父對象中只會出現(xiàn)一個變量的定義:
dGMCdispGetMaxColors;
為了給它賦初值,我們也可以定義一個空指針,作為一個未使用的判斷標(biāo)志:
#defineNIL0
那么初始化dispGetMaxColors的時候只需要寫條如下語句就可以了:
dispGetMaxColors=NIL;
而且功能調(diào)用也很簡單,與實質(zhì)的函數(shù)是一樣的:
if(dispGetMaxColors!=NIL)vMaxColors=dispGetMaxColors();
如果再加上約定,連前面的判斷語句完全可以省略。因為我們的裸程序的程序空間實際上也是運行空間,不存在代碼調(diào)入內(nèi)存和移出內(nèi)存的事情發(fā)生,所以我們不需要考慮程序內(nèi)存的優(yōu)化問題,只要我們規(guī)定對象一定是先創(chuàng)建后使用,判斷語句就會變得沒有意義,而且我們創(chuàng)建后即使不再使用,函數(shù)體我們也不會釋放,因為它被放在程序空間內(nèi)是固定死的,你想移出去,還不能實現(xiàn)呢。
第三,盡量讓程序所使用的語法簡單化,以減少編譯器可能帶來的差別而產(chǎn)生的理解上的誤區(qū)。有人說C51是C的子集,這說法我認(rèn)為不科學(xué),只能說二者繼承了C的基本精神,而在實質(zhì)上它們針對的是不同的對象,所以也出現(xiàn)了一些不同的結(jié)果,所以我看到有些高手或者一些面試題弄出一些題目來讓我望而生畏,也許我做一輩子的裸程序也做不出他們的題目,但是我并不覺得我不如他們。他們只不過是在編譯器上花了很多時間研究他們的一些約定,而我并不想花時間去研究那些將來可能發(fā)生變化的東西,我希望我能用一個更簡單的途徑把我的事情做出色,我只關(guān)心我的項目的速度、代碼的大小、運行的效率、維護(hù)的復(fù)雜度,所以我也建議和我交流的人能用一種通俗的方法來實現(xiàn)我們的功能,也不必過多的考慮我的程序在8位單片機(jī)上可以用16位單片機(jī)上也可以用,我們的系統(tǒng)要經(jīng)常升級,但不要輕易去增加硬件成本。當(dāng)然如果你有精力去研究那些約定,也沒什么,只要你樂意。
好了,上面三條,只有第二條是我要說的關(guān)鍵,其他兩條個人可以根據(jù)自己的具體情況來尋找一些快捷實用的方法。其實說來我們把父對象用函數(shù)指針來代替實體函數(shù),這完全是一種思想,我們并沒有使用復(fù)雜的語法,而是從管理上把對象分離開來,讓不同的人做不同的事情,比較容易。但是同時我們又不能讓這種中間環(huán)節(jié)影響我們程序的效率,所以我們完全可以自己尋求一些方法,而不必去循規(guī)蹈矩。我這樣說也許會遭到一些非議,但是我可以告訴大家,計算機(jī)語言這門學(xué)科,本身就是一門人造科學(xué),沒有最好只有更好,我們沒必要完全按照別人的思路去發(fā)展設(shè)計思想,所以也不要拿著人家的一些約定來引以為榮,那些東西,只有先學(xué)后學(xué)的問題,沒有水平的差異;我們應(yīng)該更注意的是,人家創(chuàng)造了工具,我們就用工具再來創(chuàng)造世界!如果你停留在欣賞工具的層次上,那就是無所鳥事了!
本帖實質(zhì)上只說了一個轉(zhuǎn)換,兩條建議,這都不是具體的程序,而是思想。我想強(qiáng)調(diào)的,也不是格式,而是思想。下帖將再回到對象上,和大家探討一下對象本身的組織問題,比如對象的層次關(guān)系、對象的創(chuàng)建、對象的書寫等等,我也希望有人能有一些更好的方法回到帖子里,我們互相學(xué)習(xí),共同提高。
(七)
前面的思想衍變過程已經(jīng)說了很久了,面向?qū)ο蟮乃枷胍簿偷搅斯鲜斓俾浜糁龅木辰缌恕O旅嫖揖拖葓D示一下裸程序設(shè)計中面向?qū)ο笏枷氲膶哟侮P(guān)系:
相信這張圖已經(jīng)足夠說清楚我們在KeilC中如何用語言來組織我們的顯示器對象disp了。disp是一個抽象的對象,它只是一種聯(lián)系,完成對所有子對象d000、d001、d002到最多d255的歸納概括并提供一組被調(diào)用者所使用的功能接口。這些功能接口正是上貼所提到的函數(shù)指針。而具體的功能實現(xiàn)及不同顯示對象對數(shù)據(jù)結(jié)構(gòu)的要求,我們都可以交給子對象設(shè)計工程師自己去決定。
很顯然,大家在這套方案具體的程序設(shè)計過程中,最主要的精力還是要放在自己做自己的問題上,多思考如何把事情做得更漂亮,而不必在代碼編寫時黏糊不清。父對象設(shè)計者必須要完成總體方案的設(shè)計,抽象思維多而具體工作量少,子對象的設(shè)計者則恰恰相反,只需要多考慮考慮具體的設(shè)計,而不必去擔(dān)心別人是怎么用自己的東西。
很顯然,作為總體設(shè)計者,必須要嚴(yán)格考慮這中間的數(shù)據(jù)交換關(guān)系,因為我們沒有操作系統(tǒng),所以對于可用的內(nèi)存資源的使用法則,直接關(guān)系到我們整個系統(tǒng)的成敗,混亂使用常常會導(dǎo)致系統(tǒng)崩潰,相對獨立的代碼則在編譯過程中由KeilC直接安排好了,我們不需要去考慮它們對程序的影響。
例子中的顯存大小及顯存的位置都是我們方案成敗的關(guān)鍵。我們都知道KeilC對單片機(jī)內(nèi)存劃分了四種,即data、idata、pdata、xdata四種,各種存儲器的大小與特點都決定著我們代碼的運行效果,我們既要考慮信息所需要的空間,又要考慮單片機(jī)處理時足夠達(dá)到我們的視覺要求。在這個例子中,我覺得我們可以選擇xdata來作為顯存。為什么呢?因為我覺得只要我們處理得當(dāng),我們的單片機(jī)完全可以克服處理速度上的缺陷,所以我們可以優(yōu)先滿足信息量的要求,提供足夠多的空間來實現(xiàn)我們想要的功能。
提速的方式有很多,比如:選擇一些性能優(yōu)越的新型單片機(jī),傳統(tǒng)的是12T的,現(xiàn)在有6T的,也有1T的,這讓很多指令都會有更高的效率;適當(dāng)?shù)奶岣呔д耦l率;選擇更科學(xué)的算法;等等。
到目前為止,基本上可以去構(gòu)造我們的對象了,如果你有興趣,你可以使用#define進(jìn)行一些偽碼定義,把我們的對象定義得更美觀一點,更接近C 一些,不過我要說的是:我們這里沒有嚴(yán)格的類定義,所以定義時類與對象經(jīng)常是沒有界限的。
本文來源于網(wǎng)絡(luò),版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請聯(lián)系我進(jìn)行刪除,感謝~
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。
關(guān)鍵字:
阿維塔
塞力斯
華為
加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...
關(guān)鍵字:
AWS
AN
BSP
數(shù)字化
倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...
關(guān)鍵字:
汽車
人工智能
智能驅(qū)動
BSP
北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...
關(guān)鍵字:
亞馬遜
解密
控制平面
BSP
8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。
關(guān)鍵字:
騰訊
編碼器
CPU
8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。
關(guān)鍵字:
華為
12nm
EDA
半導(dǎo)體
8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。
關(guān)鍵字:
華為
12nm
手機(jī)
衛(wèi)星通信
要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...
關(guān)鍵字:
通信
BSP
電信運營商
數(shù)字經(jīng)濟(jì)
北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...
關(guān)鍵字:
VI
傳輸協(xié)議
音頻
BSP
北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...
關(guān)鍵字:
BSP
信息技術(shù)
山海路引?嵐悅新程 三亞2024年8月27日 /美通社/ --?近日,海南地區(qū)六家凱悅系酒店與中國高端新能源車企嵐圖汽車(VOYAH)正式達(dá)成戰(zhàn)略合作協(xié)議。這一合作標(biāo)志著兩大品牌在高端出行體驗和環(huán)保理念上的深度融合,將...
關(guān)鍵字:
新能源
BSP
PLAYER
ASIA
上海2024年8月28日 /美通社/ -- 8月26日至8月28日,AHN LAN安嵐與股神巴菲特的孫女妮可?巴菲特共同開啟了一場自然和藝術(shù)的療愈之旅。 妮可·巴菲特在療愈之旅活動現(xiàn)場合影 ...
關(guān)鍵字:
MIDDOT
BSP
LAN
SPI
8月29日消息,近日,華為董事、質(zhì)量流程IT總裁陶景文在中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式上表示,中國科技企業(yè)不應(yīng)怕美國對其封鎖。
關(guān)鍵字:
華為
12nm
EDA
半導(dǎo)體
上海2024年8月26日 /美通社/ -- 近日,全球領(lǐng)先的消費者研究與零售監(jiān)測公司尼爾森IQ(NielsenIQ)迎來進(jìn)入中國市場四十周年的重要里程碑,正式翻開在華發(fā)展新篇章。自改革開放以來,中國市場不斷展現(xiàn)出前所未有...
關(guān)鍵字:
BSP
NI
SE
TRACE
上海2024年8月26日 /美通社/ -- 第二十二屆跨盈年度B2B營銷高管峰會(CC2025)將于2025年1月15-17日在上海舉辦,本次峰會早鳥票注冊通道開啟,截止時間10月11日。 了解更多會議信息:cc.co...
關(guān)鍵字:
BSP
COM
AI
INDEX
上海2024年8月26日 /美通社/ -- 今日,高端全合成潤滑油品牌美孚1號攜手品牌體驗官周冠宇,開啟全新旅程,助力廣大車主通過駕駛?cè)ヌ剿鞲鼜V闊的世界。在全新發(fā)布的品牌視頻中,周冠宇及不同背景的消費者表達(dá)了對駕駛的熱愛...
關(guān)鍵字:
BSP
汽車制造
此次發(fā)布標(biāo)志著Cision首次為亞太市場量身定制全方位的媒體監(jiān)測服務(wù)。 芝加哥2024年8月27日 /美通社/ -- 消費者和媒體情報、互動及傳播解決方案的全球領(lǐng)導(dǎo)者Cis...
關(guān)鍵字:
CIS
IO
SI
BSP
上海2024年8月27日 /美通社/ -- 近來,具有強(qiáng)大學(xué)習(xí)、理解和多模態(tài)處理能力的大模型迅猛發(fā)展,正在給人類的生產(chǎn)、生活帶來革命性的變化。在這一變革浪潮中,物聯(lián)網(wǎng)成為了大模型技術(shù)發(fā)揮作用的重要陣地。 作為全球領(lǐng)先的...
關(guān)鍵字:
模型
移遠(yuǎn)通信
BSP
高通
北京2024年8月27日 /美通社/ -- 高途教育科技公司(紐約證券交易所股票代碼:GOTU)("高途"或"公司"),一家技術(shù)驅(qū)動的在線直播大班培訓(xùn)機(jī)構(gòu),今日發(fā)布截至2024年6月30日第二季度未經(jīng)審計財務(wù)報告。 2...
關(guān)鍵字:
BSP
電話會議
COM
TE
8月26日消息,華為公司最近正式啟動了“華為AI百校計劃”,向國內(nèi)高校提供基于昇騰云服務(wù)的AI計算資源。
關(guān)鍵字:
華為
12nm
EDA
半導(dǎo)體