與C共舞
我們在之前的內(nèi)容學習了段的聲明以及相關(guān)變量內(nèi)存的聲明,現(xiàn)在我們要開始最重要的一節(jié)課——匯編和C的混合編程
首先是在C中調(diào)用匯編
我們在匯編的學習中知道了匯編代碼的跳轉(zhuǎn)和調(diào)用都是依賴所謂的標記來進行,我們還是拿LCD1602的驅(qū)動舉例
- <div class="blockcode"><blockquote>LCDWRCOM:
- CLR LCD_RS;寫命令
- SJMP GOON
- LCDWRDAT:
- SETB LCD_RS;寫數(shù)據(jù),這個沒有jmp,應(yīng)該更快一些
- GOON:
- SETB LCD_EN;兩段代碼合一,這種技巧很常見,甚至編譯器都這么優(yōu)化
- MOV LCD_BUS,R7;注意,根據(jù)文檔,單個char會直接傳入R7,多看文檔
- MOV R6,#10H;等待LCD
- DJNZ R6,$
- CLR LCD_EN
- CLR LCD_RS
- RET
復(fù)制代碼
- LCDINIT:
- CLR LCD_RW
- CLR LCD_EN
- MOV R7,#38H
- CALL LCDWRCOM
- MOV R7,#0CH
- CALL LCDWRCOM
- MOV R7,#06H
- CALL LCDWRCOM
- MOV R7,#01H
- CALL LCDWRCOM;啟動序列,別處抄的
-
- MOV R7,#0FH
- LCDFINWAIT:
- MOV R6,#0FFH
- DJNZ R6,$
- DJNZ R7,LCDFINWAIT;LCD需要一段時間準備
-
- RET
復(fù)制代碼
我們可以知道,上面有三個程序,一個是LCDWRCOM,一個是LCDWRDAT,以及LCDINIT,這三個都是程序的入口,我們要做的就是把入口聲明告知C編譯器,有這么個東西
我們以LCDINIT為例
首先,LCDINIT是一個沒有輸入輸出的函數(shù),所以一般來說它在C語言里的聲明是這樣的
其次,我們在C文件中調(diào)用的是別的文件中的函數(shù),我們需要extern來表明,這個函數(shù)是從外部薅過來的(函數(shù)入口在別的文件)
最終,我們寫在頭文件里的聲明函數(shù)是這樣的
還記得我說過傳到匯編編譯器的名字不分大小寫嗎?這里就體現(xiàn)出來了,你只要名字對上就可以,它能找到認出來
光這樣還不行,匯編語言的入口實際上是不符合C編譯器的命名規(guī)則的,所以我們需要在匯編里做些操作,讓匯編編譯器知道,這個入口是可以被外界使用的,這里就要用到PUBLIC,具體用法是這樣的
PUBLIC 標記名
所以最終的代碼是這樣的
- PUBLIC LCDINIT
- LCDINIT:
- CLR LCD_RW
- CLR LCD_EN
- MOV R7,#38H
- CALL LCDWRCOM
- MOV R7,#0CH
- CALL LCDWRCOM
- MOV R7,#06H
- CALL LCDWRCOM
- MOV R7,#01H
- CALL LCDWRCOM;啟動序列,別處抄的
- MOV R7,#0FH
- LCDFINWAIT:
- MOV R6,#0FFH
- DJNZ R6,$
- DJNZ R7,LCDFINWAIT;LCD需要一段時間準備
- RET
復(fù)制代碼
C語言中
這樣,我們就完成了C語言調(diào)用匯編
但是對于有傳入形參的函數(shù),情況稍微復(fù)雜一些,C51傳遞參數(shù)有些不同,它是寄存器傳參,參數(shù)放置在從R7開始的寄存器,然后才輪得到內(nèi)存(可以關(guān)掉這個選項,但是內(nèi)存能省則省),如果是一個char,那就R7,兩個那就R7和R6,三個那就765,一個int就是R7和R6,以此類推,至于內(nèi)存?zhèn)鲄,你可以看看手冊,一般來講,超過四五個形參的我建議直接傳指針進去
其次,匯編語言中的標記的前面必須帶有一個下劃線,比如ABC要變?yōu)開ABC
這里回收上一節(jié)埋下的伏筆,實際上內(nèi)存段的標記也可以public
- ?DT?LCDMEM SEGMENT DATA
- PUBLIC LCDMEM
- RSEG ?DT?LCDMEM
- LCDMEM:
- DS 32
復(fù)制代碼 C語言中是這樣的
- extern unsigned char LCDMEM[32];
復(fù)制代碼
好了,C調(diào)用匯編已經(jīng)學會了,我們現(xiàn)在要倒反天罡,讓匯編也揩一下C語言的油
這一次,我們直接扒keil一開始給的StartUp.A51里面的例子
從116行開始
- ?C_C51STARTUP SEGMENT CODE
- ?STACK SEGMENT IDATA
- RSEG ?STACK
- DS 1
- EXTRN CODE (?C_START)
- PUBLIC ?C_STARTUP
- CSEG AT 0
- ?C_STARTUP: LJMP STARTUP1
- RSEG ?C_C51STARTUP
復(fù)制代碼
好了,那個?C_START就是我們的目標,它就是我們調(diào)用C語言的方法(也是STARTUP以后跳轉(zhuǎn)主函數(shù)的入口)
EXTRN CODE (函數(shù)名)
實際上還能用EXTERN,兩者的區(qū)別就是EXTERN只能把PUBLIC過的標記薅過來用(順便一提,變量也可以薅過來用,只需要把CODE改成DATA)
我們這次不寫函數(shù)給匯編調(diào)用,我們在StartUp.A51上搞事情,眾所周知,StartUp.A51先于主函數(shù)運行,所以它一定會跳轉(zhuǎn)到主函數(shù),我們使個壞把主函數(shù)改成mian(我不太確定是不是有副作用,就僅僅整活用吧)
所以我們需要改成
好了,還有最后一步,我們可以在StartUp.A51的尾部看見這個
把它也改了,然后……
- StartUp.A51
- ……
- EXTRN CODE (mian)
- ……
- LJMP mian
- END
- main.c
- void mian(){
- ……
- }
復(fù)制代碼 順便一說,提示找不到?C_START也可以用這招
最后,匯編里面調(diào)用函數(shù)用CALL,這個應(yīng)該不用多說
|