写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

SDCC是一个小型设备的 C语言编译器,该编译器支持标准 C语言;相对于 GCC编译器来说可能知名度不是很高,但它跟 GCC一样,是跨平台,并且遵循 GPL开源协议。本次实验是使用 nuvoton的 MS51系列单片机来操作(基于 8051内核)

sdcc官方网址:http://sdcc.sourceforge.net/

Wiki主页:https://sourceforge.net/p/sdcc/wiki/Home/

目录
一、关于 SDCC
二、安装及环境配置
三、SDCC规则(仅对于 MCS51说明)
四、SDCC头文件处理
五、工程构建
六、VSCode语法修饰
七、Makefile程序化管理
八、程序编译
九、总结
十、相关链接

一、关于 SDCC
SDCC是可重定目标的、优化的标准 C(ANSI C89,ISO C99,ISO C11)编译器套件,针对的是基于 Intel MCS51的微处理器(8031、8032、8051、8052等),Maxim(以前为达拉斯)DS80C390变体,飞思卡尔( 基于 HC08(hc08,s08),基于 Zilog Z80的 MCU(z80,z180,gbz80,Rabbit 2000/3000,Rabbit 3000A,TLCS-90),Padauk(pdk14,pdk15)和 STMicroelectronics STM8。

在安装了 SDCC后,通过指令查看版本号可以看到它所支持的设备类型:

然后,这里有个帖子有讨论 SDCC的一些相关东西,而且好像(我也不确定)SDCC的开发者也在里面,感兴趣的可以看一下:https://wap.newsmth.net/article/905eb27dddf829f15c81077215d66284?title=%E7%94%B5%E8%B7%AF%E8%AE%BE%E8%AE%A1%E4%B8%8E%E8%B0%83%E8%AF%95&from=search

SDCC较于 Keil来说,它对 C语法的严谨度是很高的,更像一个标准的 C语言编译器,并不会像 Keil那样把一些 warning去除掉,自动帮你优化;前面说了, SDCC是一个好的编译器,可优化方面稍微有点不够完美,以至于代码生成的体积还是比 Keil C51大一些(是不是我还有些优化指令没 get到呢?)。

二、安装及环境配置
1、 SDCC

软件的下载路径在上面的 sdcc主页上有对应的接口,只需要下载相关的平台程序包安装就好了,安装完成后添加系统环境变量这个就不用多说了,最后就 sdcc -v 测试检验。就这么简单。。。

2、 MinGW-w64

下载地址:https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/

安装完成后需要添加系统环境变量,可以利用 cmd命令:gcc -v 测试。

3、 MSYS2或者 Git(只要支持 shell命令的终端控制台就行)

msys2下载 <-- 自戳

Git自行搜索下载。

2、 VSCode

VSCode环境部署可以看之前的 STM32开发之 VS Code + gcc环境编译 的第三节,然后如果你懂得配置 VSCode的配置项的话,那么你可以跳过下面的配置操作自己写。

{
“configurations”: [
{
“name”: “C51”,
“includePath”: [ // 你的工程中存放 include的文件夹路径
“workspaceFolder/∗∗","{workspaceFolder}/**", "workspaceFolder/∗∗","{workspaceFolder}/App”,
“workspaceFolder/Libraries/Device/Include","{workspaceFolder}/Libraries/Device/Include", "workspaceFolder/Libraries/Device/Include","{workspaceFolder}/Libraries/StdDriver/inc”
],
“defines”: [
“_DEBUG”,
“UNICODE”,
“_UNICODE”,
],
“compilerPath”: “C:\Program Files\SDCC\bin\sdcc.exe”, // sdcc bin路径
“cStandard”: “gnu18”,
“cppStandard”: “gnu++14”,
“intelliSenseMode”: “gcc-x64”
}
],
“version”: 4
}

tasks.json

{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
“version”: “2.0.0”,
“tasks”: [
{
“label”: “Build”,
“type”: “shell”,
“command”: “make”,
“args”: [
“target=${fileBasenameNoExtension}”
],
“group”: {
“kind”: “build”,
“isDefault”: true
}
}
]
}

settings.json

{
“files.encoding”: “gb2312”,
“files.autoGuessEncoding”: true,

"C_Cpp.errorSquiggles": "Enabled",     // 语法错误"files.associations": {"main.h": "c",
},/* 终端在Windows上使用的shell的路径 */
"terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe",

}

三、SDCC规则(仅对于 MCS51说明)
1、支持的数据类型

2、存储类型

相对于 Keil,其存储类型关键字加上了前缀 ’ __ ’ 双下划线,这也是 SDCC的特色风格。

__data :这是小内存模式的默认(通用)地址空间,声明的变量将被放在 8051内核的直接寻址 RAM中。

__idata :这个地址空间中的变量将被分配到 8051的内部 RAM的间接可寻址部分。

__pdata :存储类型 pdata用于访问分页的外部数据存储器。

__xdata :这个地址空间中的变量将被放在外部 RAM中。

__code :存放程序代码的内存地址空间。

3、存储器模式

SDCC支持四种存储器模式:(small, medium, large, huge)

采用 SDCC编译时,默认为小模式。如果要强制 SDCC使用特定的存储器模式,可使用以下命令行参数(在手册的 P3.3.6章节可以查询得到):

类似于 Keil的这个选项(只不过 Keil是 GUI操作,SDCC是命令操作):

关于不同模式下变量的存储位置不一样,可以查阅手册 P3.13章节;总的来说,对于中(medium)、大(large)、巨大(huge)存储模式来说,所有未指定内部命名地址空间而声明的变量都将分配到外部 RAM中,这包括所有参数和局部变量(用于不可重入函数),中型模式使用 pdata,大型模式使用 xdata;而小内存模式(small)则默认存放在 data。

4、bit 和 sbit关键字

bit和 int、char之类的差不多,只不过 char = 8位,bit = 1位;

sbit是对应可位寻址空间的一个位。

同样的,在 SDCC这里加上了前缀 ’ __ ’ 双下划线,变成 __bit、__sbit

5、SFR(特殊功能寄存器)

与 bit关键字类似,表示命名地址空间,用于描述 8051的特殊函数寄存器和特殊位变量。

eg:

__sfr __at (0x80) P0; /* special function register P0 at location 0x80 */

6、绝对寻址

SDCC支持采用 __at 关键字表示绝对寻址。

7、内嵌汇编

SDCC完全支持内嵌汇编。使用该功能时,汇编代码应嵌在 __asm 和 __endasm 标识符之间。

8、编译生成文件

xxx.asm:程序的汇编文件。
xxx.lst:程序的列表文件。
xxx.rst:被链接器更新的列表文件。
xxx.sym:由链接器生成的符号清单。
xxx.rel:由汇编器生成的对象文件,提供给链接器使用。
xxx.map:被链接器更新的最终存储器映射。
xxx.mem:内存的使用情况摘要。
xxx.ihx:Intel十六进制格式的加载模块。该文件必须被下载到微控制器中。

四、SDCC头文件处理
前面说了,sdcc的非标关键字是带 ‘ __ ’ 双下滑线的,但是 MS51的官方 SDK包中,寄存器寻址的关键字全是 keil格式的,这就需要转换过来;如果是一些成熟的 8051内核单片机,那么你可以在 sdcc的安装路径 …\SDCC\include\mcs51下找到对应的芯片头文件,若是没有,那么就要自己进行格式转换了。sdcc格式转换,你可以去上网搜一下,这里给一个链接:https://www.amobbs.com/thread-5625040-1-1.html,里面有提供一个转换工具,当然你也可以自己去写一个程序。

五、工程构建
因为是用 VSCode做编辑开发,只要有 .vscode文件夹的配置项就可以了,剩下的编译过程就交给 sdcc编译,所以工程的构建比较简单,文件夹创建以及移植 SDK库都方便,以下是我的工程文件分布(看起来还是比较容易理解的):

这里 Libraries文件夹的内容是直接移植 SDK库的,其余的看文件名就知道用途了。

另外就是,App文件夹里,除了 lint.h(用来语法解析 mcs51特定代码)和添加了 main.h头文件(往常 main主文件是不带头文件);然后为什么要这两个呢,是为了避免 VSCode的语法错误的,当然你也可以一劳永逸,直接关了 VSCode的语法提示(这个可以看上面的 settings.json配置文件),至于 lint.h是从 sdcc的安装路径 …\SDCC\include\mcs51提取出来的,原滋原味。

六、VSCode语法修饰
上面也讲了,sdcc使用了部分非 ASCII C关键字,所以 VSCode会在程序中凸显语法错误;那么,我们就来解决这个问题(当然,不是用关闭语法检查这种粗暴形式):

1、首先要了解的是,在使用 sdcc进行编译的时候,是会自动在进行编译前预定义 __SDCC 宏的,这样就好办,利用条件编译,区别智能提示运行环境和 SDCC实际编译环境,用空的 define去取代这些关键字,寄存器也都用宏代替,然后在 SDCC实际编译时调用原来 C51语法的寄存器定义。

2、根据上面第一点,然后结合上面的提到的 lint.h(默认是留了 sdcc关键字的空 define),得到这样的一个例子:

#ifdef __SDCC
__sfr __at (0x80) P0; //实际有效的寄存器定义

#else
/* 关键字部分 /
#define __sfr // 空的关键字宏,消除关键字不兼容(在 lint.h上可以获取到相关的关键字)


/
寄存器部分 /
#define P0 (
(char *) (0x80)) // 无实际意义,用于兼容(欺骗)标准 C语法的寄存器符号

#endif

通过以上条件编译,就可以把代码区分到智能提示和实际编译两个环境:

在实际编译时,SDCC 编译器会预定义 __SDCC 宏,因此实际编译时使用实际有效的寄存器定义;
而在智能提示环境,用空的宏取代所有关键字,消除关键字的不兼容,然后用一个宏定义寄存器,保证寄存器名智能提示依然可以使用。这里将寄存器定义为 char* 指针解引用的左值表达式,目的是为迎合语法上对寄存器赋值是合法的,括号里的值可以是任意值,意义不大,当然如果使用寄存器本来的值更合适,但处理起来比较麻烦。
3、对上面的 1、2点总结起来,就可以得到:

#ifndef MAIN_H
#define MAIN_H

#include <stdint.h>

#ifdef __SDCC
#include “MS51_16K.h” // 把 keil格式转换后的 sdcc寄存器头文件

#else
#include <stdbool.h> // lint.文件中需要用到 bool关键字定义
#include “lint.h” // 关键字处理
#include “SFR_Macro_MS51_16K.h”

/*****************************************************************************/
/
Macro define header files /
/
*****************************************************************************/

#define P0 (*(char ) (0)) //= 0x80;
#define SP (
(char ) (0)) //= 0x81;
#define DPL (
(char ) (0)) //= 0x82;
#define DPH (
(char ) (0)) //= 0x83;
#define RCTRIM0 (
(char ) (0)) //= 0x84;
#define RCTRIM1 (
(char ) (0)) //= 0x85;
#define RWK (
(char ) (0)) //= 0x86;
#define PCON (
(char *) (0)) //= 0x87;

