51学习(2):vscode+ Embedded IDE开发环境搭建

fengbohan1 发布于 2022-02-07 11746 次阅读


前言

platformIO虽然可以用于编译8051,但是修改头文件包含路径,以及编译器选项都较为麻烦。Embedded IDE界面类似Visual Studio,设置较为方便。

环境

  • 单片机:普中-STC89C516RD+

正文

一,安装Embedded IDE和SDCC编译器

platformIO使用的也是SDCC编译器,但是Embedded IDE需要自己下载安装SDCC编译器

  1. 下载安装SDCC编译器:
    sdcc官方网页
    sdcc-4.1.0-x64-setup.exe
  2. Embedded IDE 配置 sdcc 编译器路径
    点击下图中的构建配置即可配置。
    Snipaste_2022-02-07_17-19-52.png

二,单片机头文件

首先,如果是STC-ISP系列的单片机可以在下图下载软件界面的头文件栏获取对应的头文件。
Snipaste_2022-02-07_17-23-43.png

这个头文件 SDCC无法直接使用。
这是因为SDCC 使用了一些非标准的关键字

头文件转换方法一

使用附件一:main.c代码进行转换,转换的时候注意待转换文件中不得包含特别长的注释(也可能出现其他的问题,这个是C代码问题,有兴趣可以重写)。

//转换前
sfr P0   = 0x80;
sbit P00 = P0^0;
//转换后
SFR(P0, 0x80);
SBIT(P00, 0x80, 0);

转换后就可以直接使用了。

头文件转换方法二(推荐)

使用perl脚本

这个脚本是sdcc源码文件中自带的,用于keil头文件转sdcc头文件。
但是实际使用时,我又做了一些修改,修改后的脚本见附件2:keil2sdcc.pl
运行脚本需要安装perl运行环境:官方 perl strawberry版本下载地址

解决vscode 不能识别 sdcc 扩展语法问题

最后为了vscode能够正确的标记表示语法,还需要加上下面的代码

#ifndef __SDCC
#include <lint.h>
#error "This header file must be used for SDCC compiler !"
#endif
#include <compiler.h>

lint.h是sdcc自带的一个头文件,里面是sdcc自定义的关键字的空定义,可以用来避免vscode语法检查报错。
具体实现详见附件3:stc89xx.h

三,附件

1,main.c

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main(int argc, char* args[])
{
    string filename = "STC89xx.h";
    ifstream file_in(filename.c_str());

    string s;
    string previous_addr;
    while( getline(file_in,s) )
    {
        if(s[0] == '/' || s[0] == '#')
            cout << s << endl;
        else if(s.length() > 4)
        {
            string::size_type type_end = s.find_first_of(' ');
            string type = s.substr(0, type_end);

            string::size_type name_begin = s.find_first_not_of(' ', type_end);
            string::size_type name_end = s.find_first_of(' ', name_begin);
            string name = s.substr(name_begin, name_end - name_begin);

            string::size_type addr_begin = s.find_first_not_of(" =", name_end);
            string::size_type addr_end = s.find_first_of(" ;", addr_begin);
            string addr = s.substr(addr_begin, addr_end - addr_begin);

            string comment;
            bool haveComment = true;
            if(addr_end == s.length() - 1)
            {
                haveComment = false;
            }

            if(haveComment)
            {
                string::size_type comment_begin = s.find_first_not_of("; ", addr_end);
                comment = s.substr(comment_begin);
            }

            if(type == "sfr")
            {
                cout << "SFR(" << name << ", "  << addr << ");";
                if(haveComment) cout << comment;
                cout << endl;
                previous_addr = addr;
            }
            else if(type == "sbit")
            {
                string bit_offset = addr.substr(addr.length()-1);

                cout << "SBIT(" << name << ", "  << previous_addr << ", " << bit_offset << ");";
                if(haveComment) cout << comment;
                cout << endl;
            }

        }
    }
    return 0;
}

2,keil2sdcc.pl (在sdcc原版上修改后)

#!/usr/bin/perl -w

# keil2sdcc.pl
# Scott Bronson
# 22 June 2003

# usage (UNIX):
#    perl keil2sdcc.pl < keil_header.h > sdcc_header.h
# or
#    perl keil2sdcc.pl keil_header.h > sdcc_header.h
#
# usage (Windows):
#    perl keil2sdcc.pl keil_header.h > sdcc_header.h
#
#
# keil_header.h and sdcc_header.h must not be the same file since
# most shells overwrite the output file before opening the input file.

# This script converts Keil-style header files to SDCC.  It tries to
# be pedantic so don't be surprised if you need to munge it a bit to
# get it to work.  On the other hand, it doesn't fully parse the C
# file (for obvious reasons).

# It takes the Keil header file either as an argument or on
# stdin and it produces the output on stdout.

# This script is inspired by keil2sdcc.pl by Bela Torok but a lot
# more pedantic.

use strict;
my %data;

