第2章:开发环境搭建:VS Code + PlatformIO 环境配置,ESP-IDF / RT-Thread SDK 安装,第一个多核点灯程序

好,咱们直接进入正题。这一章,说白了就是“磨刀”。刀磨快了,后面砍柴才不费劲。很多同学学多核MCU,上来就啃手册,结果环境配了三天没跑通,心态直接崩了。我当年刚接触ESP32的时候,也踩过类似的坑——IDE装了好几个,SDK版本对不上,最后发现是路径里有中文。嗯,咱们今天就把这些坑提前填上。

2.1 为什么选VS Code + PlatformIO?

你可能会问:用Arduino IDE不行吗?或者直接用ESP-IDF的命令行?

我的答案是:都可以,但如果你想做“正经”的嵌入式开发,VS Code + PlatformIO是目前最舒服的组合。原因有三:

  • 跨平台:Windows、macOS、Linux,一套配置到处跑。我在公司用Windows,回家用Mac,项目代码直接同步,不用改任何东西。
  • 插件生态:代码补全、语法检查、Git集成、串口监视器,全都有。说白了,你不需要在好几个窗口之间来回切换。
  • 多框架支持:Arduino、ESP-IDF、RT-Thread、STM32Cube,一个PlatformIO全搞定。你想想看,如果每个芯片厂商都装一个IDE,你的硬盘受得了吗?

核心观点:PlatformIO不是IDE,它是一个“项目构建系统”。它帮你管理编译器、库依赖、烧录工具。你只需要写代码,剩下的交给它。

2.2 安装VS Code与PlatformIO插件

这一步没什么技术含量,但细节决定成败。我建议你按顺序来:

  1. 下载VS Code:去官网 code.visualstudio.com,下载对应系统的版本。别去第三方网站,你懂的。
  2. 安装VS Code:一路默认就行。但注意:安装路径不要有中文或空格。我曾经见过有人装在“D:\软件\VS Code”下面,结果编译报错,查了半天才发现是路径问题。
  3. 安装PlatformIO插件:打开VS Code,点击左侧扩展图标(四个方块那个),搜索“PlatformIO IDE”,点击安装。这个过程大概需要几分钟,因为插件会下载一些依赖。
  4. 重启VS Code:安装完成后,VS Code会提示你重启。别偷懒,重启一下。

小技巧:安装完成后,你会在左侧看到一个“蚂蚁头”图标(PlatformIO的logo)。点击它,就能看到PlatformIO的主界面。如果没出现,试试按 Ctrl+Shift+P,输入“PlatformIO: Home”手动打开。

2.3 安装ESP-IDF与RT-Thread SDK

这里有两种方式。我个人习惯用PlatformIO的库管理器来装,因为它会自动处理依赖和版本冲突。但如果你想用官方工具链,也可以手动装。咱们分别说。

2.3.1 通过PlatformIO安装ESP-IDF

PlatformIO支持多种开发框架。对于ESP32,你只需要在项目配置里指定框架为“espidf”即可。具体步骤:

  1. 创建一个新项目:点击PlatformIO Home -> New Project。
  2. Board(开发板)选择“Espressif ESP32 Dev Module”或你手头的具体型号。
  3. Framework(框架)选择“ESP-IDF”。
  4. 点击“Finish”,PlatformIO会自动下载ESP-IDF工具链和SDK。这个过程比较慢,取决于你的网速。我第一次装的时候等了快20分钟,还以为是死机了。

注意:如果你用的是ESP32-S3或C3等较新的芯片,记得在board选项里选对型号。否则编译出来的固件可能无法运行。我曾经在ESP32-S3上选成了ESP32,结果WiFi死活连不上,折腾了两天才发现是芯片型号选错了。

2.3.2 手动安装ESP-IDF(备选方案)

如果你喜欢用官方工具链,也可以手动安装。但我不推荐初学者这么做,因为环境变量配置比较麻烦。如果你非要试,记住以下几点:

  • 从GitHub克隆ESP-IDF仓库:git clone --recursive https://github.com/espressif/esp-idf.git
  • 运行安装脚本:./install.sh(Linux/Mac)或 install.bat(Windows)
  • 设置环境变量:source ./export.sh(每次打开终端都要执行一次)

嗯,看到这里你可能已经觉得麻烦了。所以还是用PlatformIO吧,省心。

2.3.3 安装RT-Thread SDK

RT-Thread在PlatformIO里也支持得很好。步骤类似:

  1. 创建新项目,Board选择“STM32”或“ESP32”等支持RT-Thread的板子。
  2. Framework选择“RT-Thread”。
  3. PlatformIO会自动下载RT-Thread的源码和工具链。

