找回密码
 立即注册
查看: 1259|回复: 1

使用SDCC开发51单片机的极简VSCode插件

[复制链接]
  • TA的每日心情
    开心
    2024-3-23 15:15
  • 签到天数: 21 天

    [LV.4]偶尔看看III

    1

    主题

    0

    回帖

    181

    积分

    注册会员

    积分
    181
    发表于 2024-2-24 00:31:49 | 显示全部楼层 |阅读模式
    前言
    想要使用开源的SDCC来编译一些简单的51单片机程序,但不想建立繁多的工程文件夹?在线工具对网络环境要求高?头文件用了Keil的关键字?这个自制的VsCode插件或许可以解决你的问题。
    功能介绍
    • 文件标签右键菜单“使用SDCC编译(Ctrl+F9)”,快捷编译
    • 编辑区域右键菜单“Keil语法转SDCC”,快速转换语法
    • 编辑区域右键菜单“转到汇编代码”,快速查看指定语句编译后的汇编码与机器码
    • 展示SDCC编译得到的空间占用分布情况(.mem文件)
    安装与使用
    • 安装SDCC编译器,并添加到PATH。
    • 安装VSCode,在VSCode中搜索安装“C/C++”(微软官方)、“51forSDCC”插件(自制)。
    • 在VSCode中打开想要存放代码的文件夹,建立头文件、以“main”开头的.c文件等文件,开始编码。所有代码需要是GBK编码格式。
    • 编码完成后,保存文件并编译,使用STC-ISP烧录hex文件。由于STC-ISP只有Windows版本,本插件也因此仅考虑Windows兼容性,并且要求VSCode的终端使用默认的PowerShell,而不是cmd。
    说明
    • SDCC的帮助文档位于:C:\Program Files\SDCC\doc\sdccman.pdf(默认安装位置)
    • VSCode的工作文件夹中,文件名以“main”或“test”开头的.c文件视为主程序(需要含有main函数),其他.c文件视为库文件。本插件将必要的的文件编译、链接在一起,在“build”文件夹下生成与主程序文件同名的hex文件。因此,共享相同库的简单主程序可以放在一个文件夹下,不需要建立工程,尤其适合单文件的简单代码。
    • 在扩展设置中,可以添加额外的编译命令行。SDCC默认将变量储存在data区中(即“--model-small”),可改用“--model-medium”将变量默认储存到pdata区,或改用“--model-large”将变量默认储存到xdata区。一般不需要修改,因为可以手动修饰较大的变量,将其放到pdata或xdata区域,而剩余的变量保持在默认的data区,获得较快的速度和较短的代码。
    • SDCC的变量修饰关键字为:__code、__data、__idata、__pdata、__xdata、__bit,可根据实际需求修饰,如将不频繁使用、较大的变量放在pdata或xdata区。内存占用与代码长度会在编译成功后显示。
    • 只要一个.c文件中实现的函数被用到至少一个,则SDCC就会将该.c文件中所有实现的函数链接到最终二进制文件中,造成代码长度增大。因此,编写库文件时应尽量将可能不会同时用到的函数拆分到不同.c文件中,极端情况下甚至可以让一个函数对应一个.c文件。
    • SDCC默认使用较新的C语言标准,不支持C++,变量不强制定义在函数开头,无参函数需要使用(void)修饰(如main函数)。
    • SDCC的中断函数需要在main函数所在文件中声明,格式为:void 名称(void) __interrupt(中断号)
    • SDCC只支持float浮点数,不支持double,书写浮点常量时注意以f结尾。
    • SDCC可使用__asm__("汇编代码")实现内联汇编,如__asm__("nop\nnop");
    • 如果找不到头文件,可添加包含文件目录“C:\Program Files\SDCC\include”。某些关键字可能会有下划线报错,不理睬即可
    • SDCC的printf对于小容量单片机来说太过笨重,可以使用功能较少的printf_fast_f(带浮点)、printf_fast甚至printf_tiny代替。需要包含stdio.h,并自行提供一个标准的putchar函数,原型为:int putchar(int ch)。不同printf的对比表格:

    SDCC自带的不同printf函数

    SDCC自带的不同printf函数


    这里我以STC8G1K08为例,给出一个初始化串口输出以使用printf的例子。定时器1既用作波特率发生器也兼用作嘀嗒计时器,因此无需计算指令周期数即可实现延时。
    #include "STC8G.H" // 直接由STC-ISP提供的头文件经过“Keil语法转SDCC”转换得到
    #include <stdint.h>
    #include <stdio.h>

    #define FOSC 11059200UL
    #define BAUD 115200UL
    #define LOAD (65536U - FOSC / BAUD / 4)
    void UART_Init(void) {
        SCON = 0x50;
        TMOD = 0x00, AUXR = 0x40;
        TL1 = LOAD & 0xff, TH1 = LOAD >> 8;
        TR1 = 1, ET1 = 1, EA = 1;
    }
    int putchar(int ch) {
        SBUF = ch;
        while (!TI)
            ;
        TI = 0;
        return ch;
    }
    uint16_t systick_10ms = 0, systick = 0;
    void Timer1_ISR(void) __interrupt(3) {
        if (++systick == BAUD * 4 / 100) {
            ++systick_10ms;
            systick = 0;
        }
    }
    void delay(uint16_t ms) {
        uint16_t end = systick_10ms + ms / 10;
        while (systick_10ms < end)
            ;
    }
    void main(void) {
        P3M1 = 0b00000000, P3M0 = 0b00000000; // 准双向口
        UART_Init();
        delay(1000);

        int i = 0;
        while (1) {
            printf_tiny("%d\n", i++);
            delay(1000);
        }
    }

    以上面的代码为例,编译后,在“TL1 = LOAD & 0xff, TH1 = LOAD >> 8;”这句代码上右击,选择“转到汇编代码”,可以看到以下几行汇编码:                                          636 ;    main_simple_printf.c:12: TL1 = LOAD & 0xff, TH1 = LOAD >> 8;
          000009 75 8B E8         [24]  637     mov    _TL1,#0xe8
          00000C 75 8D FF         [24]  638     mov    _TH1,#0xff

    这说明SDCC可以自动展开简单的常量表达式,可以通过宏定义自动适配IRC频率和波特率,而不需要手动重新计算或在运行时计算定时器装载值。

    编译后显示的内存分布为:
    Internal RAM layout:
          0 1 2 3 4 5 6 7 8 9 A B C D E F
    0x00:|0|0|0|0|0|0|0|0|a|a|a|a|b|Q|Q|S|
    0x10:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x20:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x80:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0x90:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0xa0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0xb0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0xc0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0xd0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0xe0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0xf0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
    0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute

    Stack starts at: 0x0f (sp set to 0x0e) with 241 bytes available.
    No spare internal RAM space left.

    Other memory:
       Name             Start    End      Size     Max
       ---------------- -------- -------- -------- --------   
       PAGED EXT. RAM                         0      256      
       EXTERNAL RAM                           0    65536      
       ROM/EPROM/FLASH  0x0000   0x028a     651    65536

    其中,16×16的矩阵表示了idata区域中每个字节的作用,最后表格的Size列分别展示了PDATA、XDATA、FLASH占用的空间。由于编译速度很快,可以对比不同写法代码的空间占用,SDCC提供的该编译信息使得优化空间占用变得更加容易。


    希望该VSCode插件对大家有所帮助!


    3 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
    回复 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 15:00
  • 签到天数: 124 天

    [LV.7]常住居民III

    已绑定手机

    73

    主题

    791

    回帖

    2380

    积分

    超级版主

    积分
    2380
    QQ
    发表于 2024-2-28 08:58:42 | 显示全部楼层
    优秀如你
    热线19952583534
    www.STCAI.com
    回复 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-11-1 08:35 , Processed in 0.083805 second(s), 43 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表