Ⅰ 用keil編譯C時出現 non-address/-consatant initializer 該怎麼辦啊求教!!
你定義的數組用code修飾了以後是放在rom上,這樣是在編譯以後就固定的。
而你的數組賦值的時候用的是全局變數動態的。
所以出現無法初始化的error。
你要麼去掉code,要麼把數組用常量表示。
Ⅱ 「Keil C51」下如何讓編譯器優先使用片內「RAM」
C51內存拿歲者結構深度剖析x0dx0a在編寫應用程序時,定義一個變數,一個數組,或是說一個固定表格,到底存儲在什麼地方;當定義變數大小超過MCU的內存范圍時怎麼辦;如何控制變數定義不超過存儲范圍;以及如何定義變數才能使得變數訪問速度最快,寫出的程序運行效率最高。以下將一一解答。x0dx0ax0dx0a1 六類關鍵字(六類存儲類型)x0dx0adata idata xdata pdata code bdatax0dx0ax0dx0a code: code memory (程序存儲器也即只讀存儲器)用來保存常量或是程序。code memory 採用16位地址線編碼,可以是在片內,或是片外,大小被限制在64KBx0dx0a 作用:定義常量,如八段數碼表或是編程使用的常,在定義時加上code 或明確指明定義的常量保存到code memory(只讀)x0dx0a 使用方法:x0dx0a char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};x0dx0a 此關鍵字的使用方法等同於constx0dx0ax0dx0adata data memory (數據存儲區)只能用於聲明變數,不能用來聲明函數,該區域位於片內,採用8位地址線編碼,具有最快的存儲速度,但是數量被限制在128byte或更少。x0dx0a 使用方法:x0dx0a unsigned char data fast_variable=0;x0dx0ax0dx0a idata idata memory(數據存儲區)只能用於聲明變數,不能用來聲明函數. 該區域位於片內,採用8位地址線編碼,內存大小被限制在256byte或更少。該區域消薯的低地址區與data memory地址一致;高地址區域是52系列在51系列基礎上擴展的並與特殊功能寄存器具有相同地址編碼的區域。即:data memory是idata memory的一個子集。x0dx0a x0dx0a xdata xdata memory 只能用於聲明變數,不能用來聲明函數,該區域位於MCUx0dx0a 外部,採用16位地址線進行編碼,存儲大小被限制在64KB以內。x0dx0a 使用方法:x0dx0a unsigned char xdata count=0;x0dx0ax0dx0apdata pdata memory 只能用於聲明變數,不能用雀衫來聲明函數,該區域位於MCU外部,採用8位地址線進行編碼。存儲大小限制在256byte. 是xdata memory的低256byte。為其子集。x0dx0a 使用方法x0dx0a unsigned char pdata count=0;x0dx0ax0dx0a bdata bdata memory 只能用於聲明變數,不能用來聲明函數。該區域位於8051內部位數據地址。定義的量保存在內部位地址空間,可用位指令直接讀寫。x0dx0a 使用方法:x0dx0a unsigned char bdata varab=0x0dx0ax0dx0a 註:有些資料講,定義字元型變數時,在預設unsigned 時,字元型變數,默認為無符號,與標准C不同,但我在Keil uVision3中測試的時候發現並非如此。在預設的情況下默認為有符號。或許在以前的編譯器是默認為無符號。所以看到有的資料上面這樣講的時候,要注意一下,不同的編譯器或許不同。所以我們在寫程序的時候,還是乖乖的把unsigned signed 加上,咱也別偷這個懶。x0dx0a 2函數的參數和局部變數的存儲模式x0dx0a C51 編譯器允許採用三種存儲器模式:SMALL,COMPACT 和LARGE。一個函數的存儲器模式確定了函數的參數的局部變數在內存中的地址空間。處於SMALL模式下的函數參數和局部變數位於8051單片機內部RAM中,處於COMPACT和LARGE模式下的函數參數和局部變數則使用單片機外部RAM。在定義一個函數時可以明確指定該函數的存儲器模式。方法是在形參表列的後面加上一存儲模式。x0dx0a x0dx0a 示例如下:x0dx0a #pragma large //此預編譯必須放在所有頭文前面x0dx0a int func0(char x,y) small;x0dx0a char func1(int x) large;x0dx0a int func2(char x);x0dx0a 註:x0dx0a 上面例子在第一行用了一個預編譯命令#pragma 它的意思是告訴c51編譯器在對程序進行編譯時,按該預編譯命令後面給出的編譯控制指令LARGE進行編譯,即本常式序編譯時的默認存儲模式為LARGE.隨後定義了三個函數,第一個定義為SMALL存儲模式,第二個函數定義為LARGE第三個函數未指定,在用C51進行編譯時,只有最後一個函數按LARGE存儲器模式處理,其它則分別按它們各自指定的存儲器模式處理。x0dx0a 本例說明,C51編譯器允許採用所謂的存儲器混合模式,即允許在一個程序中將一些函數使用一種存儲模式,而其它一些則按另一種存儲器模式,採用存儲器混合模式編程,可以充分利用8051系列單片機中有限的存儲器空間,同時還可以加快程序的執行速度。x0dx0ax0dx0a3絕對地址訪問 absacc.h(相當重要)x0dx0ax0dx0a#define CBYTE ((unsigned char volatile code *) 0)x0dx0a#define DBYTE ((unsigned char volatile data *) 0)x0dx0a#define PBYTE ((unsigned char volatile pdata *) 0)x0dx0a#define XBYTE ((unsigned char volatile xdata *) 0)x0dx0a 功能:CBYTE 定址 CODE區x0dx0a DBYTE 定址 DATA區x0dx0a PBYTE 定址 XDATA(低256)區x0dx0a XBYTE 定址 XDATA區x0dx0a 例: 如下指令在對外部存儲器區域訪問地址0x1000x0dx0a xvar=XBYTE[0x1000];x0dx0a XBYTE[0x1000]=20;x0dx0ax0dx0a#define CWORD ((unsigned int volatile code *) 0)x0dx0a#define DWORD ((unsigned int volatile data *) 0)x0dx0a#define PWORD ((unsigned int volatile pdata *) 0)x0dx0a#define XWORD ((unsigned int volatile xdata *) 0)x0dx0ax0dx0a 功能:與前面的一個宏相似,只是它們指定的數據類型為unsigned int .。x0dx0a 通過靈活運用不同的數據類型,所有的8051地址空間都是可以進行訪問。x0dx0a 如x0dx0aDWORD[0x0004]=0x12F8;x0dx0a即內部數據存儲器中(0x08)=0x12; (0x09)=0xF8x0dx0ax0dx0a註:用以上八個函數,可以完成對單片機內部任意ROM和RAM進行訪問,非常方便。還有一種方法,那就是用指鍾,後面會對C51的指針有詳細的介紹。x0dx0ax0dx0a4寄存器變數(register)x0dx0a 為了提高程序的執行效率,C語言允許將一些頻率最高的那些變數,定義為能夠直接使用硬體寄存器的所謂的寄存器變數。定義一個變數時,在變數類型名前冠以「register」 即將該變數定義成為了寄存器變數。寄存器變數可以認為是一自動變數的一種。有效作用范圍也自動變數相同。由於計算機寄存器中寄存器是有限的。不能將所有變數都定義成為寄存器變數,通常在程序中定義寄存器變數時,只是給編譯器一個建議,該變數是否真正成為寄存器變數,要由編譯器根據實際情況來確定。另一方面,C51編譯器能夠識別程序中使用頻率最高的變數,在可能的情況下,即使程序中並未將該變數定義為寄存器變數,編譯器也會自動將其作為寄存器變數處理。被定義的變數是否真正能成為寄存器變數,最終是由編譯器決定的。x0dx0ax0dx0a5內存訪問雜談x0dx0a 1指鍾x0dx0a指鍾本身是一個變數,其中存放的內容是變數的地址,也即特定的數據。8051的地址是16位的,所以指針變數本身佔用兩個存儲單元。指針的說明與變數的說明類似,僅在指針名前加上「*」即可。x0dx0a 如 int *int_point; 聲明一個整型指針x0dx0a char *char_point; 聲明一個字元型指針x0dx0a 利用指針可以間接存取變數。實現這一點要用到兩個特殊運算符x0dx0a & 取變數地址x0dx0a * 取指針指向單元的數據x0dx0ax0dx0a示例一:x0dx0aint a,b;x0dx0a int *int_point; //定義一個指向整型變數的指針x0dx0a a=15;x0dx0a int_point=&a; //int_point指向 ax0dx0a *int_point=5; //給int_point指向的變數a 賦值5 等同於a=5; x0dx0a示例二:x0dx0a char i,table[6],*char_point;x0dx0a char_point=table;x0dx0a for(i=0;i<6;i++)x0dx0a {x0dx0a char_point=i;x0dx0a char_point++;x0dx0a}x0dx0a註:x0dx0a 指針可以進行運算,它可以與整數進行加減運算(移動指針)。但要注意,移動指針後,其地址的增減量是隨指針類型而異的,如,浮點指針進行自增後,其內部將在原有的基礎上加4,而字元指針當進生自增的時候,其內容將加1。原因是浮點數,佔4個內存單元,而字元佔一個位元組。x0dx0ax0dx0a宏晶科技最新一代STC12C5A360S2系列,每一個單片機出廠時都有全球唯一身份證號碼(ID號),用戶可以在單片機上電後讀取內部RAM單元F1H~F7H的數值,來獲取此單片機的唯一身份證號碼。使用MOV @Ri 指令來讀取。下面介紹C51 獲取方法:x0dx0a char id[7]={0};x0dx0a char i;x0dx0a char idata *point;x0dx0a for(i=0;i<7;i++)x0dx0a {x0dx0a id[i]=*point;x0dx0a point++;x0dx0a}x0dx0a x0dx0a(此處只是對指針做一個小的介紹,達到訪問內部任何空間的方式,後述有對指針使用的詳細介紹)x0dx0a2對SFR,RAM ,ROM的直接存取x0dx0aC51提供了一組可以直接對其操作的擴展函數x0dx0a若源程序中,用#include包含頭文件,io51.h 後,就可以在擴展函數中使用特殊功能寄存器的地址名,以增強程序的可讀性:x0dx0ax0dx0a 注 此方法對SFR,RAM,ROM的直接存取不建議使用.因為,淡io51.h這個頭文件在KEIL中無法打開,可用指針,或是採用absacc.h頭文件,