器→工具, 工具软件

使用VSCode+PlatformIO开发51单片机

钱魏Way · · 12 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

PlatformIO 是什么?

PlatformIO 是一个开源的跨平台物联网(IoT)开发环境,专注于嵌入式系统和微控制器开发。它集成了代码编辑、编译、调试、库管理和固件烧录等功能,支持 650+种开发板40+种框架(如Arduino、ESP-IDF、STM32Cube、Raspberry Pi Pico等)。

核心功能与优势

  • 多硬件平台支持
    • 主流架构:ARM Cortex-M(STM32、Nordic)、ESP32/ESP8266、AVR、RISC-V等。
    • 开发板兼容性:Arduino Uno、Raspberry Pi Pico、STM32 Nucleo、ESP32-C3等。
    • 传统芯片支持:通过自定义配置可支持51单片机。
  • 集成开发工具链
    • 编译器:自动下载GCC、Clang、IAR等工具链。
    • 调试器:支持GDB调试(通过J-Link、ST-Link等硬件调试器)。
    • 烧录工具:兼容OpenOCD、py、STC-ISP等。
  • 依赖管理与库生态
    • 库管理器:通过ini声明依赖,自动从PlatformIO Registry或GitHub拉取库。
    • 示例工程:内置数千个开源示例(如传感器驱动、通信协议实现)。
  • 高效开发体验
    • 代码智能提示:基于Clang的代码补全和语法检查。
    • 远程开发:支持SSH和容器化开发(Docker集成)。
    • 单元测试:集成Unity测试框架,支持自动化测试。

与传统工具(如Keil)的对比

特性 PlatformIO Keil μVision
跨平台支持 全平台(Windows/macOS/Linux) 仅Windows
开源免费 完全免费(MIT协议) 商业软件,免费版功能受限
生态开放性 社区驱动,支持自定义硬件和框架 封闭生态,依赖厂商支持包
多框架支持 Arduino、ESP-IDF、Zephyr等 主要针对Keil自有工具链
开发效率 自动化依赖管理,集成现代工具链 手动配置库和工程选项
适用场景 IoT、多平台项目、开源协作 传统嵌入式教学、单一平台开发

典型应用场景

  • 多硬件平台开发:同一项目适配不同硬件(如ESP32和STM32共用代码)。
  • 物联网设备开发:快速集成WiFi/蓝牙(ESP32)、传感器(BME280)和云服务(AWS IoT)。
  • 开源协作:通过Git管理代码,依赖声明式配置(ini)确保环境一致性。
  • 教育与实验:提供Arduino兼容性,适合新手快速上手嵌入式开发。

局限性

  • 对传统芯片支持有限。部分老旧MCU(如STC89C51)需手动配置平台和工具链。
  • 学习曲线。CLI和配置文件(ini)需一定学习成本。
  • 调试依赖硬件。需额外调试器(如J-Link)实现高级调试功能。

PlatformIO的安装

安装必要工具

SDCC编译器(8051专用开源编译器):

  • Windows:从 SDCC官网 下载安装包。
  • Linux/macOS:使用包管理器安装。

烧录工具 stcgal(用于STC单片机): pip install stcgal

PlatformIO:在Vscode扩展市场中搜索PlatformIO进行安装

创建自定义STC89C51平台

在PIO Home的Boards中搜索STC89C51,并安装Intel MCS-51(8051) Platform

然后切换到Projects创建自己的项目。

由于我使用的是STC 89C516RD+的芯片,创建项目时会报错,报错内容为:

Could not initialize project
PIO Core Call Error: "The following files/directories have been created in C:\\Users\\qw\\Documents\\PlatformIO\\Projects\\STC89C51\r\ninclude - Put project header files here\r\nlib - Put project specific (private) libraries here\r\nsrc - Put project source files here\r\nplatformio.ini - Project Configuration File\r\n\n\nInvalidEnvNameError: Invalid environment name 'STC89C516RD+'. The name can contain alphanumeric, underscore, and hyphen characters (a-z, 0-9, -, _)"

解决方案1:选择Generic STC89C51RC也能创建成功。芯片稍有区别,但能正常使用。

解决方案2:打开C:\Users\{username}\.platformio\platforms\intel_mcs51\boards,找到STC89C516RD+.json文件,复制并重命名为STC89C516RD.json,并去除文件内的+号。修改完毕后重新启动VSCode再次搜索就能找到STC89C516RD的型号。

重新创建项目,创建项目的时候选择STC89C516RD即可。

完成后生成如下项目:

配置 PlatformIO 项目

进入系统后报:

[2025/5/25 10:06:24] 无法使用 compilerPath 解析配置:“C:/Users/qw/.platformio/packages/toolchain-sdcc/bin/sdcc.exe”

