<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > HC(S)08單片機的高效C語(yǔ)言編程

HC(S)08單片機的高效C語(yǔ)言編程

——
作者:飛思卡爾半導體 李志堅 楊沛騏 時(shí)間:2007-02-12 來(lái)源:電子產(chǎn)品世界 收藏

摘要: 本文主要討論在開(kāi)發(fā)環(huán)境下如何寫(xiě)出適用于HC(S)08的高效程序。首先介紹編程的特點(diǎn),然后介紹HC(S)08系列編程方面的優(yōu)勢,并給出各種高效C代碼的例子程序和相關(guān)注釋。

關(guān)鍵詞: ;C語(yǔ)言編程;HC(S)08;

的C語(yǔ)言編程

C語(yǔ)言最初是為UNIX操作系統的開(kāi)發(fā)與應用而開(kāi)發(fā)設計的,目前已經(jīng)成為一種非常流行的編程語(yǔ)言。 因為C語(yǔ)言既有高級語(yǔ)言可讀性強和易于維護升級的特點(diǎn),又能很好的支持位運算操作,所以C常常被稱(chēng)為中級語(yǔ)言。另外,C語(yǔ)言數據類(lèi)型的定義比較自由,所以用它比較容易寫(xiě)出結構化的程序。和匯編語(yǔ)言相比,大多數電子工程師對C語(yǔ)言的代碼效率更關(guān)注。他們關(guān)心的問(wèn)題主要集中在RAM、ROM和堆??臻g的使用效率以及編譯器編譯優(yōu)化效率等方面。要寫(xiě)出一個(gè)高效的C語(yǔ)言程序,工程師們必須清楚的了解嵌入式系統中C語(yǔ)言編程的特點(diǎn),掌握MCU的硬件架構和領(lǐng)會(huì )C語(yǔ)句是如何轉換成匯編語(yǔ)句的。從臺式機轉向嵌入式系統編程必須先了解嵌入式系統的特點(diǎn)。

* 存儲空間有限:盡管有些MCU有外部總線(xiàn)可以外擴存儲器,但大多數情況下,程序越小系統成本就越低,所以要盡可能優(yōu)化系統縮減代碼,經(jīng)濟地使用RAM(包括堆棧)和ROM存儲空間。

* 硬件導向:在臺式機上常常需要一個(gè)美觀(guān)的人機交互界面,但是在嵌入式系統中更關(guān)注的是對器件的控制。這就需要我們不僅要掌握這些器件的特性,還要了解與MCU時(shí)鐘有關(guān)的操作(比如中斷響應),在精準的時(shí)間點(diǎn)上對通用I/O口(GPIO)操作等。某些情況下,還需要根據生成的匯編語(yǔ)句去計算精確的運行時(shí)間,甚至直接用匯編語(yǔ)句編寫(xiě)代碼。

* 特殊的處理:與臺式機系統不同,MCU系統的編程常會(huì )用到一些非標準的語(yǔ)法來(lái)幫助編譯器根據不同的MCU內核編譯生成不同的代碼。例如,在HC(S)08單片機中,有一種直接頁(yè)(或者叫零頁(yè),地址從0x00到0xFF的頁(yè)面)的尋址模式。這種尋址模式比其他尋址模式的效率要高,所以我們常常會(huì )用一些編譯器指令來(lái)告訴編譯器把常用的變量放置在零頁(yè)地址內。另外,不同的MCU內核有不同的中斷處理方式、不同的存儲模式和不同的硬件語(yǔ)法結構。要充分利用MCU內核的優(yōu)點(diǎn),我們就必須靈活的使用一些關(guān)鍵字和特定的語(yǔ)法。

通常來(lái)說(shuō),在嵌入式系統中,一個(gè)優(yōu)秀的程序員用匯編寫(xiě)出的代碼的效率要比C語(yǔ)言寫(xiě)出的代碼高。但是,用C語(yǔ)言更容易寫(xiě)出一個(gè)集效率、可讀性和可移植性于一身的好代碼。要寫(xiě)出高效的C代碼,除了程序員有豐富的經(jīng)驗外,MCU內核對于C語(yǔ)言支持的好壞也起了很重要的作用。飛思卡爾公司的HC(S)08系列單片機的內核在這方面是比較優(yōu)秀的,它可以很高效的支持C語(yǔ)言的編程。

