SDCC-Linux下的51 MCU编译器

2024-02-28  

SDCC (小型设备C编译器)是为8位微控制器开发的免费C编译器。尽管兼容多种不同体系结构,但SDCC C编译器更适合8051内核。 SDCC是命令行固件开发工具,含预处理器、编译器、汇编器、链接器和优化器。安装文件中还捆绑了SDCDB、类似于gdb (GNU调试器)的源码级调试器。无错的程序采用SDCC编译、链接后,生成一个Intel十六进制格式的加载模块。


安装SDCC免费C编译器



如果需要安装SDCC,请从网址下载SDCC最新版本。虽然也可使用该软件的日常构建(nightly builds)版,但通常最安全的方式是下载经过完全测试的最新发布版。


在“Download”页为不同的操作系统提供不同的SDCC。如果您使用运行Microsoft Windows的PC,请下载并运行win32自解压SDCC安装文件。


安装程序时会出现一个提示,询问是否将含有程序二进制文件的目录添加到您的路径中。


采用SDCC编译器编译一个简单的C程序


为确保SDCC已在硬盘上正确安装,请在命令提示符下键入sdcc --version,然后回车,窗口中应出现图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十六进制格式的加载模块。该文件必须被下载到微控制器中。


同时还生成其它文件(多数用于源码级调试器)。请阅读SDCC文档了解更详细的信息。


SDCC专有数据类型


SDCC支持多数ANSI-C数据类型。此外,SDCC支持多种扩展数据类型(也称为存储类型),以充分利用8051体系结构的优势,这将在后面以实例说明。


与一些商用8051微控制器开发工具不同,SDCC仅支持声明位和字节可寻址特殊功能寄存器。尽管8051汇编语言支持,但SDCC并不支持共享位和字节可寻址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 a.a_byte = 5;

                            160 ;     genPointerSet

                            161 ;     genNearPointerSet

                            162 ;     genDataPointerSet

   0031 75 21 05            163     mov _a,#0x05

                            164 ;sdcctest.c a.a_bit.bit6 = 1;

                            165 ;     genPointerSet

                            166 ;     genNearPointerSet

   0034 78 21               167     mov r0,#_a

                            168 ;     genPackBits

   0036 E6                  169     mov a,@r0

   0037 44 40               170     orl a,#0x40

   0039 F6                  171     mov @r0,a

                            172 ;sdcctest.c b = 1;

                            173 ;     genAssign

   003A D2 00               174     setb    _b

                            175 ;sdcctest.c while(1); // program loop

                            ...


尽管在声明中“a”看起来是位寻址存储器,但汇编列表文件(来自由SDCC生成的.rst文件)表明变量并没有使用位寻址。在列表中不要混淆“a”和“_a”。“a”指累加器,而“_a”指变量。


near/data


以near或data存储类型声明的变量将被放在8051内核的直接寻址RAM中。DS89C430/450系列微控制器具有128字节直接寻址存储器,这是8051能够访问的速度最快的存储器,生成的汇编代码只需一个MOV指令即可读写该RAM中的数据。


#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中。这样开发人员能够访问更大的RAM空间,但生成的汇编代码需要使用MOVX指令来读写该存储器,这要求将外部存储器地址装入数据指针。


DS89C430/450系列微控制器含有1K字节的内部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字节(不包括特殊功能寄存器)。但是,访问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用于访问分页的外部数据存储器。该存储类型超出了本应用笔记范畴,有兴趣的读者可以阅读SDCC文档的pdata部分。


code


以code存储类型声明的变量将被放在程序存储器(DS89C430/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字节直接寻址RAM可用作位寻址存储器(字节0x20至0x2F),提供128个可寻址位。使用该类变量作为标志位可高效利用存储空间。


#include "sdcc_reg420.h"



#define ESCAPE 0x1B



bit 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标识符定义了DS89C430/450微控制器中的所有SFR。


注意,下面的实例已定义了SFR,因此没有必要包含定义文件s


dcc_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标识符定义了DS89C430/450微控制器SFR的所有可寻址位。


sfr  at 0x80 P0;   // Port 0



sbit at 0x80 P0_0; // Port 0 bit 0

sbit at 0x81 P0_1; // Port 0 bit 1

sbit at 0x82 P0_2; // Port 0 bit 2

sbit at 0x83 P0_3; // Port 0 bit 3

sbit at 0x84 P0_4; // Port 0 bit 4

sbit at 0x85 P0_5; // Port 0 bit 5

sbit at 0x86 P0_6; // Port 0 bit 6

sbit at 0x87 P0_7; // Port 0 bit 7



void main(void)

{

     P0   = 0x00; // P0 = 0x00



     P0_4 =    1; // P0 = 0x10



     while (1);   // program loop

}

绝对寻址


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字节,那么它将覆盖位于地址0x0010处的变量"y"和"z"。

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。