#define TCON (*(char ) (0)) //= 0x88;
#define TMOD (
(char ) (0)) //= 0x89;
#define TL0 (
(char ) (0)) //= 0x8A;
#define TL1 (
(char ) (0)) //= 0x8B;
#define TH0 (
(char ) (0)) //= 0x8C;
#define TH1 (
(char ) (0)) //= 0x8D;
#define CKCON (
(char ) (0)) //= 0x8E;
#define WKCON (
(char *) (0)) //= 0x8F;

#define P1 (*(char ) (0)) //= 0x90;
#define SFRS (
(char ) (0)) //= 0x91; //TA Protection
#define CAPCON0 (
(char ) (0)) //= 0x92;
#define CAPCON1 (
(char ) (0)) //= 0x93;
#define CAPCON2 (
(char ) (0)) //= 0x94;
#define CKDIV (
(char ) (0)) //= 0x95;
#define CKSWT (
(char ) (0)) //= 0x96; //TA Protection
#define CKEN (
(char *) (0)) //= 0x97; //TA Protection

#define SCON (*(char ) (0)) //= 0x98;
#define SBUF (
(char ) (0)) //= 0x99;
#define SBUF_1 (
(char ) (0)) //= 0x9A;
#define EIE (
(char ) (0)) //= 0x9B;
#define EIE1 (
(char ) (0)) //= 0x9C;
#define CHPCON (
(char *) (0)) //= 0x9F; //TA Protection