这里要注意:RT-Thread的版本有很多,有标准版、Nano版、Smart版。对于多核MCU,我建议用标准版,因为它支持SMP(对称多处理)。Nano版虽然轻量,但不支持多核调度。

2.4 第一个多核点灯程序

好了,环境搭好了,咱们来写第一个程序。目标很简单:让两个LED分别由Core 0和Core 1控制,交替闪烁。这样你就能直观地看到两个核心在同时工作。

先看代码:

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gpio.h>

#define LED_GPIO_0 2    // Core 0 控制的LED
#define LED_GPIO_1 4    // Core 1 控制的LED

// Core 0 上的任务
void task_core0(void *pvParameters) {
    gpio_set_direction(LED_GPIO_0, GPIO_MODE_OUTPUT);
    while (1) {
        gpio_set_level(LED_GPIO_0, 1);
        vTaskDelay(pdMS_TO_TICKS(500));
        gpio_set_level(LED_GPIO_0, 0);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

// Core 1 上的任务
void task_core1(void *pvParameters) {
    gpio_set_direction(LED_GPIO_1, GPIO_MODE_OUTPUT);
    while (1) {
        gpio_set_level(LED_GPIO_1, 1);
        vTaskDelay(pdMS_TO_TICKS(300));
        gpio_set_level(LED_GPIO_1, 0);
        vTaskDelay(pdMS_TO_TICKS(300));
    }
}

void app_main(void) {
    // 创建任务,并指定运行在哪个核心
    xTaskCreatePinnedToCore(task_core0, "task0", 2048, NULL, 1, NULL, 0);
    xTaskCreatePinnedToCore(task_core1, "task1", 2048, NULL, 1, NULL, 1);
    
    printf("多核点灯程序启动!\n");
}

这段代码的核心在于 xTaskCreatePinnedToCore 这个函数。它比普通的 xTaskCreate 多了一个参数——最后一个数字,就是指定核心编号。0代表Core 0,1代表Core 1。

你想想看,两个任务分别跑在不同的核心上,互不干扰。Core 0上的LED以500ms周期闪烁,Core 1上的LED以300ms周期闪烁。如果你用示波器看,会发现两个波形是完全独立的,没有互相影响。

关键点xTaskCreatePinnedToCore 是ESP-IDF基于FreeRTOS的扩展。标准的FreeRTOS没有这个函数。它让你能精确控制任务在哪个核心上运行。这在多核编程中非常重要——你可以把实时性要求高的任务放在一个核心,把后台任务放在另一个核心。

2.5 编译、烧录与验证

代码写好了,怎么跑起来?

  1. 编译:在VS Code底部状态栏,点击“PlatformIO: Build”(或者按 Ctrl+Alt+B)。第一次编译会比较慢,因为要编译整个ESP-IDF库。后面就快了。
  2. 烧录:把ESP32开发板用USB线连到电脑,点击“PlatformIO: Upload”(或者按 Ctrl+Alt+U)。
  3. 查看串口输出:点击“PlatformIO: Serial Monitor”(或者按 Ctrl+Alt+S)。你应该能看到“多核点灯程序启动!”的打印信息。

如果LED没亮,别慌。先检查GPIO引脚对不对。ESP32 Dev Module上,GPIO2通常连接板载LED,但有些版本可能不一样。我建议你用万用表量一下,或者直接看原理图。

调试技巧:如果程序烧录后没反应,可以试试在 app_main 里加个死循环打印,看看程序有没有跑到那里。比如:while(1) { printf("alive\n"); vTaskDelay(1000); }。如果串口能看到“alive”,说明系统启动正常,问题出在GPIO配置上。

2.6 避坑指南

最后,分享几个我当年踩过的坑:

  • 电源不足:ESP32在WiFi开启时电流较大,如果USB线质量不好,可能导致供电不稳,程序跑飞。我建议用带屏蔽的USB线,或者外接3.3V稳压电源。
  • GPIO冲突:有些GPIO引脚在启动时有特殊功能(比如JTAG、ADC),不能随便用。比如GPIO12是MTDI引脚,如果拉高会导致芯片进入下载模式。我建议初学者先用GPIO2、GPIO4、GPIO5这些安全的引脚。
  • 任务栈大小xTaskCreatePinnedToCore 的第三个参数是任务栈大小,单位是字节。如果栈太小,任务会崩溃。我一般给2048字节起步,如果任务里有大的局部变量或递归调用,再加大。

嗯,这一章的内容就到这里。环境搭好了,第一个多核程序也跑起来了,后面咱们就可以深入多核调度的细节了。记住,动手实践是最好的学习方式。别光看,赶紧把代码烧进去试试。