久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

標(biāo)題: KEIL C語(yǔ)言與匯編混合編程操作 [打印本頁(yè)]

作者: 51黑科技    時(shí)間: 2016-2-5 01:58
標(biāo)題: KEIL C語(yǔ)言與匯編混合編程操作
這一篇來講講混合編程的問題,在網(wǎng)上找了一下,講混合編程的文件章也有不少,但進(jìn)行實(shí)例操作講解的不多也不完整,本來書上混合編程的內(nèi)容看著就讓人覺得抽象難懂,再?zèng)]有個(gè)實(shí)際操作圖例,就很讓人覺得云里霧里。在這里我就針對(duì)KEIL做個(gè)混合編程的實(shí)例的文章希望對(duì)初學(xué)者有所幫助。先搞清幾個(gè)問題。
①混合編程的必要性:也就是為什么需要混合編程,初學(xué)者一定會(huì)覺得,我C用的好好的為什么要混進(jìn)匯編呢,不是自找麻煩嗎?其實(shí)不然,最簡(jiǎn)單的例子就是延時(shí)子程序,用C寫的話連你自己也不知道幾層的循環(huán)后確切地用多少時(shí)間吧?但用匯編寫你就能很準(zhǔn)確地計(jì)算出要延時(shí)的時(shí)間。還有當(dāng)你要對(duì)那些時(shí)序要求很高IC模塊或步進(jìn)電機(jī)行操作時(shí)用匯編來寫就能做到操控的直接與精準(zhǔn)。
②在進(jìn)行實(shí)際操作前要弄清C與匯編之間的調(diào)用關(guān)系,C的函數(shù)大家都會(huì)用了,主要分為無反回參數(shù)的和有反回參數(shù)的,例如 void delay(void);就是無反回參數(shù)的,int readdata(void);就是有返回參數(shù)的。還有就是有參數(shù)傳遞和無參數(shù)傳遞的,void delay(void);就是無參數(shù)傳遞的,unsigned int add(unsigned char aa,unsigned char bb);就是有參數(shù)傳遞的函數(shù)。在教材上講起C與匯編的混合編程就會(huì)說起寄存器最多傳遞三個(gè)函數(shù),這樣可以產(chǎn)生高效代碼。



在參數(shù)返回時(shí)寄存器的傳遞規(guī)律為:


下面我們用實(shí)際的混合編程操作來講講如何實(shí)現(xiàn)函數(shù)的調(diào)用及參數(shù)的傳遞。
      打開KEIL,我的用的版本是綠色免安裝2.0中文版,編譯器為7.0:無程序代碼長(zhǎng)度限制。現(xiàn)在有3.0版也是綠色免安裝版本,好處是已支持雙字節(jié)中文注釋,但是英文版。用哪個(gè)版本都無所謂,只要用著習(xí)慣功能夠用就行。


下面是版本信息:


在網(wǎng)上經(jīng)常有朋友說為什么我下載了KEIL解壓出目錄后運(yùn)行卻不能編譯呢,老是報(bào)告出錯(cuò):
--- Error: can't execute 'E:\old_pc\txz001\單片機(jī)c51\KEIL4\C51\BIN\C51.EXE'
--- 錯(cuò)誤: 不能執(zhí)行 'E:\old_pc\txz001\單片機(jī)c51\KEIL2_70\Keil2\C51\BIN\C51.EXE'
這是由于編譯時(shí),C51.exe編譯器沒能在你給出的路徑上找到。你需要修改路徑。
在選擇KEIL的菜單欄“工程”--“文件擴(kuò)展名、書籍和編譯環(huán)境屬性”--“環(huán)境設(shè)置”的如下圖:


看到上圖的“使用TOOLS.INI設(shè)定”前的鉤了嗎?對(duì)了,它是按照你TOOLS.INI里給出的路徑去找的。因此的得打開那個(gè)tools.ini文件修改它。KEIL的目錄結(jié)構(gòu)一般是這樣的:


我們KEIL軟件運(yùn)行主程序uvision2是在目錄UV2里,而那個(gè)設(shè)置文件TOOLS.INI文件是在它的上一級(jí)目錄Keil里,見上圖。用記事本打開這個(gè)TOOLS.INI文件:


看見紅筆圈出的[C51]下的路徑了嗎?將它修改正確指向你硬盤上KEIL下C51目錄,存盤,運(yùn)行KEIL。就可以正確編譯了。(廢話又多了。。。)好!言歸正傳。
    我們?cè)贙EIL里創(chuàng)建一個(gè)新的工程TEST1。在這個(gè)工程里我們添加了兩個(gè)文件,main.c和delay.c,程序如下:
   文件main.c:
    #include <AT89X52.H>
    extern void delay(void);
   main(void)
    {
      delay();
     }