#define P2 (*(char ) (0)) //= 0xA0;
#define AUXR1 (
(char ) (0)) //= 0xA2;
#define BODCON0 (
(char ) (0)) //= 0xA3; //TA Protection
#define IAPTRG (
(char ) (0)) //= 0xA4; //TA Protection
#define IAPUEN (
(char ) (0)) //= 0xA5; //TA Protection
#define IAPAL (
(char ) (0)) //= 0xA6;
#define IAPAH (
(char *) (0)) //= 0xA7;

#define IE (*(char ) (0)) //= 0xA8;
#define SADDR (
(char ) (0)) //= 0xA9;
#define WDCON (
(char ) (0)) //= 0xAA; //TA Protection
#define BODCON1 (
(char ) (0)) //= 0xAB; //TA Protection
#define P3M1 (
(char ) (0)) //= 0xAC;
#define P3S (
(char ) (0)) //= 0xAC; //Page1
#define P3M2 (
(char ) (0)) //= 0xAD;
#define P3SR (
(char ) (0)) //= 0xAD; //Page1
#define IAPFD (
(char ) (0)) //= 0xAE;
#define IAPCN (
(char *) (0)) //= 0xAF;

#define P3 (*(char ) (0)) //= 0xB0;
#define P0M1 (
(char ) (0)) //= 0xB1;
#define P0S (
(char ) (0)) //= 0xB1; //Page1
#define P0M2 (
(char ) (0)) //= 0xB2;
#define P0SR (
(char ) (0)) //= 0xB2; //Page1
#define P1M1 (
(char ) (0)) //= 0xB3;
#define P1S (
(char ) (0)) //= 0xB3; //Page1
#define P1M2 (
(char ) (0)) //= 0xB4;
#define P1SR (
(char ) (0)) //= 0xB4; //Page1
#define P2S (
(char ) (0)) //= 0xB5;
#define IPH (
(char ) (0)) //= 0xB7;
#define PWMINTC (
(char *) (0)) //= 0xB7; //Page1

#define IP (*(char ) (0)) //= 0xB8;
#define SADEN (
(char ) (0)) //= 0xB9;
#define SADEN_1 (
(char ) (0)) //= 0xBA;
#define SADDR_1 (
(char ) (0)) //= 0xBB;
#define I2DAT (
(char ) (0)) //= 0xBC;
#define I2STAT (
(char ) (0)) //= 0xBD;
#define I2CLK (
(char ) (0)) //= 0xBE;
#define I2TOC (
(char *) (0)) //= 0xBF;

#define I2CON (*(char ) (0)) //= 0xC0;
#define I2ADDR (
(char ) (0)) //= 0xC1;
#define ADCRL (
(char ) (0)) //= 0xC2;
#define ADCRH (
(char ) (0)) //= 0xC3;
#define T3CON (
(char ) (0)) //= 0xC4;
#define PWM4H (
(char ) (0)) //= 0xC4; //Page1
#define RL3 (
(char ) (0)) //= 0xC5;
#define PWM5H (
(char ) (0)) //= 0xC5; //Page1
#define RH3 (
(char ) (0)) //= 0xC6;
#define PIOCON1 (
(char ) (0)) //= 0xC6; //Page1
#define TA (
(char *) (0)) //= 0xC7;

