<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 51單片機(jī)基礎(chǔ)剖析(基于C語(yǔ)言)

          51單片機(jī)基礎(chǔ)剖析(基于C語(yǔ)言)

          作者: 時(shí)間:2016-11-20 來(lái)源:網(wǎng)絡(luò) 收藏
          一、51單片機(jī)內(nèi)存剖析

          在編寫應(yīng)用程序時(shí),定義一個(gè)變量,一個(gè)數(shù)組,或是說(shuō)一個(gè)固定表格,到底存儲(chǔ)在什么地方;當(dāng)定義變量大小超過(guò)MCU的內(nèi)存范圍時(shí)怎么辦;如何控制變量定義不超過(guò)存儲(chǔ)范圍;

          本文引用地址:http://www.ex-cimer.com/article/201611/318636.htm

          以及如何定義變量才能使得變量訪問(wèn)速度最快,寫出的程序運(yùn)行效率最高。以下將一一解答。

          1.六類存儲(chǔ)類型 code data idata xdata pdata bdata

          code:程序存儲(chǔ)器,也即只讀存儲(chǔ)器,用來(lái)保存常量或是程序,采用16位地址線編碼,可以是在片內(nèi),或是片外,大小被限制在64KB。

          作用:定義常量,如八段數(shù)碼表或是編程使用的常,在定義時(shí)加上code或明確指明定義的常量保存到code memory(只讀。)比如:

          char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

          此關(guān)鍵字的使用方法等同于const。

          data:數(shù)據(jù)存儲(chǔ)區(qū),只能用于聲明變量,不能用來(lái)聲明函數(shù),該區(qū)域位于片內(nèi),采用8位地址線編碼,具有最快的存儲(chǔ)速度,但是數(shù)量被限制在128byte或更少。

          使用方法:unsigned char data fast_variable=0;

          Idata:數(shù)據(jù)存儲(chǔ)區(qū),只能用于聲明變量,不能用來(lái)聲明函數(shù)。該區(qū)域位于片內(nèi),采用8位地址線編碼,內(nèi)存大小被限制在256byte或更少。該區(qū)域的低地址區(qū)與data區(qū)地址一致,高地址區(qū)域是52系列在51系列基礎(chǔ)上擴(kuò)展的并與特殊功能寄存器具有相同地址編碼的區(qū)域。即:data memory是idata memory的一個(gè)子集。

          xdata:只能用于聲明變量,不能用來(lái)聲明函數(shù),該區(qū)域位于MCU外部,采用16位地址線進(jìn)行編碼,存儲(chǔ)大小被限制在64KB以內(nèi)。如:unsigned char xdata count=0;

          pdata:只能用于聲明變量,不能用來(lái)聲明函數(shù),該區(qū)域位于MCU外部,采用8位地址線進(jìn)行編碼。存儲(chǔ)大小限制在256byte,是xdata memory的低256byte。為其子集。如:unsigned char pdata count=0;

          bdata:只能用于聲明變量,不能用來(lái)聲明函數(shù)。該區(qū)域位于8051內(nèi)部位數(shù)據(jù)地址。定義的量保存在內(nèi)部位地址空間,可用位指令直接讀寫。使用方法:unsigned char bdata varab=0。

          注:一般情況下,定義字符型變量時(shí),在缺省unsigned的情況下,默認(rèn)為無(wú)符號(hào)。但是本人在Keil uV3中遇到并非如此的案例。在缺省的情況下默認(rèn)為有符號(hào)。要注意一下,或許不同的編譯器規(guī)則不同。所以我們?cè)趯懗绦虻臅r(shí)候,還是最好把unsigned signed加上。

          2.函數(shù)的參數(shù)和局部變量的存儲(chǔ)模式

          C51 編譯器允許采用三種存儲(chǔ)器模式:SMALL,COMPACT 和LARGE。一個(gè)函數(shù)的存儲(chǔ)器模式確定了函數(shù)的參數(shù)的局部變量在內(nèi)存中的地址空間。處于SMALL模式下的函數(shù)參數(shù)和局部變量位于8051單片機(jī)內(nèi)部RAM中,處于COMPACT和LARGE模式下的函數(shù)參數(shù)和局部變量則使用單片機(jī)外部RAM。在定義一個(gè)函數(shù)時(shí)可以明確指定該函數(shù)的存儲(chǔ)器模式。方法是在形參表列的后面加上一存儲(chǔ)模式。

          示例如下:

          #pragma large //此預(yù)編譯必須放在所有頭文前面

          int func0(char x,y) small;

          char func1(int x) large;

          int func2(char x);

          注:上面例子在第一行用了一個(gè)預(yù)編譯命令#pragma,它的意思是告訴c51編譯器在對(duì)程序進(jìn)行編譯時(shí),按該預(yù)編譯命令后面給出的編譯控制指令LARGE進(jìn)行編譯,即本例程序編譯時(shí)的默認(rèn)存儲(chǔ)模式為L(zhǎng)ARGE。隨后定義了三個(gè)函數(shù),第一個(gè)定義為SMALL存儲(chǔ)模式,第二個(gè)函數(shù)定義為L(zhǎng)ARGE第三個(gè)函數(shù)未指定,在用C51進(jìn)行編譯時(shí),只有最后一個(gè)函數(shù)按LARGE存儲(chǔ)器模式處理,其它則分別按它們各自指定的存儲(chǔ)器模式處理。

          本例說(shuō)明,C51編譯器允許采用所謂的存儲(chǔ)器混合模式,即允許在一個(gè)程序中將一些函數(shù)使用一種存儲(chǔ)模式,而其它一些則按另一種存儲(chǔ)器模式,采用存儲(chǔ)器混合模式編程,可以充分利用8051系列單片機(jī)中有限的存儲(chǔ)器空間,同時(shí)還可以加快程序的執(zhí)行速度。

          3.絕對(duì)地址訪問(wèn)(頭文件為:absacc.h(相當(dāng)重要))

          #define CBYTE ((unsigned char volatile code *) 0)

          #define DBYTE ((unsigned char volatile data *) 0)

          #define PBYTE ((unsigned char volatile pdata *) 0)

          #define XBYTE ((unsigned char volatile xdata *) 0)

          功能:CBYTE尋址CODE區(qū)

          DBYTE尋址DATA區(qū)

          PBYTE尋址XDATA(低256)區(qū)

          XBYTE尋址XDATA區(qū)

          例:如下指令在對(duì)外部存儲(chǔ)器區(qū)域訪問(wèn)地址0x1000

          xvar=XBYTE[0x1000];

          XBYTE[0x1000]=20;

          #define CWORD ((unsigned int volatile code *) 0)

          #define DWORD ((unsigned int volatile data *) 0)

          #define PWORD ((unsigned int volatile pdata *) 0)

          #define XWORD ((unsigned int volatile xdata *) 0)

          功能:與前面的一個(gè)宏相似,只是它們指定的數(shù)據(jù)類型為unsigned int。

          通過(guò)靈活運(yùn)用不同的數(shù)據(jù)類型,所有的8051地址空間都是可以進(jìn)行訪問(wèn)。例如:

          DWORD[0x0004]=0x12F8;// 即內(nèi)部數(shù)據(jù)存儲(chǔ)器中(0x08)=0x12; (0x09)=0xF8

          注:用以上八個(gè)函數(shù),可以完成對(duì)單片機(jī)內(nèi)部任意ROM和RAM進(jìn)行訪問(wèn),非常方便。還有一種方法,那就是用指鐘,后面會(huì)對(duì)C51的指針有詳細(xì)的介紹。

          4.寄存器變量(register)

          為了提高程序的執(zhí)行效率,C語(yǔ)言允許將一些頻率最高的那些變量,定義為能夠直接使用硬件寄存器的所謂的寄存器變量。定義一個(gè)變量時(shí),在變量類型名前冠以“register” 即將該變量定義成為了寄存器變量。寄存器變量可以認(rèn)為是一自動(dòng)變量的一種。有效作用范圍也自動(dòng)變量相同。由于計(jì)算機(jī)寄存器中寄存器是有限的。不能將所有變量都定義成為寄存器變量,通常在程序中定義寄存器變量時(shí),只是給編譯器一個(gè)建議,該變量是否真正成為寄存器變量,要由編譯器根據(jù)實(shí)際情況來(lái)確定。另一方面,C51編譯器能夠識(shí)別程序中使用頻率最高的變量,在可能的情況下,即使程序中并未將該變量定義為寄存器變量,編譯器也會(huì)自動(dòng)將其作為寄存器變量處理。被定義的變量是否真正能成為寄存器變量,最終是由編譯器決定的。

          5.內(nèi)存訪問(wèn)的實(shí)現(xiàn)

          (1)指鐘

          指鐘本身是一個(gè)變量,其中存放的內(nèi)容是變量的地址,也即特定的數(shù)據(jù)。8051的地址是16位的,所以指針變量本身占用兩個(gè)存儲(chǔ)單元。指針的說(shuō)明與變量的說(shuō)明類似,僅在指針名前加上“*”即可。

          如: int *int_point; //聲明一個(gè)整型指針

          char *char_point; //聲明一個(gè)字符型指針

          利用指針可以間接存取變量。實(shí)現(xiàn)這一點(diǎn)要用到兩個(gè)特殊運(yùn)算符

          & 取變量地址

          * 取指針指向單元的數(shù)據(jù)

          示例一:

          int a=15,b;

          int *int_point; //定義一個(gè)指向整型變量的指針

          int_point=&a; //int_point指向 a

          *int_point=5; //給int_point指向的變量a 賦值5 等同于a=5;

          示例二:

          char i,table[6],*char_point;

          char_point=table;

          for(i=0;i<6;i++)

          {

          char_point=i;

          char_point++;

          }

          注:指針可以進(jìn)行運(yùn)算,它可以與整數(shù)進(jìn)行加減運(yùn)算(移動(dòng)指針)。但要注意,移動(dòng)指針后,其地址的增減量是隨指針類型而異的,如,浮點(diǎn)指針進(jìn)行自增后,其內(nèi)部將在原有的基礎(chǔ)上加4,而字符指針當(dāng)進(jìn)生自增的時(shí)候,其內(nèi)容將加1。原因是浮點(diǎn)數(shù),占4個(gè)內(nèi)存單元,而字符占一個(gè)字節(jié)。

          宏晶科技最新一代STC12C5A360S2系列,每一個(gè)單片機(jī)出廠時(shí)都有全球唯一身份證號(hào)碼(ID號(hào)),用戶可以在單片機(jī)上電后讀取內(nèi)部RAM單元F1H~F7H的數(shù)值,來(lái)獲取此單片機(jī)的唯一身份證號(hào)碼。使用MOV @Ri指令來(lái)讀取。下面介紹C51獲取方法:

          char id[7]={0};

          char i;

          char idata *point;

          for(i=0;i<7;i++)

          {

          id[i]=*point;

          point++;

          }

          (此處只是對(duì)指針做一個(gè)小的介紹,達(dá)到訪問(wèn)內(nèi)部任何空間的方式,后述有對(duì)指針使用的詳細(xì)介紹)

          (2)對(duì)SFR,RAM ,ROM的直接存取

          C51提供了一組可以直接對(duì)其操作的擴(kuò)展函數(shù)

          若源程序中,用#include包含頭文件,io51.h 后,就可以在擴(kuò)展函數(shù)中使用特殊功能寄存器的地址名,以增強(qiáng)程序的可讀性:

          注 此方法對(duì)SFR,RAM,ROM的直接存取不建議使用.因?yàn)?淡io51.h這個(gè)頭文件在KEIL中無(wú)法打開(kāi),可用指針,或是采用absacc.h頭文件,

          (3) PWM與PCA

          STC12系列有兩路PWM/PCA

          PWM:(Pulse Width Modulation)脈寬調(diào)制,是一種使用程序來(lái)控制波形占空比,周期,相位波形的技術(shù)。

          PCA:(Programmable Counter Array)可編程計(jì)數(shù)陣列,它比通常的定時(shí)/計(jì)數(shù)器的定時(shí)能力強(qiáng),需要CPU的干預(yù)少。其優(yōu)勢(shì)一是軟件簡(jiǎn)單,二是精度大有提高。

          *6.動(dòng)態(tài)內(nèi)存分配的實(shí)現(xiàn)

          在單片機(jī)的實(shí)際開(kāi)發(fā)中,很多情況下我么需要開(kāi)辟一塊內(nèi)存,但是具體開(kāi)辟多大,也就是內(nèi)存的字節(jié)數(shù)我們還無(wú)法確定,比如可能要等到上位機(jī)的指令發(fā)送下來(lái)才能確定,這個(gè)時(shí)候我們就得動(dòng)態(tài)分配內(nèi)存。注意,單片機(jī)內(nèi)部存儲(chǔ)資源是極其有限的,不允許開(kāi)發(fā)人員開(kāi)辟出一塊很大的存儲(chǔ)區(qū)來(lái)備用。在VC 6.0環(huán)境下很容易用malloc()來(lái)得到一塊RAM,但是由于單片機(jī)內(nèi)部沒(méi)有操作系統(tǒng)(如何在51上跑uC/OS-II我以后會(huì)寫出來(lái)),所以在51上實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存分配就是個(gè)難點(diǎn)也是一個(gè)重點(diǎn)問(wèn)題。下面給出代碼,詳細(xì)分析大家可以參考求是科技編的《8051系列單片機(jī)C程序設(shè)計(jì)完全手冊(cè)》這本書。

          #include

          #include //init_mempool()、malloc()、free()函數(shù)所在的頭文件

          ……

          void main (void)

          {

          char *ptr1;

          init_mempool (0x1000,0x500); //內(nèi)存池初始化,0x1000為起始地址,0x500為內(nèi)存大小

          ptr1=malloc(30); /*動(dòng)態(tài)為指針變量分配長(zhǎng)度為30字節(jié)的存儲(chǔ)空間*/

          ……

          //此處為你的代碼

          ……

          free(ptr1) ; //注意,動(dòng)態(tài)內(nèi)存用完之后務(wù)必要釋放,否則程序?qū)?huì)出錯(cuò)

          while (1);

          }

          二、變量類型及其作用域剖析

          變量可分為 1.局部變量;2.全局變量(按變量的有效作用范圍劃分)

          1.局部變量

          是指函數(shù)內(nèi)部(包括main函數(shù))定義的變量,僅在定義它的那個(gè)函數(shù)范圍內(nèi)有效,不同函數(shù)可使用相同的局部變量名,函數(shù)的形式參數(shù)也屬于局部變量,在一個(gè)函數(shù)的內(nèi)部復(fù)合語(yǔ)句中也可以定義局部變量,該局部變量只在該復(fù)合語(yǔ)合中有效。

          2.全局變量

          是指函數(shù)外部定義的變量,以稱外部變量??蔀槎鄠€(gè)函數(shù)共同使用,其有效作用范圍是從它定義開(kāi)始到整個(gè)程序文件結(jié)束。如果全局變量,定義在一個(gè)程序文件的開(kāi)始處,則在整個(gè)程序文件范圍都可以使用它,如果一個(gè)全局變量不是在程序文件的開(kāi)始處定義,但又希望在它定義之前的函數(shù)中引用該變量,這時(shí)應(yīng)在引用該變量的函數(shù)中用關(guān)鍵字extern將其聲明為“外部變量”。另個(gè),如果在一個(gè)程序模塊文件中引用另一個(gè)程序模塊文件中定義的變量時(shí),也必須用extern進(jìn)行說(shuō)明。

          外部變量的說(shuō)明與外部變量的定義是不同的,外部變量定義只能有一次,定義的位置在所有函數(shù)之外,而同一個(gè)程序文件中(不是指模塊文件)的外部變量聲明可以有多次,聲明的置在需要引用該變量的函數(shù)之內(nèi),外部變量的聲明的作用只是聲明該變量是一個(gè)已經(jīng)在外部定義過(guò)了的變量而已。

          如在同一個(gè)程序文件中,全局變量與局部變量同名,則在局部變量的有效作用范圍之內(nèi),全局變量不起作用,也就是說(shuō),局部變量的優(yōu)先級(jí)比全局變量高。

          在編寫C語(yǔ)言程序時(shí),不是特別必要的地方一般不要使用全局變量,而應(yīng)當(dāng)盡可能的使用局部變量。因?yàn)榫植孔兞恐辉谑褂盟臅r(shí)候,才為其分配內(nèi)存單元,而全局變量在整個(gè)程序的執(zhí)行過(guò)程中都要占用內(nèi)存單元,且當(dāng)全局變量使用過(guò)多時(shí),會(huì)降低程序的可讀性。

          變量的存儲(chǔ)種類

          (1).自動(dòng)變量(auto)

          定義變量時(shí),在變量類型名前加上 “auto” ,自動(dòng)變量是C語(yǔ)言中使用最為廣泛的一類變量,在函數(shù)體內(nèi)部或是復(fù)合語(yǔ)句內(nèi)部定義的變量,如果省略了存儲(chǔ)種類說(shuō)明,則該變量默認(rèn)為自動(dòng)變量。

          例如:

          { 等價(jià)于 {

          char x; auto char x;

          int y; auto int y;

          …… ……

          } }

          注:自動(dòng)變量的作用范圍在定義它的函數(shù)體或是復(fù)合語(yǔ)句內(nèi)部,只有在定義它的函數(shù)內(nèi)被調(diào)用,或是定義它的復(fù)合語(yǔ)句被執(zhí)行時(shí),編譯器才會(huì)為其分配內(nèi)存空間,開(kāi)始其生存期。當(dāng)函數(shù)調(diào)用結(jié)束返回,或復(fù)合語(yǔ)句執(zhí)行結(jié)束,自動(dòng)變量所占用的內(nèi)存空間就被釋放,變量的值當(dāng)然也就不復(fù)存在,其生存期結(jié)束。當(dāng)函數(shù)再次調(diào)用,或是復(fù)合語(yǔ)句被再次執(zhí)行時(shí),編譯器又會(huì)為其內(nèi)部的自動(dòng)變量重新分配內(nèi)存空間。但不會(huì)保留上一次運(yùn)行的值。而必須被重新分配。因此自動(dòng)變量始終是相對(duì)于函數(shù)或復(fù)合語(yǔ)句的局部變量。

          (2).外部變量(extern)

          用說(shuō)明符“extern”定義的變量稱為外部變量。按缺省規(guī)則,凡是在所有函數(shù)之前,在函數(shù)外部定義的變量都是外部變量,定義時(shí)可以不寫extern說(shuō)明符,但是一個(gè)函數(shù)體內(nèi)說(shuō)明一個(gè)已在該函數(shù)體外或別的程序模塊文件中定義過(guò)的外部變量時(shí),剛必須要使用extern說(shuō)明符。外部變量定義后,它就被分配了固定的內(nèi)存空間。外部變量的生存期為程序的整個(gè)執(zhí)行時(shí)間。 外部變量的存儲(chǔ)不會(huì)隨函數(shù)或復(fù)合語(yǔ)句執(zhí)行完畢而釋放,因此外部變量屬于全局變量。

          C語(yǔ)言允許將大型程序分解為若干個(gè)獨(dú)立的程序模塊文件,各個(gè)模塊可分別進(jìn)行編譯,然后再將它們連接在一起,如果某個(gè)變量需要在所有程序模塊文件中使用,只要在一個(gè)程序模塊文件中將該變量定義成全局變量,而在其它程序模塊文件中用extern聲明該變量是已被定義過(guò)的外部變量就可以了。

          函數(shù)是可以相互調(diào)用的,定義函數(shù)時(shí),如果冠以關(guān)鍵字extern 即將其明確定義為一個(gè)外部函數(shù)。例如 extern int func2(char a,b) 。如果在定義函數(shù)時(shí)省略關(guān)鍵字extern,則隱含為外部函數(shù)。如果在調(diào)用一個(gè)在本程序模塊文件以外的其它模塊文件所定義的函數(shù),則必須要用關(guān)鍵字extern說(shuō)明被調(diào)用的函數(shù)是一個(gè)外部函數(shù)。對(duì)于具有外部函數(shù)相互調(diào)用的多模塊程序,可用C51編譯器分別對(duì)各個(gè)模塊文件進(jìn)行編譯,最后再用L51連接定位器將它們連接成為一個(gè)完整的程序。如下為一個(gè)多模塊程序:

          程序模塊1,文件名為file1.c

          #include

          int x=5;

          void main()

          {

          extern void fun1( );

          extern viod fun2(int y);

          fun1( );

          fun1( );

          fun1( );

          printf( “n%d %dn”,x,fun2(x));

          }

          程序模塊2,文件名為file2.c

          #include

          extern int x;

          void fun1( )

          {

          static int a=5; //靜態(tài)變量只在第一次調(diào)用函數(shù)時(shí)賦值,退出函數(shù)時(shí)

          //會(huì)保留上次的值,下次調(diào)用不再重新賦值。

          int b=5;

          printf(“%d %d %d |”,a,b,x);

          a-=2;

          b-=2

          x-=2;

          printf(“%d %d %d |”,a,b,x);

          }

          int fun2(int y)

          {

          return(35*x*y);

          }

          程序執(zhí)行如果如下:

          5 5 5 | 3 3 3

          3 5 3 | 1 3 1

          1 5 1 | -1 3 1

          -1 35

          注:C語(yǔ)言不允許在一個(gè)函數(shù)內(nèi)嵌套定義另一個(gè)函數(shù)。為了能夠訪問(wèn)不同文件中各個(gè)函數(shù)的變量,除了可以采用參數(shù)傳遞的方法外,還可以采用外部變量的方法,上面的例子就說(shuō)了這一點(diǎn)。不過(guò),盡管使用外部變量在不同函數(shù)之間傳遞數(shù)據(jù)有時(shí)比使用函數(shù)參數(shù)傳遞更為方便,不過(guò)當(dāng)外部變量過(guò)多時(shí),會(huì)增加程序的調(diào)試排錯(cuò)的困難。使得程序不便于維護(hù)。別外不通過(guò)參數(shù)傳遞直接在函數(shù)中改變?nèi)肿兞康闹?,有時(shí)還會(huì)發(fā)生一些意想不到的副作用。因些最好還是使用函數(shù)參數(shù)來(lái)傳遞數(shù)據(jù)。

          (3).寄存器變量(register)

          為了提高程序的執(zhí)行效率,C語(yǔ)言允許將一些頻率最高的那些變量,定義為能夠直接使用硬件寄存器的所謂的寄存器變量。定義一個(gè)變量時(shí),在變量類型名前冠以“register” 即將該變量定義成為了寄存器變量。寄存器變量可以認(rèn)為是一自動(dòng)變量的一種。有效作用范圍也自動(dòng)變量相同。由于計(jì)算機(jī)寄存器中寄存器是有限的。不能將所有變量都定義成為寄存器變量,通常在程序中定義寄存器變量時(shí),只是給編譯器一個(gè)建議,該變量是否真正成為寄存器變量,要由編譯器根據(jù)實(shí)際情況來(lái)確定。另一方面,C51編譯器能夠識(shí)別程序中使用頻率最高的變量,在可能的情況下,即使程序中并未將該變量定義為寄存器變量,編譯器也會(huì)自動(dòng)將其作為寄存器變量處理。被定義的變量是否真正能成為寄存器變量,最終是由編譯器決定的。

          (4).靜態(tài)變量(static)

          使用存儲(chǔ)種類說(shuō)明符“static”定義的變量為靜態(tài)變量,在上面模塊2程序文件中使用了一個(gè)靜態(tài)變量:static int a =5 ;由于這個(gè)變量是在函數(shù)fun1( )內(nèi)部定義,因此稱為內(nèi)部靜態(tài)變量或局部靜態(tài)變量。局部靜態(tài)變量始終都是存在的,但只有在定義它的函數(shù)內(nèi)部進(jìn)行訪問(wèn),退出函數(shù)之后,變量的值仍然保持,但不能進(jìn)行訪問(wèn)。

          還有一種全局靜態(tài)變量,它是在函數(shù)外部被定義的。作用范圍從它的定義點(diǎn)開(kāi)始,一直到程序結(jié)束,當(dāng)一個(gè)C語(yǔ)言程序由若干個(gè)模塊文件所組成時(shí),全局靜態(tài)變量始終存在,但它只能在被定義的模塊文件中訪問(wèn),其數(shù)據(jù)值可為該模塊文件內(nèi)的所有函數(shù)共享,退出該文件后,雖然變量的值仍然保持著,但不能被其它模塊文件訪問(wèn)。在一個(gè)較大的程序中,這就方便了多人設(shè)計(jì)時(shí),各自寫的程序模塊不會(huì)被別的模塊文件所引用。全局靜態(tài)變量和單純的全局變量,在編譯時(shí)就已經(jīng)為期分配了固定的內(nèi)存空間,只是他們的作用范圍不同而已。局部靜態(tài)變量是一種在兩次函數(shù)調(diào)用之間仍能保持其值的局部變量。如下,局部變量的使用——計(jì)算度輸出1~5的階乘值。

          #include

          int fac( int n)

          {

          static int f=1;

          f=f*n;

          return(f);

          }

          main( )

          {

          int i;

          for(i=1;i<=5;i++)

          printf(“%d!=%dn”,i,fac(i));

          }

          程序執(zhí)行結(jié)果

          1!=1

          2!=2

          3!=6

          4!=24

          5!=120

          注:在這個(gè)程序中一共調(diào)用了5次計(jì)算階乘的函數(shù)fac(i),每次調(diào)用后輸出一個(gè)階乘值i!,同時(shí)保留了這個(gè)i!值,以便下次再乘(i+1).由此可見(jiàn),如果要保留函數(shù)上一次調(diào)用結(jié)束時(shí)的值,或是在初始化之后變量只被引用而不改變其值,則這時(shí)使用局部靜態(tài)變量;較為方便,以免在每調(diào)用時(shí)都要重新進(jìn)行賦值,但是,使用局部靜態(tài)變量需要占用較多的內(nèi)存空間,而且降低了程序的可讀性,因此并不建議多用局部靜態(tài)變量。

          靜態(tài)函數(shù):

          對(duì)于函數(shù)也可以定義成為具為靜態(tài)存儲(chǔ)種類的屬性,定義函數(shù)時(shí)在函數(shù)名前冠以關(guān)鍵字static即將其定義為一個(gè)靜態(tài)函數(shù)。例如static int func1(char x, y)函數(shù)是外部型的,使用靜態(tài)函數(shù)可以使該函數(shù)只局限于當(dāng)前定義它的模塊文件中。其它模塊文件是不能調(diào)用它的。換名話說(shuō),就是在其它模塊文件中可以定義與靜態(tài)函數(shù)完全同名的另一個(gè)函數(shù)。不會(huì)因?yàn)槌绦蛑写嬖谙嗤暮瘮?shù)名而發(fā)生函數(shù)調(diào)用時(shí)的混亂。 這一點(diǎn)對(duì)于進(jìn)行模塊化程序設(shè)計(jì)是很有用的。

          三、中斷淺談

          0

          外中斷0

          1

          定時(shí)器0

          2

          外中斷1

          3

          定時(shí)器1

          4

          串行口

          定義中斷函數(shù)如下

          void timer1() interrupt 3

          {

          ……

          ……

          }

          強(qiáng)烈建議:如上所述,定義中斷函數(shù)時(shí)不要加using n選項(xiàng)。除非你對(duì)你的程序以及單片機(jī)的工作過(guò)程非常熟悉,否則會(huì)帶來(lái)不必要的麻煩。具體原因由于篇幅的限制暫不討論。

          C51中斷程序編寫要求:

          1.中斷函數(shù)不能進(jìn)行參數(shù)傳遞,否則,將導(dǎo)致編譯出錯(cuò)

          2.中斷中,不能包含任何參數(shù)聲明,否則,將導(dǎo)致編譯出錯(cuò)。

          3.中斷函數(shù)沒(méi)有返回值,如果企圖定義一個(gè)返回值將得到不正確的結(jié)果,因些建議在定義中斷函數(shù)的時(shí)將其定義為void 類型,明確說(shuō)明沒(méi)有返回值。

          4.任何情況下都不能直接調(diào)用中斷函數(shù),否則會(huì)主生編譯出錯(cuò)。

          5.如果中斷函數(shù)中用到了浮點(diǎn)運(yùn)算,必須保存浮點(diǎn)寄存器的狀態(tài)。當(dāng)沒(méi)有其它的程序執(zhí)行浮點(diǎn)運(yùn)算時(shí)(即只有中斷中用到浮點(diǎn)運(yùn)算),可以不用保存。

          6.如果中斷函數(shù)中調(diào)用了其它函數(shù),則被調(diào)用的函數(shù)所使用的寄存器組必須與中斷函數(shù)相同,用戶必須保證按要求使用相同的寄存器組,否則會(huì)產(chǎn)生不正確的結(jié)果,這一點(diǎn)必須引起足夠的注意,如果定義中斷函數(shù)時(shí)沒(méi)有使用using選項(xiàng),則由編譯器選擇一個(gè)寄存器組作絕對(duì)寄存器訪問(wèn)。另外,不斷的產(chǎn)生不可預(yù)測(cè),中斷函數(shù)對(duì)其它函數(shù)的調(diào)用可能形成遞規(guī)調(diào)用,需要時(shí),可將被中斷調(diào)用的其它函數(shù)定義為再入函數(shù)。

          淺析函數(shù)的遞規(guī)調(diào)用與再入函數(shù):

          函數(shù)的遞規(guī)調(diào)用: 在調(diào)用一個(gè)函數(shù)的過(guò)程中雙直接或間接的調(diào)用該函數(shù)本身;

          再入函數(shù):一種可以在函數(shù)體內(nèi)直接或間接調(diào)用其自身的一種函數(shù)。

          C51編譯器采用一個(gè)擴(kuò)展關(guān)鍵字reentrant 作為定義函數(shù)時(shí)的選項(xiàng),需要將一個(gè)函數(shù)定義為再入函數(shù)時(shí),只要在函數(shù)名后加上關(guān)鍵字reentrant即可。空不空格以及空幾格都無(wú)所謂。

          再入函數(shù)剖析:

          再入函數(shù)可被遞歸調(diào)用,無(wú)論何時(shí),包括中斷服務(wù)函數(shù)在內(nèi)的任何函數(shù)都可調(diào)用再入函數(shù)。與非再入函數(shù)的參數(shù)傳遞和局部就是的存儲(chǔ)分配方法不同,C51編譯器為每個(gè)再入函數(shù)都生成一個(gè)模擬棧。模擬棧所在的存儲(chǔ)器空間根據(jù)再入函數(shù)的存儲(chǔ)模式的不同,可以分配到DATA,PDATA 或XDATA。

          對(duì)再入函數(shù)有如下規(guī)定:

          1.再入函數(shù)不能傳送bit類型的參數(shù)。也不能定義一個(gè)局部位變量,再入函數(shù)不能包括位操作以及8051系列單片機(jī)的可位尋址區(qū)。

          2.與PL/ M51兼容的函數(shù),不能具有reentrant屬性,也不能調(diào)用再入函數(shù)。

          3.編譯時(shí),在存儲(chǔ)器模式的基礎(chǔ)上,為再入函數(shù)在內(nèi)部或外部存儲(chǔ)中建立一個(gè)模擬堆棧區(qū),稱為再入棧,再入函數(shù)的局部變量及參數(shù)被放在再入棧中,從而使得再入函數(shù)可以進(jìn)行遞規(guī)調(diào)用。再非再入函數(shù)的局部變量被放在再入棧之外的暫存區(qū)內(nèi),如果對(duì)非再入函數(shù)進(jìn)行遞規(guī)調(diào)用,則上次調(diào)用時(shí)使用的局部變量數(shù)據(jù)將被覆蓋。

          4.在同一個(gè)程序中可以定義和使用不同存儲(chǔ)器模式的再入函數(shù),任意模式的再入函數(shù)不能調(diào)用不同模式的再入函數(shù),但可以任意調(diào)用非再入函數(shù)。

          5.在參數(shù)的傳遞上,實(shí)際參數(shù),可以傳遞給間接調(diào)用的再入函數(shù),無(wú)再入屬性的間接調(diào)用函數(shù)不能包含調(diào)用參數(shù)。但是可以使用定義的全局變量來(lái)進(jìn)行參數(shù)傳遞。

          四、C51指針深度剖析(非常重要,嵌入式系統(tǒng)開(kāi)發(fā)人員必須要掌握的內(nèi)容)

          注意:由于篇幅所限,本人暫時(shí)不打算討論抽象指針的內(nèi)容。但是你必須上網(wǎng)或去圖書館找找關(guān)于抽象指針的資料好好看看,抽象指針很有用的。

          指針是C語(yǔ)言中的一個(gè)重要概念,使用也十分普遍,正確使用指針類型數(shù)據(jù)可以有效的表示復(fù)雜的數(shù)據(jù)結(jié)構(gòu),直接處理內(nèi)存地址,而且可以更為有效的使用數(shù)組。

          在C語(yǔ)言中,為了能夠?qū)崿F(xiàn)直接對(duì)內(nèi)存單元的操作,引入了指針類型的數(shù)據(jù),指針類型數(shù)據(jù)是專門用來(lái)確定其它數(shù)據(jù)類型的地址的,因此一個(gè)變量的地址就被稱為該變量的指針如: 一個(gè)整形變量i 存放在內(nèi)存單元40H中,則該內(nèi)存單元地址40H就是變量i 的指針。如果有一個(gè)變量專門用來(lái)存放另一個(gè)變量的地址,則稱之為“指針變量”

          變量指針與指針變量

          變量的指針: 是指某個(gè)變量的地址,而一個(gè)指針變量里面存放的是另一個(gè)變量在內(nèi)存中的地址。擁有這個(gè)地址的變量則稱為該指針變量所指向的變量。 所以每個(gè)變量都有它自己的指針(地址),而每一個(gè)指針變量都是指向另一個(gè)變量的。C語(yǔ)言中用符號(hào)“*”來(lái)表示“指向”,如下:

          i=50;

          *ip=50;

          如果指針ip這個(gè)指針變量指向i那么,兩個(gè)賦值表達(dá)或同義,第二個(gè)表達(dá)式可以解釋為“給指針變量ip所指向的變量賦值50”。

          (1).指針變量的定義

          指針變量的定義與一般變量的定義類似,其一般形式如下:

          數(shù)據(jù)類型 [存儲(chǔ)器類型] * 標(biāo)識(shí)符;

          標(biāo)識(shí)符, 是所定義的指針變量名

          數(shù)據(jù)類型, 說(shuō)明了該指針變量所指向的變量類型

          存儲(chǔ)器類型,是可選的,它是C51編譯器的一種擴(kuò)展,如果帶有此選項(xiàng),指針被定義為基于存儲(chǔ)器的指針,無(wú)此選項(xiàng)時(shí),被定義為一般指針,這兩種指針的區(qū)別在于它們的存儲(chǔ)字節(jié)不同。

          一般指針:占用三個(gè)字節(jié),第一個(gè)字節(jié)存放該指針存儲(chǔ)器類型的編碼,第二和第三個(gè)字節(jié)分別存放該指針的高位和低位地址的偏移量

          存儲(chǔ)器類型

          IDATA

          XDATA

          PDATA

          DATA

          CODE

          編碼值

          1

          2

          3

          4

          5

          基于存儲(chǔ)器指針:則該指針長(zhǎng)度可為一個(gè)字節(jié),也可為兩字節(jié)

          一個(gè)字節(jié): (存儲(chǔ)器類型 idata data pdata)

          兩個(gè)字節(jié): (存儲(chǔ)器類型為code xdata)

          注:在定義指針變量時(shí)最好指定其為基于存儲(chǔ)器的指針,這個(gè)生成的匯編代碼長(zhǎng)精 練一些,而且也節(jié)省空間(讀者可自行到C51中寫一個(gè)程序,查看其反匯編程序)但在一些函數(shù)調(diào)用的參數(shù)中指針需要采用一般指針,為此C51編譯器允許這兩種指針相互轉(zhuǎn)換,轉(zhuǎn)換規(guī)則如下:

          一般指針轉(zhuǎn)換成基于存儲(chǔ)器指針,采取截?cái)啵诖鎯?chǔ)器類型指針轉(zhuǎn)換成一般指針采用擴(kuò)展的。

          (2).指針變量的引用

          指針變量是含有一個(gè)數(shù)據(jù)對(duì)象地址的特殊變量,指針變量中只能存放地址與指針變量有關(guān)的兩個(gè)運(yùn)算符:

          & 取地址運(yùn)算符

          * 間接訪問(wèn)運(yùn)算符

          &a為取變量a的地址,*P為指針變量P所指向的變量。如下:

          int i , x, y;

          int *pi,*px,*py;

          pi=&i; //將變量i的地址賦給指針變量pi,也即pi指向i

          px=&x;

          py=&py;

          *pi=0; //等價(jià)于i=0

          *px+=6; //等價(jià)于 i+=6

          (*py)++; //等價(jià)于 i++

          注:指向同類數(shù)據(jù)的指針之間可以相互賦值。如pi=px;

          (3).指針變量作為函數(shù)的參數(shù)

          函數(shù)的參數(shù)不僅可以是整型,字符型等數(shù)據(jù),還可以是指針類型,指針變量作為函數(shù)的參數(shù)的作用是將一個(gè)變量的地址傳到另一個(gè)函數(shù)中去,地址傳遞是雙向的,即主調(diào)用函數(shù)不僅可以向被調(diào)用函數(shù)傳遞參數(shù),而且還可以從被調(diào)用函數(shù)返回其結(jié)果。下面通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)進(jìn)行說(shuō)明。

          #include

          swap(int *pi,int *pj)

          {

          int temp;

          temp=*pi;

          *pi=*pj; //把指針變量pj所指向的變量的值送給pi所指向的變量

          *pj=temp;

          }

          main( )

          {

          int a,b;

          int *pa, *pb;

          a=9;

          b=7;

          pa=&a;

          pb=&b;

          if(a

          printf(“n max=%d,min=%d n”,a,b);

          }

          上程序上定義了一個(gè)swap( )函數(shù),兩個(gè)形參為指針變量,在調(diào)用函數(shù)時(shí),所用的實(shí)參也是指針變量,在調(diào)用開(kāi)始,實(shí)參變量將它的值傳遞給形參變量,采取的仍然是“值傳遞”方式,但這時(shí)傳遞的是指針的值(地址),傳遞后,形參pi的值為&a,pj的值為&b,即指針變量*pi 和*pa都指向了a, *pj和*pb指向了b。接著使*pj與*pi的值互換,從而達(dá)到了實(shí)現(xiàn)了a,和b值的互換。雖然函數(shù)返回時(shí),pi pj被釋放而不存在,但main函數(shù)中a 與b的值已經(jīng)交換。

          (4).?dāng)?shù)組的指針

          在C語(yǔ)言中,指針與數(shù)組有著十分密切的關(guān)系,任何能夠用數(shù)組實(shí)現(xiàn)的運(yùn)算都可以通過(guò)指針來(lái)完成,例如定義一個(gè)具有十個(gè)元素的整形數(shù)據(jù)可以寫成:

          int a[10];

          數(shù)組名a表示元素a[0]的地址,而*a 則表示a所代表地址中的內(nèi)容,即a[0].

          如果定義一個(gè)指向整形變量的指針pa并賦以數(shù)組a中的第一個(gè)元素a[0]的地址;

          int *pa;

          pa=&a[0]; //也可寫成pa=a;

          則可通過(guò)指針pa來(lái)操作數(shù)組a了,即可用*pa代表a[0];*(pa+i)代表a[i],也可以上pa[0];pa[1];pa[2]……pa[9]的形式

          (5).字符數(shù)組的指針

          用指針來(lái)描述一個(gè)字符數(shù)組是十分方便的,字符串是以字符數(shù)組的形式給出的,并且每個(gè)字符數(shù)組都是以轉(zhuǎn)義字符看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();