文件delay.c
    #define uchar unsigned char
    void delay(void)
     { uchar i;
       for(i=255,i>0,i--);
      }
可以看出,這兩個(gè)文件里的程序很簡(jiǎn)單,主程序里先定義了一個(gè)外部函數(shù)delay();然后就調(diào)用了這個(gè)無參數(shù)函數(shù)。而文件delay.c里也就是用for循環(huán)做了255次循環(huán)。
下面我們先進(jìn)行編譯,調(diào)試讓程序正確,通過編譯。然后我們選擇左邊工程窗口,選中文件delay.c,鼠標(biāo)右擊它出現(xiàn)下圖。


選擇“文件'delay.c'屬性”后如下圖:



見上圖,有“產(chǎn)生匯編文件”和“匯編源代碼文件”兩項(xiàng)前的鉤選框是灰色的,分別點(diǎn)擊它們兩次使它呈黑色鉤選狀態(tài)。如下圖。


點(diǎn)擊下面的確認(rèn)鈕,回到主界面。這時(shí)你再進(jìn)行一次全部的重新編譯,就會(huì)發(fā)現(xiàn)在你建立這個(gè)工程的目錄下將多產(chǎn)生一個(gè)delay.src文件。


用記事本打開這個(gè)delay.src文件。發(fā)現(xiàn)它就是一個(gè)匯編文件。
; .\delay.SRC generated from: delay.c
; COMPILER INVOKED BY:
;        E:\old_pc\txz001\單片機(jī)c51\KEIL2_70\Keil\C51\BIN\C51.EXE delay.c BROWSE DEBUG OBJECTEXTEND SRC(.\delay.SRC)
NAME DELAY
?PR?delay?DELAY      SEGMENT CODE
PUBLIC delay
; #define uchar unsigned char
; void delay(void)
RSEG ?PR?delay?DELAY
delay:
USING 0
    ; SOURCE LINE # 2
; { uchar i;
    ; SOURCE LINE # 3
;   for(i=255;i>0;i--);
    ; SOURCE LINE # 4
;---- Variable 'i?040' assigned to Register 'R7' ----
MOV R7,#0FFH
?C0001:
DJNZ R7,?C0001
; }
    ; SOURCE LINE # 5
?C0004:
RET
; END OF delay
END
可以看出原來的C程序都變成了匯編的注釋了。我們將注釋都去掉。
NAME DELAY
?PR?delay?DELAY      SEGMENT CODE
PUBLIC delay
RSEG ?PR?delay?DELAY
delay:
      USING 0
      MOV R7,#0FFH
?C0001:
      DJNZ R7,?C0001
?C0004:
      RET
      END
現(xiàn)在看看是不是很簡(jiǎn)呢。在標(biāo)號(hào)delay:前是程序的說明,就是定義函數(shù)的名字,將代碼放在哪里等,看不懂也沒關(guān)系,別亂改它就行。從delay:標(biāo)號(hào)后就是匯編的程序部分了。里面的標(biāo)號(hào)最好也別亂改。添加你要操作的程序就行了,好!我們先不改動(dòng)程序,就將上面十行匯編別存為delay.asm文件。回到KEIL界面,我們?cè)诠こ檀袄?是KEIL主界面左邊的工程窗口而不是在工程目錄里)的將delay.c刪除。然后再添加上delay.asm程序,如下圖:



這樣,你再進(jìn)行編譯,你會(huì)發(fā)現(xiàn)你已經(jīng)通過了混合編程的編譯,雖然這次你對(duì)程序的功能什么都沒有改變,但你已經(jīng)知道如何做出一個(gè)C程序調(diào)用匯編子程序的例子了。下面我們可以對(duì)這個(gè)匯編了程序進(jìn)行一些修改看它是否仍能很好的工作。
  今天我們就來對(duì)那個(gè)匯編的delay子程序進(jìn)行修改,為了讓運(yùn)行的結(jié)果能顯示出來,我先加進(jìn)一個(gè)LCD的顯示子程序12864put.c。