#define T2CON (*(char ) (0)) //= 0xC8;
#define T2MOD (
(char ) (0)) //= 0xC9;
#define RCMP2L (
(char ) (0)) //= 0xCA;
#define RCMP2H (
(char ) (0)) //= 0xCB;
#define TL2 (
(char ) (0)) //= 0xCC;
#define PWM4L (
(char ) (0)) //= 0xCC; //Page1
#define TH2 (
(char ) (0)) //= 0xCD;
#define PWM5L (
(char ) (0)) //= 0xCD; //Page1
#define ADCMPL (
(char ) (0)) //= 0xCE;
#define ADCMPH (
(char *) (0)) //= 0xCF;

#define PSW (*(char ) (0)) //= 0xD0;
#define PWMPH (
(char ) (0)) //= 0xD1;
#define PWM0H (
(char ) (0)) //= 0xD2;
#define PWM1H (
(char ) (0)) //= 0xD3;
#define PWM2H (
(char ) (0)) //= 0xD4;
#define PWM3H (
(char ) (0)) //= 0xD5;
#define PNP (
(char ) (0)) //= 0xD6;
#define FBD (
(char *) (0)) //= 0xD7;

#define PWMCON0 (*(char ) (0)) //= 0xD8;
#define PWMPL (
(char ) (0)) //= 0xD9;
#define PWM0L (
(char ) (0)) //= 0xDA;
#define PWM1L (
(char ) (0)) //= 0xDB;
#define PWM2L (
(char ) (0)) //= 0xDC;
#define PWM3L (
(char ) (0)) //= 0xDD;
#define PIOCON0 (
(char ) (0)) //= 0xDE;
#define PWMCON1 (
(char *) (0)) //= 0xDF;

#define ACC (*(char ) (0)) //= 0xE0;
#define ADCCON1 (
(char ) (0)) //= 0xE1;
#define ADCCON2 (
(char ) (0)) //= 0xE2;
#define ADCDLY (
(char ) (0)) //= 0xE3;
#define C0L (
(char ) (0)) //= 0xE4;
#define C0H (
(char ) (0)) //= 0xE5;
#define C1L (
(char ) (0)) //= 0xE6;
#define C1H (
(char *) (0)) //= 0xE7;

#define ADCCON0 (*(char ) (0)) //= 0xE8;
#define PICON (
(char ) (0)) //= 0xE9;
#define PINEN (
(char ) (0)) //= 0xEA;
#define PIPEN (
(char ) (0)) //= 0xEB;
#define PIF (
(char ) (0)) //= 0xEC;
#define C2L (
(char ) (0)) //= 0xED;
#define C2H (
(char ) (0)) //= 0xEE;
#define EIP (
(char *) (0)) //= 0xEF;

#define B (*(char ) (0)) //= 0xF0;
#define CAPCON3 (
(char ) (0)) //= 0xF1;
#define CAPCON4 (
(char ) (0)) //= 0xF2;
#define SPCR (
(char ) (0)) //= 0xF3;
#define SPCR2 (
(char ) (0)) //= 0xF3; //Page1
#define SPSR (
(char ) (0)) //= 0xF4;
#define SPDR (
(char ) (0)) //= 0xF5;
#define AINDIDS (
(char ) (0)) //= 0xF6;
#define EIPH (
(char *) (0)) //= 0xF7;

#define SCON_1 (*(char ) (0)) //= 0xF8;
#define PDTEN (
(char ) (0)) //= 0xF9; //TA Protection
#define PDTCNT (
(char ) (0)) //= 0xFA; //TA Protection
#define PMEN (
(char ) (0)) //= 0xFB;
#define PMD (
(char ) (0)) //= 0xFC;
#define EIP1 (
(char ) (0)) //= 0xFE;
#define EIPH1 (
(char *) (0)) //= 0xFF;

