在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

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

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

            ——
            作者:飛思卡爾半導體 李志堅 楊沛騏 時間:2007-02-12 來源:電子產品世界 收藏

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

            關鍵詞: ;C語言編程;HC(S)08;

            的C語言編程

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

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

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

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

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

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

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

            高效C代碼的編寫

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

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

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

            變量的定義

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

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

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

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

            靜態(tài)函數

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

            數組和指針

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

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

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

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

            存儲模式和零頁的使用

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

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

            uint8_t gTemp1, gTemp2, gTemp3;     // 存放臨時數據的全局變量,所有函數都可以訪問
            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中,每個工程都有一個模板,Start-up啟動函數已經預先寫好,我們可以在建工程的時候選擇是否采用ANSI標準初始化程序。通常,標準初始化程序的代碼效率并不高(可以參看start08.c文件中的源程序)。為了減少生成的代碼,我們應該采用非ANSI標準的初始化程序,由用戶自行編寫。比如,僅做堆棧指針初始化、RAM清空和跳轉到main函數三項工作,用如下匯編代碼實現。
            asm {
                    clra                           ; 得到清零數據
                    ldhx    #MAP_RAM_last         ; 指向RAM的尾部
                    stx     MAP_RAM_first         ; 使得RAM起始地址內的數值非零
                    txs                            ; 初始化堆棧指針
            ClearRAM:
                    psha                           ; 清空當前RAM地址
                    tst     MAP_RAM_first         ; 檢測是否完成RAM的清空
                    bne     ClearRAM              ; 沒有完成就繼續(xù)

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

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

            結語

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

            c語言相關文章:c語言教程


            單片機相關文章:單片機教程


            單片機相關文章:單片機視頻教程


            單片機相關文章:單片機工作原理




            評論


            相關推薦

            技術專區(qū)

            關閉