文章目录
前言
在前面提到作者收到了新定义电子有限公司提供的NBK-RD8x3x核心开发板以及NBK-EBS001触控拓展板,由于新定义仅提供了基于Keil的开发包,本文将在此基础上将一部分代码移植到SDCC,也算对上篇文章的填坑。
一、背景
1. SDCC (Small Device C Compiler)
为什么需要使用SDCC?原因在于相较于昂贵的Keil,SDCC免费开源,遵循GPL-v2协议,同时支持多种MCU。SDCC支持的MCU包括:基于Intel MCS51 的MCU(8031、8032、8051、8052 等)、Maxim(以前称为 Dallas)DS80C390 变体、Freescale( 基于 HC08(HC08、S08)、基于 Zilog Z80 的 MCU(Z80、Z180、SM83、Rabbit 2000、2000A、3000A、TLCS-90)、Padauk(pdk14、pdk15)和 STMicroElectronics STM8 1,同时SDCC提供了完整的C语言标准库,包括I/O、数学、字符串处理等函数。此外,SDCC还支持使用汇编语言进行底层优化和嵌入到生成的代码中。SDCC在开源社区中广泛使用,适用于嵌入式系统开发、微控制器项目和单片机的应用。
SDCC与Keil语法拓展主要不同在于SFR,寄存器寻址,与中断关键字的不同:
项目 | SDCC | Keil |
---|---|---|
扩展关键字名称 | 以两个下划线开头,如__interrupt, __bit | 无需下划线 |
特殊功能寄存器(SFR) | __sfr __at 0x80 Jcqmz; | sfr Jcqmz = 0x80; |
特殊功能寄存器位寻址 | __sbit __at 0x87 wei; | sbit wei = Jcqmz ^ 7; |
中断服务(ISR)函数 | 需要在main函数前声明 | 不需要 |
2. 易码魔盒(RDEasyCodeCube)
易码魔盒(简称魔盒)是新定义的一种代码自动生成工具集成了代码生成、引脚配置、时钟初始化、MCU选型,外设配置等功能,目的是简化开发过程,降低用户的开发门槛。
魔盒使用图形化编程,可以自动生成标准化的底层配置代码 、驱动代码、逻辑代码。
由于目前魔盒仅支持生成Keil工程,SDCC用户需要自行按照新定义提供的BSP,转义,编译。
因此先利用生成Keil工程,再对Keil工程进行改造即可完成移植,整个过程大致如下图所示。
二、代码时间
本文主要介绍第一个移植任务:使用SDCC开发一个驱动LED的程序
1. 工程生成
首先进入魔盒,在MCU筛选
中选择欲开发的单片机型号,在此选择开发板板载MCU——RD8T36P48
随后点击点击这里开始工程
接下来还需设置系统时钟和管脚功能:
在此开发板外置32MHz振荡器作为系统时钟, 按照NBK开发板PCB 2 , 以及触控评估板PCB3对针脚设置:
2. 关键字转换
使用魔盒生成Keil工程后,开始修改关键字,幸运的是已经有对应的小工具帮助完成这项工作:Keil-C51-C-to-SDCC-C-Converter4
- 蓝框: 填入对应单片机头文件,这里填入内容:RD8T36x_C.h
- 红框:Keil c文件,这里转换gpio文件:rd8_gpio.c
- 绿框:最终获得的SDCC文件
可以看到 SFR与SBIT已被替换
注意: 若提示缺少compiler.h,可从此下载
3. 编译
为了点亮开发板上的LED,编写一些基础函数:
- gpio初始函数
void led_init(void) {
GPIO_Init(GPIO0, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO5, GPIO_PIN_0|GPIO_PIN_1,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO4, GPIO_PIN_6|GPIO_PIN_1|GPIO_PIN_0,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO3, GPIO_PIN_7|GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO2, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO0, GPIO_PIN_0|GPIO_PIN_1,GPIO_MODE_OUT_PP);
GPIO_WriteHigh(GPIO0, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6);
GPIO_WriteHigh(GPIO5, GPIO_PIN_0|GPIO_PIN_1);
GPIO_WriteHigh(GPIO4, GPIO_PIN_6|GPIO_PIN_1|GPIO_PIN_0);
GPIO_WriteHigh(GPIO3, GPIO_PIN_7|GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1);
GPIO_WriteHigh(GPIO2, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
GPIO_WriteHigh(GPIO0, GPIO_PIN_0|GPIO_PIN_1);
}
- LED状态设置函数
void LED_ShowSet(unsigned char LED, LED_STEAE_ENUM State)
{
switch(LED)
{
case 1:
LED1 = !State;
break;
case 2:
LED2 = !State;
break;
case 3:
LED3 = !State;
break;
case 4:
LED4 = !State;
break;
case 5:
LED5 = !State;
break;
case 6:
LED6 = !State;
break;
case 7:
LED7 = !State;
break;
case 8:
LED8 = !State;
break;
case 9:
LED9 = !State;
break;
case 10:
LED10 = !State;
break;
case 11:
LED11 = !State;
break;
case 12:
LED12 = !State;
break;
case 13:
LED13 = !State;
break;
case 14:
LED14 = !State;
break;
case 15:
LED15 = !State;
break;
case 16:
LED16 = !State;
break;
case 17:
LED17 = !State;
break;
case 18:
LED18 = !State;
break;
case 19:
LED19 = !State;
break;
case 20:
LED20 = !State;
break;
}
}
完整的代码如下:
// blink.c
#include <rd8_gpio.h>
typedef enum
{
LED_DI = 0,
LED_EN = 1
}LED_STEAE_ENUM;
#define LED1 P23
#define LED2 P22
#define LED3 P31
#define LED4 P32
#define LED5 P33
#define LED6 P37
#define LED7 P40
#define LED8 P41
#define LED9 P46
#define LED10 P51
#define LED11 P50
#define LED12 P06
#define LED13 P05
#define LED14 P03
#define LED15 P02
#define LED16 P01
#define LED17 P24
#define LED18 P25
#define LED19 P26
#define LED20 P00
void led_init(void) {
GPIO_Init(GPIO0, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO5, GPIO_PIN_0|GPIO_PIN_1,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO4, GPIO_PIN_6|GPIO_PIN_1|GPIO_PIN_0,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO3, GPIO_PIN_7|GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO2, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6,GPIO_MODE_OUT_PP);
GPIO_Init(GPIO0, GPIO_PIN_0|GPIO_PIN_1,GPIO_MODE_OUT_PP);
GPIO_WriteHigh(GPIO0, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6);
GPIO_WriteHigh(GPIO5, GPIO_PIN_0|GPIO_PIN_1);
GPIO_WriteHigh(GPIO4, GPIO_PIN_6|GPIO_PIN_1|GPIO_PIN_0);
GPIO_WriteHigh(GPIO3, GPIO_PIN_7|GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1);
GPIO_WriteHigh(GPIO2, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
GPIO_WriteHigh(GPIO0, GPIO_PIN_0|GPIO_PIN_1);
}
void LED_ShowSet(unsigned char LED, LED_STEAE_ENUM State)
{
switch(LED)
{
case 1:
LED1 = !State;
break;
case 2:
LED2 = !State;
break;
case 3:
LED3 = !State;
break;
case 4:
LED4 = !State;
break;
case 5:
LED5 = !State;
break;
case 6:
LED6 = !State;
break;
case 7:
LED7 = !State;
break;
case 8:
LED8 = !State;
break;
case 9:
LED9 = !State;
break;
case 10:
LED10 = !State;
break;
case 11:
LED11 = !State;
break;
case 12:
LED12 = !State;
break;
case 13:
LED13 = !State;
break;
case 14:
LED14 = !State;
break;
case 15:
LED15 = !State;
break;
case 16:
LED16 = !State;
break;
case 17:
LED17 = !State;
break;
case 18:
LED18 = !State;
break;
case 19:
LED19 = !State;
break;
case 20:
LED20 = !State;
break;
}
}
void main(void) {
led_init();
int i=0;
while(1) {
for (i=1; i<=20; i++) {
LED_ShowSet(i, LED_EN);
}
}
}
由于SDCC一次只可编译一个文件,对于本工程而言还需要分别编译链接:
# output rd_gpio.rel
$ sdcc -c rd_gpio.c
# blink.c contain main()
$ sdcc blink.c rd_gpio.rel
由于SDCC默认将输出Intel Hex文件(.ihx),为了烧入还需要将ihx文件转换为hex
$ packihx blink.ihx > blink.hex
4. 烧录
获得hex文件之后,接下来即可使用NBK ISP烧录了:
四. 结果
可以看到开发板上的灯已被全部点亮了
总结
本文介绍了在NBK开发板上,Keil移植SDCC的基本流程,然后可以看到相较于Keil IDE的直接,SDCC工作流相对复杂,接下来将使用CMAKE对移植流程使用cmake进行优化。