/* BIT Registers /
/
SCON_1 /
#define SM0_1 (
(char ) (0)) //= SCON_1^7;
#define FE_1 (
(char ) (0)) //= SCON_1^7;
#define SM1_1 (
(char ) (0)) //= SCON_1^6;
#define SM2_1 (
(char ) (0)) //= SCON_1^5;
#define REN_1 (
(char ) (0)) //= SCON_1^4;
#define TB8_1 (
(char ) (0)) //= SCON_1^3;
#define RB8_1 (
(char ) (0)) //= SCON_1^2;
#define TI_1 (
(char ) (0)) //= SCON_1^1;
#define RI_1 (
(char *) (0)) //= SCON_1^0;

/* ADCCON0 /
#define ADCF (
(char ) (0)) //= ADCCON0^7;
#define ADCS (
(char ) (0)) //= ADCCON0^6;
#define ETGSEL1 (
(char ) (0)) //= ADCCON0^5;
#define ETGSEL0 (
(char ) (0)) //= ADCCON0^4;
#define ADCHS3 (
(char ) (0)) //= ADCCON0^3;
#define ADCHS2 (
(char ) (0)) //= ADCCON0^2;
#define ADCHS1 (
(char ) (0)) //= ADCCON0^1;
#define ADCHS0 (
(char *) (0)) //= ADCCON0^0;

/* PWMCON0 /
#define PWMRUN (
(char ) (0)) //= PWMCON0^7;
#define LOAD (
(char ) (0)) //= PWMCON0^6;
#define PWMF (
(char ) (0)) //= PWMCON0^5;
#define CLRPWM (
(char *) (0)) //= PWMCON0^4;

/* PSW /
#define CY (
(char ) (0)) //= PSW^7;
#define AC (
(char ) (0)) //= PSW^6;
#define F0 (
(char ) (0)) //= PSW^5;
#define RS1 (
(char ) (0)) //= PSW^4;
#define RS0 (
(char ) (0)) //= PSW^3;
#define OV (
(char ) (0)) //= PSW^2;
#define P (
(char *) (0)) //= PSW^0;

/* T2CON /
#define TF2 (
(char ) (0)) //= T2CON^7;
#define TR2 (
(char ) (0)) //= T2CON^2;
#define CM_RL2 (
(char *) (0)) //= T2CON^0;

/* I2CON /
#define I2CEN (
(char ) (0)) //= I2CON^6;
#define STA (
(char ) (0)) //= I2CON^5;
#define STO (
(char ) (0)) //= I2CON^4;
#define SI (
(char ) (0)) //= I2CON^3;
#define AA (
(char ) (0)) //= I2CON^2;
#define I2CPX (
(char *) (0)) //= I2CON^0;

/* IP /
#define PADC (
(char ) (0)) //= IP^6;
#define PBOD (
(char ) (0)) //= IP^5;
#define PS (
(char ) (0)) //= IP^4;
#define PT1 (
(char ) (0)) //= IP^3;
#define PX1 (
(char ) (0)) //= IP^2;
#define PT0 (
(char ) (0)) //= IP^1;
#define PX0 (
(char *) (0)) //= IP^0;

/* P3 /
#define P30 (
(char *) (0)) //= P3^0;

/* IE /
#define EA (
(char ) (0)) //= IE^7;
#define EADC (
(char ) (0)) //= IE^6;
#define EBOD (
(char ) (0)) //= IE^5;
#define ES (
(char ) (0)) //= IE^4;
#define ET1 (
(char ) (0)) //= IE^3;
#define EX1 (
(char ) (0)) //= IE^2;
#define ET0 (
(char ) (0)) //= IE^1;
#define EX0 (
(char *) (0)) //= IE^0;

/* P2 /
#define P20 (
(char *) (0)) //= P2^0;

/* SCON /
#define SM0 (
(char ) (0)) //= SCON^7;
#define FE (
(char ) (0)) //= SCON^7;
#define SM1 (
(char ) (0)) //= SCON^6;
#define SM2 (
(char ) (0)) //= SCON^5;
#define REN (
(char ) (0)) //= SCON^4;
#define TB8 (
(char ) (0)) //= SCON^3;
#define RB8 (
(char ) (0)) //= SCON^2;
#define TI (
(char ) (0)) //= SCON^1;
#define RI (
(char *) (0)) //= SCON^0;

/* P1 /
#define P17 (
(char ) (0)) //= P1^7;
#define P16 (
(char ) (0)) //= P1^6;
#define TXD_1 (
(char ) (0)) //= P1^6;
#define P15 (
(char ) (0)) //= P1^5;
#define P14 (
(char ) (0)) //= P1^4;
#define SDA (
(char ) (0)) //= P1^4;
#define P13 (
(char ) (0)) //= P1^3;
#define SCL (
(char ) (0)) //= P1^3;
#define P12 (
(char ) (0)) //= P1^2;
#define P11 (
(char ) (0)) //= P1^1;
#define P10 (
(char *) (0)) //= P1^0;

/* TCON /
#define TF1 (
(char ) (0)) //= TCON^7;
#define TR1 (
(char ) (0)) //= TCON^6;
#define TF0 (
(char ) (0)) //= TCON^5;
#define TR0 (
(char ) (0)) //= TCON^4;
#define IE1 (
(char ) (0)) //= TCON^3;
#define IT1 (
(char ) (0)) //= TCON^2;
#define IE0 (
(char ) (0)) //= TCON^1;
#define IT0 (
(char *) (0)) //= TCON^0;

/* P0 /
#define P07 (
(char ) (0)) //= P0^7;
#define RXD (
(char ) (0)) //= P0^7;
#define P06 (
(char ) (0)) //= P0^6;
#define TXD (
(char ) (0)) //= P0^6;
#define P05 (
(char ) (0)) //= P0^5;
#define P04 (
(char ) (0)) //= P0^4;
#define STADC (
(char ) (0)) //= P0^4;
#define P03 (
(char ) (0)) //= P0^3;
#define P02 (
(char ) (0)) //= P0^2;
#define RXD_1 (
(char ) (0)) //= P0^2;
#define P01 (
(char ) (0)) //= P0^1;
#define MISO (
(char ) (0)) //= P0^1;
#define P00 (
(char ) (0)) //= P0^0;
#define MOSI (
(char *) (0)) //= P0^0;

#endif /* __SDCC */

#endif /* MAIN_H */

对于寄存器定义处理,可以直接 copy原来的 keil格式的 include文件内容,然后直接把关键字 sfr、sbit 替换成 #define ,再把 = 替换成 (*(char *) (0)) //= 这样就,是不是很 nice。

七、Makefile程序化管理
SDCC并不支持同时编译多个源代码文件,所以多文件项目的编译需要分步进行。假如你的项目包含 foo1.c foo2.c main.c 三个文件,那么编译过程如下:

sdcc -c foo1.c
sdcc -c foo2.c
sdcc main.c foo1.rel foo2.rel

还可以使用以下方式编译:

sdcc -c main.c
sdcc main.rel foo1.rel foo2.rel

值得一提的是,sdcc与 gcc的命令支持还是有点出入的,但大部分都兼容,因此具体支持哪些命令,需要去翻看 sdcc的手册。