while(<>) 
{
    s/\r//g;    # remove DOS line endings if necessary

    # external register (kind of a weird format)
    #
    # in:  EXTERN xdata volatile BYTE GPIF_WAVE_DATA _AT_ 0xE400;
    # out: EXTERN xdata at 0xE400 volatile BYTE GPIF_WAVE_DATA;
    # $1: leading whitespace
    # $2: variable name
    # $3: variable location
    # $4: trailing comments, etc.

    if(/^(\s*)EXTERN\s*xdata\s*volatile\s*BYTE\s*(\w+(?:\s*\[\s*\d+\s*\])?)\s+_AT_\s*([^;]+);(.*)$/) {
        print "$1EXTERN xdata at $3 volatile BYTE $2;$4\n";
        next;
    }

    # sfr statement
    #
    # in:  sfr IOA = 0x80;
    # out: SFR(0x80, IOA);
    # $1: leading whitespace
    # $2: variable name
    # $3: variable location
    # $4: trailing comments, etc.

    if(/^(\s*)sfr\s*(\w+)\s*=\s*([^;]+);(.*)$/) {
        print "$1SFR($2, $3);$4\n";
        $data{-$2} = $3;
        next;
    }

    # sbit statement
    #
    # in:  sbit SEL = 0x86^0;
    # out: sbit (SEL, 0x86, 0);
    # $1: leading whitespace
    # $2: variable name
    # $3: variable location
    # $4: variable number
    # $5: trailing comments, etc.

    if(/^(\s*)sbit\s*(\w+)\s*=\s*(\w+)\s*\^\s*([^;]+);(.*)$/) {
        print "$1SBIT($2, $data{-$3}, $4);$5\n";
        next;
    }

    # entire line is a C++ comment, output it unchanged.
    if(/^(\s*)\/\/(.*)$/) {
        print "$1//$2\n";
        next;
    }

    # C comment, slurp lines until the close comment and output it unchanged.
    if(/^(\s*)\/\*(.*)$/) {
        my($ws,$cmt) = ($1,"$2\n");
        $cmt .= <> while $cmt !~ /\*\/\s*$/;
        $cmt =~ s/\r//g;
        print "$ws/*$cmt";
        next;
    }

    # preprocessor statement (whitespace followed by '#'), don't change
    if(/^(\s*)\#(.*)$/) {
        print "$1#$2\n";
        next;
    }

    # blank line, don't change
    if(/^(\s*)$/) {
        print "\n";
        next;
    }

    chomp;
    die "Unconvertable line: \"$_\"\n";
}

3,stc89xx.h (使用perl脚本转换后)

#ifndef __STC89C5xRC_RDP_H__
#define __STC89C5xRC_RDP_H__

#ifndef __SDCC
#include <lint.h>
#error "This header file must be used for SDCC compiler !"
#endif
#include <compiler.h>

typedef unsigned int u16;
typedef unsigned char u8;
/////////////////////////////////////////////////

/* The following is STC additional SFR */

/* sfr  AUXR  = 0x8e; */
/* sfr  AUXR1 = 0xa2; */
/* sfr  IPH   = 0xb7; */

SFR(P4, 0xe8);
SBIT(P46, 0xe8, 6);
SBIT(P45, 0xe8, 5);       //ISP下载需勾选"ALE脚用作P4.5口"
SBIT(P44, 0xe8, 4);
SBIT(P43, 0xe8, 3);
SBIT(P42, 0xe8, 2);
SBIT(P41, 0xe8, 1);
SBIT(P40, 0xe8, 0);

SFR(XICON, 0xc0);

SFR(WDT_CONTR, 0xe1);

SFR(ISP_DATA, 0xe2);
SFR(ISP_ADDRH, 0xe3);
SFR(ISP_ADDRL, 0xe4);
SFR(ISP_CMD, 0xe5);
SFR(ISP_TRIG, 0xe6);
SFR(ISP_CONTR, 0xe7);

/* Above is STC additional SFR */

