前言

这个学期学校开设了单片机课程,这是一门很有意思的课,之前也有过使用Ardunio UNO外驱舵机开发寝室NFC门禁的经历。在学校课程中学到了更多的诸如仿真的技巧,收益颇多。在当时基础知识不足的情况下反复被我擦写的板子居然能挺过来真的应该给他上柱香。

当然,学校课程所使用的工具都较老,尤其是keil,点名批评,使用起来非常的恼火,各类跳转搜索功能几乎是没有,界面老旧且交互完全算不上友好。如果一定要一个理由来使用它,那就是sdcc的编译结果相较keil稍大。但是这和我一个业余玩家有什么关系呢?整!

环境配置

C/C++

vsc必备的c语言开发插件,不必多说

PlatformIO

PlatformIO是一款vsc插件,适用于各类单片机的开发,非常好用。

安装以后会自动下载依赖,重启vsc后即可载入,需要注意的是,依赖下载会较慢,建议下载前挂上代理。

工程创建

进入PIO Home,点击New Project,根据提示创建工程

我们要进行51单片机开发的话,选择STC89C52RC即可,虽说是52,其实差不多啦,8052.h实质上也是在#include <8051.h>后增加了一些定义而已。

创建完成以后的项目结构如图所示,这里以我的课设流水灯为例,插件将为我们配置好所需的内容

代码编写

代码爆红

写上一些代码,会发现它的include爆红了,显然是includePath出问题了,自然而然的就想到要编辑一下.vscode/c_cpp_properties.json,这里其实是一个错误的操作,至于原因暂且按下不表。

查找了一下PIO的工具链路径,递归导入进去

再看眼代码,头文件确实是可以导入了,但是引脚定义又炸了

很麻,看眼头文件怎么定义的吧

查了一下,__sfrsdcc独有的定义风格,表示命名地址空间,配合__at绝对寻址可以进行针脚的定义和操作。虽然在vsc中爆红,但是实际上在使用sdcc进行编译时编译器会自动转化,实测也确实能编译,但是这也太难受了,必须给他安排明白了。

在各类搜索引擎中畅游了半天,找到的都是直接关闭语法提示这种暴力手段,那用vsc开发还有什么意义呢?终于,找了半天找到了一篇blog详细的写了sdcc的编译规则,其中提到

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

找了下,库中确实有一个lint.h文件预定义了各类独有关键字的宏,将其引入到源码

很好,终于不报错了,实际上这样做是把代码区分到智能提示和实际编译两个环境

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

原文主要是为了移植keil代码,我们进行原生sdcc style进行编写的话,就没有问题了,实际上此类关键字诸如sfr实际上是非标ANSI C,适用于单片机寄存器地址操作,采用标准C的强制转换和指针的概念来实现访问MCU的寄存器,本质实现其实是(*(volatile unsigned char *)0x**),以此方便程序操作单片机。

config重置

现在我们终于可以正常进行开发了吧,实际上并不是,如果你在刚刚的操作过程中一不小心关闭了vsc,再次打开以后你会发现

又他娘的爆红了!!!

检查.vscode/c_cpp_properties.json发现,整个文件都被重置了,这可咋整。

仔细阅读了一下c_cpp_properties.json,发现它在头部包含了一串注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//
// !!! WARNING !!! AUTO-GENERATED FILE!
// PLEASE DO NOT MODIFY IT AND USE "platformio.ini":
// https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags
//
{
"configurations": [
{
"name": "PlatformIO",
"includePath": [
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/include",
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/src",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/**"
],
"browse": {
"limitSymbolsToIncludedHeaders": true,
"path": [
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/include",
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/src",
""
]
},
"defines": [
"F_CPU=11059200",
"HEAP_SIZE=128",
"PLATFORMIO=50205",
""
],
"compilerPath": "C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/bin/sdcc.exe",
"compilerArgs": [
"-mmcs51",
""
]
}
],
"version": 4
}

好家伙,原来这玩意是个自动生成配置,我们不应该去手动管理它,而是通过上级配置platformio.ini来管理

查阅了官方文档后,在platformio.ini中引入工具链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:stc89c52rc]
platform = intel_mcs51
board = stc89c52rc
lib_extra_dirs =
C:\Users\DazeCake\.platformio\packages\toolchain-sdcc\include

保存刷新后,c_cpp_properties.json也自动为我们刷新了includePath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//
// !!! WARNING !!! AUTO-GENERATED FILE!
// PLEASE DO NOT MODIFY IT AND USE "platformio.ini":
// https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags
//
{
"configurations": [
{
"name": "PlatformIO",
"includePath": [
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/include",
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/src",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/mcs51",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/asm",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/ds390",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/ds400",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/hc08",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/pic14",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/pic16",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/z180",
""
],
"browse": {
"limitSymbolsToIncludedHeaders": true,
"path": [
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/include",
"c:/Users/DazeCake/Documents/PlatformIO/Projects/LED/src",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/mcs51",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/asm",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/ds390",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/ds400",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/hc08",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/pic14",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/pic16",
"C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/include/z180",
""
]
},
"defines": [
"F_CPU=11059200",
"HEAP_SIZE=128",
"PLATFORMIO=50205",
""
],
"compilerPath": "C:/Users/DazeCake/.platformio/packages/toolchain-sdcc/bin/sdcc.exe",
"compilerArgs": [
"-mmcs51",
""
]
}
],
"version": 4
}

虽说傻乎乎的没用vsc自带的递归搜索,但是这样也比之前智能多了

仿真

还是走个过场来仿真一下吧

编译

直接复制编译后的文件路径

填入仿真程序入口

运行结果正常