对于多文件项目最好是写一个 Makefile文件来维护或者写一个 bat批处理文件。这里就直接给出我所用的 Makefile文件吧,分析什么的,可以看以前的链接:https://blog.csdn.net/qq_42992084/article/details/95893283;如果你是 Linux用户,应该很清楚这些命令,若果诸位大佬有懂得多的,还请在评论区不吝赐教:

######################################

target path

######################################
TARGET = MS51FB

#######################################

Build path

#######################################
BUILD_DIR = build

######################################

source

######################################
SRCDIR = App
LIB_SRC = #Libraries/StdDriver/src

USER_SRC = source#/bsp.c
source/bsp_time.c
source/bsp_uart.c

C sources

C_SOURCES := $(wildcard $(SRCDIR)/.c $(LIB_SRC)/.c)
C_SOURCES += $(wildcard $(USER_SRC)/.c)
ASM_SOURCES = $(wildcard $(SRCDIR)/
.asm)

C_SRC_FILE = $(notdir $(C_SOURCES))
C_OBJ_FILE = $(C_SRC_FILE:%.c=%.c.rel)

ASM_SRC_FILE = $(notdir $(ASM_SOURCES))
ASM_OBJ_FILE = $(ASM_SRC_FILE:%.asm=%.asm.rel)

######################################

building variables

######################################

debug build?

DEBUG = 1

optimization

OPT =

#######################################

cross compile

#######################################
PREFIX =

CC = $(PREFIX)sdcc
AS = $(PREFIX)sdas8051

MCU_MODEL = -mmcs51

RM = -rm -rf
MAKE = make

------------------------------------------------------

Usually SDCC’s small memory model is the best choice. If

you run out of internal RAM, you will need to declare

variables as “xdata”, or switch to larger model

Memory Model (small, medium, large, huge)

MODEL = --model-small

------------------------------------------------------

Memory Layout

PRG Size = 4K Bytes

#CODE_SIZE = --code-loc 0x0000 --code-size 18432
CODE_SIZE = --code-size 18432

INT-MEM Size = 256 Bytes

#IRAM_SIZE = --idata-loc 0x0000 --iram-size 256
IRAM_SIZE = --iram-size 256

EXT-MEM Size = 4K Bytes

#XRAM_SIZE = --xram-loc 0x0000 --xram-size 768
XRAM_SIZE = --xram-size 768

------------------------------------------------------

#######################################

FLAGS

#######################################

macros for gcc

AS defines

AS_DEFS =

C defines

C_DEFS =

AS includes

AS_INCLUDES =

C includes

C_INCLUDES =
-IApp
-ILibraries/Device/Include
-ILibraries/StdDriver/inc
-Iinclude

libraries

LIBS =
LIBDIR =

compile gcc flags

ASFLAGS = -l -s

CFLAGS = $(MCU_MODEL) $(C_DEFS) $(C_INCLUDES) $(MODEL) --out-fmt-ihx --no-xinit-opt --peep-file tools/peep.def

ifeq ($(DEBUG), 1)
CFLAGS +=
else
CFLAGS += $(OPT)
endif

#######################################

LDFLAGS

#######################################
LDFLAGS = $(LIBDIR) $(LIBS) $(MCU_MODEL) $(MODEL) $(CODE_SIZE) $(IRAM_SIZE) $(XRAM_SIZE) --out-fmt-ihx

default action: build all

.PHONY: all
all: (BUILDDIR)/(BUILD_DIR)/(BUILDD​IR)/(TARGET).hex

#######################################

build the application

#######################################

list of objects

OBJECTS = $(addprefix (BUILDDIR)/,(BUILD_DIR)/,(BUILDD​IR)/,(C_OBJ_FILE))

list of ASM program objects

OBJECTS += $(addprefix (BUILDDIR)/,(BUILD_DIR)/,(BUILDD​IR)/,(ASM_OBJ_FILE))

$(BUILD_DIR)/%.c.rel: $(USER_SRC)/%.c
$(CC) -o $@ $(CFLAGS) -c $^

$(BUILD_DIR)/%.c.rel: $(LIB_SRC)/%.c
$(CC) -o $@ $(CFLAGS) -c $^

$(BUILD_DIR)/%.c.rel: $(SRCDIR)/%.c
$(CC) -o $@ $(CFLAGS) -c $^

$(BUILD_DIR)/%.asm.rel: $(SRCDIR)/%.asm
$(AS) $(ASFLAGS) -o $@ $^

$(BUILD_DIR)/%.ihx: $(OBJECTS)
$(CC) -o $@ $(LDFLAGS) $^

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.ihx | $(BUILD_DIR)
packihx $^ > $@

$(BUILD_DIR):
mkdir $@

#######################################

clean up