HC(S)08系列單片機的嵌入式C語(yǔ)言

HC08和系列單片機都是采用CPU08內核,該內核能很好的支持C語(yǔ)言編程(更準確的說(shuō),用的是增強型內核,對C的支持更好)。CPU08內核中有幾種尋址模式對C的支持非常好,第一種是變址后自加一尋址模式,這種尋址模式對于查表的操作十分有效。舉例來(lái)說(shuō),采用這種尋址模式的4字節指令加上CBEQ和BRA指令可以快速的從H:X寄存器所指向的表格中找到和累加寄存器A中相同值的字節。第二種是存儲器到存儲器的尋址,這種尋址方式能有效的支持變量的賦值。在零頁(yè)內(地址從0x00 到 0xFF)數據拷貝,只需用一句MOV指令就可以了。最后一種但也很有用的尋址模式就是堆棧指針尋址。堆棧指針尋址使得函數參數的傳遞以及函數內局部變量的訪(fǎng)問(wèn)變得十分容易。另外,當中斷屏蔽不用時(shí),堆棧指針可以用作第二個(gè)變址寄存器,這對多重表格的訪(fǎng)問(wèn)很有用。堆棧在C中的作用主要有三點(diǎn):子程序參數的傳遞、局部變量的存放和遞歸函數的調用。CPU寄存器中如果沒(méi)法存放子程序的參數(包括地址),可以把它們存放在堆棧中。CPU08內核在硬件上不僅提供了堆棧指針,還提供了堆棧指針尋址模式,這樣可以在不通過(guò)出棧入棧操作的情況下直接提取參數值。有了這種尋址模式,也就不需要給局部變量專(zhuān)門(mén)開(kāi)辟一段存儲空間了。

高效C代碼的編寫(xiě)

在討論代碼優(yōu)化之前,我們先要了解以下內容。

* 編程經(jīng)驗—隨著(zhù)程序員編程經(jīng)驗的增長(cháng),優(yōu)化代碼的技術(shù)也會(huì )相應提高。
* 對指令集映射的理解—單片機的內核不同其架構和特性也不相同。必須清楚C語(yǔ)言和匯編語(yǔ)句之間的映射關(guān)系,即這句C語(yǔ)句生成了哪幾句匯編語(yǔ)句。
* 對編譯器/連接器特性的了解—單片機不同其編譯器也不同,即使是同一內核的單片機,不同編譯器的代碼效率和優(yōu)化方法也是不同的。
* 清楚地認識系統—除了要了解與系統成本相關(guān)的內存,也要了解系統中其他重要的部分,比如對系統運行時(shí)間和運行速度的控制、哪些存儲資源有限(RAM、ROM/Flash 和堆棧等) 以及系統的可讀性等等。

從減少ROM、RAM和堆??臻g的消耗以及提高系統執行速度的角度來(lái)說(shuō),優(yōu)化代碼的方法有許多種。這里不可能給出所有的方法,只是將一些能顯著(zhù)提高代碼效率的方法羅列出來(lái)。

變量的定義

要寫(xiě)出好的程序,變量起了很重要的作用,因為大部分的代碼都是和數據有關(guān)的操作。即使是在以硬件控制為主的系統中,變量也起了很大的作用,MCU的大部分工作是在把外部硬件(如傳感器,按鈕等)的數值讀進(jìn)來(lái),進(jìn)行運算處理(和存儲)之后輸出相應的結果,用以驅動(dòng)外圍硬件。在使用變量的時(shí)候,以下幾點(diǎn)需要注意:

(1)變量的大小
不同架構的MCU中,數據類(lèi)型的長(cháng)度是不同的,這對于代碼效率有很大的影響。在8位機中,例如HC(S)08系列單片機,8bit數據的執行效率是最高的,因為大部分的指令都以字節為運算單位。在臺式機環(huán)境下,我們通常用int(整型)作為數據類(lèi)型,但是int數據的長(cháng)度在不同的機器和編譯器中是不同的。所以,要得到高效的C語(yǔ)言程序,我們應該使用類(lèi)型定義(typedef)的方式規定各種數據類(lèi)型的長(cháng)度,盡可能的采用8位數據長(cháng)度。例如,用uint8_t表示一個(gè)無(wú)符號8位整型數據(一個(gè)字節),用uint16_t表示一個(gè)無(wú)符號16位整型數據。在運算表達式中,采用類(lèi)型轉換方式把表達式結果值的數據長(cháng)度縮減到最低所需。表1給出了零頁(yè)地址內不同數據長(cháng)度的兩個(gè)變量相加得到不同數據長(cháng)度結果所需代碼的多少。從中我們可以看出,數據類(lèi)型長(cháng)度的選擇對于代碼效率的影響是很大的。

(2)無(wú)符號數和定點(diǎn)數
除了數據長(cháng)度,數據是否是有符號數也會(huì )影響代碼效率。比如兩個(gè)8位長(cháng)度的有符號數相加,得到一個(gè)16位長(cháng)度的有符號數,這需要31個(gè)字節的代碼,有符號數與無(wú)符號數進(jìn)行比較運算所需的代碼也比兩個(gè)都是無(wú)符號數運算所需的代碼要多。對于運算復雜、精度要求較高的場(chǎng)合,常常需要用到浮點(diǎn)運算。如果控制器硬件上帶有浮點(diǎn)運算單元的話(huà),執行起來(lái)效率會(huì )比較高。但是,大多數8位MCU只支持整數運算。對于浮點(diǎn)運算,既要得到精確的計算結果又不降低代碼效率的話(huà),我們可以先把數據按比例放大,運算結束后再按相同比例縮小。例如,要進(jìn)行十進(jìn)制小數的運算,可以用101表示10.1,待運算結束后,再用除法得到我們所需的浮點(diǎn)值。因為HC(S)08系列單片機的乘除運算效率很高,把浮點(diǎn)數轉成定點(diǎn)數運算,能提高代碼效率。此外,還可以用移位的方法來(lái)替代乘除運算,Codewarrior支持用移位來(lái)替代2的倍數的乘除運算。當然,是否采用移位方式由程序員自己決定。當然,在這個(gè)過(guò)程中需要考慮是否有溢出、取整是否合理等問(wèn)題,否則不但可能得到錯誤的結果,還有可能需要大的數據長(cháng)度(比如32位的數據)來(lái)存儲中間值,反而降低了代碼效率。

(3)全局變量、靜態(tài)變量和局部變量
在嵌入式系統中,全局變量的使用可以有效地提高代碼效率。全局變量一般會(huì )有一個(gè)固定的存儲位置,如果把它放在零頁(yè)地址中,代碼效率將大大提高。給零頁(yè)地址中的全局變量賦值可以采用MOV指令,只有3個(gè)字節的代碼。而給非零頁(yè)地址中的全局變量賦值就需要用LDA和STA指令,這需要5個(gè)字節的代碼。如果用局部變量,因為它是存放在堆棧中的,所以在某些情況下需要用到H:X寄存器,而把堆棧指針?lè )诺紿:X寄存器中去需要4到6個(gè)字節的代碼(如果堆棧是在零頁(yè)地址內)。在全局資源有限的情況下,使用局部變量反而代碼效率更高。這里的建議是把那些要頻繁使用的或者有位操作的變量定義為全局變量放置在零頁(yè)地址內,這樣能極大的提高代碼效率。使用靜態(tài)變量也是一種非常有用的方法,可以在把變量存儲在全局地址范圍的同時(shí)保持代碼的可移植性和再使用性。但是,用來(lái)存放靜態(tài)變量的RAM空間不能釋放出來(lái)給其他子程序使用。

靜態(tài)函數

