1引言
隨著市場(chǎng)競(jìng)爭的日趨激烈,要求電子工程師能夠在短時(shí)間內(nèi)編寫出執(zhí)行效率高而又可靠的嵌入式系統(tǒng)的執(zhí)行代碼。同時(shí),由于實(shí)際系統(tǒng)的日趨復(fù)雜,要求所寫的代碼規(guī)范、模塊化并便于多個(gè)工程師以軟件工程的形式進(jìn)行協(xié)同開發(fā)。匯編語言作為傳統(tǒng)的嵌入式系統(tǒng)的編程語言,已經(jīng)不能滿足這樣的實(shí)際需要了。而C語言以其結(jié)構(gòu)化和能產(chǎn)生高效代碼滿足了這樣的需要,成為電子工程師在進(jìn)行嵌入式系統(tǒng)編程時(shí)的開發(fā)工具而得到了廣泛支持:早在1985年就推出了針對(duì)8051的C51編譯器,而其它流行嵌入式處理器系統(tǒng)如196系列,PIC系列,MOTORAL系列,MSP430系列,AD公司和TI公司的DSP系列都有功能強(qiáng)大的C語言編譯系統(tǒng)以及豐富的C語言庫函數(shù)。
2C語言編程與匯編語言編程相比的優(yōu)勢(shì)
在國內(nèi),大量的程序員仍采用匯編語言作為開發(fā)工具進(jìn)行編程,匯編語言有執(zhí)行效率高的優(yōu)點(diǎn),但其可移植性和可讀性差,以及它本身就是一種編程效率低下的低級(jí)語言,這些都使它的編程和維護(hù)極不方便,從而導(dǎo)致整個(gè)系統(tǒng)的可靠性也較差。而使用C語言進(jìn)行嵌入式系統(tǒng)的開發(fā),有著匯編語言編程不可比擬的優(yōu)勢(shì)。
(1)編程調(diào)試靈活方便
C語言作為語言的特點(diǎn)決定了它靈活的編程方式,同時(shí),當(dāng)前幾乎所有系列的嵌入式系統(tǒng)都有相應(yīng)的C語言級(jí)別的仿真調(diào)試系統(tǒng),使得它的調(diào)試環(huán)境十分方便。
(2)生成的代碼編譯效率高
當(dāng)前較好的C語言編譯系統(tǒng)的編譯效率已基本達(dá)到中等程序開發(fā)人員的水平。
(3)完全模塊化
一種功能由一個(gè)函數(shù)模塊完成,數(shù)據(jù)交換可方便地約定實(shí)現(xiàn),這樣十分有利于多人協(xié)同進(jìn)行大系統(tǒng)項(xiàng)目的合作開發(fā);同時(shí),由于C語言的模塊化開發(fā)方式,使得用它開發(fā)的程序模塊可不經(jīng)修改的被其它項(xiàng)目所用。可以很好地利用現(xiàn)成的大量C程序資源與豐富的庫函數(shù),從而地實(shí)現(xiàn)資源共享。
(4)可移植性好
由于不同系列的嵌入式系統(tǒng)C語言編譯工具都是以1983年的ANSI-C作為基礎(chǔ)進(jìn)行開發(fā)的,因此,一種C語言環(huán)境下所編寫的C語言程序,只需將部分與硬件相關(guān)的地方進(jìn)行適度修改,就可方便地移值到另外一種系列上,例如,C51下編寫的程序通過改寫頭文件,同時(shí)做少量的程序修改,可方便地移值到196或PIC系列上。也就是說,基于C語言環(huán)境下的嵌入式系統(tǒng)能基本達(dá)到平臺(tái)的無關(guān)性。
(5)便于項(xiàng)目維護(hù)管理
用C語言開發(fā)的代碼便于開發(fā)小組計(jì)劃項(xiàng)目、靈活管理、分工合作,以及后期維護(hù),基本上可以杜絕因開發(fā)人員變化而給項(xiàng)目進(jìn)度或后期維護(hù)或升級(jí)所帶來的影響,從而保證整個(gè)系統(tǒng)的高品質(zhì)、可靠性以及可升級(jí)性。
3嵌入式C語言編譯器與PC機(jī)上的標(biāo)準(zhǔn)ANSI-C編譯器的主要區(qū)別
不同系列的嵌入式系統(tǒng)的C編譯器,根據(jù)它所對(duì)應(yīng)的不同芯片系列有其各自的特點(diǎn),在這里,以KEIL公司的針對(duì)51系列的KEILC51編譯器為例,簡要說明它與ANSI-C的主要區(qū)別,其它的編譯系統(tǒng)與ANSI-C的差別,可具體參照指定編譯系統(tǒng)手冊(cè),找出它們的不同之處。清楚嵌入式系統(tǒng)的C編譯器與標(biāo)準(zhǔn)ANSI-C的區(qū)別是用C編譯器系統(tǒng)進(jìn)行嵌入式系統(tǒng)開發(fā)的前提條件。
不同的嵌入式C編譯系統(tǒng)之所以與ANSI-C有所不同,主要是由于它們所針對(duì)的硬件系統(tǒng)有其各自不同的硬件特點(diǎn),對(duì)國內(nèi)開發(fā)人員熟悉的51系列單片機(jī),有著為豐富的編譯系統(tǒng),其中為出色的當(dāng)屬KEIL(也就是大家熟知的FRANKLING,但FRANKLING只相當(dāng)于KEIL的早期產(chǎn)品,它是KEIL公司在美國銷售時(shí)曾使用的一個(gè)品牌)。
從頭文件來說,51系列有不同的廠家,不同的系列產(chǎn)品,如僅ATMEL公司就有大家熟悉的89c2051、89c51、89c52以及大家不熟悉的89s8252等系列產(chǎn)品。它們都是基于51系列的芯片,不同之處在于內(nèi)部資源如定時(shí)器、中斷、I/O等數(shù)量以及功能的不同,為了實(shí)現(xiàn)這些功能,只需將相應(yīng)的功能寄存器的頭文件加載在程序中就可實(shí)現(xiàn)它們所指定的不同功能。因此,KEILC51系列頭文件集中體現(xiàn)了各系列芯片的不同功能。
從數(shù)據(jù)類型來說,由于8051系列器件包含位操作空間和豐富的位操作指令,直接嵌入式C與ANSI-C相比,比ANSI-C多一種位類型,使得它能如同匯編一樣,靈活的進(jìn)行位指令操作。
從數(shù)據(jù)存儲(chǔ)類型來說,8051系列有片內(nèi)、片外程序存儲(chǔ)器,片內(nèi)、片外數(shù)據(jù)存儲(chǔ)器,片內(nèi)程序存儲(chǔ)器還分直接尋址區(qū)和間接尋址類型,分別對(duì)應(yīng)code、data、xdata、idata以及根據(jù)51系列特點(diǎn)而設(shè)定的pdata類型,使用不同的存儲(chǔ)器,將使程序執(zhí)行效率不同,在編寫C51程序時(shí),指定變量的存儲(chǔ)類型,這樣將有利于提高程序執(zhí)行效率(此問題將在后面專門講述)。與ANSI-C稍有不同,它只分SAMLL、COMPACT、LARGE模式,各種不同的模式對(duì)應(yīng)不同的實(shí)際硬件系統(tǒng),也將有不同的編譯結(jié)果。
從數(shù)據(jù)運(yùn)算操作和程序控制語句以及函數(shù)的使用上來講,它們幾乎沒有什么明顯的不同,只是在函數(shù)的使用上,由于嵌入式系統(tǒng)的資源有限,它的編譯系統(tǒng)不允許太多的程序嵌套,C語言的豐富的庫函數(shù)對(duì)程序開發(fā)提供了很大的幫助,但它的庫函數(shù)和ANSI-C也有一些不同之處,從編譯相關(guān)的不同來說,由于51系列是8位機(jī),擴(kuò)展16位字符不被C51所支持,其次,ANSI-C所具備的遞歸特性不被C51所支持,在C51中,要使用遞歸特性,必須用REENTRANT進(jìn)行申明才能使用。
KEILC51與標(biāo)準(zhǔn)ANSI-C在庫函數(shù),由于部分庫函數(shù)不適合嵌入式處理系統(tǒng),因此被排除在外,如字符屏幕和圖形函數(shù),也有一些庫函數(shù)繼續(xù)使用,但這些庫函數(shù)是廠家針對(duì)硬件特點(diǎn)相應(yīng)開發(fā)的,它們與ANSI-C的構(gòu)成及用法都有很大不同,如printf和scanf。在ANSI-C中這兩個(gè)函數(shù)通常用于屏幕打和,接收字符,而在KEILC51中,它們則主要用于串行數(shù)據(jù)的收發(fā)。
4如何充分利用C語言的特點(diǎn)和優(yōu)勢(shì)快速編出高效的C語言程序和對(duì)程序的優(yōu)化
盡管嵌入式C語言是一種強(qiáng)大而方便的開發(fā)工具,但開發(fā)人員如果要達(dá)到用C語言快速編出高效而易于維護(hù)的嵌入式系統(tǒng)程序,首先必須對(duì)C語言編程有較透徹的掌握,其次,還應(yīng)該對(duì)實(shí)際電子硬件系統(tǒng)有深入的理解,當(dāng)然在學(xué)習(xí)嵌入式C之前,較為熟練地掌握用匯編語言編程是十分必要的。下面,結(jié)合我們的實(shí)際開發(fā)經(jīng)驗(yàn)和實(shí)例,對(duì)一些需要特別注意和用心揣摩的地方進(jìn)行講解:
圖1工作流程圖
(1)以軟件工程的方式進(jìn)行總體程序設(shè)計(jì)安排,養(yǎng)成良好的、規(guī)范化的編程風(fēng)格,對(duì)能高效編出易于維護(hù)的嵌入式系統(tǒng)程序具有至關(guān)重要的作用,圖1給出較為合理的工作流程框圖。
使用正規(guī)、一致的編程風(fēng)格十分重要,建議使用"TheCProgrammingLanguage"一書中使用的書寫風(fēng)格,對(duì)變量命名采用大部分Windows程序員所采納的Hungarian命名法則,再加上模塊化的編程方式,詳盡的程序說明文檔,久而久之,能夠?qū)崿F(xiàn)高效編程,適應(yīng)多人分工協(xié)作,也適合項(xiàng)目開發(fā)的規(guī)范化,以及后期維護(hù),同時(shí),也便于程序的重新利用,這樣使人感到邏輯清楚,代碼簡單,注釋明白。
(2)靈活選擇變量的存儲(chǔ)類型是提高程序運(yùn)行效率的重要途徑。
由于嵌入式系統(tǒng)的資源有限,它有不同的有限存儲(chǔ)器資源,對(duì)需進(jìn)行位操作的變量,其存儲(chǔ)類型為bdata,對(duì)直接尋址存儲(chǔ)器類型為data,對(duì)間接尋址存儲(chǔ)器類型為idata(間接尋址存儲(chǔ)器也可訪問直接尋址存儲(chǔ)器區(qū)),對(duì)外部尋址存儲(chǔ)器類型為xdata,當(dāng)對(duì)不同的存儲(chǔ)器類型進(jìn)行操作時(shí),,編譯后的代碼執(zhí)行效率各不相同,內(nèi)部存儲(chǔ)器中直接尋址空間和間接尋址空間也不相同。
為了提高執(zhí)行效率,對(duì)存儲(chǔ)器類型的設(shè)定,應(yīng)該根據(jù)以下原則:只要條件滿足,盡量先使用內(nèi)部直接尋址存儲(chǔ)器(data),其次設(shè)定變量為間接尋址存儲(chǔ)器(idata),在內(nèi)部存儲(chǔ)器數(shù)量不夠的情況下,才使用外部存儲(chǔ)器,而且在外部存儲(chǔ)器中,優(yōu)先選擇(pdata),才是(xdata),而且,在內(nèi)部和外部存儲(chǔ)器共同使用的情況下,要合理分配存儲(chǔ)器,對(duì)經(jīng)常使用和計(jì)算頻繁的數(shù)據(jù),應(yīng)該使用內(nèi)部存儲(chǔ)器,其它的則使用外部存儲(chǔ)器。要根據(jù)它們的數(shù)量進(jìn)行分配,在例程中,因?yàn)閮?nèi)部數(shù)據(jù)存儲(chǔ)器的數(shù)量足夠,我們?cè)诶讨袑⑺凶兞慷忌昝鳛橹苯訑?shù)據(jù)存儲(chǔ)器,這樣能使程序訪問數(shù)據(jù)存儲(chǔ)器的時(shí)間少,從而提高程序運(yùn)行效率。
(3)合理設(shè)置變量類型以及設(shè)置運(yùn)算模式可以大大減小代碼量,提高程序執(zhí)行效率。
由于51系列是8位機(jī),它只能直接處理8位無符號(hào)數(shù)的運(yùn)算,而對(duì)其它類型的數(shù)據(jù)則需通過額外的算法來實(shí)現(xiàn),因此,在對(duì)變量進(jìn)行類型設(shè)置時(shí),要盡可能選用無符號(hào)的字符類型,盡量少使用有符號(hào)以及多字節(jié)類型,因此,在程序設(shè)計(jì)中,都盡量采用無符號(hào)數(shù)以提高運(yùn)算速度,以此避免進(jìn)行多余運(yùn)算。
在運(yùn)算時(shí),可以進(jìn)行定點(diǎn)運(yùn)算的盡量進(jìn)行定點(diǎn)運(yùn)算,避免進(jìn)行浮點(diǎn)運(yùn)算。如*2或/2,就可以使用移位操作來代替除法運(yùn)算。這樣不僅可以減少代碼量,同時(shí),還能大大提高程序執(zhí)行效率,例如,對(duì)直流信號(hào)通常采用一滯后濾波法。它的表達(dá)式為:Y0=(1-K)X0+K*Y0,在這個(gè)運(yùn)算式中,如果將K值取為0.75,則可以通過移位運(yùn)算代替浮點(diǎn)運(yùn)算或多字節(jié)的算術(shù)運(yùn)算,從而簡小編譯后的程序模塊,提高程序執(zhí)行效率。而將K值取為0.75并不會(huì)影響數(shù)據(jù)濾波的效果。
(4)靈活設(shè)置變量,高效利用存儲(chǔ)器,提高程序執(zhí)行效率。
在C嵌入式編程中,由于嵌入式系統(tǒng)資源有限,而C語言中通常是采用模塊化編程方法,如何實(shí)現(xiàn)高效的數(shù)據(jù)傳輸對(duì)提高程序執(zhí)行效率十分關(guān)鍵。
通常,在C程序中,子程序模塊中與其它變量無關(guān)的變量盡可能使用局部變量;對(duì)整個(gè)程序都要使用的變量將其設(shè)置為全局變量更合適;這樣,子程序模塊可以直接申明要使用的變量為外部變量即可。其次要結(jié)合C語言的特點(diǎn)進(jìn)行靈活的數(shù)據(jù)傳輸,C語言中所特有的指針、結(jié)構(gòu)、聯(lián)合如果能夠靈活使用,可以大大提高編程效率,也可以方便我們進(jìn)行數(shù)據(jù)傳輸,主程序和子程序傳遞數(shù)據(jù)量不能過多,否則影響執(zhí)行效率;子程序模塊和主程序模塊都定義了相同的數(shù)據(jù)類型,在進(jìn)行數(shù)據(jù)傳輸時(shí),主程序模塊和子程序模塊定義相同的數(shù)據(jù)類型,在進(jìn)行數(shù)據(jù)傳輸時(shí),只需將指針傳送到子程序模塊,這樣,既可以使不同的程序有很好的獨(dú)立性和良好的封裝性,又能實(shí)現(xiàn)不同程序數(shù)據(jù)的靈活高效傳輸,這樣,可使不同的開發(fā)人員開發(fā)出獨(dú)立性很強(qiáng)的通用子程序模塊。
(5)將匯編程序嵌入嵌入式C程序中,完成實(shí)時(shí)響應(yīng)或精確控制任務(wù)。
在一些實(shí)時(shí)性要求很高,如中斷程序處理、數(shù)據(jù)采集程序、實(shí)時(shí)控制程序以及一些實(shí)時(shí)的帶符號(hào)或多位運(yùn)算中,建議將匯編語言嵌入C程序中進(jìn)行處理。
在KEILC51的C編譯系統(tǒng)中,C程序能夠與匯編程序?qū)崿F(xiàn)方便靈活的接口,在C程序中調(diào)用匯編十分方便靈活,對(duì)二者調(diào)用的主要難度在于:實(shí)現(xiàn)數(shù)據(jù)的準(zhǔn)確傳輸,匯編與C中實(shí)現(xiàn)數(shù)據(jù)傳輸可通過兩種方式,一是利用工作寄存器進(jìn)行數(shù)據(jù)傳送,這種方式安全,但根據(jù)傳送數(shù)據(jù)類型的不同,只能傳送1個(gè)~3個(gè)參數(shù),另外一種方案是指定特定的數(shù)據(jù)區(qū),二者在特定的工作區(qū)內(nèi)進(jìn)行數(shù)據(jù)傳輸,這種方式,其傳遞數(shù)據(jù)量可自行控制,但不安全,需要開發(fā)人員仔細(xì)控制。
(6)利用豐富的庫函數(shù),可以大大提高編程效率。在KEILC51中,開發(fā)廠家提供了許多常用的庫函數(shù),以供編程人員使用。開發(fā)人員可靈活地使用這些函數(shù),以提高編程效率。
在實(shí)際進(jìn)行項(xiàng)目開發(fā)時(shí),如果能遵守科學(xué)的工程開發(fā)規(guī)則,靈活地運(yùn)用C語言的強(qiáng)大功能,熟悉硬件特點(diǎn),就能夠在較短時(shí)間內(nèi)編寫出高效率、高可靠、易維護(hù)的嵌入式系統(tǒng)的執(zhí)行代碼。