#######################################
.PHONY: clean
clean:
(RM)(RM)(RM)(BUILD_DIR)/*

#######################################

build asm

#######################################
HEADER_FILE = MS51_16K.h
HEADER_PATH = App

disasm: (BUILDDIR)/(BUILD_DIR)/(BUILDD​IR)/(TARGET).hex
./tools/mcs51-disasm.pl -M $(HEADER_FILE) -I $(HEADER_PATH) -fl -rj -as (BUILDDIR)/(BUILD_DIR)/(BUILDD​IR)/(TARGET).hex > (BUILDDIR)/(BUILD_DIR)/(BUILDD​IR)/(TARGET).a51

*** EOF ***

这里说一下,sdcc特有的 packihx 命令是用来产生 Intel HEX文件的;mkdir 命令在 sdcc中是不支持,可以把他删掉,由于这一点,所以得保留着 build文件夹存放编译文件,如果删除的话,执行会出错;执行 disasm 命令需要工具链 mcs51-disasm.pl的支持,它的说明如下:

;另外,+= 好像也不支持单文件添加,看 USER_SRC处,只能通过 wildcard 扫描添加。。。不知为啥,望大佬解答一二。

八、程序编译
#include “main.h”
#include “bsp.h”
#include “bsp_uart.h”
#include “bsp_time.h”
#include “bsp_eeprom.h”
#include “bsp_adc.h”
#include “bsp_pwm.h”
#include “bsp_wdt.h”

__bit BIT_TMP; //EA暂存(对应官方库)

#define ENABLE_WDT 1

/* ISR中断函数原型声明(原因看手册 P3.8章节) */
void UART0_ISR(void) __interrupt (4);
void Timer3_ISR(void) __interrupt (16);

/************************************************
函数名称 : System_Start
功 能 : 系统初始化
参 数 : 无
返 回 值 : 无
*************************************************/
void System_Start(void)
{
clr_EA;

Bsp_Init();
UART0_Timer1_Init();
Timer3_Init(TIME_DIV16, 15000);  // 10ms
// Timer0_Init();
// ADC_Config();
// PWM0_Init();

#if ENABLE_WDT
WDT_Init();
#endif /* ENABLE_WDT */

set_EA;

}

/************************************************
函数名称 : main
功 能 : 主函数入口
参 数 : 无
返 回 值 : int
*************************************************/
int main(void)
{
uint16_t i = 0;

System_Start();P12_QUASI_MODE;
P12 = 1;
for(i=0; i<3; i++)
{P12 ^= 1;SoftwareDelay_ms(0xFF);
}

#if ENABLE_WDT
WDT_EnableOpen();

#else
WDT_DisableClose();

#endif /* ENABLE_WDT */

while(1)
{

#if ENABLE_WDT
WDT_ReloadCounter();
#endif /* ENABLE_WDT */

    printf_small("\n Hello world!");SoftwareDelay_ms(0xFF);
}

}

/---------------------------- END OF FILE ----------------------------/

然后 make 编译,最终输出(方框处显示成功):

下载进去,就可以看到 Hello world! 在不停的打印输出了:

在这里,需要注意以下几点:

1、中断函数必须在 main函数文件中给出 ISR原型,不然就无法进中断。详细请看手册的 P3.8章节。以下摘自部分解释:

2、一般,我们在 C程序中打印输出是调用 printf 语句进行输出的,但在 sdcc上,比较建议使用 printf_small 输出,因为对于 8位微控制起来说,资源是很紧缺的,使用 printf_small 已经可以满足一般输出需求了,当然以上仅限于输出整型以及字符型变量;对于浮点型变量,需要使用特殊的指令对程序进行编译才能得到输出效果,具体的介绍可以看手册的 P3.14.1章节。

3、如果是使用 bin文件烧写到芯片上,可以用 sdcc自带的 makebin.exe 命令行工具进行转换(不过这个转换出来文件比较大),路径可以在 …\SDCC\bin下找到,通过以下命令:makebin xxx.ihx > xxx.bin

或者利用 hex2bin,下载地址:https://sourceforge.net/projects/hex2bin/files/latest/download,这个的命令则是:hex2bin xxx.hex > xxx.bin

Makefile下的 bin文件生成命令:

两者相比之下,由于前者是做了剩余空间填充处理的,所以转换出来的文件比较大,个人更倾向于后者。

九、总结
1、不能使用 double数据类型,否则报错。

2、make编译只能根据法则编译对应文件夹的全部源文件,不能选择编译相应源文件。

3、中断函数这里是要在 main函数所在文件处进行原型声明,否者是无法进入中断程序,原因不声明是并没用把中断函数的向量地址加载到执行文件中。

4、sdcc使用的关键字是跟 keilC51里面的关键字不同的;对于一些非 ANSI C的关键字,SDCC均采用双下滑线开头的方式定义,具体可看 sdcc手册。

5、sdcc支持的命令行命令,跟我们平常用的 gcc命令行命令有所不同,具体翻看 sdcc手册。

6、一般串口重定向后,是使用 printf函数输出,但在 sdcc编译器中要改用 printf_small这个函数进行替代。

7、sdcc在编译文件时,会把用不到的代码也编译进来,所以如果空间紧张,建议注释掉一些无关的代码,避免代码空间膨胀。

十、相关链接
SDCC Compiler User Guide

8051 C Development Using SDCC(Small Device C Compiler)

Nuvoton N76E003 with SDCC

新唐 N76E003 8051 1T 单片机入坑记录

SDCC编译器简明使用教程

使用免费的SDCC C编译器开发DS89C430/450系列微控制器固件

51单片机之开发环境使用VSCode结合SDCC取代Keil

使用Visual Studio Code + CMake + SDCC 进行C51 开发的一次尝试

台湾同胞对 SDCC的使用介绍

sdcc man阅读笔记

SDCC printf 函數介紹

原文链接:https://blog.csdn.net/qq_42992084/article/details/109375443