我們先修改主程序如下:
//****************
//    主函數(shù)
//****************
main(void)
{ uchar aa,bb;
TMOD=0x01;//定義T0為模式1即16位計(jì)數(shù)方式
TH0=0;//將計(jì)數(shù)器高位初值清0
TL0=0;//將計(jì)數(shù)器低位初值清0
TR0=1;//計(jì)數(shù)器開始計(jì)數(shù)
//delay(); //調(diào)用匯編的子函數(shù)
TR0=0;//停止計(jì)數(shù)

aa=TH0;//把計(jì)數(shù)的值高位交給aa
bb=TL0;//把計(jì)數(shù)的值低位交給aa
LcmInit();//初始化LCD12864
LcmClear();//清屏LCD
LcmPutstr( 0,28,"C&A TEST" );//顯示
LcmPutstr( 3,0,"TH0:" );
LcmPutstr( 3,24,uchartostr(aa) );
LcmPutstr( 3,46,"TL0:");
LcmPutstr( 3,70,uchartostr(bb) );
LcmPutstr( 5,0,"BLOG:http://" );
LcmPutstr( 6,18,"hi.baidu.com/txz01" );
LcmPutstr( 7,8,"Email:TXZ001@139.com" );
看見上面的程序了嗎?我用了T0在調(diào)用匯編子函數(shù)delay()前開始計(jì)數(shù),調(diào)用完后就關(guān)掉,然后看計(jì)數(shù)器內(nèi)的計(jì)數(shù)值來知道我們這個(gè)子函數(shù)的精確程度。我先把delay()函數(shù)給注釋掉,看看開始計(jì)數(shù)后就立即關(guān)掉要用去多少時(shí)間。結(jié)果顯示為1,就是說用了一個(gè)脈沖的時(shí)間。12M的晶振就是一微秒。見下圖:

看到?jīng)]有,用了TR0=1;TR0=0;本身就用去了一個(gè)脈沖。好!現(xiàn)在我們將那個(gè)調(diào)用匯編子函數(shù)delay()語(yǔ)句啟用,但我將匯編內(nèi)的語(yǔ)句給清空。也就是說我把delay.asm這個(gè)子程序讓它什么也沒做。是個(gè)空函數(shù),看它要用掉幾個(gè)脈沖時(shí)間。匯編程序如下:
NAME DELAY
?PR?delay?DELAY      SEGMENT CODE
PUBLIC delay
RSEG ?PR?delay?DELAY
delay:
RET
END
看到了嗎?標(biāo)號(hào)delay:下面什么也沒有了,直接就RET返回了。好!編譯,燒寫,運(yùn)行!如下圖:
結(jié)果是用了5個(gè)脈沖,其中一個(gè)是調(diào)用計(jì)數(shù)器本身用的,也就是說調(diào)用一個(gè)空函數(shù)用了4個(gè)脈沖時(shí)間。好!我們?cè)賮硇薷囊幌聟R編程序:
NAME DELAY
?PR?delay?DELAY      SEGMENT CODE
PUBLIC delay
RSEG ?PR?delay?DELAY
delay:
     mov r7,#100
     djnz r7,$
   RET
   END
在標(biāo)號(hào)delay:下面我加了兩行,我們計(jì)算一下,第一行MOV r7,#100要用一個(gè)機(jī)器周期,也就是一個(gè)脈沖。第二行djnz r7,$要循環(huán)100次每次用2個(gè)機(jī)器周期,這樣算來共是201個(gè)脈沖再加上剛才我們計(jì)算過的調(diào)用函數(shù)要4個(gè)脈沖和開關(guān)計(jì)數(shù)器用1個(gè),總共是206個(gè)。編譯,燒寫,運(yùn)行!
看來計(jì)算的沒錯(cuò)呀!我們?cè)傺h(huán)多些:
NAME DELAY
?PR?delay?DELAY      SEGMENT CODE
PUBLIC delay
RSEG ?PR?delay?DELAY
delay:
     mov r7,#100  ;1
loop:mov r6,#50   ;100
     djnz r6,$        ;50×100×2
   djnz r7,loop    ;100×2
   RET
   END
這次的計(jì)算應(yīng)該是1+100+50×100×2+100×2+5=10306。再次編譯燒寫運(yùn)行!

高位數(shù)值為40,低位數(shù)值為66,則總數(shù)=40×256+66=10306。精準(zhǔn)吧!好了!無參函數(shù)的調(diào)用就討論到此。

  下面接著說說帶參數(shù)據(jù)函數(shù)的調(diào)用:
我們重新建立一個(gè)目錄TEST2(因?yàn)橐粋(gè)項(xiàng)目有很多個(gè)文件如果都放在一個(gè)目錄里會(huì)很混亂,以后想挪到U盤帶到其它機(jī)子上用時(shí)就很困難了),建立新的項(xiàng)目test2.Uv2,里面還是main.c主程序和12864put.c顯示子程序:
主函數(shù)main()如下:
#include <AT89X52.H>
#include <intrins.H>
#define uchar unsigned char
#define uint unsigned int
extern void LcmClear( void );       //清屏
extern void LcmInit( void );        //初始化
extern void LcmPutstr( uchar row,uchar y,uchar * str ); //在設(shè)定位置顯示字符串
//row:是LCD的行數(shù)(0-7)
//y:是LCD的列數(shù)(0-127)
//str:是字符串的首地址
extern uint add(uchar aa,uchar bb);
extern void inttostr(uint intval,uchar data * str);
uchar str[6];//定義四個(gè)字節(jié)空間用來存放數(shù)值轉(zhuǎn)換成的字符值
//****************
//    主函數(shù)
//****************
main(void)
{ uchar aa,bb;
uint cc;
aa=145;
bb=236;
cc=add(aa,bb);
LcmInit();//初始化LCD12864
LcmClear();//清屏LCD
LcmPutstr( 0,28,"C&A TEST" );//顯示
inttostr(aa,str);
LcmPutstr( 3,0,str );
LcmPutstr( 3,18," + " );
inttostr(bb,str);
LcmPutstr( 3,36,str);
LcmPutstr( 3,54," = ");
inttostr(cc,str);
LcmPutstr( 3,72,str);
//LcmPutstr( 3,46,"TL0:");
//LcmPutstr( 3,70,uchartostr(bb) );
LcmPutstr( 5,0,"BLOG:http://" );
LcmPutstr( 6,18,"hi.baidu.com/txz01" );
LcmPutstr( 7,8,"Email:TXZ001@139.com" );
while(1);
}
項(xiàng)目中還有uinttostr.c是無符號(hào)整型轉(zhuǎn)字符串子程序和我們要做匯編調(diào)用的這個(gè)有返回參數(shù)有傳遞參數(shù)的子程序add.c,子程序add.c如下。

#define uchar unsigned char
#define uint unsigned int
uint add(uchar aa,uchar bb)
{
uint cc;
cc=aa+bb;
return(cc);
}
我們主要目的是為了表達(dá)清楚怎樣在C程序里去調(diào)用匯編子函數(shù),所以程序還是很簡(jiǎn)單,就是把主程序傳過來的無符號(hào)字符型變量aa和bb相加,相加的結(jié)果交給無符號(hào)整型變量cc返回給主程序。編譯前我們還是點(diǎn)取add.c文件屬性,讓它產(chǎn)生src文件。上面的圖已顯示了編譯的過程信息。現(xiàn)在我們打開這個(gè)add.src文件:
; .\add.SRC generated from: add.c
; COMPILER INVOKED BY:
;        E:\old_pc\txz001\單片機(jī)c51\KEIL2_70\Keil\C51\BIN\C51.EXE add.c BROWSE DEBUG OBJECTEXTEND SRC(.\add.SRC)
NAME ADD?
?PR?_add?ADD         SEGMENT CODE
PUBLIC _add
; #define uchar unsigned char
; #define uint unsigned int
;
; uint add(uchar aa,uchar bb)
RSEG ?PR?_add?ADD
_add:
USING 0
    ; SOURCE LINE # 4
;---- Variable 'bb?041' assigned to Register 'R5' ----
;---- Variable 'aa?040' assigned to Register 'R7' ----
; {
    ; SOURCE LINE # 5
;   uint cc;
;   cc=aa+bb;
    ; SOURCE LINE # 7
MOV A,R5
ADD A,R7
MOV R7,A
CLR A
RLC A
MOV R6,A
;---- Variable 'cc?042' assigned to Register 'R6/R7' ----
;   return(cc);
    ; SOURCE LINE # 8
; }
    ; SOURCE LINE # 9
