新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > PIC 單片機 C 語言編程簡介(4)

        PIC 單片機 C 語言編程簡介(4)

        作者: 時間:2016-11-22 來源:網絡 收藏
        11.9

        C 和匯編混合編程

        本文引用地址:http://www.104case.com/article/201611/319598.htm

        有兩個原因決定了用 C 語言進行單片機應用程序開發時使用匯編語句的必要性:單片


        機的一些特殊指令操作在標準的 C 語言語法中沒有直接對應的描述,例如 PIC 單片機的清

        看門狗指令“clrwdt”和休眠指令“sleep”;單片機系統強調的是控制的實時性,為了實現這

        一要求,有時必須用匯編指令實現部分代碼以提高程序運行的效率。這樣,一個項目中就會

        出現 C 和匯編混合編程的情形,我們在此討論一些混合編程的基本方法和技巧。

        11.9.1 嵌入行內匯編的方法

        C 原程序中直接嵌入匯編指令是最直接最容易的方法。如果只需要嵌入少量幾條的

        匯編指令,PICC 提供了一個類似于函數的語句:

        asm(“clrwdt”);

        雙引號中可以編寫任何一條 PIC 的標準匯編指令。例如:

        for (;;) {

        asm("clrwdt"); //清看門狗

        Task();

        ClockRun();


        asm("sleep");

        asm("nop");

        }


        //休眠

        //空操作延時

        例 11-8 逐行嵌入匯編的方式

        如果需要編寫一段連續的匯編指令,PICC 支持另外一種語法描述:用“#asm”開始匯

        編指令段,用“#endasm”結束。例如下面的一段嵌入匯編指令實現了將 0x20~0x7F 間的

        RAM 全部清零:

        #asm

        movlw 0x20

        movwf _FSR

        clrf _INDF

        incf _FSR,f

        btfss _FSR,7

        goto $-3

        #endasm

        例 11-9 整段嵌入匯編的方式

        11.9.2 匯編指令尋址 C 語言定義的全局變量

        C 語言中定義的全局或靜態變量尋址是最容易的,因為這些變量的地址已知且固定。按

        C 語言的語法標準,所有 C 中定義的符號在編譯后將自動在前面添加一下劃線符“_”,因

        此,若要在匯編指令中尋址 C 語言定義的各類變量,一定要在變量前加上一“_”符號,我

        們在上面例 11-9 中已經體現了這一變量引用的法則,因為 FSR 和 INDF 等所有特殊寄存器

        是以 C 語言語法定義的,因此匯編中需要對其尋址時前面必須添加下劃線。

        對于 C 語言中用戶自定義的全局變量,用行內匯編指令尋址時也同樣必須加上“_”

        下面的例 11-10 說明了具體的引用方法:

        volatile unsigned char tmp; //定義位于 bank0 的字符型全局變量

        void Test(void)

        {

        #asm

        clrf _STATUS

        movlw 0x10

        movwf _tmp

        #endasm

        if (tmp==0x10) {

        ;

        }

        }

        //測試程序

        //開始行內匯編

        //選擇 bank0

        //設定初值

        //tmp=0x10

        //結束行內匯編

        //開始 C 語言程序


        例 11-10 行內匯編尋址 C 全局變量(位于 bank0)

        上面的例子說明了匯編指令中尋址 C 語言所定義變量的基本方法。PICC 在編譯處理嵌

        入的行內匯編指令時將會原封不動地把這些指令復制成最后的機器碼。所有對 C 編譯器所

        作的優化設定對這些行內匯編指令而言將不起任何作用。編程員必須自己負責編寫最高效的

        匯編代碼,同時處理變量所在的 bank 設定。對于定義在其它 bank 中的變量,還必須在匯編

        指令中加以明確指示,見例 11-11 的代碼范例。

        volatile bank1 unsigned char tmpBank1; //定義位于 bank1 的字符型全局變量

        volatile bank2 unsigned char tmpBank2; //定義位于 bank2 的字符型全局變量

        volatile bank3 unsigned char tmpBank3; //定義位于 bank3 的字符型全局變量

        void Test(void)

        {

        #asm

        bcf _STATUS,6

        bsf _STATUS,5

        movlw 0x10

        movwf _tmpBank1^0x80

        bsf _STATUS,6

        bcf _STATUS,5

        movlw 0x20

        //測試程序

        //開始行內匯編

        //選擇 bank1

        //設定初值

        //tmpBank1=0x10

        //選擇 bank2

        //設定初值


        movwf _tmpBank1^0x100 //tmpBank2=0x20

        bsf _STATUS,6

        bsf _STATUS,5

        movlw 0x30


        //選擇 bank3

        //設定初值


        movwf _tmpBank1^0x180 //tmpBank1=0x30


        }


        #endasm


        //結束行內匯編

        例 11-11 行內匯編尋址 C 全局變量(非 bank0 變量)


        通過上面的代碼實例,我們可以掌握這樣一個規律:在行內匯編指令中尋址 C 語言定

        義的全局變量時,除了在尋址前設定正確的 bank 外,在指令描述時還必須在變量上異或

        所在 bank 的起始地址,實際上位于 bank0 的變量在匯編指令中尋址時也可以這樣理解,只

        是異或的是 0x00,可以省略。如果你了解 PIC 單片機的匯編指令編碼格式,上面異或的 bank

        起始地址是無法在真正的匯編指令中體現的,其目的純粹是為了告訴 PICC 連接器變量所在

        的 bank,以便連接器進行 bank 類別檢查。

        11.9.3 匯編指令尋址 C 函數的局部變量

        前面已經提到,PICC 對自動型局部變量(包括函數調用時的入口參數)采用一種“靜

        態覆蓋”技術對每一個變量確定一個固定地址(位于 bank0),因此嵌入的匯編指令對其尋

        址時只需采用數據寄存器的直接尋址方式即可,唯一要考慮的是如何才能在編寫程序時知道

        這些局部變量的尋址符號(具體地址在最后連接后才能決定,編程時無需關心)。一個最實

        用也是最可靠的方法是先編寫一小段 C 代碼,其中有最簡單的局部變量操作指令,然后參

        考圖 11-5(B)對話框選擇“Compile to assembly only”,把此 C 原代碼編譯成對應的 PICC 匯

        編指令;查看 C 編譯器生成的匯編指令是如何尋址這些局部變量的,你自己編寫的行內匯

        編指令就采用同樣的尋址方式。例如,例 11-12 的一小段 C 原代碼編譯出的匯編指令

        //C 原程序代碼

        void Test(unsigned char inVar1, inVar2)

        {

        unsigned char tmp1, tmp2;

        inVar1++;

        inVar2--;

        tmp1 = 1;

        tmp2 = 2;

        }

        //編譯器生成的匯編指令

        _Test

        ; _tmp1 assigned to ?a_Test+0 //tmp1 的尋址符為 ?a_Test+0


        _Test$tmp1 set


        ?a_Test


        ; _tmp2 assigned to ?a_Test+1 //tmp2 的尋址符為 ?a_Test+1


        _Test$tmp2 set


        ?a_Test+1


        ; _inVar1 assigned to ?a_Test+2 //inVar1 的尋址符為 ?a_Test+2

        _Test$inVar1 set ?a_Test+2

        44line


        ;_inVar1 stored from w

        bcf

        bcf

        movwf ?a_Test+2


        //第一個字符型行參由 W 寄存器傳遞


        ;ht16.c: 43: unsigned char tmp1, tmp2;

        incf

        45line

        ;ht16.c: 45: inVar2--;


        decf

        46line

        ;ht16.c: 46: tmp1 = 1;

        clrf

        incf

        47line

        ;ht16.c: 47: tmp2 = 2;

        movlw 2

        movwf ?a_Test+1

        48line

        ;ht16.c: 48: }

        return


        //行參 inVar2 的尋址符為 ?_Test


        例 11-12 PICC 實現局部變量操作的尋址方式

        基于上面得到的 PICC 編譯后局部變量的尋址方式,我們在 C 語言程序中用嵌入匯編指

        令時必須采樣同樣的尋址符以實現對應變量的存取操作,見下面的例 11-13。

        //C 原程序代碼

        void Test(unsigned char inVar1, inVar2)

        {

        unsigned char tmp1, tmp2;

        #asm

        //開始嵌入匯編


        incf

        decf

        movlw

        addwf

        rrf

        0x10


        ?a_Test+0,f

        ?a_Test+1,f

        ?a_Test+2,f

        ?_Test,w


        //tmp1++;

        //tmp2--;

        //inVar1 +=

        //inVar2 循環右移一位

        }


        rrf

        #endasm


        ?_Test,f

        //結束嵌入匯編


        例 11-13 嵌入匯編指令實現局部變量尋址操作

        如果局部變量為多字節形式組成,例如整型數、長整型等,必須按照 PICC 約定的存儲

        格式進行存取。前面已經說明了 PICC 采用“Little endian”格式,低字節放在低地址,高字

        節放在高地址。下面的例 11-14 實現了一個整型數的循環移位,在 C 語言中沒有直接針對循

        環移位的語法操作,用標準 C 指令實現的效率較低。

        //16 位整型數循環右移若干位

        unsigned int RR_Shift16(unsigned int var, unsigned char count)

        {

        }


        while(count--)

        {

        #asm

        rrf ?_RR_Shift16+0,w

        rrf ?_RR_Shift16+1,f

        rrf ?_RR_Shift16+0,f

        #endasm

        }

        return(var);


        //移位次數控制

        //開始嵌入匯編

        //最低位送入 C

        //var 高字節右移 1 位,C 移入最高位

        //var 低字節右移 1 位

        //結束嵌入匯編

        //返回結果


        例 11-14 嵌入匯編指令對多字節變量的操作

        11.9.4 混合編程的一些經驗

        C 和匯編語言混合編程可以使單片機應用程序的開發效率和程序本身的運行效率達到

        最佳的配合。筆者從實際應用中得到一些經驗供讀者一起分享。

        慎用匯編指令

        相比于匯編語言,用 C 語言編程的優勢是毋庸置疑的:開發效率大大提高、人性化的

        語句指令加上模塊化的程序易于日常管理和維護、程序在不同平臺間的移植方便。所以既然

        用了 C 語言編程,就盡量避免使用嵌入匯編指令或整個地編寫匯編指令模塊文件。PICC 已

        具備高效的優化功能,如果在寫 C 原程序時就十分注意程序的編譯和運行效率問題,加上

        PICC 的后道編譯優化,最后得到的代碼效率不會比全部用匯編編寫的代碼差多少,尤其是

        程序量較大時。另外,PICC 對數據存儲空間的利用率肯定比用戶人工定位變量時的利用率

        要高,同時還提供完整的庫函數支持。C 語言的語法功能強大,能夠高效率地實現絕大部分

        控制和運算功能。因此,除了一些十分強調單片機運行時間的代碼或 C 語言沒有直接對應

        的操作可以考慮用匯編指令實現外,其它部分都應該用 C 語言編寫。

        以上面的例 11-14 進一步說明,變量的循環右移操作用 C 語言實現非常不方便,PIC 單

        片機已有對應的移位操作匯編指令,因此用嵌入匯編的形式實現效率最高。同時對移位次數

        的控制,本質上說變量 count 的遞減判零也可以直接用匯編指令實現,但這樣做節約不了多

        少代碼,用標準的 C 語言描述更直觀,更易于維護。

        一句話:用了 C 語言后,就不要再老想著用匯編。

        盡量使用嵌入匯編

        這和上面的慎用匯編指令的說法并不矛盾。如果確實需要用匯編指令實現部分代碼以提

        高運行效率,應盡量使用行內匯編,避免編寫純匯編文件(*.as 文件)。

        雖然 PICC 支持 C 和匯編原程序模塊存在于同一個項目中,但要編寫純匯編文件必須首

        先了解 PICC 特有的匯編語法結構。Hitech 公司提供了完整的文檔介紹其匯編器的使用方法,

        有興趣者可以從其網站上下載 PICC 的用戶使用手冊查看。

        筆者認為,類似于純匯編文件的代碼也可以在 C 語言框架下實現,方法是基于 C 標準

        語法定義所有的變量和函數名,包括需要傳遞的形式參數、返回參數和局部變量,但函數內

        部的指令基本用嵌入匯編指令編寫,只有最后的返回參數用 C 語句實現。這樣做后函數的

        運行效率和純匯編編寫時幾乎一模一樣,但各參數的傳遞統一用 C 標準實現,這樣管理和

        維護就比較方便。例如下面的例 11-15 實現一個字節變量的偶校驗位計算。

        bit EvenParity(unsigned char data)

        {

        #asm


        swapf ?a_EvenParity+0,w

        xorwf ?a_EvenParity+0,f

        rrf ?a_EvenParity+0,w

        xorwf ?a_EvenParity+0,f

        btfsc ?a_EvenParity+0,2

        incf ?a_EvenParity+0,f

        #endasm

        //至此,data 的最低位即為偶校驗位

        if (data&0x01) return(1);

        else return(0);

        }


        //入口參數 data 的尋址符為 ?a_EvenParity+0


        例 11-15 C 函數框架中使用嵌入匯編指令

        盡量使用全局變量進行參數傳遞

        使用全局變量最大的好處是尋址直觀,只需在 C 語言定義的變量名前增加一個下劃線

        符即可在匯編語句中尋址;使用全局變量進行參數傳遞的效率也比形參高。編寫單片機的 C

        程序時不能死硬強求教科書上的模塊化編程而大量采用行參和局部變量的做法,在開發編程

        時應視實際情況靈活變通,一切以最高的代碼效率為目標。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 霍城县| 谷城县| 英德市| 安吉县| 泽州县| 山东| 新野县| 湟中县| 徐州市| 周口市| 罗山县| 郧西县| 水富县| 庆城县| 千阳县| 香港| 望奎县| 杨浦区| 融水| 永宁县| 棋牌| 上蔡县| 文水县| 遂宁市| 三明市| 大名县| 乐安县| 共和县| 太和县| 刚察县| 余干县| 泾阳县| 乌拉特后旗| 西宁市| 定兴县| 茂名市| 祁连县| 晋中市| 枣强县| 棋牌| 南投县|