|
|
| <!--插入廣告JS代碼--> |
SDCC (小型設備C編譯器)是為8位微控制器開發(fā)的免費C編譯器。本應用筆記演示如何使用SDCC來開發(fā)DS89C420/430/440/450系列超高速8051兼容微控制器固件。文中還介紹了如何安裝免費SDCC C編譯器。
<!-- BEGIN: DB HTML -->簡介
SDCC (小型設備C編譯器)是為8位微控制器開發(fā)的免費C編譯器。盡管兼容多種不同體系結構,但SDCC C編譯器更適合8051內核。本應用筆記主要介紹采用SDCC來開發(fā)Maxim/Dallas Semiconductor的DS89C420/430/440/450系列超高速8051兼容微控制器固件。
SDCC是命令行固件開發(fā)工具,含預處理器、編譯器、匯編器、鏈接器和優(yōu)化器。安裝文件中還捆綁了SDCDB、類似于gdb (GNU調試器)的源碼級調試器。無錯的程序采用SDCC編譯、鏈接后,生成一個Intel十六進制格式的加載模塊。之后可采用串行加載器將該文件加載至 DS89C420/430/440/450微控制器閃存。(參見DS89C420/430/440/450文檔和應用筆記,了解固件下載至器件的詳細信息。)
關于SDCC的最新信息,請訪問http://sdcc.sourceforge.net,或者閱讀SDCC手冊sdccman.pdf (在安裝過程中復制到您的硬盤上)。也可以將問題提交給SDCC在線消息論壇,或發(fā)郵件至SDCC網(wǎng)頁“Support”列出的郵件地址。
安裝SDCC免費C編譯器
如果需要安裝SDCC,請從網(wǎng)址http://sdcc.sourceforge.net下載SDCC最新版本。雖然也可使用該軟件的日常構建(nightly builds)版,但通常最安全的方式是下載經(jīng)過完全測試的最新發(fā)布版(目前是v2.4.0)。
在“Download”頁為不同的操作系統(tǒng)提供不同的SDCC。如果您使用運行Microsoft Windows的PC,請下載并運行win32自解壓SDCC安裝文件(sdcc-2.4.0-setup.exe)。
安裝程序時會出現(xiàn)一個提示,詢問是否將含有程序二進制文件的目錄添加到您的路徑中。建議同意添加,本應用筆記假設用戶路徑中已添加該目錄。
采用SDCC編譯器編譯一個簡單的C程序
為確保SDCC已在您的硬盤上正確安裝,請在命令提示符下鍵入sdcc --version,然后回車,窗口中應出現(xiàn)圖1所示文本(實際文本與您下載的SDCC版本有關):
圖1. 通過版本檢查確認SDCC是否正確安裝
為測試包含路徑,生成名為sdcctest.c的文件,并將以下源代碼復制到該文件中。
#include char str[6] = "MAXIM";bit flag;void main(void){ if (strcmp(str,"MAXIM") == 0) flag = 0; else flag = 1; while(1); // program loop}
以普通ASCII格式(如使用Microsoft記事本程序)保存該文件。在命令提示符下,鍵入sdcc sdcctest.c,然后回車。如像圖2那樣沒有任何反應,則說明程序編譯成功。
圖2. 編譯簡單的SDCC程序
當源代碼編譯成功時,SDCC會生成多個文件。在編譯目錄中可找到以下文件:
- sdcctest.asm: 程序的匯編文件
- sdcctest.lst: 程序的列表文件
- sdcctest.rst: 被鏈接器更新的列表文件
- sdcctest.map: 被鏈接器更新的最終存儲器映射
- sdcctest.ihx: Intel十六進制格式的加載模塊。該文件必須被下載到微控制器中。
同時還生成其它文件(多數(shù)用于源碼級調試器)。請閱讀SDCC文檔了解更詳細的信息。 SDCC專有數(shù)據(jù)類型
SDCC支持多數(shù)ANSI-C數(shù)據(jù)類型,如:
- char: 1字節(jié) - 可以有符號或無符號
- short: 2字節(jié) - 可以有符號或無符號
- int: 2字節(jié) - 可以有符號或無符號
- long: 4字節(jié) - 可以有符號或無符號
- float: 4字節(jié)
此外,SDCC支持多種擴展數(shù)據(jù)類型(也稱為存儲類型),以充分利用8051體系結構的優(yōu)勢,這將在后面以實例說明。 與一些商用8051微控制器開發(fā)工具不同,SDCC僅支持聲明位和字節(jié)可尋址特殊功能寄存器。盡管8051匯編語言支持,但SDCC并不支持共享位和字節(jié)可尋址RAM。為證實這一點,請觀察以下代碼實例和編譯完的匯編代碼。
C源程序:
union{ unsigned char a_byte; struct { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; unsigned char bit3 : 1; unsigned char bit4 : 1; unsigned char bit5 : 1; unsigned char bit6 : 1; unsigned char bit7 : 1; } a_bit;} a;bit b;void main(void){ a.a_byte = 0x05; a.a_bit.bit6 = 1; b = 1; while(1); // program loop}Assembly listing (.rst file): ... 159 ;sdcctest.c:21: a.a_byte = 5; 160 ; genPointerSet 161 ; genNearPointerSet 162 ; genDataPointerSet 0031 75 21 05 163 mov_a,#0x05 164 ;sdcctest.c:23: a.a_bit.bit6 = 1; 165 ; genPointerSet 166 ; genNearPointerSet 0034 78 21 167 movr0,#_a 168 ; genPackBits 0036 E6 169 mova,@r0 0037 44 40 170 orla,#0x40 0039 F6 171 mov@r0,a 172 ;sdcctest.c:25: b = 1; 173 ; genAssign 003A D2 00 174 setb_b 175 ;sdcctest.c:27: while(1); // program loop ...盡管在聲明中“a”看起來是位尋址存儲器,但匯編列表文件(來自由SDCC生成的.rst文件)表明變量并沒有使用位尋址。在列表中不要混淆“a”和“_a”!癮”指累加器,而“_a”指變量。 注意,本應用筆記在“絕對尋址”一節(jié)介紹了一種可真正實現(xiàn)存儲器位尋址的方法。
near/data
以near或data存儲類型聲明的變量將被放在8051內核的直接尋址RAM中。DS89C420/430/440/450系列微控制器具有128字節(jié)直接尋址存儲器,這是8051能夠訪問的速度最快的存儲器,生成的匯編代碼只需一個MOV指令即可讀寫該RAM中的數(shù)據(jù)。
#include "sdcc_reg420.h"data unsigned char outPort0 = 0x4A;void main(void){ P0 = outPort0; while (1); // program loop}該例中使用的定義文件sdcc_reg420.h見附錄A。 far/xdata
以far或xdata存儲類型聲明的變量將被放在外部RAM中。這樣開發(fā)人員能夠訪問更大的RAM空間,但生成的匯編代碼需要使用MOVX指令來讀寫該存儲器,這要求將外部存儲器地址裝入數(shù)據(jù)指針。
DS89C420/430/440/450系列微控制器含有1K字節(jié)的內部SRAM,可被用于以far/xdata聲明的變量。注意,電源管理寄存器(PMR)中的DME1:0位在該存儲器初始化或使用之前,必須先被置為內部SRAM模式。
#include "sdcc_reg420.h"xdata unsigned char ioPorts[2];void main(void){ PMR |= 0x01; // Enable internal 1K SRAM ioPorts[0] = 0x4A; ioPorts[1] = 0x56; P0 = ioPorts[0]; P1 = ioPorts[1]; while (1); // program loop}idata
以idata存儲類型聲明的變量將被放在8051內核的間接尋址存儲器中。間接可尋址存儲器與直接尋址存儲器類似,在8051內核中共有128字節(jié)(不包括特殊功能寄存器)。但是,訪問idata需要額外的MOV命令將RAM地址移至工作寄存器中。 #include "sdcc_reg420.h"idata unsigned int port0_x2;void main(void){ while (1) // program loop { port0_x2 = P0 * 2; }}pdata
存儲類型pdata用于訪問分頁的外部數(shù)據(jù)存儲器。該存儲類型超出了本應用筆記范疇,有興趣的讀者可以閱讀SDCC文檔的pdata部分。 code
以code存儲類型聲明的變量將被放在程序存儲器(DS89C420/430/440/450微控制器內部的閃存)中。對于SDCC來說,這類變量只讀,因此常使用code來聲明常量(如:查找表)。
#include "sdcc_reg420.h"code unsigned char out[10] = {0x03,0x45,0xFA,0x43,0xDD, 0x1A,0xE0,0x00,0x87,0x91};void main(void){ data unsigned char i = 0; while (1) // program loop { P0 = out[i++]; if (i==10) i=0; }}bit
以bit存儲類型聲明的變量被放在8051內核的位尋址存儲器中。8051內核的16字節(jié)直接尋址RAM可用作位尋址存儲器(字節(jié)0x20至0x2F),提供128個可尋址位。使用該類變量作為標志位可高效利用存儲空間。 #include "sdcc_reg420.h"#define ESCAPE 0x1Bbit esc_char_flag = 0;void main(void){ P1 = 0x00; while (!esc_char_flag) { if (P0 == ESCAPE) esc_char_flag = 1; } P1 = 0xFF; while (1); // program loop}sfr
存儲類型sfr被用來定義8051內核專有的特殊功能寄存器(SFR)。附錄A定義文件中使用sfr標識符定義了DS89C420/430/440/450微控制器中的所有SFR。 注意,下面的實例已定義了SFR,因此沒有必要包含定義文件sdcc_reg420.h。
sfr at 0x80 P0;sfr at 0x90 P1;void main(void){ P0 = 0x00; P1 = 0xFF; while (1); // program loop}sbit
存儲類型sbit用于定義可位尋址SFR中的特殊位。在8051內核中,地址以0或者8(十六進制)結束的所有SFR均可位尋址。附錄A定義文件中使用sbit標識符定義了DS89C420/430/440/450微控制器SFR的所有可尋址位。 sfr at 0x80 P0; // Port 0sbit at 0x80 P0_0; // Port 0 bit 0sbit at 0x81 P0_1; // Port 0 bit 1sbit at 0x82 P0_2; // Port 0 bit 2sbit at 0x83 P0_3; // Port 0 bit 3sbit at 0x84 P0_4; // Port 0 bit 4sbit at 0x85 P0_5; // Port 0 bit 5sbit at 0x86 P0_6; // Port 0 bit 6sbit at 0x87 P0_7; // Port 0 bit 7void main(void){ P0 = 0x00; // P0 = 0x00 P0_4 = 1; // P0 = 0x10 while (1); // program loop}Absolute Addressing
SDCC支持采用at標識符的絕對尋址。但是,SDCC不跟蹤聲明的絕對尋址變量,而且可能在其地址聲明其它變量,造成相互覆蓋。 以下程序顯示了有趣的潛在錯誤。
#include "sdcc_reg420.h"unsigned char a = 0x4A;unsigned int b = 0x0000;unsigned char c[64] = {0x00};unsigned char at 0x0010 y;unsigned char at 0x0010 z;void main(void){ for(b=0; b<64; b++) c[b] = 0xAA; y = 0xF1; z = 0xF2; a = c[5]; while (1); // program loop}使用SDCC時,盡管變量"y"和"z"分配同一個位置,也可進行無錯誤或警告的編譯。如果要運行該程序,我們認為程序(a = c[5])中"a"最終將被設置為0xAA。但情況并非如此。"a"最終被分配的值為0xF2。 如果查看SDCC生成的.map文件中以下幾行語句(顯示每個變量的實際地址),便會明白這種情況的原因。
Area Addr Size Decimal Bytes (Attributes)-------------------------------- ---- ---- ------- ----- ------------. .ABS. 0000 0000 = 0. bytes (ABS,OVR) Value Global -------- -------------------------------- ... 0010 _y 0010 _z ...Area Addr Size Decimal Bytes (Attributes)-------------------------------- ---- ---- ------- ----- ------------DSEG 0008 0043 = 67. bytes (REL,CON) Value Global -------- -------------------------------- 0008 _a 0009 _b 000B _c
注意,變量名稱前的下劃線是由編譯器添加的。如果"c"位于地址0x000B,長度為64字節(jié),那么它將覆蓋位于地址0x0010處的變量"y"和"z"。 絕對尋址可用于仿真位尋址變量。在下面的例子中,在位尋址存儲器的最后一個字節(jié)處定義變量n_byte。然后,在8051內核位尋址存儲器的最后8位定義n_bit0至n_bit7。由于這種重疊,可采用變量n_bit0至n_bit7對變量n_byte進行位尋址。
#include "sdcc_reg420.h"data unsigned char at 0x002F n_byte;bit at 0x78 n_bit0;bit at 0x79 n_bit1;bit at 0x7A n_bit2;bit at 0x7B n_bit3;bit at 0x7C n_bit4;bit at 0x7D n_bit5;bit at 0x7E n_bit6;bit at 0x7F n_bit7;void main(void){ n_byte = 0x00; n_bit4 = 1; P0 = n_byte; // P0 = 0x10 while (1); // program loop}存儲器模式
SDCC支持兩種存儲器模式:小模式和大模式。使用存儲器小模式時,SDCC在內部RAM中聲明所有不帶存儲類型的變量(如,data、idata、xdata、pdata、bit、code)。使用存儲器大模式時,SDCC在外部RAM中聲明所有不帶存儲類型的變量。 采用SDCC編譯時,默認為小模式。如果要強制SDCC使用特定的存儲器模式,可使用以下命令行參數(shù):
sdcc --model-small sdcctest.c
或者 sdcc --model-large sdcctest.c
不要鏈接使用不同存儲器模式編譯的模塊或目標文件。 SDCC的中斷
定義中斷服務程序(ISR)時,應使用以下格式:
void interrupt_identifier (void) interrupt interrupt_number using bank_number{ ...}其中interrupt_identifier可以是任意有效的SDCC函數(shù)名,interrupt_number代表中斷在中斷向量表中的位置。表1列出了DS89C420/430/440/450系列微控制器支持的每個中斷的中斷號?蛇x參數(shù)bank_number用于指示SDCC采用哪個寄存器區(qū)存儲ISR中的局部變量。 表1. DS89C420/430/440/450中斷服務程序的中斷號
| Interrupt Name | Interrupt Vector | Interrupt Number |
| External Interrupt 0 | 0x03 | 0 |
| Timer 0 Overflow | 0x0B | 1 |
| External Interrupt 1 | 0x13 | 2 |
| Timer 1 Overflow | 0x1B | 3 |
| Serial Port 0 | 0x23 | 4 |
| Timer 2 Overflow | 0x2B | 5 |
| Power Fail | 0x33 | 6 |
| Serial Port 1 | 0x3B | 7 |
| External Interrupt 2 | 0x43 | 8 |
| External Interrupt 3 | 0x4B | 9 |
| External Interrupt 4 | 0x53 | 10 |
| External Interrupt 5 | 0x5B | 11 |
| Watchdog Interrupt | 0x63 | 12 |
SDCC處理與ISR編程相關的許多細節(jié),如使用堆棧保存和恢復累加器及數(shù)據(jù)指針。(實際上所有函數(shù)均進行此操作。請參考SDCC手冊中的_naked關鍵字來禁止在堆棧中保存這些變量)。其它細節(jié)不由SDCC處理(因為合理的原因),這對嵌入式編程開發(fā)新手帶來一定難度。許多這類問題屬于高級編程范疇,已超出本文討論的范圍,SDCC手冊和嵌入式編程教材可提供更深入的內容。歡迎訪問無由電子開發(fā)網(wǎng)(技術文章 在線閱讀 在線商城)
使用中斷時,應遵循以下原則。
- 可在ISR內部寫、并可在ISR外部訪問的每個全局變量必須被聲明為volatile,以確保優(yōu)化器不會刪除與該變量相關的指令。
- 以非原子(non-atomic)方式使用數(shù)據(jù)時(如,訪問16位/32位變量)應禁止中斷。當對變量的訪問為原子方式時,處理器無法中斷(帶有ISR)對存儲器的數(shù)據(jù)存取。
- 避免在ISR內部調用函數(shù)。如果必須這樣做,需要將函數(shù)聲明為reentrant (參見SDCC手冊),這樣函數(shù)中的所有局部變量被分配在堆棧中,而不是在RAM中。
注意,如果被SDCC使用的含ISR的源文件不含main()函數(shù),那么含main()函數(shù)的源文件應包含每個ISR的函數(shù)原型。 下面的例子定義了一個處理串行通信接口1 (SCI_1)的中斷服務程序(ISR)。程序接收來自SCI_1接收器的一個字節(jié),將接收字節(jié)加1,通過SCI_1發(fā)射器連續(xù)發(fā)送出去。
#include "sdcc_reg420.h"volatile unsigned char n = 0x4A;void sci1ISR (void) interrupt 7{ if (RI_1) { n = SBUF1+1; // Save Rx byte RI_1 = 0; // Reset SCI_1 Rx interrupt flag } else if (TI_1) { SBUF1 = n; // Load byte to Tx TI_1 = 0; // Reset SCI_1 Tx interrupt flag }}void main(void){ // 1. Init Serial Port EA = 0; // Enable global interrupt mask SCON1 = 0x50; // Set SCI_1 to 8N1, Rx enabled TMOD |= 0x20; // Set Timer 1 as Mode 2 TH1 = 0xDD; // Set SCI_1 for 2400 baud TR1 = 1; // Enable Timer 1 ES1 = 1; // Enable interrupts for SCI_1 EA = 1; // Disable global interrupt mask // 2. Initiate SCI_1 Tx SBUF1 = n; // 3. Program loop... while (1);}內嵌匯編
SDCC完全支持內嵌匯編。使用該功能時,匯編代碼應嵌在_asm和_endasm標識符之間。注意,通過在變量名前加下劃線,內嵌匯編代碼也可以訪問C變量。以下實例采用內嵌匯編執(zhí)行nop指令(用于在微控制器內部占用一個時鐘周期),然后將變量"a"加1。 #include "sdcc_reg420.h"unsigned char a;void main(void){ // program loop... while (1) { a = P0; _asm nop nop nop inc _a _endasm; P1 = a; }}SDCC還可用于C和匯編函數(shù)接口,這是較深入的問題;請參考SDCC手冊,了解詳細信息。 附錄A:DS89C420/430/440/450的SFR定義文件(sdcc_reg420.h)
/* * sdcc_reg420.h * * Author: Paul Holden * MAXIM INTEGRATED PRODUCTS * * Special Function Register definitions file * DS89C420/430/440/450 Ultra-High Speed 8051-compatible uCs * */#ifndef __REG420_H__#define __REG420_H__/* BYTE Registers */sfr at 0x80 P0;sfr at 0x81 SP;sfr at 0x82 DPL;sfr at 0x83 DPH;sfr at 0x84 DPL1;sfr at 0x85 DPH1;sfr at 0x86 DPS;sfr at 0x87 PCON;sfr at 0x88 TCON;sfr at 0x89 TMOD;sfr at 0x8A TL0;sfr at 0x8B TL1;sfr at 0x8C TH0;sfr at 0x8D TH1;sfr at 0x8E CKCON;sfr at 0x90 P1;sfr at 0x91 EXIF;sfr at 0x96 CKMOD;sfr at 0x98 SCON0;sfr at 0x99 SBUF0;sfr at 0x9D ACON;sfr at 0xA0 P2;sfr at 0xA8 IE;sfr at 0xA9 SADDR0;sfr at 0xAA SADDR1;sfr at 0xB0 P3;sfr at 0xB1 IP1;sfr at 0xB8 IP0;sfr at 0xB9 SADEN0;sfr at 0xBA SADEN1;sfr at 0xC0 SCON1;sfr at 0xC1 SBUF1;sfr at 0xC2 ROMSIZE;sfr at 0xC4 PMR;sfr at 0xC5 STATUS;sfr at 0xC7 TA;sfr at 0xC8 T2CON;sfr at 0xC9 T2MOD;sfr at 0xCA RCAP2L;sfr at 0xCB RCAP2H;sfr at 0xCC TL2;sfr at 0xCD TH2;sfr at 0xD0 PSW;sfr at 0xD5 FCNTL;sfr at 0xD6 FDATA;sfr at 0xD8 WDCON;sfr at 0xE0 ACC;sfr at 0xE8 EIE;sfr at 0xF0 B;sfr at 0xF1 EIP1;sfr at 0xF8 EIP0;/* BIT Registers *//* P0 */sbit at 0x80 P0_0;sbit at 0x81 P0_1;sbit at 0x82 P0_2;sbit at 0x83 P0_3;sbit at 0x84 P0_4;sbit at 0x85 P0_5;sbit at 0x86 P0_6;sbit at 0x87 P0_7;/* TCON */sbit at 0x88 IT0;sbit at 0x89 IE0;sbit at 0x8A IT1;sbit at 0x8B IE1;sbit at 0x8C TR0;sbit at 0x8D TF0;sbit at 0x8E TR1;sbit at 0x8F TF1;/* P1 */sbit at 0x90 P1_0;sbit at 0x91 P1_1;sbit at 0x92 P1_2;sbit at 0x93 P1_3;sbit at 0x94 P1_4;sbit at 0x95 P1_5;sbit at 0x96 P1_6;sbit at 0x97 P1_7;/* SCON0 */ sbit at 0x98 RI_0;sbit at 0x99 TI_0;sbit at 0x9A RB8_0;sbit at 0x9B TB8_0;sbit at 0x9C REN_0;sbit at 0x9D SM2_0;sbit at 0x9E SM1_0;sbit at 0x9F SM0_0;sbit at 0x9F FE_0;/* P2 */sbit at 0xA0 P2_0;sbit at 0xA1 P2_1;sbit at 0xA2 P2_2;sbit at 0xA3 P2_3;sbit at 0xA4 P2_4;sbit at 0xA5 P2_5;sbit at 0xA6 P2_6;sbit at 0xA7 P2_7;/* IE */sbit at 0xA8 EX0;sbit at 0xA9 ET0;sbit at 0xAA EX1;sbit at 0xAB ET1;sbit at 0xAC ES0;sbit at 0xAD ET2;sbit at 0xAE ES1;sbit at 0xAF EA;/* P3 */sbit at 0xB0 P3_0;sbit at 0xB1 P3_1;sbit at 0xB2 P3_2;sbit at 0xB3 P3_3;sbit at 0xB4 P3_4;sbit at 0xB5 P3_5;sbit at 0xB6 P3_6;sbit at 0xB7 P3_7;/* IP0 */sbit at 0xB8 LPX0;sbit at 0xB9 LPT0;sbit at 0xBA LPX1;sbit at 0xBB LPT1;sbit at 0xBC LPS0;sbit at 0xBD LPT2;sbit at 0xBE LPS1;/* SCON1 */sbit at 0xC0 RI_1;sbit at 0xC1 TI_1;sbit at 0xC2 RB8_1;sbit at 0xC3 TB8_1;sbit at 0xC4 REN_1;sbit at 0xC5 SM2_1;sbit at 0xC6 SM1_1;sbit at 0xC7 SM0_1;/* T2CON */sbit at 0xC8 CP_RL_2;sbit at 0xC9 C_T_2;sbit at 0xCA TR_2;sbit at 0xCB EXEN_2;sbit at 0xCC TCLK;sbit at 0xCD RCLK;sbit at 0xCE EXF_2;sbit at 0xCF TF_2;/* PSW */sbit at 0xD0 PARITY;sbit at 0xD0 P;sbit at 0xD1 F1;sbit at 0xD2 OV;sbit at 0xD3 RS0;sbit at 0xD4 RS1;sbit at 0xD5 F0;sbit at 0xD6 AC;sbit at 0xD7 CY;/* WDCON */sbit at 0xD8 RWT;sbit at 0xD9 EWT;sbit at 0xDA WTRF;sbit at 0xDB WDIF;sbit at 0xDC PFI;sbit at 0xDD EPFI;sbit at 0xDE POR;sbit at 0xDF SMOD_1;/* EIE */sbit at 0xE8 EX2;sbit at 0xE9 EX3;sbit at 0xEA EX4;sbit at 0xEB EX5;sbit at 0xEC EWDI;/* EIP0 */sbit at 0xF8 LPX2;sbit at 0xF9 LPX3;sbit at 0xFA LPX4;sbit at 0xFB LPX5;sbit at 0xFC LPWDI;#endif