SDCC编译器学习旅之一相关推荐

  1. Proteus8.9 VSM Studio SDCC编译器仿真STC15W4K32S4系列013_iic_03_AT24C08

    一,打开文件(可以随文下载放置在文档中打开).(如下图1所示) 图1 二,调整原理图大小,适合可视,另存工程文件.(如下图2,3,4所示) 图2 图3 图4 三,点击Source Code标签.(如下 ...

  2. Proteus8.9 VSM Studio SDCC编译器仿真STC15W4K32S4系列013_iic_02_AT24C04

    一,打开文件(可以随文下载放置在文档中打开).(如下图1所示) 图1 二,调整原理图大小,适合可视,另存工程文件.(如下图2,3,4所示) 图2 图3 图4 三,点击Source Code标签.(如下 ...

  3. sdcc编译器使用makefile

    sdcc编译器使用makefile 环境:make(window下有mingw32-make)和sdcc ############################################ # ...

  4. matlab2019 编译器_方舟编译器学习

    ​2.方舟编译器源码编译 3.方舟可执行文件简介 4.方舟编译器文档导读 5.方舟开始接受代码合并请求 6.方舟编译器学习笔记6 方舟开源目录简介 7.方舟编译器学习笔记7 对java2jar及方舟工 ...

  5. 方舟编译器学习笔记分类与导读

    方舟学习笔记系列,从方舟开源到现在,已经写了50多篇,保证了每天一篇的更新频率.篇数增加之后,文章的分类以及文章之间的关系,逐渐变得复杂起来.本文将对已发表的学习笔记系列进行分类和导读,方便读者更好的 ...

  6. Proteus8.9 VSM Studio SDCC编译器仿真STC15W4K32S4系列013_iic_09_AT24C512

    一,打开文件(可以随文下载放置在文档中打开).(如下图1所示) 图1 二,调整原理图大小,适合可视,另存工程文件.(如下图2,3,4所示) 图2 图3 图4 三,点击Source Code标签.(如下 ...

  7. Proteus8.9 VSM Studio SDCC编译器仿真STC15W4K32S4系列013_iic_04_AT24C16

    一,打开文件(可以随文下载放置在文档中打开).(如下图1所示) 图1 二,调整原理图大小,适合可视,另存工程文件.(如下图2,3,4所示) 图2 图3 图4 三,点击Source Code标签.(如下 ...

  8. 方舟编译器只能编译Java_方舟编译器学习笔记8 工具链可运行(helloworld编译成功)...

    前面的文章 方舟可执行文件简介和 对java2jar及方舟工具链的进一步分析 对工具链中的可执行文件以及测试用例的编译都进行了分析,并对java2jar进行了修改,保证了其可运行.但是后续的jbc2m ...

  9. eclipse c语言_如果你的电脑是windows7/10的环境,用什么编译器学习C语言好?

    既然问题已经限制了Windows环境,那么就不再推荐Linux环境下的编译器了,虽然在Linux环境进行C语言的编程会比Windows可以更好的掌握一些基础知识,自己动手gcc,写makefile文件 ...

  10. 自己写编译器学习总结

    如何写一个简单的编译器? https://www.zhihu.com/question/36756224 初学编译原理,想写一个简单的编译器. 是时候亮出我的 LL 语言了,全称:Lambda Lit ...

最新文章

  1. 将优化问题转化为决策问题
  2. 第一行代码学习笔记第七章——探究内容提供器
  3. Opencv获取身份证号码区域
  4. # 删除注册表项键值_强制删除:让你的电脑上不再有“顽固”程序
  5. MVC简单实现插件Demo-从底层理解MVC路由匹配浏览器请求的URL
  6. 王自如、罗永浩将一起出镜直播带货?罗永浩亲自回应
  7. 感谢Karen--oracle的system密码忘记修改办法|附sqlplus /nolog|
  8. springboot中配置文件application.properties的配置详情,数据源配置
  9. MySQL中向下查询_mysql
  10. Mac Android studio: Gradle Build Running 、 run build 运行卡顿 、 加载慢问题问题(亲测可用)
  11. php模板建站seo,phpwin建站教程,phpwind模板
  12. 资源调度_提前计划+实时调度,实现月台资源高效利用
  13. v65智慧屏是真的鸿蒙,华为智慧屏V65深测:真智慧or增智慧?
  14. SQL Server列转行
  15. MIUI12 Google play无法下载chrome及没有快捷方式的解决方法
  16. 复习————吃饭睡觉打豆豆
  17. 华为手机被diss:AI拍照涉造假,国外网友又炸锅了
  18. js 微信公众号开发chooseWXPay:fail, the permission value is offline verifying
  19. Google浏览器打开axure产品原型的解决方案
  20. 2022图灵奖出炉!以太网之父鲍勃·梅特卡夫获奖,76岁进入新领域再出发

热门文章

  1. java雪崩_了解java架构之微服务架构—雪崩效应
  2. Java单身狗,java 的23种设计模式 之单身狗和隔壁老王的故事
  3. fixed在ios失效解决方案
  4. 电脑没声音怎么调出来?让电脑重新“开口说话”,黑客仅用了2招
  5. GBK版本和UTF-8版本的区别
  6. 耿建超英语语法---时态+疑问句
  7. 《小鑫发现》之GraphQL框架Prisma
  8. 使用 WebSphere eXtreme Scale 处理事件流
  9. Tensorflow基础语法以及网络搭建
  10. oracle inst 自动重启,oracle rac 节点自动重启