?C0001:
RET
; END OF _add
END
我們還是將注釋的部分刪去,這樣便于我們分析:
NAME ADD?
?PR?_add?ADD         SEGMENT CODE
PUBLIC _add
RSEG ?PR?_add?ADD
_add:
USING 0
MOV A,R5
ADD A,R7
MOV R7,A
CLR A
RLC A
MOV R6,A
RET
END
現(xiàn)在我們首先來看函數(shù)名,上面我們講過的那個(gè)無參數(shù)函數(shù)delay()的調(diào)用,產(chǎn)生的匯編子函數(shù)名就是delay,而這次我我們?cè)瓉鞢的函數(shù)名add變成了匯編的_add。前面多了個(gè)下劃線,這就是有參數(shù)函數(shù)的特征。C語(yǔ)言函數(shù)名轉(zhuǎn)變?yōu)閰R編函數(shù)名的規(guī)律為:無參數(shù)傳遞時(shí)void func(void)----FUNC。寄存器參數(shù)傳遞時(shí)char func(char)----_FUNC。再入函數(shù)使用時(shí)void func(void) reentrant----_?FUNC。
  不過這些名字的變化規(guī)律記沒記住好象關(guān)系并不大。我們想要用到匯編調(diào)用時(shí),就先用C做個(gè)假函數(shù)然后產(chǎn)生匯編文件名字就自然出來,并不用我們?nèi)ス芩拿缓笕バ薷某晌覀兿胱龅膮R編程序就行了。
  但是,這參數(shù)傳遞的位置規(guī)律就必須得知道,否則你就無法使用這個(gè)匯編了,我們看上面的匯編程序,第一句是將寄存器R5的值傳到A中,第二句將A與寄存器R7相加,第三句將相加的結(jié)果A的值傳給R7,后面的幾句是將剛才相加的進(jìn)位值C,傳給R6,然后返回。對(duì)照本篇最上面給的那兩張表我們可以看出C子函數(shù)add.c的第一個(gè)參數(shù)aa被傳到了匯編的R7,第二個(gè)參數(shù)bb被傳到了R5,將它們相加后,返回值的低位交給了R7,高位交給了R6。完全符合參數(shù)傳遞表和返回值表所述。下面我們將匯編子程序另存為asm文件后替換掉原來的C子程序:
編譯、燒寫后運(yùn)行:
這個(gè)加法函數(shù)我們沒有改動(dòng)任何參數(shù)當(dāng)然運(yùn)行起來是不會(huì)錯(cuò)的,下面我們將在匯編里將它改成乘法試試,
將標(biāo)號(hào)_add:下面的語(yǔ)句全都改掉,程序如下?
NAME ADD?
?PR?_add?ADD         SEGMENT CODE
PUBLIC _add
RSEG ?PR?_add?ADD
_add:
USING 0
MOV A,R7
MOV B,R5
MUL AB ;A與B相乘,乘積的高位值在B中,低位值在A中
MOV R7,A   ;將低位值傳給R7
MOV R6,B ;將高位值傳給R6
RET
END
上面的改動(dòng)我們已將原來的加法依照寄存器的傳遞規(guī)律改為乘法函數(shù),看看是否還能正常運(yùn)行并正確,改完后仍編譯燒寫運(yùn)行:
哈哈!完全正確。
總結(jié):
  我們可以經(jīng)常性地采用在C中建立簡(jiǎn)單的子函數(shù),轉(zhuǎn)成匯編后看它的操作方法和傳遞規(guī)律,慢慢地熟悉掌握和運(yùn)用如何在C中調(diào)用匯編函數(shù)。

修改于(2009.3.15)


作者: ls0899    時(shí)間: 2016-2-11 21:53
看來許多文章,也沒搞清楚,頂一下
作者: nameligang    時(shí)間: 2019-12-18 17:32
可以 樓主這個(gè)辦法 贊一個(gè)




歡迎光臨 (http://www.zg4o1577.cn/bbs/) Powered by Discuz! X3.1
主站蜘蛛池模板: 国产二区视频 | 少妇黄色| 精品欧美一区二区三区久久久 | 日本高清视频在线播放 | 日韩精品极品视频在线观看免费 | 在线观看免费毛片 | 国产精品免费一区二区三区四区 | 午夜精品一区二区三区三上悠亚 | 黄色一级免费 | 国产h视频 | 欧美日韩中文字幕在线播放 | 亚洲精品视频在线观看视频 | 国产色 | 国产精品一级 | 精品国产欧美日韩不卡在线观看 | 天堂在线中文 | 亚洲一区二区中文字幕 | 中文字幕在线观看www | 日本三级网站在线 | 国产一区不卡 | 另类一区 | 亚洲精品国产成人 | 欧美色人 | 91天堂| 国产精品区一区二区三区 | 欧美日韩国产一区二区 | 一区二区三区高清 | 日韩av在线一区 | 亚洲精品乱码久久久久久9色 | 97福利在线| 黄色网址大全在线观看 | 日韩综合在线 | 97视频网站 | 久久国产精品网 | 91pao对白在线播放 | 日本字幕在线观看 | 一道本不卡 | 欧美视频在线一区 | 亚洲精品福利在线 | 亚洲精品久久久久久宅男 | 精品国产欧美日韩不卡在线观看 |