/*  BYTE Registers  */
SFR(P0, 0x80);
SBIT(P00, 0x80, 0);
SBIT(P01, 0x80, 1);
SBIT(P02, 0x80, 2);
SBIT(P03, 0x80, 3);
SBIT(P04, 0x80, 4);
SBIT(P05, 0x80, 5);
SBIT(P06, 0x80, 6);
SBIT(P07, 0x80, 7);
SFR(P1, 0x90);
SBIT(P10, 0x90, 0);
SBIT(P11, 0x90, 1);
SBIT(P12, 0x90, 2);
SBIT(P13, 0x90, 3);
SBIT(P14, 0x90, 4);
SBIT(P15, 0x90, 5);
SBIT(P16, 0x90, 6);
SBIT(P17, 0x90, 7);
SFR(P2, 0xA0);
SBIT(P20, 0xA0, 0);
SBIT(P21, 0xA0, 1);
SBIT(P22, 0xA0, 2);
SBIT(P23, 0xA0, 3);
SBIT(P24, 0xA0, 4);
SBIT(P25, 0xA0, 5);
SBIT(P26, 0xA0, 6);
SBIT(P27, 0xA0, 7);
SFR(P3, 0xB0);
SBIT(P30, 0xB0, 0);
SBIT(P31, 0xB0, 1);
SBIT(P32, 0xB0, 2);
SBIT(P33, 0xB0, 3);
SBIT(P34, 0xB0, 4);
SBIT(P35, 0xB0, 5);
SBIT(P36, 0xB0, 6);
SBIT(P37, 0xB0, 7);
SFR(PSW, 0xD0);
SFR(ACC, 0xE0);
SFR(B, 0xF0);
SFR(SP, 0x81);
SFR(DPL, 0x82);
SFR(DPH, 0x83);
SFR(PCON, 0x87);
SFR(TCON, 0x88);
SFR(TMOD, 0x89);
SFR(TL0, 0x8A);
SFR(TL1, 0x8B);
SFR(TH0, 0x8C);
SFR(TH1, 0x8D);
SFR(IE, 0xA8);
SFR(IP, 0xB8);
SFR(SCON, 0x98);
SFR(SBUF, 0x99);

/*  80C51Fx/Rx Extensions  */
SFR(AUXR, 0x8E);
SFR(AUXR1, 0xA2);
SFR(SADDR, 0xA9);
SFR(IPH, 0xB7);
SFR(SADEN, 0xB9);
SFR(T2CON, 0xC8);
SFR(T2MOD, 0xC9);
SFR(RCAP2L, 0xCA);
SFR(RCAP2H, 0xCB);
SFR(TL2, 0xCC);
SFR(TH2, 0xCD);

/*  BIT Registers  */
/*  PSW   */
SBIT(CY, 0xD0, 7);
SBIT(AC, 0xD0, 6);
SBIT(F0, 0xD0, 5);
SBIT(RS1, 0xD0, 4);
SBIT(RS0, 0xD0, 3);
SBIT(OV, 0xD0, 2);
SBIT(F1, 0xD0, 1);
SBIT(P, 0xD0, 0);

/*  TCON  */
SBIT(TF1, 0x88, 7);
SBIT(TR1, 0x88, 6);
SBIT(TF0, 0x88, 5);
SBIT(TR0, 0x88, 4);
SBIT(IE1, 0x88, 3);
SBIT(IT1, 0x88, 2);
SBIT(IE0, 0x88, 1);
SBIT(IT0, 0x88, 0);

/*  IE   */
SBIT(EA, 0xA8, 7);
SBIT(EC, 0xA8, 6);
SBIT(ET2, 0xA8, 5);
SBIT(ES, 0xA8, 4);
SBIT(ET1, 0xA8, 3);
SBIT(EX1, 0xA8, 2);
SBIT(ET0, 0xA8, 1);
SBIT(EX0, 0xA8, 0);

/*  IP   */ 
/*  sbit PPC  = IP^6;*/
SBIT(PT2, 0xB8, 5);
SBIT(PS, 0xB8, 4);
SBIT(PT1, 0xB8, 3);
SBIT(PX1, 0xB8, 2);
SBIT(PT0, 0xB8, 1);
SBIT(PX0, 0xB8, 0);

/*  P3  */
SBIT(RD, 0xB0, 7);
SBIT(WR, 0xB0, 6);
SBIT(T1, 0xB0, 5);
SBIT(T0, 0xB0, 4);
SBIT(INT1, 0xB0, 3);
SBIT(INT0, 0xB0, 2);
SBIT(TXD, 0xB0, 1);
SBIT(RXD, 0xB0, 0);

/*  SCON  */
SBIT(SM0, 0x98, 7); // alternatively "FE"
SBIT(FE, 0x98, 7);
SBIT(SM1, 0x98, 6);
SBIT(SM2, 0x98, 5);
SBIT(REN, 0x98, 4);
SBIT(TB8, 0x98, 3);
SBIT(RB8, 0x98, 2);
SBIT(TI, 0x98, 1);
SBIT(RI, 0x98, 0);

SBIT(T2EX, 0x90, 1);
SBIT(T2, 0x90, 0);

/*  T2CON  */
SBIT(TF2, 0xC8, 7);
SBIT(EXF2, 0xC8, 6);
SBIT(RCLK, 0xC8, 5);
SBIT(TCLK, 0xC8, 4);
SBIT(EXEN2, 0xC8, 3);
SBIT(TR2, 0xC8, 2);
SBIT(C_T2, 0xC8, 1);
SBIT(CP_RL2, 0xC8, 0);

/////////////////////////////////////////////////

#endif

参考文献

  1. SDCC编译器 + VSCode开发 8位微控制器
  2. Embedded IDE 文档
  3. 突然要用 STC 写个东西,但是发现没有SDCC头文件,写个工具
  4. sdcc官方手册