把函數定義成靜態(tài)函數對于提高代碼效率是很有必要的。因為模塊內的靜態(tài)函數只能被模塊中的函數所調用,不能被模塊以外的函數調用。因此,編譯器會(huì )有意識的把靜態(tài)函數放置在靠近其調用者的地方,這樣就可以用代碼少且執行速度快的指令去訪(fǎng)問(wèn)靜態(tài)函數。比如用BSR(短調用指令)而不是JSR(長(cháng)調用指令)。BSR是雙字節指令,花費4個(gè)總線(xiàn)周期;JSR指令一般占用1~3個(gè)字節(跳轉到H:X寄存器所指的地址占用一字節,但把地址移入H:X寄存器需要幾個(gè)字節的代碼)和4~6個(gè)總線(xiàn)周期。

數組和指針

當需要訪(fǎng)問(wèn)一系列數據的時(shí)候,在C語(yǔ)言中通常使用數組或者指針的方式。用固定序號的訪(fǎng)問(wèn)方式(如Array[0])生成的代碼最少,執行速度也比遞增索引方式(如Array[i++])快。在有些應用場(chǎng)合,數組指針(*(Array++))比數組具有更好的靈活性,因為它可以間接的存取數據。但是,采用數組指針的話(huà)會(huì )占用較多的ROM(額外的代碼用于指針的初始化和使用過(guò)程中)和RAM(可能需要其他指針指向數組)。數組和指針除了用于數據的存取也可用于對函數的訪(fǎng)問(wèn)。在嵌入式系統中,不同情況下經(jīng)常需要調用不同的函數。例如,在通訊中要根據不同的輸入數據給出相對應的處理和應答。在C中一般有三種方式來(lái)處理這類(lèi)情形:嵌套if語(yǔ)句、"Switch-case"語(yǔ)句、函數指針。下面是這三種方法的例子,根據狀態(tài)寄存器中不同的狀態(tài)值調用相應的響應函數。

i) 嵌套的if語(yǔ)句:
if (STATUS = = A) React_A();
else if (STATUS = = B) React_B();
else if ....
ii) switch-case語(yǔ)句:
switch (STATUS)
 case (A): React_A(); break;
 case (B): React_B(); break;
 ...
iii) 函數指針: (假定狀態(tài)A, B, ... 是順序編號的值,或是枚舉類(lèi)型值)
void React_Func[] = {React_A, React_B, ...};
...
React_Func[STATUS]();

具體采用哪種方式,依據反復次數而定。表2給出了不同方法對ROM和RAM空間的占用情況。從中可看出“switch”方式的可讀性最強,但在反復次數少(函數個(gè)數少)的情況下,占用的空間最大。

“if”方式的可讀性較好,占用的空間也比較小。而“pointer”方式占用ROM的空間相對變化不大,但占用許多RAM空間。

存儲模式和零頁(yè)的使用

不同的MCU有不同的存儲模式。在 for HC(S)08 (V3.1)中,建立工程的時(shí)候有small和tiny兩種模式可供選擇:SMALL模式,如果沒(méi)有特殊的說(shuō)明,所有的指針和函數地址都被假定為16位的地址,此模式中代碼和數據都被存儲在64k的地址空間內;TINY 模式,所有的數據包括堆棧都分配在零頁(yè)地址空間內,如果沒(méi)用關(guān)鍵字_far作特殊說(shuō)明,所有數據指針都被假定為8位地址,但是代碼的地址空間仍然是64k,函數指針也仍是16位的長(cháng)度。