解决方案,在platformio.ini中新增compiler_path(路径为一开始安装的sdcc的路径)

; 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:STC89C516RD]
platform = intel_mcs51
board = STC89C516RD
compiler_path = C:/Program Files/SDCC/bin/sdcc.exe

在src目录下新建main.c文件,文件内容为:

#include <8051.h>

void delay() {
    unsigned int i;
    for (i=0; i<30000; i++);
}
void main() {
    while (1) {
        P2 = 0x00;  // LED全亮
        delay();
        P2 = 0xFF;  // LED全灭
        delay();
    }
}

对代码进行编译:

能够顺利完成编译,但当尝试进行Upload时发现卡住了。

经排查这里是stcgal的一个Bug,用户在上传时需要指定stcgal的协议参数-P stc89,否则上传会失败。尝试修改platformio.ini文件,新增upload选项:

; 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:STC89C516RD]
platform = intel_mcs51
board = STC89C516RD
compiler_path = C:/Program Files/SDCC/bin/sdcc.exe

upload_protocol = stcgal
upload_port = COM5                ; 根据实际串口号修改(Linux/macOS为/dev/ttyUSB0)
upload_speed = 2400               ; 必须降低波特率[11,12](@ref)
build_flags = -D F_CPU=11059200   ; 与单片机晶振频率一致[6,12](@ref)
upload_flags = -P stc89                      ; 强制指定协议类型[4,8](@ref)

改完后重新尝试,报如下错误:

*  正在文件夹 STC89C516RD 中执行任务: C:\Users\qw\.platformio\penv\Scripts\platformio.exe run --target upload --environment STC89C516RD 

Processing STC89C516RD (platform: intel_mcs51; board: STC89C516RD)
----------------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/intel_mcs51/STC89C516RD.html
PLATFORM: Intel MCS-51 (8051) (2.2.0) > Generic STC89C516RD
HARDWARE: STC89C516RD 11MHz, 1.25KB RAM, 64KB Flash
PACKAGES:
 - tool-stcgal @ 1.110.0 (1.10)
 - tool-vnproch55x @ 1.0.220407
 - toolchain-sdcc @ 1.40400.0 (4.4.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Checking size .pio\build\STC89C516RD\firmware.hex
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
Flash: [          ]   0.2% (used 130 bytes from 65536 bytes)
Configuring upload protocol...
AVAILABLE: stcgal
CURRENT: upload_protocol = stcgal
Looking for upload port...
Using manually specified: COM5
Uploading .pio\build\STC89C516RD\firmware.hex
usage: stcgal.py [-h] [-e] [-a] [-A {dtr,rts}] [-r RESETCMD]
                 [-P {stc89,stc89a,stc12a,stc12b,stc12,stc15a,stc15,stc8,stc8d,stc8g,usb15,auto}]
                 [-p PORT] [-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D]
                 [-V]
                 [code_image] [eeprom_image]
stcgal.py: error: argument -P/--protocol: invalid choice: ' stc89' (choose from 'stc89', 'stc89a', 'stc12a', 'stc12b', 'stc12', 'stc15a', 'stc15', 'stc8', 'stc8d', 'stc8g', 'usb15', 'auto')
*** [upload] Error 2
============================================= [FAILED] Took 1.20 seconds =============================================
 *  终端进程“C:\Users\qw\.platformio\penv\Scripts\platformio.exe 'run', '--target', 'upload', '--environment', 'STC89C516RD'”已终止,退出代码: 1。 
 *  终端将被任务重用,按任意键关闭。

发现是stc89前多了空格导致的。删除空格再次尝试:发现还是卡住了,-P参数并没有被正确的解析到。

再次实验,通过命令行手动进行上传:stcgal -P stc89 -p COM5 -b 2400 .pio/build/STC89C516RD/firmware.hex

发现顺利进入流程,当Waiting for MCU, please cycle power: done时,重启开关即可。

针对命令行可顺利上传的表现,再次调整platformio.ini文件为:

; 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:STC89C51RC]
platform = intel_mcs51
board = STC89C51RC
compiler_path = C:/Program Files/SDCC/bin/sdcc.exe

# 禁用默认烧录工具,启用自定义命令
upload_protocol = custom
# 自定义烧录命令(跨平台兼容)
upload_command = stcgal -P stc89 -p COM5 -b 2400 .pio/build/STC89C516RD/firmware.hex

调整单片机引用

在前面的实验代码中,我们使用了#include <8051.h>,为了更高的使用我们的芯片,我们需要从STC-IPS中导出一个型号对应的头文件。

注意这里需要选择SDCC格式头文件。将文件保存为STC89C516RD.h。并抢其复制到项目文件夹下的include目录下。

