1、在Windows2000下麵,我們(men) 可以把字體(ti) 設置為(wei) Courier,這樣就可以顯示正常。
2、當使用有片外內(nei) 存的MCU(如W77E58,它有1K片外內(nei) 存)的時候,肯定要設置標誌位,並且編譯方式要選擇大模式,否則會(hui) 出錯。
3、當使用Keil C跟蹤程序運行狀態的時候,要把引起Warning的語句屏蔽,否則有可能跟蹤語句的時候會(hui) 出錯。
4、在調用數組的時候,Keil C是首先把數組Load進內(nei) 存。如果要在C中使用長數組的時候,我們(men) 可以使用code關(guan) 鍵字,這樣就實現了匯編的DB的功能,Keil C是不會(hui) 把標誌code的數組Load入內(nei) 存的,它會(hui) 直接讀取Rom。
5、拉高管腳的執行速度遠遠比檢查管腳電平的要快。當編程涉及到有關(guan) 通信,時序是很重要的。
6、在等待管腳電平變化的時候,我們(men) 需要設置好超時處理,否則程序就會(hui) 因為(wei) 一個(ge) 沒有預計的錯誤而死鎖。
7、能用C語言實現的地方,盡量不要用匯編,尤其在算法的實現,用匯編是晦澀難懂。
8、程序的幾個(ge) 參數數組所占篇幅很大,其中液晶背景數組最長,有四千個(ge) Byte,因而把那些初始化數組都放在另外一個(ge) C文件,在主文件使用使用關(guan) 鍵字extern定義(yi) ,這樣就不會(hui) 對主文件的編寫(xie) 造成幹擾。
9、所有函數之間的相關(guan) 性越低越有利於(yu) 以後功能的擴展。
10、6.20版在編譯帶code關(guan) 鍵字的數組時,編譯通過但是單片機運行結果是錯誤的,改用6.14版後正常。
------------------------------------------------------------------------------------------------------------------------------------------------------
問:C51 怎樣將1個(ge) 子程序段定位在1個(ge) 固定的地址位置?
以下2問題均要用C51解決(jue)
1。 怎樣將1個(ge) 子程序段定位在1個(ge) 固定的地址位置?
例如將 INT BCD2HEX(INT XX)定位在1000H
2。 HOW在EEPROM 中固定的位置存放1字符串?
如在200H處放“COPYRIGHT 2001-11”
答: 函數定位與(yu) 變量定位...
1、函數定位:
假如要把C源文件 tools.c 中的函數
int BIN2HEX(int xx)
{
...
}
放在CODE MEMORY的0x1000處,先編譯該工程,然後打開該工程的M51文件,在
* * * C O D E M E M O R Y * * *
行下找出要定位的函數的名稱,應該形如:
CODE xxxxH xxxxH UNIT ?PR?_BCD2HEX?TOOLS
然後在:
Project->Options for Target ...->BL51 Locate:Code
中填寫(xie) 如下內(nei) 容:
?PR?_BCD2HEX?TOOLS(0x1000)
再次Build,在M51中會(hui) 發現該函數已放在CODE MEMORY的0x1000處了
2、賦初值的變量定位:
要將某變量定位在一絕對位置且要賦初值,此時用 _at_ 不能完成,則如下操作:
在工程中建立一個(ge) 新的文件,如InitVars.c,在其中對要處理的變量賦初值(假設是code變
量):
char code myVer = {"COPYRIGHT 2001-11"};
然後將該文件加入工程,編譯,打開M51文件,若定義(yi) 的是code型,則在
* * * C O D E M E M O R Y * * *
下可找到:
CODE xxxxH xxxxH UNIT ?CO?INITVARS
然後在:
Project->Options for Target ...->BL51 Locate:Code
中填入:
?CO?INITVARS(0x200)
再次編譯即可。
相應地,如為(wei) xdata變量,則InitVars.c中寫(xie) :
char xdata myVer = {"COPYRIGHT 2001-11"};
然後將該文件加入工程,編譯,打開M51文件,在
* * * X D A T A M E M O R Y * * *
下可找到:
XDATA xxxxH xxxxH UNIT ?XD?INITVARS
然後在:
Project->Options for Target ...->BL51 Locate:Xdata
中填入:
?XD?INITVARS(0x200)
再次編譯即可。相應地,若定義(yi) 的是data/idata等變量,則相應處理即可。
3、若有多個(ge) 變量或函數要進行絕對地址定位,則應按地址從(cong) 低到高的順序排列。
------------------------------------------------------------------------------------------------------------------------------------------------------
C51的一些誤區和注意事項
2005-11-24 9:03:43
1)C忌諱絕對定位。 常看見初學者要求使用_at_,這是一種謬誤,把C當作ASM看待了。在C中變量的定位是編譯器的事情,初學者隻要定義(yi) 變量和變量的作 用域,編譯器就把一個(ge) 固定地址給這個(ge) 變量。怎麽(me) 取得這個(ge) 變量的地址?要用指針。比如unsigned char data x;後,x的地址就是&x, 你隻要查看這個(ge) 參數,就可以在程序中知道具體(ti) 的地址了。所以俺一看見要使用絕對定位的人,第一印象就是:這大概是個(ge) 初學者。
2)設置SP的問題。 原因和1差不對,編譯器在把所有變量和緩衝(chong) 區賦予地址後,自動把最後一個(ge) 字節開始的地方,作為(wei) SP的開始位置,所以初學者是不必 要去理會(hui) 的。這體(ti) 現C的優(you) 越性,很多事情C編譯時候做了。
3)用C的主程序結構: #i nclude <reg52.h> void main(void) { while(1); } 這是個(ge) 最小的成功的C程序,包括頭部文件和程序主體(ti) 。 頭部文件的名詞解釋:引用的外部資源文件,這個(ge) 文件包括了硬件信息和外部模塊提供的可使用的函數和變量的說明。可以用文本方 式打開reg52.h,仔細研究下,會(hui) 有一些寫(xie) 程序的體(ti) 會(hui) 。
4)這樣構成一個(ge) C項目 在C中,常用項目來管理。項目一般分為(wei) 兩(liang) 大塊:C文件塊和頭部文件塊。 我們(men) 常把不同功能寫(xie) 在不同的C文件中,依靠項目的管理,最後把所有文件連接起來,這樣就可以得到可以燒錄的HEX文件或BIN文件。 這些C文件中,有且隻有唯一一個(ge) 包括main()函數,和3)中一樣的C文件。 用頭部文件把各個(ge) 不同的C互相連接起來。一個(ge) C文件基本上要對應有一個(ge) H頭部文件,這個(ge) H文件就包含本C文件中可以提供給外麵使 用的變量和函數,沒有在H文件中列出的文件,可以算是該C文件的內(nei) 部函數和變量,外部C不能使用。 例子:a.C: unsigned char i; unsigned char mWork; void Test1(void) { mWork ; } void Test2(void) { i ; } a.h文件中: extern unsigned char i; extern void Test1(void); 這樣主程序M.c中: #i nclude <reg52.h> /*C編譯器內(nei) 部自帶的H文件,使用<>*/ #i nclude "a.h" /*自定義(yi) 的H文件,一般用""*/ void main(void) { Test1(); /*使用a.c模塊文件中的函數*/ while(1){ i ; /*使用a.c模塊文件中的變量*/ } }
5)51家族 核心都是基於(yu) 8031的,有很多在此核心上進行擴展,有的把程序存儲(chu) 器放在內(nei) 部:89c(S)51..,有的增加了RAM:89c(S)52..,有的增加 了一些專(zhuan) 用硬件80C552...,有的改變時鍾時序W77E58...。市麵上現在常用的主要有ATMEL公司的AT89X係列,PHILIPS的P87(89)x,台 灣WINBOND的w77(78)x係列,Cygnal的C8051Fx係列。
6)51單片機結構的C描述 這裏不講51的具體(ti) 結構,隻是引導初學者快速理解51單片機的物理結構。寄存器和IO及其它硬件設備的地址名稱,在相應的C頭部文件 中可以找到。51為(wei) reg51.h,52為(wei) reg52.h,以次類推,比如winbond的78E58就為(wei) w78e58.h這些H文件中的描述: srf,定義(yi) 一個(ge) 8位的設備。 srf16,定義(yi) 一個(ge) 16位的設備。 sbit,定義(yi) 一個(ge) 位的設備。 用這些語句定義(yi) 後,就可以在C中象匯編一樣使用這些硬件設備,這是單片機應用比標準C特殊的地方,其它差別很少。
7)在51係列中data,idata,xdata,pdata的區別 data:固定指前麵0x00-0x7f的128個(ge) RAM,可以用acc直接讀寫(xie) 的,速度最快,生成的代碼也最小。 idata:固定指前麵0x00-0xff的256個(ge) RAM,其中前128和data的128完全相同,隻是因為(wei) 訪問的方式不同。idata是用類似C中的指針方式 訪問的。匯編中的語句為(wei) :mox ACC,@Rx.(不重要的補充:c中idata做指針式的訪問效果很好) xdata:外部擴展RAM,一般指外部0x0000-0xffff空間,用DPTR訪問。 pdata:外部擴展RAM的低256個(ge) 字節,地址出現在A0-A7的上時讀寫(xie) ,用movx ACC,@Rx讀寫(xie) 。這個(ge) 比較特殊,而且C51好象有對此BUG, 建議少用。但也有他的優(you) 點,具體(ti) 用法屬於(yu) 中級問題,這裏不提。
8)startup.a51的作用 和匯編一樣,在C中定義(yi) 的那些變量和數組的初始化就在startup.a51中進行,如果你在定義(yi) 全局變量時帶有數值,如unsigned char data xxx=100;,那startup.a51中就會(hui) 有相關(guan) 的賦值。如果沒有=100,startup.a51就會(hui) 把他清0。(startup.a51==變量的初始化)。 這些初始化完畢後,還會(hui) 設置SP指針。對非變量區域,如堆棧區,將不會(hui) 有賦值或清零動作。 有人喜歡改startup.a51,為(wei) 了滿足自己一些想當然的愛好,這是不必要的,有可能錯誤的。比如掉電保護的時候想保存一些變量, 但改startup.a51來實現是很笨的方法,實際隻要利用非變量區域的特性,定義(yi) 一個(ge) 指針變量指向堆棧低部:0xff處就可實現。, 為(wei) 什麽(me) 還要去改? 可以這麽(me) 說:任何時候都可以不需要改startup.a51,如果你明白它的特性。
關(guan) 於(yu) 在 KEIL C51 中嵌入匯編以及C51與(yu) A51間的相互調用
如何在 KEIL C51(v6.21) 中調用匯編函數的一個(ge) 示例 [ycong_kuang]
有關(guan) c51調用匯編的方法已經有很多帖子講到,但是一般隻講要點,很少有對整個(ge) 過程作詳細描述,對於(yu) 初學者是不夠的,這裏筆者
通過一個(ge) 簡單例子對這個(ge) 過程進行描述,希望能對初學者有所幫助。幾年來,在這個(ge) 論壇裏筆者得到很多熱心人指導,因此也希望
藉此盡一點綿薄之力。
在這個(ge) 例子裏,闡述了編寫(xie) c51程序調用匯編函數的一種方法,這個(ge) 外部函數的入口參數是一個(ge) 字符型變量和一個(ge) 位變量,返回值是
一個(ge) 整型變量。例中,先用c51寫(xie) 出這個(ge) 函數的主體(ti) ,然後用SRC控製指令編譯產(chan) 生asm文件,進一步修改這個(ge) asm文件就得到我們(men) 所
要的匯編函數。該方法讓編譯器自動完成各種段的安排,提高了匯編程序的編寫(xie) 效率。
step1. 按寫(xie) 普通c51程序方法,建立工程,在裏麵導入main.c文件和CFUNC.c文件。
相關(guan) 文件如下:
//main.c文件
#include < reg51.h >
#define uchar unsigned char
#define uint unsigned int
extern uint AFUNC(uchar v_achr,bit v_bflag);
void main()
{
bit BFLAG;
uchar mav_chr;
uint mvintrslt;
mav_chr=0xd4; BFLAG=1;
mvintrslt=AFUNC(mav_chr,BFLAG);
}
//CFUNC.c文件
#define uchar unsigned char
#define uint unsigned int
uint AFUNC(uchar v_achr,bit v_bflag)
{
uchar tmp_vchr;
uint tp_vint;
tmp_vchr=v_achr;
tp_vint=(uint)v_bflag;
return tmp_vchr+(tp_vint<<8);
}
step2. 在 Project 窗口中包含匯編代碼的 C 文件上右鍵,選擇“Options for ...”,點擊右邊的“Generate Assembler SRC
File”和“Assemble SRC File”,使檢查框由灰色變成黑色(有效)狀態;
step3. 根據選擇的編譯模式,把相應的庫文件(如 Small 模式時,是 Keil\C51\Lib\C51S.Lib)加入工程中,該文件必須作為(wei) 工
程的最後文件;
step4. build這個(ge) 工程後將會(hui) 產(chan) 生一個(ge) CFUNC.SRC的文件,將這個(ge) 文件改名為(wei) CFUNC.A51(也可以通過編譯選項直接產(chan) 生CFUNC.A51文
件),然後在工程裏去掉庫文件(如C51S.Lib)和CFUNC.c,而將CFUNC.A51添加到工程裏。
//CFUNC.SRC文件如下
.\CFUNC.SRC generated from: CFUNC.c
NAME CFUNC
?PR?_AFUNC?CFUNC SEGMENT CODE
?BI?_AFUNC?CFUNC SEGMENT BIT OVERLAYABLE
PUBLIC ?_AFUNC?BIT
PUBLIC _AFUNC
RSEG ?BI?_AFUNC?CFUNC
?_AFUNC?BIT:
v_bflag?041: DBIT 1
; #define uchar unsigned char
; #define uint unsigned int
;
; uint AFUNC(uchar v_achr,bit v_bflag)
RSEG ?PR?_AFUNC?CFUNC
_AFUNC:
USING 0
; SOURCE LINE # 5
;---- Variable 'v_achr?040' assigned to Register 'R7' ----
; {
; SOURCE LINE # 6
; uchar tmp_vchr;
; uint tp_vint;
;
; tmp_vchr=v_achr;
; SOURCE LINE # 10
;---- Variable 'tmp_vchr?042' assigned to Register 'R5' ----
MOV R5,AR7
; tp_vint=(uint)v_bflag;
; SOURCE LINE # 11
MOV C,v_bflag?041
CLR A
RLC A
;---- Variable 'tp_vint?043' assigned to Register 'R6/R7' ----
; return tmp_vchr+(tp_vint<<8);
; SOURCE LINE # 12
MOV R6,A
MOV R4,#00H
CLR A
ADD A,R5
MOV R7,A
MOV A,R4
ADDC A,R6
MOV R6,A
; }
; SOURCE LINE # 13
?C0001:
RET
; END OF _AFUNC
END
step5. 檢查main.c的“Generate Assembler SRC File”和“Assemble SRC File”是否有效,若是有效則點擊使檢查框變成無效狀
態;再次build這個(ge) 工程,到此你已經得到匯編函數的主體(ti) ,修改函數裏麵的匯編代碼就得到你所需的匯編函數了。
參考文獻:
1.徐愛鈞,彭秀華。單片機高級語言C51windows環境編程與(yu) 應用,電子工業(ye) 出版社
2.www.c51bbs.com, C51編程:關(guan) 於(yu) 在 KEIL C51 中直接嵌入匯編。。。帖子編號: 83838 發表用戶:Youth
------------------------------------------------------------------------------------------------------------------------------------------------------
keil中匯編函數調用c51函數 [ycong_kuang]
在keil的寫(xie) 法可參考89852帖子,具體(ti) 如下:
與(yu) 89852帖子相比,第一步在工程裏多了一個(ge) 被匯編調用的c51的函數文件(c51func.c),至於(yu) 匯編函數還是先用c51編寫(xie) 出主體(ti)
(a51func.c),這樣匯編程序接口和段都交給編譯器處理,你隻管在編譯成匯編代碼後按你的要求改寫(xie) 匯編代碼就行了。
例程如下:
//main.c
#include < reg51.h >
#define uchar unsigned char
#define uint unsigned int
extern uint AFUNC(uchar v_achr,bit v_bflag);
void main()
{
bit BFLAG;
uchar mav_chr;
uint mvintrslt;
mav_chr=0xd4; BFLAG=1;
mvintrslt=AFUNC(mav_chr,BFLAG);
}
//a51FUNC.c
#define uchar unsigned char
#define uint unsigned int
extern uint CFUNC(uint);
uint AFUNC(uchar v_achr,bit v_bflag) //c51寫(xie) 的匯編函數,最終要變成匯編代碼
{
uchar tmp_vchr;
uint tp_vint;
tmp_vchr=v_achr;
tp_vint=(uint)v_bflag;
return CFUNC(tp_vint); //這裏調用一個(ge) c51函數
}
//c51FUNC.c
#define uchar unsigned char
#define uint unsigned int
uint CFUNC(uint v_int) //被匯編函數調用c51函數
{
return v_int<<2;
}
第二步是按89852帖子的step2,3,4把用c51寫(xie) 的(匯編)函數變成a51文件(今天我試了一下step3可以不要)例程編譯結果如
下:
; .\a51func.SRC generated from: a51func.c
NAME A51FUNC
?PR?_AFUNC?A51FUNC SEGMENT CODE
?DT?_AFUNC?A51FUNC SEGMENT DATA OVERLAYABLE
?BI?_AFUNC?A51FUNC SEGMENT BIT OVERLAYABLE
EXTRN CODE (_CFUNC)
PUBLIC ?_AFUNC?BIT
PUBLIC _AFUNC
RSEG ?DT?_AFUNC?A51FUNC
?_AFUNC?BYTE:
tmp_vchr?042: DS 1
RSEG ?BI?_AFUNC?A51FUNC
?_AFUNC?BIT:
v_bflag?041: DBIT 1
; //a51FUNC.c
;
; #define uchar unsigned char
; #define uint unsigned int
;
; extern uint CFUNC(uint);
;
; uint AFUNC(uchar v_achr,bit v_bflag)
RSEG ?PR?_AFUNC?A51FUNC
_AFUNC: ;c51所寫(xie) 的函數產(chan) 生的匯編代碼從(cong) 這裏開始
USING 0
; SOURCE LINE # 8
;---- Variable 'v_achr?040' assigned to Register 'R7' ----
; {
; SOURCE LINE # 9
; uchar tmp_vchr;
; uint tp_vint;
;
; tmp_vchr=v_achr;
; SOURCE LINE # 13
MOV tmp_vchr?042,R7
; tp_vint=(uint)v_bflag;
; SOURCE LINE # 14
MOV C,v_bflag?041
CLR A
MOV R6,A
RLC A
MOV R7,A
;---- Variable 'tp_vint?043' assigned to Register 'R6/R7' ----
; 這裏說明R6,R7內(nei) 容就是tp_vint
; return CFUNC(tp_vint);
; SOURCE LINE # 16
LCALL _CFUNC ;這裏調用了用c51寫(xie) 的函數
; }
; SOURCE LINE # 17
?C0001:
RET
; END OF _AFUNC
END
這個(ge) 文件就是你的匯編函數所在文件,把函數裏麵的匯編代碼修改成你所需的匯編函數就ok了。
建議參考 徐愛鈞,彭秀華所寫(xie) 的《單片機高級語言C51windows環境編程與(yu) 應用》或馬忠梅所寫(xie) 的
《單片機的c語言應用程序設計》有關(guan) 混合語言編程有關(guan) 章節
------------------------------------------------------------------------------------------------------------------------------------------------------
關(guan) 於(yu) 在 KEIL C51 中直接嵌入匯編。。。 [Youth]
有時在C51程序中需要嵌入一些匯編代碼,這時當然可以用通常的作法:
按照 C51 與(yu) 匯編的接口寫(xie) 一個(ge) 匯編函數,然後在 C51 程序中調用該函數。(此種方法可在論壇裏搜索,以前有很多帖子講到,不再
重複)
下麵介紹直接嵌入匯編代碼的方法:
1、在 C 文件中要嵌入匯編代碼片以如下方式加入匯編代碼:
#pragma ASM
; Assembler Code Here
#pragma ENDASM
2、在 Project 窗口中包含匯編代碼的 C 文件上右鍵,選擇“Options for ...”,點擊右邊的“Generate Assembler SRC File”
和“Assemble SRC File”,使檢查框由灰色變成黑色(有效)狀態;
3、根據選擇的編譯模式,把相應的庫文件(如 Small 模式時,是 Keil\C51\Lib\C51S.Lib)加入工程中, 該文件必須作為(wei) 工程的最
後文件;
4、編譯,即可生成目標代碼。
C51中變量的空間分配幾個(ge) 方法
在C51中變量的空間分配幾個(ge) 方法
liy-tj 發表於(yu) 2006-1-24 12:59:19
1、 data區空間小,所以隻有頻繁用到或對運算速度要求很高的變量才放到data區內(nei) ,比如for循環中的計數值。
2、 data區內(nei) 最好放局部變量。
因為(wei) 局部變量的空間是可以覆蓋的(某個(ge) 函數的局部變量空間在退出該函數是就釋放,由別的函數的局部變量覆蓋),可以提高內(nei) 存利用率。當然靜態局部變量除外,其內(nei) 存使用方式與(yu) 全局變量相同;
3、 確保你的程序中沒有未調用的函數。
在Keil C裏遇到未調用函數,編譯器就將其認為(wei) 可能是中斷函數。函數裏用的局部變量的空間是不釋放,也就是同全局變量一樣處理。這一點Keil C做得很愚蠢,但也沒辦法。
4、 程序中遇到的邏輯標誌變量可以定義(yi) 到bdata中,可以大大降低內(nei) 存占用空間。
在51係列芯片中有16個(ge) 字節位尋址區bdata,其中可以定義(yi) 8*16=128個(ge) 邏輯變量。定義(yi) 方法是: bdata bit LedState;但位類型不能用在數組和結構體(ti) 中。
5、 其他不頻繁用到和對運算速度要求不高的變量都放到xdata區。
6、 如果想節省data空間就必須用large模式,將未定義(yi) 內(nei) 存位置的變量全放到xdata區。當然最好對所有變量都要指定內(nei) 存類型。
7、 當使用到指針時,要指定指針指向的內(nei) 存類型。
在C51中未定義(yi) 指向內(nei) 存類型的通用指針占用3個(ge) 字節;而指定指向data區的指針隻占1個(ge) 字節;指定指向xdata區的指針占2個(ge) 字節。如指針p是指向data區,則應定義(yi) 為(wei) : char data *p;。還可指定指針本身的存放內(nei) 存類型,如:char data * xdata p;。其含義(yi) 是指針p指向data區變量,而其本身存放在xdata區。