前面討論中說(shuō)過(guò),變量放在零頁(yè)地址內生成的代碼較少,而且能有效的支持位運算。在HC(S)08系列單片機中,外圍寄存器一般占用$00-$3F的地址空間,所以留給RAM的零頁(yè)地址空間是有限的。為了縮減生成的代碼,就要把頻繁使用的變量放在零頁(yè)內。要根據子程序、函數參數和局部變量使用的情況,確定堆棧的使用頻率,如果頻率高就把堆棧放置在零頁(yè)地址內。減少生成的代碼,我們也要減少子程序中的參數(因為要用到A和HX寄存器),把經(jīng)常使用的臨時(shí)變量定義成全局變量放在零頁(yè)地址中。當然,全局變量是共享的,所以用的時(shí)候我們要格外小心。下面的例程中,在Calc()函數中,可以改變全局變量gTemp2和gTemp3的值,但不能改變變量gTemp1的值,因為一開(kāi)始就對子程序進(jìn)行了這個(gè)設定。通常,好的變量名可以幫我們清楚的區分變量的作用范圍。比如分別以1、2、3結尾的變量,可以設定等級1的子程序只能用1結尾的變量,等級2的子程序只能用2、3結尾的變量。

uint8_t gTemp1, gTemp2, gTemp3;     // 存放臨時(shí)數據的全局變量,所有函數都可以訪(fǎng)問(wèn)
void_t Calc( uint8_t in) {
   gTemp3 = 0 ;
   for (gTemp2 = 5 ; gTemp2 !=0 ; gTemp2--) gTemp3 += ADCR * t_in;
}

void main( ) {
...
for (gTemp1 = 0; gTemp1 < 3; gTemp1++)
  Calc(array[gTemp1]) ; 
...
}

初始化的優(yōu)化

在CodeWarrior中,每個(gè)工程都有一個(gè)模板,Start-up啟動(dòng)函數已經(jīng)預先寫(xiě)好,我們可以在建工程的時(shí)候選擇是否采用ANSI標準初始化程序。通常,標準初始化程序的代碼效率并不高(可以參看start08.c文件中的源程序)。為了減少生成的代碼,我們應該采用非ANSI標準的初始化程序,由用戶(hù)自行編寫(xiě)。比如,僅做堆棧指針初始化、RAM清空和跳轉到main函數三項工作,用如下匯編代碼實(shí)現。
asm {
        clra                           ; 得到清零數據
        ldhx    #MAP_RAM_last         ; 指向RAM的尾部
        stx     MAP_RAM_first         ; 使得RAM起始地址內的數值非零
        txs                            ; 初始化堆棧指針
ClearRAM:
        psha                           ; 清空當前RAM地址
        tst     MAP_RAM_first         ; 檢測是否完成RAM的清空
        bne     ClearRAM              ; 沒(méi)有完成就繼續

        txs                            ; 初始化堆棧指針
        jmp     main                  ; 跳轉到main()函數
 }

除了這些通用的起始程序,還需要對硬件和變量進(jìn)行初始化。盡管寄存器都有默認值,但仍要培養用軟件對硬件初始化的好習慣。對于變量,最好初始值為零,因為清空RAM代碼已經(jīng)完成了這個(gè)工作。為了防止代碼臃腫,建議把相同初始值的變量歸為一組,這樣可以用循環(huán)的方式對它們進(jìn)行初始化。在優(yōu)化代碼的時(shí)候,要特別注意那些可變型volatile變量(比如寄存器),因為編譯器是不會(huì )對這些變量進(jìn)行優(yōu)化的。

結語(yǔ)

本文簡(jiǎn)述了一些優(yōu)化代碼的方法,包括變量的選擇、使用靜態(tài)類(lèi)型、數組和指針的挑選、如何使用存儲模式和如何進(jìn)行初始化等。但是,這僅是所有方法的一部分。一個(gè)高效的C語(yǔ)言程序,不僅要代碼少、執行速度快,而且要清楚、簡(jiǎn)潔、準確和易注釋。此外,程序要有一個(gè)好的架構,便于移植和維護。代碼的再使用性(reuse)也是一個(gè)關(guān)鍵因素,這不在于代碼本身,而在于它能減少開(kāi)發(fā)調試時(shí)間。所以說(shuō),高效的C語(yǔ)言程序是各種因素的綜合體,需要我們全面考量。

c語(yǔ)言相關(guān)文章:c語(yǔ)言教程


單片機相關(guān)文章:單片機教程


單片機相關(guān)文章:單片機視頻教程


單片機相關(guān)文章:單片機工作原理




評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>