代码就可以修改为:

#include "STC89C516RD.h"

void delay() {
    unsigned int i;
    for (i=0; i<30000; i++);
}
void main() {
    while (1) {
        P2 = 0x00;  // LED全亮
        delay();
        P2 = 0xFF;  // LED全灭
        delay();
    }
}

解决IntelliSense无法理解SDCC的特殊语法

具体表现为:上面的代码能够正常编译,但是代码中P2下面会出现红色的波浪线提示,非常影响使用体验。原因是VS Code 的 IntelliSense 无法原生理解 SDCC 的特殊寄存器语法(如 __sfr __at),但我们可以通过配置让它正确识别这些定义。

我的解决方案:

在 STC89C516RD.h 中添加 IntelliSense 支持:

// STC89C52RC.h
#ifndef __STC89C52RC_H__
#define __STC89C52RC_H__

// 为 IntelliSense 定义伪关键字
#ifdef __INTELLISENSE__
    // 定义 SDCC 特殊关键字为 IntelliSense 可理解的格式
    #define __sfr volatile unsigned char
    #define __at(x)
    #define __bit unsigned char
    #define __critical
    #define __code
    #define __data
    #define __xdata
    #define __pdata
#endif

// 特殊功能寄存器定义
__sfr __at (0xA0) P2;
__sfr __at (0x80) P0;
__sfr __at (0x90) P1;
__sfr __at (0xB0) P3;

// 其他寄存器定义...
__sfr __at (0x98) SCON;
__sfr __at (0x99) SBUF;
__sfr __at (0x88) TCON;
__sfr __at (0x89) TMOD;

#endif

改完后一切就OK了。

特别注意:SDCC与 Keil的不一致

网上的很多示例代码都是keil的,在sdcc环境下并不能直接使用,所以需要进行一轮转化,比较简单的方式是使用deepseek这样的工具让他帮忙给你转化。

示例原始代码:

#include <REGX51.H>
#include <INTRINS.H>

void delay(unsigned int t) {
    while(t--);
}

void main() {
    int i; // Declare 'i' outside the loops
    P2 = 0xFE;
    while(1) {
        for(i = 0; i < 7; i++) { // Use pre-declared 'i'
            P2 = _crol_(P2, 1);
            delay(50000);
        }
        for(i = 0; i < 7; i++) { // Same here
            P2 = _cror_(P2, 1);
            delay(50000);
        }
    }
}

转换后的代码:

#include "STC89C516RD.h"
/*
 * SDCC兼容的8051流水灯程序 - 不使用intrins.h
 * 使用标准位操作实现循环移位功能
 * 
 * 连接说明:P2口连接8个LED(共阳接法或串联限流电阻)
 * 初始状态:P2.0亮(0xFE: 11111110)
 * 效果:先左移7次(LED灯依次向左流动),然后右移7次(LED灯依次向右流动)
 */

/**
 * 自定义循环左移函数
 * @param c 输入的字节
 * @return 循环左移一位后的结果
 */
unsigned char custom_crol(unsigned char c) {
    // 1. 检查最高位(MSB)是否为1
    unsigned char msb = c & 0x80;
    
    // 2. 左移一位(最低位自动补0)
    unsigned char result = c << 1;
    
    // 3. 如果最高位为1,设置最低位为1
    if (msb) {
        result |= 0x01;
    }
    
    return result;
}

/**
 * 自定义循环右移函数
 * @param c 输入的字节
 * @return 循环右移一位后的结果
 */
unsigned char custom_cror(unsigned char c) {
    // 1. 检查最低位(LSB)是否为1
    unsigned char lsb = c & 0x01;
    
    // 2. 右移一位(最高位自动补0)
    unsigned char result = c >> 1;
    
    // 3. 如果最低位为1,设置最高位为1
    if (lsb) {
        result |= 0x80;
    }
    
    return result;
}

/**
 * 简单的软件延时函数
 * @param t 延时参数(值越大延时越长)
 */
void delay(unsigned int t) {
    while(t--);  // 空循环实现延时
}

void main() {
    unsigned char led = 0xFE;  // 初始值:11111110 (P2.0亮)
    int i;
    
    P2 = led;  // 初始化P2口
    
    while(1) {
        // 向左循环移动7次
        for(i = 0; i < 7; i++) {
            led = custom_crol(led);  // 自定义循环左移
            P2 = led;                // 更新P2口
            delay(50000);            // 延时
        }
        
        // 向右循环移动7次
        for(i = 0; i < 7; i++) {
            led = custom_cror(led);  // 自定义循环右移
            P2 = led;                // 更新P2口
            delay(50000);            // 延时
        }
    }
}

Happy Hacking!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注