版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、內(nèi) 容 提 要 C51 C51的數(shù)據(jù)類型 數(shù)據(jù)的存貯類型和存貯模式 C51對SFR、可尋址位、存儲器和I/O口的定義 C51的運算符 函 數(shù) C語言編程實例 單片機(jī)資源的C語言編程實例 匯編語言和C語言的混合編程 C語言函數(shù)庫的管理與使用 小結(jié) 51系列單片機(jī)支持三種高級語言,即PL/M,C和BASIC。8052單片機(jī)內(nèi)固化有解釋BASIC語言 ,BASIC語言適用于簡單編程而對編程效率運行速度要求不高的場合;PL/M是一種結(jié)構(gòu)化的語言,很象PASCAL,PL/M 編譯器好象匯編器一樣產(chǎn)生緊湊的機(jī)器代碼,可以說是高級匯編語言,但它不支持復(fù)雜的算術(shù)運算,無豐富庫函數(shù)支持,學(xué)習(xí)PL/M無異于學(xué)習(xí)一
2、種新的語言。C語言是一種通用的程序設(shè)計語言,其代碼率高,數(shù)據(jù)類型及運算符豐富,并具有良好的程序結(jié)構(gòu),適用于各種應(yīng)用的程序設(shè)計,是目前使用較廣的單片機(jī)編程語言。 10.2.2 關(guān)于指針型數(shù)據(jù) (1)關(guān)于指針型變量 在匯編語言程序中,要取存貯單元m的內(nèi)容可用直接尋址方式,也可用寄存器間接尋址方式 ,如果用R1寄存器指示m的地址,用R1取m單元的內(nèi)容。相對應(yīng)的在C語言中用變量名表示取變量的值(相當(dāng)于直接尋址),也可用另一個變量(如P)存放m的地址,P就相當(dāng)于R1寄存器 。用*P取得m單元的內(nèi)容(相當(dāng)于匯編的間接尋址方式)這里P即為指針型變量。下面表格表示兩種語言將m單元的內(nèi)容送n單元的對照語句。 注
3、: 上表省略了匯編語言程序中對符號地址n和m用EQU偽指令進(jìn)行具體地址定義的 語句以及C語言對變量n、m和指針變量P進(jìn)行類型定義的語句,實際程序設(shè)計中,此步是不可 缺少的。表中&為取地址運算符,*為取內(nèi)容運算符。 表10-2 匯編語言和C語言的對照 (2)指針型數(shù)據(jù)的類型 由于C51是結(jié)合51單片機(jī)硬件的,51單片機(jī)的不同存貯空間,有不同的地址范圍,即使對于同一外部數(shù)據(jù)存貯器,又有用Ri分頁尋址(Ri為八位)和用DPTR尋址(DPTR為十六位)兩種尋址方式,而指針本身也是一個變量,有它存放的存貯區(qū)和數(shù)據(jù)長度。因此,在指針類型的定義中要說明:被指的變量的數(shù)據(jù)類型和存貯類型;指針變量本身的數(shù)據(jù)類型
4、(占幾個字節(jié))和存貯類型(即指針本身存放在什么存貯區(qū))。 例如類型定義為data或idata,表示指針指示內(nèi)部數(shù)據(jù)存貯器;而pdata表示指針指向外部數(shù)據(jù)存貯器,用Ri間址。以上均為八位地址;而類型code/xdata表示指針指向外部程序存貯器或外部數(shù)據(jù)存貯器指針,本身(即被指 示地址)應(yīng)為十六位長度。如果想使指針能適用于指向任何存貯空間,則可以定義指針為通用型,此時指針長度為3字節(jié),第一字節(jié)表示存貯器類型編碼,第二、三字節(jié)分別表示所指 地址的高位和低位。第一字節(jié)表示的存貯器類型編碼見表10-3: 表10-3 通用型指針的存貯類型編碼 10.3 數(shù)據(jù)的存貯類型和存貯模式 10.3.1數(shù)據(jù)的存貯
5、類型 C51是面向8XX51系列單片機(jī)及硬件控制系統(tǒng)的開發(fā)語言,它定義的任何變量必須以一定的存貯類型的方式定位在8XX51的某一存貯區(qū)中,否則便沒有意義。因此在定義變量類型時,還必須定義它的存貯類型,C51的變量的存貯類型如表10-4所示: 表10-4 C51的變量的存貯類型 訪問內(nèi)部數(shù)據(jù)存貯器(idata)比訪問外部數(shù)據(jù)存貯器(xdata)相對要快一些,因此,可將經(jīng)常使用的變量置于內(nèi)部數(shù)據(jù)存貯器中,而將較大及很少使用的數(shù)據(jù)變量置于外部數(shù)據(jù)存貯器中。例如定義變量x語句:data char x (等價于char data x)。如果用戶不對變量的存貯類型定義,則編譯器承認(rèn)默認(rèn)存貯類型,默認(rèn)的存貯
6、類型由編譯控制命令的存貯的模式部分決定。 10.3.2 存貯器模式 存貯器模式?jīng)Q定了變量的默認(rèn)存貯器類型、參數(shù)傳遞區(qū)和無明確存貯區(qū)類型的說明。C51的存貯器模式有SMALL、LARGE和COMPACT(見表10-5)。 在固定的存貯器地址進(jìn)行變量參數(shù)傳遞是C51的一個標(biāo)準(zhǔn)特征,在SMALL模式下參數(shù)傳遞是在內(nèi)部數(shù)據(jù)存貯區(qū)中完成的。LARGE和COMPACT模式允許參數(shù)在外部存貯器中傳遞。C51同時也支持混 合模式,例如在LARGE模式下生成的程序可將一些函數(shù)分頁放入SMALL模式中從而加快執(zhí)行速度。 例如設(shè)C語言源程序為PROR.C,若使程序中的變量類型和參數(shù)傳遞區(qū)限定 在外部數(shù)據(jù)存貯區(qū) ,有
7、兩種方法: 方法1:用C51對PROR.C進(jìn)行編譯時,使用命令C51 PROR.C COMPACT。 方法2:在程序的第一句加預(yù)處理命令 #pragma compact 表10-5 存貯器模式 10.3.3 變量說明舉例 data char var; /*字符變量var定位在片內(nèi)數(shù)據(jù)存貯區(qū)*/ char code MSG=PARAMETER:; /*字符數(shù)組MSG 定位在程序存 貯區(qū)*/unsigned long xdata array100; /*無符號長型數(shù)組定位在片外RAM區(qū),每元素占4bytes*/ float idata x,y,z; /*實型變量x,y,z,定位在片內(nèi)用間址訪問的內(nèi)
8、部 RAM區(qū)*/ bit lock; /*位變量Lock定位在片內(nèi)RAM可位尋址區(qū)*/unsigned int pdata sion; /*無符號整型變量sion定位在分頁的外部RAM*/unsigned char xdata vector1044 /*無符號字符型三維數(shù)組, 定位在片外RAM區(qū)*/ sfr P0=0 x80; /*定義P0口,地址為80H*/char bdata flags; /*字符變量flags定位在可位尋址內(nèi)部RAM區(qū)*/ sbit flag0=flags0; /*定義flag0為flags.0 */ 如果在變量說明時略去存貯器類型標(biāo)志符,編譯器會自動選擇默認(rèn)的存貯器類
9、型。默認(rèn)的存貯器類型由控制指令SMALL、COMPACT和LARGE限制。例如如果聲明char var,則默認(rèn)的存貯器模式為SMALL,var放在data存貯區(qū);如果使用COMPACT模式,var放入idata存貯區(qū) ;在使用LARGE模式的情況下,var被放入外部數(shù)據(jù)存貯區(qū)(xdata存貯區(qū))。 10.3.4指針變量說明舉例long xdata *px; /*指針px指向long型xdata區(qū)(每個數(shù)據(jù)占四個單元,指針自身在默認(rèn)存貯器(如不指定編譯模式在data區(qū)),指針長度為2個字節(jié)*/char xdata *data pd;/*指針pd指向字符型xdata區(qū),自身在data區(qū),長度2字
10、節(jié)*/data char xdata *pd; /*與上例等效*/ data int *pn;(和int *data pn及intr*pn等效) /*定義一個類型為int型的通用型指針,指針自身在data區(qū)長度為3字節(jié)*/ 在上例的指針聲明中包含如下幾個內(nèi)容: 1) 指針變量名(如px)前面冠以“*”,表示px為指針型變量,此處*不帶取內(nèi)容之意。 2) 指針指向的存貯類型,即指向哪個存貯區(qū),它決定了指針本身的長度(見表10.1)。存貯類型聲明的位置在數(shù)據(jù)類型和指針名(如*px)之間,如無次項聲明,則此指針型變量為通用型。 3) 指針指向的存貯區(qū)的數(shù)據(jù)類型,即被指向的存貯區(qū)以多少個單元作一個數(shù)據(jù)
11、單位,當(dāng)程序通過指針對該區(qū)操作時,將按此規(guī)定的單元個數(shù)的內(nèi)容作為一個數(shù)據(jù)操作。 4) 指針變量自身的存貯類型,即指針處于什么區(qū)與自身的長度無關(guān),該聲明可位于聲明語句的開頭,也可在“*”和變量名之間。此項由編譯模式放在默認(rèn)區(qū),如無規(guī)定編譯模式,通常在data區(qū)。 10.4 C51對SFR、可尋址位、存儲器和I/O口的定義 10.4.1 特殊功能寄存器SFR定義 C51提供了一種自主形式的定義方式,使用特定關(guān)鍵字sfr 如 sfr SCON=0 x98; /*串行通信控制寄存器地址98H*/ sfr TMOD=0 x89; /*定時器模式控制寄存器地址89H*/ sfr ACC=0 xe0; /*
12、A累加器地址E0H*/ sfr P1=0 x90; /*P1端口地址90H*/ 定義了以后,程序中就可以直接引用寄存器名。 C51也建立了一個頭文件reg51.h (增強(qiáng)型為reg52.h),在該文件中對所有的特殊功能寄存器的進(jìn)行了sfr定義, 對特殊功能寄存器的有位名稱的可尋址位進(jìn)行了sbit定義,因此,只要用包含語句#include,就可以直接引用特殊功能寄存器名,或直接引用位名稱。要特別注意:在引用時特殊功能寄存器或者位名稱必須大寫。 10.4.2 對位變量的定義 C51對位變量的定義有三種方法: 1. 將變量用bit類型的定義符定義為bit類型: 如 bit mn; mn為位變量,其值
13、只能是“0”或“1”,其位地址C51自行安排在可位尋址區(qū)的bdata區(qū)。 2. 采用字節(jié)尋址變量.位的方法: 如 bdata int ibase; /*ibase定義為整型變量*/ sbit mybit=ibase15; /*mybit定義為ibase的D15位*/ 這里位是運算符“”相當(dāng)于匯編中的“”,其后的最大取值依賴于該位所在的字節(jié)尋址變量的定義類型,如定義為char最大值只能為7。 3. 對特殊功能寄存器的位的定義 方法1:使用頭文件及sbit定義符;多用于無位名的可尋址位。 例如 #include sbit P1-1=P11; /*P1-1為P1口的第1位*/ sbit ac=ACC
14、7; /*ac定義為累加器A的第7位*/ 方法2:使用頭文件reg51.h,再直接用位名稱。 例如 #include RS1=1; RS0=0; 方法3:用字節(jié)地址位表示 例如 sbit OV=0 xD02; 方法4:用寄存器名.位定義 例如 sfr PSW=0 xd0; /*定義PSW地址為d0H*/ sbit CY=PSW7; /*CY為PSW7*/ 10.4.3 C51對存貯器和外接I/O口的絕對地址訪問 1.對存貯器的絕對地址訪問 利用絕對地址訪問的頭文件absacc.h可對不同的存貯區(qū)進(jìn)行訪問。該頭文件的函數(shù)有: CBYTE (訪問code區(qū)字符型) DBYTE (訪問data區(qū)字符
15、型) PBYTE (訪問pdata或I/O區(qū)字符型) XBYTE (訪問xdata或I/O區(qū)字符型) 還有CWORD、DWORD、PWORD和XWORD四個函數(shù),它們的訪問區(qū)域同上,只是訪問的類型為int 型。例10-1 #include #define com XBYTE0 x07ff 那么后面程序com變量出現(xiàn)的地方,就是對地址為07ffH的外部RAM或I/O口進(jìn)行訪問。 例10-2 XWORD0=0 x9988; 即將9988H(int類型)送入外部RAM的0號和1號單元。 使用中要注意:absacc.h一定要包含進(jìn)程序,XBYTE必須大寫。 2.對外部I/O口的訪問 由于單片機(jī)的I/O
16、口和外部RAM統(tǒng)一編址,因此對I/O口地址的訪問可用XBYTE (MOVX DPTR )或PBYTE (MOVX Ri)進(jìn)行。例10-3 XBYTE0Xefff=0 x10;將10H輸出到地址為EFFFH端口 7復(fù)合賦值運算符:+=;-=;*=;/=;%=;=;&=;=;|=。例:a+=b相當(dāng)于a=a+b。a=7. 相當(dāng)于a=a 7.。 8. 對指針操作的運算符: &取地址運算 *間址運算符例 a=&b;取b變量的地址送變量a c=*b;將以b的內(nèi)容為地址的單元的內(nèi)容送c這里要注意: “&”與按位與運算符的差別,如果“&”為“與”,&的兩邊必須為變量或常量; “*”與指針定義時指針前的“*”的
17、差別。如char *pt,這里的“*”只表示pt為指針變量,不代表間址取內(nèi)容的運算。 10.6.4對被調(diào)函數(shù)的說明 如果被調(diào)函數(shù)出現(xiàn)在主調(diào)函數(shù)之后,在主調(diào)函數(shù)前應(yīng)對被調(diào)函數(shù)作以說明,形式為: 返回值類型 被調(diào)函數(shù)名(形參表列); 如果被調(diào)函數(shù)出現(xiàn)在主調(diào)函數(shù)之前,可以不對被調(diào)函數(shù)說明。下面以一個簡單例子來說明int fun1(a,b) int a,b; int c; c=a+b; return(c); main() int d,0u=3,v=2;d=2*fun(u,v); 上例被調(diào)函數(shù)在主調(diào)函數(shù)前,不用說明。 int fun1(a,b); main() int d,u=3,v=2; d=2*fu
18、n1(u,v); int fun1(a,b); int a,b; int c; c=a+b; return(c); 上例中被調(diào)函數(shù)在主調(diào)函數(shù)后,在前面對被調(diào)函數(shù)進(jìn)行說明。 10.7.1 順序程序的設(shè)計 例105 完成1980524503的編程 分析:兩個乘數(shù)比較大,其積更大,采用unsigned long類型,設(shè)乘積存放在外部數(shù)據(jù)存貯器0號開始的單元。程序如下: main() unsigned long xdata *p; /*設(shè)定指針p指向類型為unsigned long的外部RAM區(qū)*/ unsigned long a=19805; /* 設(shè)置a為unsigned long類型,并賦初值
19、*/ unsigned long b=24503,c; /*設(shè)置b和積為unsigned long類型,并賦初值 */ p=0; /*設(shè)地址指向0號單元*/ c=a*b; *p=c; /*積存入外部RAM 0號單元*/ 上機(jī)通過軟件仿真調(diào)試,在變量觀察窗口看到運算結(jié)果c=48528195,即為乘積的十進(jìn)制數(shù)。觀察XDATA區(qū)(外部RAM)的0000H0003H單元分別為1C EC D0 7B,即存放的為乘積的十六進(jìn)制數(shù)。 觀察DATA區(qū)(內(nèi)部RAM區(qū)): 地址 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 1C EC D0 7B 00 00 4D 5D 00 00 5
20、F B7 C變量(積) a變量 b變量 可見定義為unsigned long類型,給每個變量分配四個單元,如果定義類型不對,將得不到 正確的結(jié)果。 對于復(fù)雜的運算通常采用查表的方法。如同匯編程序設(shè)計一樣,在程序存貯器建立一張表, 在C語言中表格定義為數(shù)組,表內(nèi)數(shù)據(jù)(元素)的偏移量表現(xiàn)為下標(biāo)。數(shù)組的使用如同變量一樣,要先進(jìn)行定義:說明數(shù)組名、維數(shù)、數(shù)據(jù)類型和存貯類型,在定義數(shù)組的同時,還可以給數(shù)組各元素賦初值。通過下例說明C51數(shù)組的定義方法和用C語言編查表程序的方法。 例106 片內(nèi)RAM 20H單元存放著一個005H的數(shù),用查表法,求出該數(shù)的平方值放入內(nèi)部RAM 21H單元。 main()
21、char x,*p char code tab6=0,1,4,9,16,25;p=0 x20; x=tab*p; p+; *p=x; 例10-7 while(P1&0 x01)=0); 即如果P1.0=0,循環(huán)執(zhí)行空語句,直到P1.0變?yōu)?,此語句用于對P1.0進(jìn)行檢測。 例10-8 分析下列程序的執(zhí)行結(jié)果:main() int sum=0,i; do sum+=I; i+; while(i=10); 本程序完成0+1+2+10的累加,執(zhí)行后sum=55例10-9 將例10-8改用for語句編程 main int sum=0,i; for(i=0;i10;i+) sun+=i; 例10-10
22、片內(nèi)RAM的20H單元存放一個有符號數(shù)x,函數(shù)y與x有如下關(guān)系式: x x0 y= 20H x=0 x+5 x0 設(shè)y存放于21H單元,程序如下main() char x,*p,*y;p=0 x20; y=0 x21; for(;) x=*p; if(x0)*y=x; if(x0)*y=x+5; if(x=0)*y=0 x20; 程序中為觀察不同數(shù)的執(zhí)行結(jié)果,采用了死循環(huán)語句for(;) 。 例10-11 有兩個數(shù)a和b,根據(jù)R3的內(nèi)容轉(zhuǎn)向不同的處理子程序:r3=0,執(zhí)行子程序pr0(完成兩數(shù)相加) r3=1,執(zhí)行子程序pr1(完成兩數(shù)相減) r3=2,執(zhí)行子程序pr2(完成兩數(shù)相乘) r3=
23、3,執(zhí)行子程序pr3(完成兩數(shù)相除) 分析: C語言中的子程序即為函數(shù),因此需編四個處理的函數(shù),如果主函數(shù)在前,主函數(shù)要對子函數(shù)進(jìn)行說明;如果子函數(shù)在前,主函數(shù)無須對子函數(shù)說明,但是無論子、主函數(shù)的順序如何,程序總是從主函數(shù)開始執(zhí)行,執(zhí)行到調(diào)用子函數(shù)就會轉(zhuǎn)到子函數(shù)執(zhí)行. 在C51編譯器中通過頭文件reg51.h可以識別特殊功能寄存器,但不能識別R0R7通用寄存器,因此R0R7只有通過絕對地址訪問識別,程序如下: #include #define r3 DBYTE0 x03 int c,c1,a,b; pr0() c=a+b; pr1() c=a-b; pr2() c=a*b; pr3() c=
24、a/b; main() a=90;b=30; for (;) switch(r3) case 0: pr0();break; case 1: pr1();break; case 2: pr2();break; case 3: pr3();break; c1=56; 在上述程序中,為便于調(diào)試觀察,加了C1=56語句,并使用了死循環(huán)語句for(;),用Ctrl+C 可退出死循環(huán)。10.8 單片機(jī)資源的C語言編程實例 例10-12 在3.1節(jié)曾用匯編語言完成了外部RAM的000EH單元和000FH單元的內(nèi)容交換,現(xiàn)改用C語言編程。C語言對地址的指示方法可以采用指針變量,也可以引用absacc.h頭文
25、件作絕對地址訪問,下面采用絕對地址訪問方法。#include main() char c; for(;) c=XBYTE14; XBYTE14=XBYTE15; XBYTE15=c; 程序中為方便反復(fù)觀察,使用了死循環(huán)語句for(;)只要用Ctrl+C即可退出死循環(huán)。上面程序通過編譯,生成的機(jī)器代碼和反匯編程序如下: 0000 020014 LJMP 0014H 0003 90000E MOV DPTR,#000EH 0006 E0 MOVX A,DPTR0007 FF MOV R7,A0008 A3 INC DPTR 0009 E0 MOVX A,DPTR000A 90000E MOV DP
26、TR,#000EH 000D F0 MOVX DPTR,A 000E A3 INC DPTR 000F EF MOV A,R7 0010 F0 MOVX DPTR,A 0011 80F0 SJMP 0003H 0013 22 RET 0014 787F MOV R0,#7FH 0016 E4 CLR A 0017 F6 MOV R0,A 0018 D8FD DJNZ R0,0017H 001A 758107 MOV SP,#07H 001D 020003 LJMP 0003H 例中可見: 一進(jìn)入C語言程序,首先執(zhí)行初始化,將內(nèi)部RAM的07FH 128個單元清零,然后置SP為07H(視變量多少
27、不同,SP置不同值,依程序而定),因此如果要對內(nèi)部RAM置初值,一定要在執(zhí)行了一條C語言語句后進(jìn)行。 C語言程序設(shè)定的變量,C51自行安排寄存器或存貯器作參數(shù)傳遞區(qū),通常在R0R7(一組或兩組,視參數(shù)多少定),因此,如果對具體地址置數(shù)據(jù),應(yīng)避開這些R0R7的地址。 如果不特別指定變量的存貯類型,通常被安排在內(nèi)部RAM中。 10.8.2 并行口及鍵盤的C語言編程 例10-13 用P1.0輸出1KHz和500Hz的音頻信號驅(qū)動揚聲器,作報警信號,要求1KHz信號響100ms,500Hz信號響200ms,交替進(jìn)行,P1.7接一開關(guān)進(jìn)行控制,當(dāng)開關(guān)合上,響報警信號,當(dāng)開關(guān)斷Kk告警信號停止,編出程序.
28、分析 500Hz信號周期為2ms,信號電平為每1ms變反一次.1KHz信號周期為1ms,信號電平每500s變反一次。用C語言編程如下: #includesbit P10=P10;sbit P17=P17;main()unsigned char i, j;while(1) while(P17=0)for(i=1;i=150;i+) /*控制音響時間*/ P10=P10;for(j=0;j=50;j+); /*延時完成信號gou周期時間*/for(i=1;i=100;i+) /*控制音響時間*/ P10=P10; for(j=0;j=100;j+); /*延時,完成信號周期時間*/ 例10-14
29、在 下圖中8XX51接有五個共陰極數(shù)碼管的動態(tài)顯示接口電路,開關(guān)打向位置“1”時,顯示“12345”字樣,當(dāng)開關(guān)打向“2”時,顯示HELLO字樣,C語言編程程序清單如下。 圖10-7 接五個共陰極數(shù)碼管的動態(tài)顯示接口 用C語言完成上述功能編程#include#define uint unsigned int#deefine uchar unsigned charsbitP17=P17;main ( )uchar code tab15=0 x86,0 xdb,0 xcf,0 xe6,0 xed ; /*“15”的字形碼,因P1.7接的開關(guān),最高位送的“1”*/ uchar code tab25=
30、0 xf8,0 xf9,0 xb8,0 xb8,0 x,bf; /*“HELLO”的段碼 “1”*/uchar i;unit j;while(1) p3=0 x011 for(i=0;i+) if(p17=1)P1=tab1i; elseP1=tab2i; P3=1;for(j=0;j=25000;j+); 例10-15 以P1.0P1.3作輸出線,以P1.4P1.7作輸入線,如圖4-6所示。 C語言編程程序清單如下:#include#define uchar unsigned char#define uint unsigned intvoid dlms (void);uchar kbscan
31、(void); / * 函數(shù)說明 * /void main (void0;uchar key;while (1) key=kbscan( ); / * 鍵盤掃描函數(shù),返回鍵碼送key保存 * / dlms( ); void dlms (void) /* 延時 * /uchar i; for (i=200;i0;i- -) 圖10-8 44矩陣鍵盤 uchar kbscan(void) / * 鍵盤掃描函數(shù) * /uchar sccode,recode;P1=0 xf0; / *P1.0P1.3發(fā)全0,P1.4P1.7輸入 * / if ( (P1 & 0 xf0)! =0 xf0) / * 如
32、P1口高四位不全為1有鍵按下 * / dlms( ); / * 延時去抖動 * / if ( (P1 & 0 xf0) ! =0 xf0) / * 在讀輸入值 * / sccode =0 xfe / * 最低位置0 * / while ( ( sccode & 0 x10)! =0) / * 不到最后一行循環(huán) * / P1 =sccode; /*P1口輸出掃描碼 * / If ( (P1 & 0 xf0)! =0 xf0) / * 如P1.4P1.7不全為1,該行有鍵按下 * / recode = (P1 & 0 xf0;); / * 保留P1口高四位輸入值,低四位變?yōu)槿?”, 作為列值 *
33、 / return( (sccode) + (recode) ); /*行碼+列值=鍵編碼返回主程序 * / else sccode =(sccode1) | 0 x01; / * 如該行無鍵按下,查下一行,行掃描值左移一位*/ return( 0 ); / * 無鍵按下,返回值為0 * / 10.8.3 C51中斷程序的編制 C51使用戶能編寫高效的中斷服務(wù)程序,編譯器在規(guī)定的中斷源的矢量地址中放入無條件轉(zhuǎn)移指令,使CPU響應(yīng)中斷后自動地從矢量地址跳轉(zhuǎn)到中斷服務(wù)程序的實際地址,而無需用戶去安排。中斷服務(wù)程序定義為函數(shù),函數(shù)的完整定義如下。返回值 函數(shù)名(參數(shù))模式再入interrupt nu
34、sing m 其中必選項 interrupt n表示將函數(shù)聲明為中斷服務(wù)函數(shù),n為中斷源編號,可以是031間的整數(shù) ,不允許是帶運算符的表達(dá)式,n通常取以下值:0 外部中斷0;1 定時器/計數(shù)器0溢出中斷2 外部中斷1;3 定時器/計數(shù)器1溢出中斷4 串行口發(fā)送與接收中斷5 定時器/計數(shù)器2中斷 各可選項的意義如下:using m 定義函數(shù)使用的工作寄存器組,m的取值范圍為03,可缺省。它對目標(biāo)代碼的影響是:函數(shù)入口處將當(dāng)前寄存器保存,使用 m 指定的寄存器組,函數(shù)退出時 原寄存器組恢復(fù)。選不同的工作寄存器組,可方便實現(xiàn)寄存器組的現(xiàn)場保護(hù)。再入屬性關(guān)鍵字reentrant將函數(shù)定義為再入的,在
35、C51中,普通函數(shù)(非再入的)不能遞歸調(diào)用, 只有再入函數(shù)才可被遞歸調(diào)用。中斷服務(wù)函數(shù)不允許用于外部函數(shù),它對目標(biāo)代碼影響如下:當(dāng)調(diào)用函數(shù)時,SFR中的ACC、B、DPH、DPL和PSW當(dāng)需要時入棧。如果不使用寄存器組切換,中斷函數(shù)所需的所有工作寄存器Rn都入棧。函數(shù)退出前,所有工作寄存器都出棧。函數(shù)由“RETI”指令終止。下面示例說明C語言的編程方法。 例10-15 對10.2.3的例10-4(見圖)要求每中斷一次,發(fā)光二極管顯示開關(guān)狀態(tài) 用C語言編程 #include int0() interrupt 0 /*INT0中斷函數(shù)*/ P1=0 x0f; /*輸入端先置1,燈滅*/ P=4;
36、/* 讀入開關(guān)狀態(tài),并左移四位, 使開關(guān)反映在發(fā)光二極管上*/ main() EA=1; /*開中斷總開關(guān)*/EX0=1; /*允許INT0中斷*/ IT0=1; /*下降沿產(chǎn)生中斷*/while(1); /*等待中斷*/ 例10-16 記錄并顯示中斷次數(shù)用C語言編程,可有兩種編程方法。法1:在主程序中判斷中斷次數(shù),程序如下:#includechar i;code char tab16=0 x3f,0 x06,0 x5b,0 x4F,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f,0 x77,0 x7c,0 x39,0 x5e,0 x79,0 x71;int() int
37、errupt 2 i+; /*計中斷次數(shù)*/ P1=tabi; /*查表,次數(shù)送顯示*/ main() EA=1; EX1=1; IT1=1;ap5: P1=0 x3f /*顯示“0”*/for(i=0;i16;); /*當(dāng)i小于16等待中斷*/ goto ap5; /*當(dāng)i=16重復(fù)下一輪16次中斷*/ 法2:在中斷程序中判斷中斷次數(shù):#includechar i;code char tab16=0 x3f,0 x06,0 x5b,0 x4F,0 x66,0 x6d,0 x7d,0 x07, 0 x7f,0 x6f,0 x77,0 x7c,0 x39,0 x5e,0 x79,0 x71;in
38、t() interrupt 1 i+ if(i16)P1=tabi; elsei=0;P1=0 x3f; main() EA=1; EX1=1; IT1=1; P1=0 x3f; while(1); /*等待中斷*/ 10.8.4 定時/計數(shù)器的C語言編程 例10-17 在P1.7端接一個發(fā)光二極管LED,要求利用定時控制使LED亮一秒滅一秒周而復(fù)始,設(shè)fo sc=6MHz。分析 T0定時100ms初值=100103/2=50000,即初值為-50000。T1計數(shù)5個脈沖工作于方式2,計數(shù)初值為-5,T0和T1均采用中斷方式。程序如下:#include reg51.hsbit P1_0=P10
39、;sbit P1_7=P17;timer0() interrupt 1 using 1 /* T0中斷服務(wù)程序 */ P1_0=! P1_0; /* 100ms到P1.0反相*/ TH0=-50000/256; /* 重載計數(shù)初值 */ TL0=-50000%256; timerl() interrupt 3 using 2 /* T1中斷服務(wù)程序 */ P1_7=! P1_7; /* 1s到,燈改變狀態(tài) */ main () P1_7=0; /* 置燈初始滅 */ P1_0=1; /* 保證第一次反相便開始計數(shù) */ TMOD=0 x61; /* T0方式 1 定時,T1方式 2 計數(shù) */
40、 TH0=-50000/256; /* 預(yù)置計數(shù)初值 */ TL0=-50000%256; TH1=-5; TL1=-5; IP=0 x08; /* 置優(yōu)先級寄存器 */ EA=1;ET0=1;ET1=1; /* 開中斷 */ TR0=1;TR1=1; /* 啟動定時/計數(shù)器 */ for(;) /* 等待中斷 */ 例10-18 在內(nèi)部數(shù)據(jù)存貯器20H3FH單元中共有32個數(shù)據(jù),要求采用方式1串行發(fā)送出去, 傳送速率為1200波特,設(shè)fosc12MHZ。方法:T1工作于方式 2 作波特率發(fā)生器,取SMOD0,T1的時間常數(shù)計算如下:波特率 (2SMOD/32)fosc/(12(256-x))
41、 1200(1/32)(12106) )/ (12(256-x) x230E6H(1)查詢方式編程C語的編程: 發(fā)送程序:#includemain( ) unsingned char i;char *p;TMOD=0 x20; TH1=0 xe6;TL1=0 xe6;TR1=1;SCON=0 x40;p=0 x20;for (i=0;i=32;i+) SBUF=*p p+ while (!TI); TI=0; 接收程序:#include main( ) unsingned char i;char *p;TMOD=0 x20;TH1=0 xe6;TL1=0 xe6;TR1=1;SCON=0 x5
42、0;p=0 x20;for (i=0;i=32;i+) while (!RI); RI=0; *p=SBUF; p+ 10.8.6 外擴(kuò)并行I/O 口的C語言編程 例10-19 用8155作6位共陰極LED顯示器接口,PB口經(jīng)驅(qū)動器7407接LED的段選,PA0PA5位反相驅(qū)動器7406接位選,待顯示字符依次存于dis-buf數(shù)組,從右向左順序顯示。8155命令字03,table為段碼表,動態(tài)顯示6個字符。8155和8XX51的接口見圖10.12。 圖10.12 8155和8XX51單片機(jī)的接口電路各口的地址:A口7FF1H B口7FF2H C口7FF3H 命令/狀態(tài)口7FF0HC語言程序如下
43、:#include#include#define uchar unsigned char#define COM8155 XBYTE0 x7ff0#define PA8155 XBYTE0 x7ff1#define PB8155 XBYTE0 x7ff2#define PC8155 XBYTE0 x7ff3uchar idata dis6=2,4,6,8,10,12; /* 存放顯示字符2、4、6、8、A,C */uchar code table18=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f,0 x77,0 x7c,0
44、 x39,0 x5e,0 x79,0 x71,0 x40,0 x00; void display(uchar idata *p)uchar sel,i,j;COM8155=0 x03;scl=0 x01; /*送命令字,選最右邊的LED*/for(i=0;i6;i+)PB8155=table*p;PA8155=sel; /*送段碼和位碼*/for(j=400;j0;j-); /*延時*/p-; /*地址指針下移位*/sel=sel1; /*左移一位*/ main()display(dis+5); 10.8.7 D/A的 C 語言編程 單級緩沖工作方式下的DAC0832與51系列單片機(jī)的接口地址
45、為7FFFH,要求輸出端得到鋸齒波電壓信號用C語言編程,程序如下:#include#include#define da0832 XBYTE0X7fffmain()unsigned char i,j;while(1)for(i=0;i=255;i+)da0832=i; /* 啟動轉(zhuǎn)換 */for(j=0;j=255;j+); /* 延時 */ 圖10.4 鋸齒波電壓信號 10.9 匯編語言和C語言的混合編程 本節(jié)介紹不同的模塊,不同的語言相結(jié)合的編程方法。 通常情況下以高級語言編寫主程序,用匯編語言編與硬件有關(guān)的子程序。高級語言不同的編譯程序?qū)R編的調(diào)用方法不同,在KEIL C51中,是將不同的
46、模塊(包括不同語言的模塊)分別匯編或編譯,再通過連接生成一個可執(zhí)行文件。 C語言程序調(diào)用匯編語言程序要注意以下幾點。 1.被調(diào)函數(shù)要在主函數(shù)中說明,在匯編程序中,要使用偽指令使CODE選項有效并聲明為可再定位段類型,并且根據(jù)不同情況對函數(shù)名作轉(zhuǎn)換,見表10.6。 表10.6 函數(shù)名的轉(zhuǎn)換 2. 對為其他模塊使用的符號進(jìn)行PUBLIC聲明,對外來符號進(jìn)行EXTRN聲明。 3 要注意參數(shù)的正確傳遞。 10.9.1 C語言程序和匯編語言程序參數(shù)的傳遞 在混合語言編程中,關(guān)鍵是入口參數(shù)和出口參數(shù)的傳遞,KEIL C51編譯器可使用寄存器傳遞參數(shù),也可以使用固定存貯器或使用堆棧,由于8XX51的堆棧深度
47、有限,因此多用寄存器或存貯器傳遞。用寄存器傳遞最多只能傳遞三個參數(shù),選擇固定的寄存器,見表10.7。 表10. 7 參數(shù)傳遞的寄存器選擇 例如 funcl(int a) “a”是第一個參數(shù),在R6,R7傳遞 func2(int b, int c, int *d) “b”在R6,R7中傳遞,“c”在R4,R5中傳遞,指針變量“d”在R1,R2,R3中傳遞。 如果傳遞參數(shù)寄存器不夠用,可以使用存貯器傳送,通過指針取得參數(shù)。 匯編語言通過寄存器或存貯器傳遞參數(shù)給C語言程序,匯編語言通過寄存器傳遞給C語言 的返回值見表10.8 。表10.8 匯編語言通過寄存器傳遞給C語言的返回值 下面通過實例說明混合
48、編程的方法及參數(shù)傳遞過程。 10.9. 2 C 語言程序調(diào)用匯編語言程序舉例 例1. 用P1.0產(chǎn)生周期為4ms的方波,同時用P1.1產(chǎn)生周期為8ms的方波。 說明:用C語言編寫主程序,使P1.1產(chǎn)生周期為8ms的方波為模塊一; P1.0產(chǎn)生周期為4ms的 方波為模塊二; 用匯編語言編寫的延時1ms程序為模塊三。 模塊一調(diào)用模塊二獲得8ms方波, 模塊二調(diào)模塊三時向匯編程序傳遞了字符型參數(shù)(x=2),延時2ms,程序如下: C語言程序模塊一: #include #define uchar unsigned char sbit P1-1=P11; void delay4ms(void); /*
49、定義延時4ms 函數(shù)(模塊二) */ main() uchar i; for(;) P1-1=0;delay4ms();/* 調(diào)模塊二延時4ms */P1-1=1;delay4ms();/* 調(diào)模塊二延時4ms*/ 模塊二: #include #define uchar unsigned char sbit P1-0=P10; delaylms(uchar x); /* 定義延時1ms函數(shù)(模塊三) */ void delay4ms(void) P1-0=0;delaylms(2);/* 調(diào)匯編函數(shù)(模塊三) */P1-0=1;delaylms(2);/* 調(diào)匯編函數(shù)(模塊三)*/ 模塊三:
50、PUBLIC -DELAY1MS ;DE LAY1MS為其他模塊調(diào)用 DE SEGMENT CODE ;定義DE段為再定位程序段 RSEG DE ;選擇DE為當(dāng)前段 - DELAY1MS: NOP DELA: MOV R1,#0F8H ;延時 LOP1: NOP NOP DJNZ R1,LOP1 DJNZ R7,DELA ;R7為C程序傳遞過來的參數(shù) EXIT: RET END上例可見匯編語言程序從R7中獲取參數(shù)(x=2)。 模塊編譯連接方法 以上各模塊可以先分別匯編或編譯(選擇DEBUG編譯控制項),生成各自的.OBJ文件,然后運行L51將各OBJ文件連接,生成一個新的文件。 在集成環(huán)境下的
51、連接調(diào)試可以連續(xù)進(jìn)行,比上面方法更為方便,現(xiàn)使用wave(偉福)的仿真軟件的編譯連接步驟如下: 1.編輯好各個模塊,保存。 2.點擊文件/新建項目,彈出項目窗口。 3.點擊項目菜單,選加入模塊,此時彈出有文件目錄的對話框,選中要加入剛才編輯好的文件(模塊),并打開。此時在項目窗口中可以看到加入的模塊文件。 4.點擊項目菜單中的全部編輯,并取名保存項目。于是系統(tǒng)對加入各模塊進(jìn)行編譯,并進(jìn)行連接。 5.編譯連接完成會彈出信息窗口,如編譯連接有錯,信息窗口將出現(xiàn)錯誤信息。 6.模塊連接成功,生成二進(jìn)制文件(.BIN)和十六進(jìn)制文件(.HEX)。7.點擊跟蹤或單步按鈕,就可對程序進(jìn)行跟蹤調(diào)試,程序運行
52、到不同模塊時,wave就會彈出相應(yīng)的模塊源程序窗口,顯示程序運行情況。 例2.在匯編程序中比較兩數(shù)大小,將大數(shù)放到指定的存儲區(qū),由C程序的主調(diào)函數(shù)取出模塊一:C語言程序 #define uchar unsigned char void max(uchar a, uchar b); /* 定義匯編函數(shù) */ main()uchar a=5,b=35,*c,d;c=0 x30;/* c指針變量指向內(nèi)部 RAM 30H單元 */max(a,b);/* 調(diào)匯編函數(shù),a,b為傳遞的參數(shù) */d=*c;/* d存放模塊二傳遞過來的參數(shù) */ 模塊二:匯編語言程序PUBLIC -MAX MAX為其 他模塊調(diào)
53、 DE SEGMENT CODE ;定義DE段為再定位程序段 RSEG DE ;選擇DE為當(dāng)前段 MAX: MOV A,R7 ;取模塊一的參數(shù)aMOV 30H,R5 ;取模塊一的參數(shù)bCJNE A,30H,TAG1 ;比較a,b的大小 TAG1: JC EXITMOV 30H,R7 ;大數(shù)存于30H單元 EXIT: RET END此例中,C語言程序通過R7和R5傳遞字符型參數(shù)a和b到匯編語言程序,匯編語言程序?qū)⒎祷刂捣旁诠潭ù尜A單元,主調(diào)函數(shù)通過指針取出返回值。 10.9.3 C和匯編混合編程傳遞的參數(shù)多于三個的編程方法 C語言程序調(diào)用匯編程序最多只能傳遞三個參數(shù),如果多于三個參數(shù),就需要通過
54、存貯區(qū)傳遞,可以通過數(shù)組,也可以在匯編程序中建立數(shù)據(jù)段,下面例中C語言程序向匯編傳遞的參數(shù)多于三個的編程方法例3 A/D采用查詢方式采樣50個數(shù)據(jù)(A/D地址為7FF8H),將其求平均并送數(shù)碼管顯示.分析 8位A/D最大值255,用三個數(shù)碼管顯示,以P3.4為查詢位,電路設(shè)計如圖,:以匯編編A/D轉(zhuǎn)換程序,采集50個數(shù)據(jù),以C編求平均值,變十進(jìn)制顯示,程序如下: 圖 例3電路 #include #define uint unsigned int#define uchar unsigned charextern void callasm(uchar); /* 定義外部匯編函數(shù) */extern
55、void dayl(uint); /* 定義外部匯編函數(shù)dayl */void main(void)uint i,j,m,total = 0;uchar idata buf50,dis3;uchar code tab16=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f,0 x77,0 x7c,0 x39,0 x5e,0 x79,0 x71;/*段碼表*/P1 =0 xf8;while(1) total = 1;callasm( buf ); /* 調(diào)匯編函數(shù),傳遞參數(shù)為數(shù)組首址*/for( i = 50; i0;i- )
56、/ *匯編函數(shù)執(zhí)行完后返回于此*/total += bufi-1; /*50個數(shù)累加 */total=total/50; /*求平均 */dis0=total%10; /*求個位,并存入顯示緩沖區(qū) */total=total/10;dis1=total%10; /*求十位,并存入顯示緩沖區(qū) */dis2=total/10; /*求百位,并存入顯示緩沖區(qū) */P3=0 x01; /*P3口位選 */for(m=0;m=50;m+) for(i=0;i=3;i+) /*顯示*/ P1=tabdisi; dayl (50) ;/* 調(diào)匯編函數(shù)DAYL,延時*/P3=1; 匯編語言程序CALLASM.
57、ASM-完成50個數(shù)據(jù)采集并存于BUF為首址的單元 PUBLIC CALLASM ;公共符號定義DFFE SEGMENT CODE ;DFFE定為可再定位段RSEG DFFE;DFFE為當(dāng)前段-CALLASM: PUSH 07H PUSH 00H;保護(hù)變量,因在下述程序中要用R7和R0 MOV A, R7;取BUF地址 MOV R0, A;R0指示存放地址MOV R7, #50MOV DPTR, #7FF8H;DPTR 指向A/D地址 AGA:MOV A, #0 MOVX DPTR, A ;啟動轉(zhuǎn)換JB P3.4, $ ;等待轉(zhuǎn)換結(jié)束MOVX A,DPTR ;讀轉(zhuǎn)換數(shù)據(jù)MOV R0,A ;存入
58、BUF數(shù)組 INC R0DJNZ R7,AGAPOP 00HPOP 07H;恢復(fù)BUF地址RETEND 匯編語言程序 DAYL.ASM延時 PUBLIC -DAYL ;公共符號定義 DTE SEGMENT CODE ;定義DTE段為再定位程序段 RSEG DTE ;選擇DTE為當(dāng)前段 -DAYL: NOP DELA: MOV R1,#0F8H ;延時 LOP1: NOP NOP DJNZ R1,LOP1 DJNZ R7,DELA ;R7為C程序傳遞過來的參數(shù) EXIT: RET END 10.10 C語言函數(shù)庫的管理與使用 C語言作為一種高級編程語言,其主要的優(yōu)勢之一就是有大量的豐富的庫函數(shù)可直接使用。而庫函數(shù)的使用是解決程序共享和提高編程效率的最有效的途徑之一。函數(shù)庫是具有目標(biāo)代碼形式的函數(shù)的集合。雖然在許多方面,庫就像一個獨立編譯的模塊,但它有一個不同于目標(biāo)文件的特別之處:當(dāng)某個獨立編譯的目標(biāo)文件與其它文件連接時,所有該目標(biāo)文件中的函數(shù),無論它們是否真正被程序所用,都成為可執(zhí)行的一部分;而當(dāng)一個庫文件與其它文件連接時,可執(zhí)行程序中只包含那些真正由程序所用的庫函數(shù)。例如,C51標(biāo)準(zhǔn)庫中包含很多函數(shù),而你的程序只包含真正由你的程序所調(diào)用的函數(shù)。 10.10.1 庫函數(shù)的編寫 庫函數(shù)的編寫同普通的函數(shù)編寫的方法一樣,需要主要的幾點是:庫函數(shù)命名時,不能用
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- GB/T 2812-2024頭部防護(hù)通用測試方法
- 二零二五版裝修工程合同范本:合同生效與解除條件2篇
- 2024跨區(qū)域電網(wǎng)工程建設(shè)與運營管理合同
- 二零二五版家居行業(yè)導(dǎo)購員聘用與考核合同3篇
- 二零二五年餐飲行業(yè)食堂承包合作協(xié)議范本3篇
- 二零二五版家庭住家保姆綜合能力培訓(xùn)聘用合同3篇
- 2025年度新能源出租車特許經(jīng)營合同3篇
- 二零二五年度跨境電商進(jìn)口商品代理銷售合同9篇
- 二零二五年股權(quán)質(zhì)押貸款擔(dān)保合同3篇
- 二零二五按揭房離婚財產(chǎn)分割與子女監(jiān)護(hù)協(xié)議范本3篇
- 2025新北師大版英語七年級下單詞表
- 2024公路瀝青路面結(jié)構(gòu)內(nèi)部狀況三維探地雷達(dá)快速檢測規(guī)程
- 《智慧城市概述》課件
- 2024年北京市家庭教育需求及發(fā)展趨勢白皮書
- GB/T 45089-20240~3歲嬰幼兒居家照護(hù)服務(wù)規(guī)范
- 中建道路排水工程施工方案
- 拆機(jī)移機(jī)合同范例
- 智能停車充電一體化解決方案
- 化學(xué)驗室安全培訓(xùn)
- 天書奇譚美術(shù)課件
- GB/T 18916.15-2024工業(yè)用水定額第15部分:白酒
評論
0/150
提交評論