嵌入式开发环境配置实战
一句话结论
只用一套工具就选 PlatformIO:一份 platformio.ini 覆盖 AVR / ESP8266 / ESP32 / STM32 / RP2040,库依赖写进配置就能复现环境,换机器只需 git clone && pio run。要深挖 ESP32 再补一套 ESP-IDF,要搞 STM32 底层再补 STM32CubeIDE。Arduino IDE 只用来跑别人的示例和教学。
零、装工具之前:先把通用地基打好
0.1 工具链选型总表
| 工具 | 主力场景 | 支持平台 | 优点 | 缺点 | 建议 |
|---|---|---|---|---|---|
| PlatformIO(VS Code 插件 / CLI) | 日常主力 | AVR、ESP8266/32、STM32、RP2040、nRF、Mbed 等数十种 | 依赖可复现、库管理强、多环境、CLI 可进 CI | 冷门板卡支持滞后、调试配置要手改 | 首选,本文重点 |
| Arduino IDE 2.x | 教学、跑示例、快速验证 | Arduino 全系 + ESP8266/32 + RP2040 等 | 上手最快、示例最多、自带串口绘图仪 | 库版本不可锁定、无项目概念、大项目难维护 | 备用,别拿它做正经项目 |
| ESP-IDF | ESP32 深度开发 | 仅乐鑫 ESP32 全系 | 官方框架、FreeRTOS、组件化、安全特性(Secure Boot / Flash 加密)最全 | 学习曲线陡、构建慢、文档量大 | 需要 ESP32 全部能力时 |
| STM32CubeIDE / CubeMX | STM32 裸机与 HAL | 仅 ST | 图形化配引脚时钟、自动生成初始化、内置调试 | 只管 ST、代码生成器会覆盖手写部分 | 搞 STM32 必备 |
| Arduino CLI | 无 GUI / CI / 批量 | 同 Arduino | 可脚本化 | 配置繁琐 | 自动化场景 |
| MicroPython / CircuitPython | 快速原型、教学 | ESP8266/32、RP2040、部分 STM32 | REPL 交互式、改完即跑 | 性能与实时性差、占 Flash | 原型验证 |
| 树莓派 / Jetson 官方工具 | Linux 单板 | 对应板卡 | 官方镜像、开箱即用 | 与普通嵌入式工具链不通用 | 单起章节讲 |
0.2 先认清你的 USB 转串口芯片
板子插上电脑后第一步不是装 IDE,而是确认它用什么 USB 转串口芯片。下面的命令直接复制执行。
# —— Linux ——
lsusb # 列出 USB 设备
dmesg -w # 插拔一次,看内核打印(Ctrl+C 退出)
ls -la /dev/ttyUSB* /dev/ttyACM* 2>/dev/null
udevadm info -q property -n /dev/ttyUSB0 2>/dev/null | grep -E "ID_VENDOR|ID_MODEL"
# —— macOS ——
system_profiler SPUSBDataType | grep -A5 -i "serial\|USB"
ls /dev/cu.*
# —— Windows(PowerShell,需管理员) ——
Get-PnpDevice -PresentOnly | Where-Object { $_.FriendlyName -match "COM|USB Serial|CH34|CP210|FT232" } | Select-Object FriendlyName, InstanceId
# 或在设备管理器里看「端口 (COM 和 LPT)」下的名字
常见芯片对照:
| 丝印 / 设备名 | 厂商 | 驱动 | 典型出现位置 | VID:PID |
|---|---|---|---|---|
| CH340 / CH341 | 沁恒 WCH | 需装驱动(Win7/10 部分版本自带) | Arduino Nano 克隆、ESP8266/ESP32 廉价板 | 1A86:7523(CH340)、1A86:5523(CH341) |
| CP210x(CP2102/CP2104) | Silicon Labs | Win10+ 多自带 | NodeMCU、ESP32 DevKitC、部分 STM32 板 | 10C4:EA60 |
| FT232RL / FT2232 | FTDI | Win 自带 / 官方 VCP | 高端下载器、Arduino 原装(用的是 ATmega16U2 另算) | 0403:6001 |
| ATmega16U2 / 8U2 | Microchip | 免驱(CDC ACM) | Arduino Uno R3 原装 | 2341:0043 等 |
| 原生 USB(ESP32-S2/S3/C3、RP2040) | — | 免驱 CDC | ESP32-S3 DevKitC 的 USB 口、Pico | 结果为 /dev/ttyACM*,不是 ttyUSB |
⚠️ 注意:ESP32-S3 / S3 类板子通常有两个口:标
USB的是原生 USB(CDC / JTAG 调试),标UART的是经 CP210x/CH340 转出来的串口。程序下载走哪个口取决于板子设计,插错口pio run -t upload会连不上。两个口都试一遍是最快的判定方法。
0.3 Windows 驱动安装
# 方式一:winget 安装串口驱动(管理员 PowerShell)
winget install --id WCH.CH34xSER # CH340/CH341
winget install --id SiliconLabs.CP210xVCP # CP210x(若仓库有收录)
winget install --id FTDI.FTDISerialDriver # FT232(通常系统自带,非必要)
# 方式二:ST-Link / J-Link 调试器驱动
winget install --id STMicroelectronics.STLinkDriver # 若提示无此包,去 ST 官网下 STSW-LINK009
# 方式三:zadig 给 libusb 类设备(OpenOCD、逻辑分析仪)换驱动
winget install --id pbatard.Zadig
| 驱动 | 官方下载地址(关键词搜索即可) | 装完在设备管理器里应该显示 |
|---|---|---|
| CH341SER | WCH 官网 wch.cn → 产品 → 接口芯片 → CH341 → 驱动 |
USB-SERIAL CH340 (COMx) |
| CP210x VCP | Silicon Labs 官网 silabs.com → CP210x USB to UART Bridge VCP Drivers |
Silicon Labs CP210x USB to UART Bridge (COMx) |
| FTDI VCP | ftdichip.com → VCP Drivers |
USB Serial Port (COMx) |
| ST-Link V2 | ST 官网 → STSW-LINK009 | STMicroelectronics STLink dongle(通用串行总线设备里) |
装完驱动仍不出现 COM 口的排查顺序:
- 换 USB 线。充电线只有 VCC/GND 两根,没有数据线,这是新手第一位故障原因。
- 换 USB 口,别接在显示器/键盘的 HUB 上。
- 设备管理器里看是否有「未知设备」带黄色感叹号 → 右键更新驱动 → 手动指定目录。
- Windows 11 上部分 CH340 克隆被驱动签名拦截 → 需禁用驱动强制签名(启动设置 → 疑难解答 → 高级选项 → 启动设置 → 按 7)。
- 板子本身坏:换一台电脑试。
0.4 Linux 串口权限(必做)
# 把当前用户加入 dialout 组(串口设备所属组)
sudo usermod -aG dialout $USER
# 若用 OpenOCD / ST-Link / J-Link / 逻辑分析仪,通常还需 plugdev 与 udev 规则
sudo usermod -aG plugdev $USER
# 查看自己已在哪些组(确认 dialout 出现)
groups
# ⚠️ 必须注销并重新登录(或重启)才生效,仅开新终端无效
不想重新登录的临时办法(重启后失效):
sudo chmod 666 /dev/ttyUSB0
更规范的做法是写 udev 规则,让设备固定名字、固定权限:
# 先查设备的厂商/产品 ID
udevadm info -a -n /dev/ttyUSB0 | grep -E "idVendor|idProduct|serial" | head -6
# 新建规则文件
sudo tee /etc/udev/rules.d/99-usb-serial.rules > /dev/null <<'EOF'
# CH340
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666", SYMLINK+="ttyCH340"
# CP210x
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="0666", SYMLINK+="ttyCP210"
# FT232
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", SYMLINK+="ttyFTDI"
# ST-Link V2(供 OpenOCD / stlink 使用,无需 root)
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", MODE="0666", GROUP="plugdev"
# ST-Link V2.1(Nucleo 板载)
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="374b", MODE="0666", GROUP="plugdev"
EOF
# 重载规则并触发
sudo udevadm control --reload-rules && sudo udevadm trigger
# 验证:拔插设备后应该出现固定别名的软链接
ls -la /dev/ttyCH340 /dev/ttyCP210 2>/dev/null
⚠️ 注意:
MODE="0666"等于任何用户都能读写串口,方便但降低安全性。多人共享的机器上建议改用GROUP="dialout", MODE="0660"并把用户加进dialout。
Ubuntu 上还有一个高频坑:
# brltty(盲文终端守护进程)会抢占 CH340,导致 /dev/ttyUSB0 出现几秒后消失
systemctl status brltty-udev.service 2>/dev/null
sudo apt remove brltty # 确认不用盲文设备就直接卸载
0.5 macOS 注意事项
# 查看串口(macOS 用 cu.* 而非 tty.*,cu 是 callout 设备,不会被系统占用)
ls /dev/cu.*
# 连接串口
screen /dev/cu.usbserial-1420 115200
# 退出 screen:Ctrl+A 然后按 k,再按 y 确认
# Homebrew 装常用工具
brew install --cask visual-studio-code
brew install minicom esptool stlink open-ocd sigrok-cli
⚠️ 注意:较新的 Mac(Apple Silicon + macOS 12+)上 CH340 驱动需要允许系统扩展:开机按住电源键进恢复模式 → 安全策略 → 勾选「允许用户管理来自被认可开发者的内核扩展」。CP210x 与 FTDI 情况类似,具体以驱动厂商的安装说明为准。
0.6 网络加速:镜像源的思路
乐鑫、PlatformIO 的索引和工具链都在境外,国内直连经常超时。可选方案:
| 目标 | 原始地址 | 替代思路 |
|---|---|---|
| Arduino ESP32 索引 | https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json |
用 ghproxy / jsDelivr 等公共加速前缀改写 URL;或离线下载索引与工具包后放到本地 HTTP 服务。具体镜像域名会变动,以当时可用的镜像为准 |
| PlatformIO 平台包 | https://dl.registry.platformio.org |
设置环境变量 PLATFORMIO_CORE_DIR 换缓存目录;离线时可把已下载的 ~/.platformio 整个目录拷贝到新机器 |
| ESP-IDF 工具链 | https://github.com/espressif/...、https://dl.espressif.com |
安装时设置 IDF_GITHUB_ASSETS="dl.espressif.com/github_assets" 走乐鑫国内 CDN |
| pip 包 | https://pypi.org/simple |
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple(清华源) |
| GitHub Release / git clone | github.com |
配置 git 代理,或用 git config --global url."https://ghproxy.com/https://github.com/".insteadOf 形式改写。镜像域名随时可能失效,别写进长期脚本 |
# pip 换清华源(通用、长期稳定)
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# ESP-IDF 走乐鑫国内 CDN(安装前设置)
export IDF_GITHUB_ASSETS="dl.espressif.com/github_assets"
# git 走代理(按你自己的代理端口改)
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
# 用完取消
git config --global --unset http.proxy
git config --global --unset https.proxy
一、Arduino IDE 2.x
1.1 安装
# —— Windows(管理员 PowerShell) ——
winget install ArduinoSA.IDE.stable # 2.x 稳定版
# 或 winget install ArduinoSA.IDE.nightly # 抢先版
# —— Ubuntu / Debian ——
# 官方 deb 与 AppImage 版本更新更快,优先官网下载;仓库版通常较旧
sudo apt install arduino
# —— 通用:官网下载 AppImage(Linux,无需 root)——
# https://www.arduino.cc/en/software → Linux AppImage 64 bits
chmod +x arduino-ide_2.x.x_Linux_64bit.AppImage
./arduino-ide_2.x.x_Linux_64bit.AppImage --no-sandbox
# —— macOS ——
brew install --cask arduino-ide
# —— 命令行版(服务器/CI 用,无 GUI)——
# https://arduino.github.io/arduino-cli/latest/installation/
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
arduino-cli config init
arduino-cli core update-index
arduino-cli core install arduino:avr
1.2 添加 ESP32 / ESP8266 板卡支持
菜单路径:File → Preferences(首选项)→ Additional boards manager URLs(附加开发板管理器网址),填入(多个地址用英文逗号分隔或每行一个):
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
https://arduino.esp8266.com/stable/package_esp8266com_index.json
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
然后:Tools → Board → Boards Manager → 搜索 esp32 → 安装 "esp32 by Espressif Systems"。
ESP32 索引访问不畅时的处理思路(镜像域名会变,以下为方法而非固定地址):
# 思路 A:手动下载索引文件,起本地 HTTP 服务再把 URL 指向 127.0.0.1
curl -L -o package_esp32_index.json \
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
python3 -m http.server 8111 --directory .
# 然后在 Arduino 首选项里填 http://127.0.0.1:8111/package_esp32_index.json
# 索引里各工具包的 URL 仍指向 github,需一并替换或配合代理使用
# 思路 B:用公共加速前缀改写原始 URL(前缀域名以当时可用的为准)
# 例:https://<加速前缀>/https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
# 思路 C:直接用 Arduino CLI 走系统代理
export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890
arduino-cli core update-index --additional-urls \
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
1.3 板卡关键参数(ESP32 为例)
| 菜单项 | 常用值 | 说明 |
|---|---|---|
| Board | ESP32S3 Dev Module / ESP32 Dev Module / ESP32C3 Dev Module |
按你的芯片选,选错会下载失败或运行异常 |
| USB CDC On Boot | Enabled(S3/C3 用原生 USB 串口时) |
关掉的话 Serial 走外部 UART 芯片 |
| CPU Frequency | 240MHz (WiFi) |
想省电选 80/160 MHz |
| Flash Mode | QIO 80MHz |
一般不动 |
| Flash Size | 4MB (32Mb) / 16MB (128Mb) |
与实际颗粒一致,超出部分写不进去 |
| Partition Scheme | Default 4MB with spiffs / 16M Flash (3MB APP) |
程序放不下就换更大 APP 分区的方案 |
| PSRAM | OPI PSRAM(S3 带 PSRAM 的板) |
不打开就用不了那 8 MB PSRAM |
| Upload Speed | 921600 |
下载不稳定就降到 115200 |
| Port | 实际 COM 口 / /dev/ttyUSB0 |
为空说明驱动没装好 |
1.4 验证:点灯 + 打印芯片信息
// Arduino:Blink + 芯片信息打印(ESP32 / ESP8266 / AVR 通用写法)
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
delay(1000);
Serial.println("\n==== board info ====");
Serial.printf("CPU Freq : %u MHz\n", getCpuFrequencyMhz()); // AVR 无此函数,需删掉
Serial.printf("Flash Size : %u bytes\n", ESP.getFlashChipSize());
Serial.printf("Free Heap : %u bytes\n", ESP.getFreeHeap());
Serial.printf("Chip Model : %s rev %u\n", ESP.getChipModel(), ESP.getChipRevision());
Serial.printf("SDK Version : %s\n", ESP.getSdkVersion());
Serial.println("====================");
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
Serial.println("tick"); // 串口监视器应每 1 秒打印一次
}
⚠️ 注意:
getCpuFrequencyMhz()、ESP.getChipModel()是 ESP32 专有 API,Arduino Uno(AVR)上编译会报错,用 Uno 时把这些行删掉,只保留pinMode/digitalWrite/Serial.println。ESP.getFlashChipSize()在 ESP8266 上也可用于验证 Flash 是否虚标。
打开 Tools → Serial Monitor,右下角波特率选 115200(与代码一致),看到每秒一行 tick 即成功。
1.5 Arduino IDE 常见报错排查
| 现象 | 原因 | 解法 |
|---|---|---|
| Tools → Port 灰色不可选 | 驱动未装 / 数据线只有电源线 / 权限不足 | 见 0.3、0.4 节;Linux 确认 groups 里有 dialout 并已重登录 |
Failed to connect to ESP32: Timed out waiting for packet header |
芯片没进下载模式 | 按住 BOOT 键 → 点上传 → 出现 Connecting... 后松开 BOOT;或给 EN 对地接 10 µF 电容 |
A fatal error occurred: Wrong boot mode detected (0x13) |
GPIO0 未拉低 / strapping 引脚被外设占用 | 手动进下载模式;检查 GPIO0/2/12 上是否接了会改变电平的外设 |
Sketch too big(区溢出) |
分区方案 APP 空间不足 | 换 APP 更大的 Partition Scheme;关掉 Tools → USB CDC On Boot 之外的冗余功能;精简库 |
Compilation error: xxx.h: No such file or directory |
库没装或装错版本 | Sketch → Include Library → Manage Libraries 搜索安装;注意库与板卡架构是否匹配 |
| 下载成功但串口无输出 | 波特率不一致 / TX-RX 没交叉 / 用了外部串口口 | 监视器波特率改为代码里的值;换另一个 USB 口;S3/C3 打开 USB CDC On Boot |
编译时卡在 Downloading tools 十几分钟 |
网络访问索引/工具链超时 | 挂代理或按 1.2 的镜像思路处理;Arduino IDE 2.x 的下载目录在 ~/.arduino15/staging(Windows: %LOCALAPPDATA%\Arduino15\staging) |
exit status 1 且日志里出现 python 相关 |
系统 Python 与工具链冲突 | Linux 上确认 python3 存在;Windows 上删掉 PATH 里多余的 Python 软链接(Microsoft Store 版最容易冲突) |
| 上传完板子反复重启 | 供电不足 / 代码里访问了非法地址触发 panic | 换 USB 口或外接 5 V 电源;打开串口日志看 panic 行,用 ESP32 Exception Decoder 或 addr2line 定位 |
二、PlatformIO(推荐主力)
2.1 安装
# 方式一(推荐):VS Code 扩展
# 扩展市场搜索 "PlatformIO IDE"(作者 PlatformIO),安装后重启 VS Code,左侧出现外星人图标
# 方式二:仅装 CLI(服务器 / CI / 不爱用 VS Code)
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade platformio
pio --version
# 把常用命令做成短别名,写进 ~/.bashrc 或 ~/.zshrc
cat >> ~/.bashrc <<'EOF'
alias pio='platformio'
alias piob='pio run -t upload'
alias piom='pio device monitor'
EOF
source ~/.bashrc
⚠️ 注意:PlatformIO 首次启动会下载平台包与工具链,体积可达数 GB,务必在稳定网络下等待完成。Windows 上若卡在
Installing PlatformIO Core,多半是杀软拦截或 Python 冲突,尝试以管理员运行 VS Code 一次。
2.2 项目目录结构
my_project/
├── platformio.ini # 唯一需要手写的配置文件,一切依赖写在这里
├── src/ # 源码(.c / .cpp / .ino 都可以)
│ └── main.cpp
├── include/ # 头文件
├── lib/ # 本地私有库(每个库一个子目录)
│ └── my_driver/
│ ├── my_driver.h
│ └── my_driver.cpp
├── test/ # 单元测试(pio test)
└── .pio/ # 构建产物(务必加进 .gitignore)
# .gitignore 必须包含
printf '.pio/\n.vscode/\n' >> .gitignore
# 从零初始化一个项目(交互式选板卡)
pio project init --board esp32-s3-devkitc-1 --project-option="framework=arduino"
2.3 platformio.ini 完整注释示例
; ===== 通用区:所有环境共享 =====
[env]
platform = espressif32
framework = arduino
; 串口监视器波特率,必须和代码里 Serial.begin() 一致
monitor_speed = 115200
; 监视器端口,不写则自动探测;多设备时强烈建议写死
; monitor_port = /dev/ttyUSB0
; 监视器过滤器:esp32_exception_decoder 可直接把 panic 地址翻译成代码行
monitor_filters = esp32_exception_decoder, time, colorize
; 打开后串口收到 DTR/RTS 变化不复位板子(某些场景需要)
; monitor_rts = 0
; monitor_dtr = 0
; 依赖库,支持三种写法:
; 1) 库名 → 从 PIO 注册表拉最新稳定版
; 2) 库名@^版本号 → 语义化版本锁定
; 3) https://git 地址 → 直接用仓库
lib_deps =
adafruit/Adafruit SSD1306@^2.5.7
paulstoffregen/OneWire@^2.3.8
knolleary/PubSubClient@^2.8
https://github.com/bblanchon/ArduinoJson.git#v7.0.4
; 编译期宏,等同于代码里的 #define
build_flags =
-D CORE_DEBUG_LEVEL=3 ; ESP32 日志级别 0=None 1=Error 2=Warn 3=Info 4=Debug 5=Verbose
-D MY_WIFI_SSID=\"myap\" ; 字符串宏要转义引号
-D BAUD=115200
-Wall -Wextra ; 打开更多编译警告
; ===== ESP32-S3 开发环境 =====
[env:esp32-s3-devkitc-1]
board = esp32-s3-devkitc-1
; board 已隐含 platform/framework,这里重复声明只为演示,不写也行
framework = arduino
monitor_speed = 115200
upload_speed = 921600
; upload_port 多设备时写死端口,Windows 写 COM3,Linux 写 /dev/ttyUSB0
upload_port = /dev/ttyACM0
lib_deps =
${env.lib_deps} ; 继承通用区的依赖
adafruit/Adafruit NeoPixel@^1.12.0
build_flags =
${env.build_flags}
-D ARDUINO_USB_CDC_ON_BOOT=1 ; S3 用原生 USB 串口
-D BOARD_HAS_PSRAM=1 ; 启用 PSRAM
board_build.arduino.memory_type = qio_opi ; Flash + PSRAM 均为 OPI 模式(S3 常见)
board_build.partitions = default_16MB.csv ; 16MB Flash 的分区表
board_upload.flash_size = 16MB
; ===== ESP32-C3 量产小板 =====
[env:esp32-c3-devkitm-1]
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 460800
; 使用 esptool 之外的下载协议(C3/S3 支持 esp-builtin 走 USB-OTG)
upload_protocol = esptool
build_flags =
${env.build_flags}
-D ARDUINO_USB_CDC_ON_BOOT=1
; ===== STM32 蓝丸:用 ST-Link 下载并调试 =====
[env:bluepill_f103c8]
platform = ststm32
board = bluepill_f103c8
framework = arduino ; 也可改为 stm32cube / cmsis / libopencm3
upload_protocol = stlink
debug_tool = stlink
monitor_speed = 115200
build_flags =
-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC ; 启用 USB 虚拟串口
-D PIO_FRAMEWORK_ARDUINO_STANDARD_LIB
-D HSE_VALUE=8000000U ; 蓝丸外部晶振 8 MHz,填错则时钟全错
; ===== Arduino Uno(AVR)=====
[env:uno]
platform = atmelavr
board = uno
framework = arduino
monitor_speed = 9600
upload_port = /dev/ttyACM0
; ===== Raspberry Pi Pico(RP2040)=====
[env:pico]
platform = raspberrypi
board = pico
framework = arduino
upload_protocol = picotool ; 或 mbed / cmsis-dap(用另一块 Pico 当调试器)
monitor_speed = 115200
2.4 常用命令速查
# —— 构建 ——
pio run # 编译当前默认环境(或全部环境)
pio run -e esp32-s3-devkitc-1 # 只编译指定环境
pio run -t clean # 清理构建产物
pio run -v # 显示完整编译命令(查宏定义问题时很有用)
# —— 下载 ——
pio run -t upload # 编译并烧录
pio run -t upload -e uno # 指定环境
pio run -t nobuild_upload # 跳过编译直接烧录
pio run -t upload --upload-port /dev/ttyUSB0 # 临时指定端口
pio run -t uploadfs # 烧录文件系统(LittleFS/SPIFFS)镜像
pio run -t erase # 全片擦除 Flash
# —— 串口 ——
pio device monitor # 打开串口监视器(Ctrl+C 退出)
pio device list # 列出所有串口设备及 VID/PID(找端口用这个最快)
pio device monitor -b 115200 -p /dev/ttyUSB0 --eol LF
# —— 库管理 ——
pio pkg search "ssd1306" # 搜索库
pio pkg search "mqtt" --platform "espressif32" # 限定平台搜索
pio pkg install --library "knolleary/PubSubClient@^2.8" # 安装并写进 platformio.ini
pio pkg list # 列出当前项目依赖
pio pkg outdated # 检查可升级的依赖
pio pkg update # 更新依赖
pio pkg uninstall "OneWire"
# —— 平台与板卡 ——
pio platform install espressif32
pio platform list
pio boards esp32-s3 # 按关键词查板卡 ID
pio settings get # 查看全局配置
# —— 调试(需 debug_tool,ST-Link / J-Link / esp-builtin)——
pio debug # 启动调试会话(VS Code 里按 F5 更好用)
pio test -e native # 在 PC 上跑单元测试(环境 platform = native)
# —— 项目信息 ——
pio project config # 打印解析后的完整配置(排查继承问题神器)
pio run -t sizedata # 查看固件体积
2.5 库依赖管理要点
| 写法 | 含义 | 何时用 |
|---|---|---|
Adafruit SSD1306 |
按名字模糊匹配 | 快速试,但可能装到同名不同库 |
adafruit/Adafruit SSD1306 |
指定作者,精确 | 推荐 |
adafruit/Adafruit SSD1306@^2.5.7 |
语义化版本锁定 | 做项目必用,避免半年后编译不过 |
adafruit/Adafruit SSD1306@2.5.7 |
精确版本 | 需要完全可复现时 |
https://github.com/xxx/yyy.git#v1.2.3 |
Git 仓库 + tag/分支/commit | 库未发布到注册表时用,务必钉 commit |
file:///abs/path/to/lib / symlink://../lib |
本地路径 | 同时开发库和项目时 |
依赖锁定的正确姿势:
# 生成的 .pio/libdeps/<env>/ 里有实际安装的版本
# 把版本写回 platformio.ini 就是"锁定";更彻底的做法:
pio pkg list --json-output > pio-deps-$(date +%F).json # 留档
⚠️ 注意:
lib_deps只写库名不写版本是最常见的团队协作地雷。一年后新同事拉代码,库 API 已不兼容,编译报错且难以定位。所有依赖都带@^版本号或钉死 commit。
2.6 验证:helloworld + 读芯片 ID
# 1) 确认板子被识别
pio device list
# 期望输出包含 /dev/ttyUSB0(或 COM3),并带 VID/PID 与描述
# 2) 建工程并跑通
mkdir -p ~/pio_hello/src && cd ~/pio_hello
cat > platformio.ini <<'EOF'
[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
EOF
cat > src/main.cpp <<'EOF'
#include <Arduino.h>
void setup() {
Serial.begin(115200);
delay(1000);
Serial.printf("Chip : %s rev %u\n", ESP.getChipModel(), ESP.getChipRevision());
Serial.printf("Cores : %u Freq: %u MHz\n", ESP.getChipCores(), getCpuFrequencyMhz());
Serial.printf("Flash : %u MB\n", ESP.getFlashChipSize() / 1024 / 1024);
Serial.printf("PSRAM : %u bytes\n", ESP.getPsramSize());
Serial.printf("Heap : %u bytes free\n", ESP.getFreeHeap());
Serial.printf("MAC : %s\n", WiFi_mac_to_string().c_str());
}
void loop() { delay(1000); }
EOF
上面最后一行需要额外实现,用下面这版更省事(直接打印 MAC 字节):
void setup() {
Serial.begin(115200);
delay(1000);
Serial.printf("Chip : %s rev %u\n", ESP.getChipModel(), ESP.getChipRevision());
Serial.printf("Cores : %u Freq : %u MHz\n", ESP.getChipCores(), getCpuFrequencyMhz());
Serial.printf("Flash : %u MB\n", ESP.getFlashChipSize() / 1024 / 1024);
Serial.printf("PSRAM : %u bytes\n", ESP.getPsramSize());
Serial.printf("Heap : %u bytes free\n", ESP.getFreeHeap());
uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_WIFI_STA);
Serial.printf("MAC : %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void loop() { delay(1000); }
pio run -t upload && pio device monitor
# 期望看到芯片型号、频率、Flash 大小、MAC 地址,说明工具链/驱动/下载链路全通
2.7 PlatformIO 常见报错排查
| 现象 | 原因 | 解法 |
|---|---|---|
Error: Please specify upload_port`` |
没探测到串口 | pio device list 确认;板子驱动见 0.3/0.4;多设备时在 ini 里写死 upload_port |
Error: Unknown board ID 'xxx' |
板卡名写错 | pio boards esp32 查正确 ID |
fatal error: xxx.h: No such file or directory |
库未装 / 库与平台不兼容 | pio pkg search 后加进 lib_deps;检查库是否支持该 framework |
undefined reference to ... |
只装了头文件没装实现,或 C/C++ 混编没加 extern "C" |
确认库完整;C 头在 C++ 里用 extern "C" { } 包住 |
PlatformIO Core is not responding |
Core 进程崩溃,多因杀软/权限 | VS Code 里 PlatformIO: Rebuild IntelliSense Index;或 pio --version 确认 CLI 正常 |
| 编译通过但板子无反应 | 程序没跑起来 / 分区表不对 | 看串口日志;确认 board_build.partitions 与 Flash 实际大小匹配 |
libdeps 下载失败 |
网络 | 挂代理;或手动下载库放到 lib/ 目录(本地目录不需要下载) |
| 中文路径导致编译报奇怪错误 | 路径含中文/空格 | 项目路径改成纯英文无空格 |
pio run 报 Permission denied |
Linux 串口权限 | 见 0.4 节 |
| 构建很慢(每次全量) | 正常;或 .pio 被误删 |
可开启 build_cache_dir = ~/.platformio/.cache 复用编译缓存 |
三、ESP-IDF(乐鑫官方框架)
3.1 安装
# ===== Windows:最简单,用官方离线/在线安装包 =====
# https://dl.espressif.com/dl/esp-idf/ 下载 ESP-IDF Tools Offline / Online Installer
# 装完桌面会有 "ESP-IDF 5.x PowerShell" / "CMD" 快捷方式,用它启动即自动配好环境变量
# ===== Linux / macOS:git 克隆 + install.sh =====
sudo apt update
sudo apt install git wget flex bison gperf python3 python3-pip python3-venv \
cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
mkdir -p ~/esp && cd ~/esp
git clone -b v5.4 --recursive https://github.com/espressif/esp-idf.git
# 子模块拉取失败时重复执行(很常见,网络问题)
cd ~/esp/esp-idf && git submodule update --init --recursive
# 走乐鑫国内 CDN 加速工具链下载
export IDF_GITHUB_ASSETS="dl.espressif.com/github_assets"
cd ~/esp/esp-idf
./install.sh esp32 esp32s2 esp32s3 esp32c3 esp32c6 # 只装你需要的目标,能省几 GB
# ===== 备用:pip 装的 esp-idf 只提供工具管理,源码仍需上面的 git 仓库 =====
# ===== VS Code 插件(推荐配合)=====
# 扩展市场搜 "Espressif IDF",安装后 Ctrl+Shift+P → "ESP-IDF: Configure ESP-IDF extension"
每次开新终端都要导入环境:
# 一次性把别名写进 shell 配置
echo "alias get_idf='. \$HOME/esp/esp-idf/export.sh'" >> ~/.bashrc
source ~/.bashrc
# 之后每次开发前执行
get_idf
idf.py --version # 验证
3.2 完整开发流程
# 1) 从示例建工程(比 mkdir 省事,examples 里有几百个可直接抄的模板)
cd ~/esp
cp -r $IDF_PATH/examples/get-started/hello_world ./my_app
cd my_app
# 2) 设定目标芯片(只影响编译目标,可随时改;改后建议 fullclean)
idf.py set-target esp32s3
# 常见值:esp32 / esp32s2 / esp32s3 / esp32c3 / esp32c6 / esp32h2 / esp32p4
# 3) 图形化配置(会生成/更新 sdkconfig)
idf.py menuconfig
# 4) 编译
idf.py build
# 只看大小:idf.py size / idf.py size-components / idf.py size-files
# 5) 烧录(全片 + 所有分区),-p 指定端口,-b 指定波特率
idf.py -p /dev/ttyUSB0 -b 921600 flash
# 只烧 app 分区(迭代调试快很多)
idf.py -p /dev/ttyUSB0 app-flash
# 6) 打开串口监视器
idf.py -p /dev/ttyUSB0 monitor
# 退出:Ctrl+]
# 7) 一条龙:烧录 + 监视
idf.py -p /dev/ttyUSB0 flash monitor
# 8) 其他常用子命令
idf.py fullclean # 删除整个 build 目录(换 target 或改了大配置后必做)
idf.py reconfigure # 强制重跑 CMake 配置
idf.py erase-flash # 全片擦除
idf.py partition-table # 打印当前分区表
idf.py efuse-summary # 查看 eFuse 状态(安全特性)
idf.py monitor --print-filter "wifi:*,esp_netif:*" # 只打印指定 tag 的日志
3.3 menuconfig 关键配置项
idf.py menuconfig 里的路径按 / 搜索关键词最快(按 / 后输入关键词回车即可跳转)。
| 配置路径 | 作用 | 典型值 / 建议 |
|---|---|---|
Serial flasher config → Flash size |
Flash 容量 | 与颗粒一致(4 MB / 8 MB / 16 MB)。设大于实际会导致写入越界失败 |
Serial flasher config → Flash SPI mode |
QIO / DIO / OPI | 默认 QIO;S3 带 OPI PSRAM 时用对应模式 |
Serial flasher config → Flash SPI speed |
40 / 80 / 120 MHz | 默认 80 MHz,稳定性差就降 40 MHz |
Serial flasher config → Default baud rate |
下载波特率 | 921600 快但不稳;失败降 115200 |
Component config → ESP PSRAM → Support for external PSRAM |
PSRAM 使能 | 带 PSRAM 的板必须开,否则 heap_caps_malloc(MALLOC_CAP_SPIRAM) 失败 |
Component config → ESP PSRAM → SPI RAM config → Mode |
QUAD / OCT | S3 用 OCT(OPI);ESP32 用 QUAD(QSPI) |
Component config → ESP System Settings → CPU frequency |
CPU 频率 | 80 / 160 / 240 MHz;功耗敏感选 160 |
Component config → ESP System Settings → Task Watchdog |
看门狗 | 调试阶段可关(不推荐长期关) |
Component config → Partition Table → Partition Table |
分区表 | Single factory app, no OTA / Factory app, two OTA definitions / Custom partition table CSV |
Component config → Log output → Default log verbosity |
日志级别 | 开发 Debug/Verbose,量产 Warning/Error(日志本身拖慢速度且占 Flash) |
Component config → Log output → Color |
日志颜色 | Always(好看)或 Never(重定向到文件时) |
Component config → FreeRTOS → Kernel tick rate |
系统 tick | 默认 100 Hz;需要更精细延时可改 1000 Hz(增加开销) |
Component config → Wi-Fi → WiFi Task Core |
WiFi 任务跑在哪个核 | 双核场景可固定到 core 0,把应用留给 core 1 |
Component config → ESP-TLS / mbedTLS |
TLS 相关 | 做 HTTPS/MQTT-TLS 时按需开启证书包 |
Component config → Hardware Settings → Sleep Config |
功耗 | 电池项目要配 |
Security features(需目标支持) |
Secure Boot / Flash Encryption | 开发阶段切勿开启,eFuse 不可逆 |
自定义分区表示例(partitions.csv,然后在 menuconfig 里选 Custom partition table CSV 并填文件名):
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x200000,
app1, app, ota_1, 0x210000, 0x200000,
spiffs, data, spiffs, 0x410000, 0x3F0000,
⚠️ 注意:
app分区的 Offset 必须 0x10000(64 KB)对齐。改分区表后 OTA 数据可能失效,务必idf.py erase-flash再flash。
3.4 验证:hello_world + 崩溃定位
/* main/hello_world_main.c —— 官方示例改一行,加芯片信息打印 */
void app_main(void)
{
printf("Hello world!\n");
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), WiFi%s%s, silicon revision %d, ",
CONFIG_IDF_TARGET, chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "",
chip_info.revision);
unsigned major_rev = 0;
esp_flash_init(NULL);
printf("%uMB %s flash\n", 0u, "external");
printf("Minimum free heap size: %u bytes\n", esp_get_minimum_free_heap_size());
fflush(stdout);
vTaskDelay(pdMS_TO_TICKS(2000));
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}
崩溃时(Guru Meditation Error / abort())用地址翻译工具定位代码行:
# IDF 自带:把 monitor 里的 backtrace 地址喂给它
idf.py monitor | tee crash.log
# 复制 backtrace 里的 0x4xxxxxxx 地址后:
xtensa-esp32s3-elf-addr2line -pfiaC -e build/hello_world.elf 0x4200abcd 0x4037cdef
# 或者直接让 monitor 自动解码(ESP-IDF 5.x 默认已开启 panic 地址解码)
3.5 ESP-IDF 常见报错排查
| 现象 | 原因 | 解法 |
|---|---|---|
idf.py: command not found |
没导入环境 | 执行 . $HOME/esp/esp-idf/export.sh(或 get_idf) |
CMake Error: The following variables are used in this project... |
换过 target 没清 build | idf.py fullclean && idf.py set-target <chip> && idf.py build |
A fatal error occurred: Failed to connect to ESP32 |
下载模式/串口问题 | 按住 BOOT 再执行;降波特率 -b 115200;检查端口 |
idf.py menuconfig 打不开(乱码 / 报错) |
终端太小或缺少 ncurses | 终端窗口拉大;Linux sudo apt install libncurses-dev 后重装工具链 |
编译报 esp_psram.h: No such file |
PSRAM 未使能 | menuconfig 打开 PSRAM 支持 |
region 'flash' overflowed |
固件超出 app 分区 | 换更大 app 分区的分区表;关日志;-Os 优化:CONFIG_COMPILER_OPTIMIZATION_SIZE=y |
E (xxx) spi_flash: Detected size(4096k) smaller than the size in the binary image header |
menuconfig 的 Flash size 大于实际 | 改回实际值;必要时 esptool.py flash_id 确认真实容量 |
Brownout detector was triggered |
供电不足 | 换电源/USB 口;或在 menuconfig 里调低 Brownout 阈值(治标不治本) |
| 下载后串口乱码 | 波特率不对 / 晶振配置错 | 监视器 115200;检查 CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ 与实际晶振 |
install.sh 卡在下载 xtensa-esp32-elf |
网络 | export IDF_GITHUB_ASSETS="dl.espressif.com/github_assets" 后重跑 |
四、STM32:CubeIDE / CubeMX / OpenOCD
4.1 STM32CubeIDE 与 CubeMX
# ===== 安装 =====
# STM32CubeIDE(含 CubeMX + GCC + GDB + 调试):ST 官网搜索 STM32CubeIDE,需注册 ST 账号下载
# Linux 解压安装:
chmod +x st-stm32cubeide_*.sh
sudo ./st-stm32cubeide_*.sh
# Ubuntu 上常缺的依赖
sudo apt install libncurses5 libusb-1.0-0-dev
# 独立 CubeMX(只想生成代码不想装 Eclipse)
# ST 官网 → STM32CubeMX → 下载 → java -jar SetupSTM32CubeMX-*.exe
CubeMX 生成项目的关键步骤(顺序很重要):
- File → New Project → 选芯片型号(如
STM32F103C8Tx),别选错封装。 - Pinout 视图:配置
RCC → HSE = Crystal/Ceramic Resonator;SYS → Debug = Serial Wire(这一步忘了,下次就下不进程序)。 - Clock Configuration:HSE 8 MHz → PLL →
HCLK = 72 MHz(F103 上限),回车让工具自动解算。 - Connectivity / Timers / ADC 里按需开外设。
- Project Manager → 设置工程名与路径(英文无空格)→ Toolchain/IDE 选
STM32CubeIDE(或Makefile想用 CLI)。 - Code Generator 里勾选
Generate peripheral initialization as a pair of .c/.h files(外设代码分文件,好维护)。 Generate Code。
⚠️ 注意:CubeMX 生成的代码只在
/* USER CODE BEGIN */与/* USER CODE END */之间保留手写内容。Regenerate会覆盖其他地方,写代码务必放进 USER CODE 块里。
验证:在 main.c 的 while(1) 里加翻转,观察板载 LED(多数蓝丸是 PC13,低电平点亮):
/* main.c —— 放在 while(1) 的 USER CODE BEGIN WHILE 里 */
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_UART_Transmit(&huart1, (uint8_t*)"hello stm32\r\n", 13, HAL_MAX_DELAY);
HAL_Delay(500);
/* USER CODE END WHILE */
}
4.2 OpenOCD + ST-Link 烧录与调试
# ===== 安装 =====
# Linux
sudo apt install openocd gdb-multiarch stlink-tools
# macOS
brew install open-ocd stlink arm-none-eabi-gdb
# Windows:用 ST 的 STM32CubeProgrammer,或用 MSYS2 / xpack 提供的 OpenOCD
# xpack 版(推荐):https://github.com/xpack-dev-tools/openocd-xpack/releases
# ===== 确认调试器被识别 =====
lsusb | grep -i "st-link\|0483"
st-info --probe # stlink-tools 提供,能读出芯片信息即成功
# 期望输出类似:Found 1 stlink programmers ... chip id: 0x410 (STM32F1xx medium-density)
用 OpenOCD 烧录:
# F103 蓝丸(stlink + target 脚本名按芯片换)
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \
-c "program build/my_app.elf verify reset exit"
# F4 系列
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
-c "program build/my_app.bin 0x08000000 verify reset exit"
# 查看所有可用脚本
ls /usr/share/openocd/scripts/target/ | grep stm32
ls /usr/share/openocd/scripts/interface/ | grep stlink
起 GDB server 做源码级调试:
# 终端 1:启动 OpenOCD(保持运行)
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg
# 终端 2:GDB 连接(默认端口 3333)
arm-none-eabi-gdb build/my_app.elf
(gdb) target extended-remote localhost:3333
(gdb) monitor reset halt
(gdb) load
(gdb) break main
(gdb) continue
(gdb) info registers
(gdb) x/16xw 0x20000000 # 查看 SRAM 起始处
(gdb) monitor flash info 0 # 查看 Flash 信息
PlatformIO 里则只需在 platformio.ini 写 debug_tool = stlink 然后按 F5。
4.3 st-flash 命令行(最轻量的烧录方式)
# 查芯片
st-info --probe
st-info --flash --size # Flash 大小(单位字节)
st-info --chipid
st-info --serial
# 烧录(.bin 需带地址 0x08000000,.elf 自动解析)
st-flash write firmware.bin 0x08000000
st-flash write firmware.elf 0x08000000
st-flash --reset write firmware.bin 0x08000000
# 读取 Flash(备份/固件提取)
st-flash read dump.bin 0x08000000 0x10000 # 读 64 KB
# 擦除
st-flash erase
# 起 GDB server(端口 4242)
st-util
⚠️ 注意:
st-flash对设置了 RDP Level 1 读保护的芯片会失败(Failed to read memory/unknown chip id)。解除需全片擦除(st-flash erase可能也要先改 option bytes),且解除会清空 Flash,数据不可恢复。
4.4 串口 ISP 下载(无调试器时)
原理:把 BOOT0 拉高(接 3.3 V)、BOOT1 拉低,复位后芯片进系统 Bootloader,通过 USART1(PA9/PA10)下载。
| 板卡 | BOOT0 跳线位置 | 操作顺序 |
|---|---|---|
| 蓝丸 STM32F103C8T6 | 板边两个黄色跳线,标 BOOT0 / BOOT1 |
BOOT0 跳到 1 → 按 RESET → 下载 → BOOT0 跳回 0 → 再按 RESET 运行 |
| 正点原子/野火板 | BOOT0 排针 + B0 接 3.3 V |
同上 |
| Nucleo / Discovery | 一般不用(板载 ST-Link) | — |
# stm32flash(Linux / macOS,F1/F4 支持较好)
sudo apt install stm32flash
# Windows 图形工具:FlyMCU(mcuisp 的老牌国产工具,仅 Windows,选"编程后执行")
# 查看设备
stm32flash /dev/ttyUSB0
# 期望输出芯片型号与 Flash 大小;若报 "Failed to init device",说明没进 Bootloader 模式
# 烧录
stm32flash -w firmware.bin -v -g 0x08000000 /dev/ttyUSB0
# -w 写入 -v 校验 -g 从指定地址开始执行 -b 波特率(默认 57600)
# 读取
stm32flash -r dump.bin -S 0x08000000 -s 65536 /dev/ttyUSB0
# 解除读保护(会清空 Flash!)
stm32flash -k /dev/ttyUSB0
# ESP32 / Raspberry Pi 也能当 ISP 下载器(stm32flash 的 GPIO 变体)
FlyMCU 的关键设置(Windows 图形界面):
| 选项 | 值 |
|---|---|
| 串口 | 实际 COM 口 |
| 波特率 | 76800(FlyMCU 默认,可试 115200) |
| 选项字节区 | 一般不要勾"编程到配置",容易锁死芯片 |
| 编程后执行 | 勾选 |
| 校验 / 写入 | 都勾 |
4.5 STM32 常见报错排查
| 现象 | 原因 | 解法 |
|---|---|---|
Error: No Device Found / st-info --probe 无输出 |
ST-Link 驱动没装 / 接线松 / 目标板没供电 | 装 STSW-LINK009;检查 SWDIO/SWCLK/GND/VCC 四根线;目标板单独供电 |
Error: init mode failed (unable to connect to the target) |
SYS → Debug 没配成 Serial Wire,或 SWD 引脚被复用为 GPIO | CubeMX 里 SYS → Debug = Serial Wire 重新生成;或用串口 ISP 擦掉重烧 |
WARN: UNEXPECTED idcode |
芯片型号选错 / 是国产替代(GD32/CH32) | 换 target 脚本;GD32 需用适配版 OpenOCD |
st-flash: unknown chip id |
芯片被读保护 / 处于低功耗模式 / 接线错 | 按住 RESET 再执行;st-flash erase(会清 Flash);串口 ISP 解除保护 |
| 下载成功但程序不跑 | BOOT0 还在 1;或复位电路问题 |
BOOT0 跳回 0 并复位 |
| 时钟不对(串口乱码、延时不准) | HSE_VALUE 与实际晶振不符 |
CubeMX 里按板上实际晶振填(蓝丸常见 8 MHz,部分克隆是 12 MHz,以丝印为准) |
HAL_UART_Transmit 卡死 |
外设时钟未使能 / 引脚配置错 | 检查 CubeMX 里 USART 引脚与 GPIO Alternate Function |
| 程序跑一会就死 | 栈溢出 / 中断优先级配置错 | 增大 stack_size;检查 FreeRTOS 的 configTOTAL_HEAP_SIZE |
CubeIDE 编译报 make: *** No rule to make target |
路径含中文/空格 | 工程路径改纯英文 |
五、树莓派
5.1 烧录系统
# ===== 官方工具 Raspberry Pi Imager(跨平台,最省事)=====
# Windows: winget install RaspberryPiFoundation.RaspberryPiImager
# Linux: sudo apt install rpi-imager
# macOS: brew install --cask raspberry-pi-imager
# Imager 里的关键设置(点右下角齿轮进"高级设置",或按 Ctrl+Shift+X):
# ✔ 设置主机名 → raspberrypi.local
# ✔ 开启 SSH → 选"使用密码认证"或"只允许公钥认证"
# ✔ 设置用户名与密码 → 注意:新版系统不再有默认 pi/raspberry
# ✔ 配置无线局域网 → 填 SSID / 密码 / 国家 CN
# ✔ 设置区域设置 → 时区 Asia/Shanghai、键盘布局 us
# ✔ 持久化保存设置 → 下次烧录复用
# ===== 命令行烧录(Linux / macOS)=====
# 先确认设备名,别写错盘符
lsblk # Linux
diskutil list # macOS
# 方法 A:dd(简单粗暴,写错盘符会毁数据)
sudo dd if=2024-11-19-raspios-bookworm-armhf.img of=/dev/sdX bs=4M conv=fsync status=progress
sync
# 方法 B:rpi-imager --cli(支持高级选项,推荐)
sudo rpi-imager --cli --os-list # 列出可选系统
sudo rpi-imager --cli \
--os "Raspberry Pi OS (64-bit)" \
--sd-card /dev/sdX \
--options ssh.enabled=1,ssh.passwordAuthentication=1,ssh.username=pi,ssh.password=xxx,\
wifi.ssid=MyAP,wifi.password=xxx,wifi.country=CN,\
hostname=raspberrypi.local,locale.timezone=Asia/Shanghai,locale.keymap=us
⚠️ 注意:
dd的of=/dev/sdX写错会直接抹掉你电脑的硬盘。执行前务必用lsblk反复确认容量与分区,sdX不带数字(写整盘而非分区)。
5.2 无头(headless)配置
Imager 已能预配一切;若用 dd 烧录,则在 boot 分区(Windows 下可见的那个 FAT32 分区)手动放文件:
# 挂载 boot 分区后(Linux 自动挂载到 /media/$USER/bootfs 或 /run/media/...)
cd /media/$USER/bootfs
# 1) 开 SSH:放一个名为 ssh 的空文件(无扩展名)
touch ssh
# 2) 配 WiFi(Bookworm 及之后推荐用 NetworkManager 的 keyfile 方式)
# 传统 wpa_supplicant.conf 在新版系统可能不再被读取,优先用 Imager
cat > wpa_supplicant.conf <<'EOF'
country=CN
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
ssid="你的SSID"
psk="你的密码"
key_mgmt=WPA-PSK
}
EOF
# 3) 新版 Bookworm 的替代方案:NetworkManager 连接文件(放在 boot 分区根目录)
# 文件名会被系统自动拷贝为 /etc/NetworkManager/system-connections/*.nmconnection
cat > mywifi.nmconnection <<'EOF'
[connection]
id=mywifi
type=wifi
autoconnect=true
[wifi]
mode=infrastructure
ssid=你的SSID
[wifi-security]
auth-alg=open
key-mgmt=wpa-psk
psk=你的密码
[ipv4]
method=auto
[ipv6]
method=auto
EOF
# 4) 用户配置(Bookworm 起,userconf.txt 取代了默认 pi 用户)
# 生成密码哈希(在任意 Linux 上执行,树莓派自带的 openssl 也行)
echo 'mypassword' | openssl passwd -6 -stdin
# 把输出写进 userconf.txt,格式 用户名:哈希
echo 'pi:$6$xxxxxxxxxxxxxxxx$yyyyyyyy...' > userconf.txt
sync && umount /media/$USER/bootfs
首次登录:
# 方式一:mDNS(同局域网,无需知道 IP)
ssh pi@raspberrypi.local # Windows 需装 Bonjour 或用 -o 直连 IP
# 默认密码由你自己在 Imager 里设定;老版本镜像默认是 pi / raspberry
# 方式二:找 IP
# 路由器后台看 DHCP 列表;或
ping -c1 raspberrypi.local
nmap -sn 192.168.1.0/24 # 扫描整个网段找树莓派(MAC 前缀通常是 B8:27:EB 或 DC:A6:32 或 E4:5F:01)
# 首次登录后必做
sudo apt update && sudo apt full-upgrade -y
sudo raspi-config
5.3 raspi-config 常用项
sudo raspi-config
# 命令行非交互模式(写进自动化脚本)
sudo raspi-config nonint do_ssh 0 # 0=开 1=关
sudo raspi-config nonint do_camera 0 # 启用摄像头(旧版;新版用 do_camera 0 或 libcamera 自动)
sudo raspi-config nonint do_i2c 0 # 启用 I2C
sudo raspi-config nonint do_spi 0 # 启用 SPI
sudo raspi-config nonint do_serial 2 # 0=登录shell 1=关闭 2=启用串口但不开登录shell
sudo raspi-config nonint do_onewire 0 # 启用 1-Wire
sudo raspi-config nonint do_expand_rootfs # 扩容文件系统到整张卡
sudo raspi-config nonint do_hostname mypi
sudo raspi-config nonint do_wifi_country CN
sudo raspi-config nonint get_config do_ssh # 查询当前值
sudo raspi-config nonint do_boot_behaviour B2 # B1=进桌面需登录 B2=进桌面自动登录 B3=CLI需登录 B4=CLI自动登录
启用 I2C/SPI 后必须确认设备节点存在:
ls /dev/i2c-* /dev/spidev*.* 2>/dev/null
lsmod | grep -E "i2c_dev|spi_bcm2835"
sudo apt install -y i2c-tools
i2cdetect -y 1 # 扫描 I2C 总线 1(老板是 0),看到设备地址即成功
5.4 GPIO 库选型
| 库 | 实现方式 | 支持板卡 | 性能 | 现状 / 推荐度 |
|---|---|---|---|---|
| RPi.GPIO | 直接操作 /dev/gpiomem 寄存器 |
仅树莓派(BCM283x/271x) | 中(软件 PWM,翻转约 100 kHz 量级) | 已停止维护,Pi 5 不支持,新项目别用 |
| gpiozero | 基于 lgpio(新)或 RPi.GPIO(旧)的高层封装 | 树莓派为主 | 低(面向教学,抽象多) | 适合教学与简单场景,API 最友好 |
| libgpiod / gpiod | 内核 GPIO character device(/dev/gpiochipN) |
通用 Linux(树莓派、Jetson、RK、全志) | 中高 | 新项目首选,官方推荐,跨板通用,有 C/Python 绑定 |
| lgpio | 直接寄存器访问,用户态 | 树莓派(含 Pi 5) | 高 | 需要精确时序时用;是 gpiozero 在新系统上的后端 |
| pigpio | 守护进程 + 寄存器,支持远距离网络控制 | 树莓派 | 高(硬件定时 PWM、波形生成) | 强大但 Pi 5 支持需额外处理 |
sysfs GPIO(/sys/class/gpio) |
老接口 | 通用 | 低 | 已废弃,新内核不再推荐 |
# 装库
sudo apt install -y python3-gpiozero python3-rpi.gpio python3-libgpiod gpiod python3-pigpio
# gpiod 命令行工具(排错神技)
gpioinfo # 列出所有 gpiochip 与引脚状态
gpiodetect # 列出 gpiochip
gpioget gpiochip0 17 # 读 GPIO17
gpioset gpiochip0 17=1 # 写 GPIO17 高电平(默认写完立即释放!)
gpioset --mode=wait gpiochip0 17=1 # 保持直到 Ctrl+C
gpiomon gpiochip0 17 # 监视引脚电平变化(查按键/中断很方便)
# 把用户加进 gpio 组,避免每次 sudo
sudo usermod -aG gpio $USER
三种库的最小点灯代码:
# ===== libgpiod(推荐,跨板通用)=====
# pip install gpiod (注意:v2.x 与 v1.x API 差异巨大,以下代码为 v2.x)
import gpiod, time
from gpiod.line import Direction, Value
chip = gpiod.Chip('/dev/gpiochip0')
req = chip.request_lines(
consumer="blink",
config={17: gpiod.LineSettings(direction=Direction.OUTPUT)}
)
for _ in range(10):
req.set_values({17: Value.ACTIVE})
time.sleep(0.5)
req.set_values({17: Value.INACTIVE})
time.sleep(0.5)
req.release()
# ===== gpiozero(最简单,教学首选)=====
from gpiozero import LED, Button
from signal import pause
led = LED(17)
btn = Button(2)
btn.when_pressed = led.on
btn.when_released = led.off
pause() # 阻塞等待事件
# ===== RPi.GPIO(老项目维护用,Pi 5 不支持)=====
import RPi.GPIO as GPIO, time
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
p = GPIO.PWM(17, 50) # 50 Hz 软件 PWM
p.start(7.5) # 占空比 7.5%(SG90 舵机中位)
try:
while True: time.sleep(1)
finally:
p.stop(); GPIO.cleanup()
5.5 远程与摄像头快速验证
# 摄像头(Bookworm 起统一用 libcamera,raspistill 已废弃)
libcamera-hello --list-cameras # 列出摄像头(这一步能识别就成功一半)
libcamera-still -o test.jpg # 拍一张
libcamera-vid -t 10000 -o test.h264 # 录 10 秒
sudo apt install -y python3-picamera2 # Python 接口(仅 64 位系统)
# 远程桌面
sudo apt install -y xrdp # Windows 远程桌面直连
sudo raspi-config nonint do_vnc 0 # 或 RealVNC
# 系统健康检查
vcgencmd measure_temp # CPU 温度
vcgencmd get_throttled # 0x0 才正常;非 0 说明曾欠压/过温降频
vcgencmd measure_volts core
dmesg | grep -i "under-voltage"
df -h && free -h
⚠️ 注意:
vcgencmd get_throttled返回值含义(常见位):0x0正常;0x50000曾欠压;0x50005当前正在欠压并降频。出现欠压说明电源不行,换 5 V/3 A(Pi 4)或 5 V/5 A(Pi 5)官方电源。
5.6 树莓派常见报错排查
| 现象 | 原因 | 解法 |
|---|---|---|
| 绿灯不闪 / 彩虹屏不动 | 镜像烧录失败 / SD 卡坏 / 电源不足 | 重烧镜像;换 SD 卡(Class 10 A1 以上);换电源 |
| 反复重启 | 电源电流不足(最常见) | 换 3 A(4B)/ 5 A(5)电源,别用电脑 USB 口 |
ssh: Could not resolve hostname raspberrypi.local |
没开 mDNS / 跨网段 | 用 IP 直连;Linux 装 avahi-daemon;Windows 装 Bonjour |
Permission denied (publickey) |
Imager 里选了"只允许公钥"但没导公钥 | 重烧并在 Imager 里填公钥,或选密码认证 |
Connection refused(SSH) |
SSH 未启用 | boot 分区放 ssh 空文件;或 Imager 高级设置里开 |
| WiFi 连不上 | 国家码/频段/密码错;5 GHz 兼容性 | 设 country=CN;先用 2.4 GHz;确认不是 WPA3-only 的 AP |
i2cdetect 全 -- |
I2C 未启用或接线错 | raspi-config 开 I2C;确认 SDA/SCL 与 GND 共地 |
RuntimeError: No access to /dev/mem |
权限 | sudo 运行;或 sudo usermod -aG gpio $USER 后重登录 |
This module can only be run on a Raspberry Pi |
用了 RPi.GPIO 但在 Pi 5 / 非树莓派上 | 换 gpiozero / libgpiod |
| 系统卡死 | SD 卡寿命/散热 | dmesg 看 I/O 错误;Pi 4/5 加散热片 |
六、NVIDIA Jetson
6.1 SDK Manager 刷机流程
# ===== 准备工作 =====
# 宿主机要求:Ubuntu 20.04 或 22.04 的 x86_64 物理机/虚拟机(虚拟机刷机成功率低,建议物理机)
# SDK Manager 下载:developer.nvidia.com/sdk-manager(.deb 包)
sudo apt install ./sdkmanager_*.deb
sdkmanager &
# 命令行模式(适合自动化与断网重来)
sdkmanager --cli install \
--logintype devzone \
--product Jetson \
--version 6.1 \
--targetos Linux \
--target PACK_NAME \
--flash all \
--select 'Jetson Orin Nano Super 8GB' \
--additionaloptions '{"targetImageFolder":"~/nvidia/nvidia_sdk"}'
刷机步骤要点:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 进入 Recovery 模式 | 断电 → 用跳线/杜邦线短接 FC REC 与 GND 引脚 → 上电(或用 Type-C 线连好后按住 REC 键再按 RST 键,松开 RST 再松开 REC) |
| 2 | 用 Type-C 线连宿主机 | 宿主机执行 lsusb,应看到 NVIDIA Corp. APX(0955:7323 等) |
| 3 | SDK Manager 检测 | 选目标板(Orin Nano Super / NX / AGX)与 JetPack 版本 |
| 4 | 下载并烧写 | 约 20–60 分钟,含 OS 镜像、CUDA、cuDNN、TensorRT、VPI、DeepStream |
| 5 | 板端完成初始化 | 首次启动会进 Ubuntu 设置界面,建用户、连 WiFi |
| 6 | 装 jetson-stats | sudo pip3 install -U jetson-stats 后重启 |
# 确认进 Recovery(宿主机上执行)
lsusb | grep -i nvidia
# 期望:Bus 001 Device 0xx: ID 0955:7323 NVIDIA Corp. APX
⚠️ 注意:Jetson Orin Nano 系列官方推荐用 NVMe SSD 启动,SD 卡版本性能和寿命都受限。刷机时 SDK Manager 里要选对存储设备(NVMe / eMMC / SD Card),选错会烧到错误的介质上。
6.2 环境确认
# ===== 版本总览(jetson-stats 提供,最直观)=====
jetson_release
# 输出包含:JetPack 版本、L4T 版本、内核、CUDA、cuDNN、TensorRT、VPI、OpenCV 版本
# 若命令不存在
sudo pip3 install -U jetson-stats
sudo systemctl restart jtop.service
sudo reboot
关键版本核验命令:
# L4T / JetPack 版本
cat /etc/nv_tegra_release
# 或
dpkg-query --show nvidia-l4t-core
# CUDA
nvcc --version # 编译器版本
nvidia-smi # Jetson 上是精简版输出(tegra 平台)
cat /usr/local/cuda/version.json 2>/dev/null
# cuDNN
dpkg -l | grep -i cudnn
# 或查头文件
grep -E "CUDNN_MAJOR|CUDNN_MINOR|CUDNN_PATCHLEVEL" /usr/include/cudnn_version.h 2>/dev/null
# 老版本在 /usr/include/cudnn.h
# TensorRT
dpkg -l | grep -i tensorrt
python3 -c "import tensorrt as trt; print(trt.__version__); print(trt.Builder(trt.Logger()))"
# OpenCV(是否带 CUDA 加速)
python3 -c "import cv2; print(cv2.__version__); print(cv2.getBuildInformation())" | head -30
# 系统资源与温度(实时)
jtop # 交互式,按 1-6 切页面,q 退出
tegrastats # 命令行实时:CPU/GPU/内存/温度/功耗
sudo nvpmodel -q # 当前功耗模式
sudo nvpmodel -m 0 # 切到最高性能模式(不同板编号含义不同,用 -q --verbose 看)
sudo jetson_clocks # 锁定最高频率(跑 benchmark 时用)
sudo jetson_clocks --restore # 恢复
# CUDA 示例编译验证
cd /usr/local/cuda/samples/1_Utilities/deviceQuery 2>/dev/null && sudo make && ./deviceQuery
6.3 常用环境配置
# Python 深度学习栈(Jetson 上务必用 NVIDIA 提供的 wheel,别直接 pip install torch)
# PyTorch for Jetson:forums.developer.nvidia.com 的官方安装贴给出对应 JetPack 版本的 whl
sudo apt install -y python3-pip python3-dev
python3 -m pip install -U pip
# 例(版本以官方贴为准,JetPack 6.x + Python 3.10):
# wget https://developer.download.nvidia.com/compute/redist/jp/v61/pytorch/torch-*.whl
# python3 -m pip install torch-*.whl
# 容器化(推荐,避免污染系统环境)
sudo apt install -y nvidia-container docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# 运行带 GPU 的容器
docker run --runtime nvidia --gpus all -it --rm nvcr.io/nvidia/l4t-base:r36.4.0
# jetson-containers 项目提供一键构建各种 AI 容器(Ultralytics/ROS/LLM 等)
git clone https://github.com/dusty-nv/jetson-containers
cd jetson-containers && ./install.sh
# 验证 GPU 能在容器里用
docker run --runtime nvidia --rm nvcr.io/nvidia/l4t-base:r36.4.0 nvidia-smi
6.4 Jetson 常见报错排查
| 现象 | 原因 | 解法 |
|---|---|---|
宿主机 lsusb 看不到 NVIDIA APX |
没进 Recovery / Type-C 线是充电线 | 重做 Recovery 短接步骤;换数据线 |
SDK Manager 卡在 Flash Jetson OS |
虚拟机 USB 掉线 / 权限 | 用物理机;sudo apt install libgconf-2-4;给 /dev/bus/usb 权限 |
| 刷完不开机,无 HDMI 输出 | 显示器 EDID 问题 / 镜像没烧对 | 换显示器或先接串口调试;重刷并确认目标存储设备 |
jetson_release: command not found |
没装 jetson-stats | sudo pip3 install -U jetson-stats 后重启 |
nvcc --version 无输出 |
CUDA 未加入 PATH | export PATH=/usr/local/cuda/bin:$PATH 并写进 ~/.bashrc |
import tensorrt 报 libnvinfer.so 找不到 |
环境变量/版本不匹配 | 确认安装的 TensorRT 版本与 JetPack 对应;ldconfig |
| 磁盘空间不足 | JetPack 全套约 20 GB+ | 用 NVMe;sudo apt clean;删 /usr/src/ 下用不到的内核源码与 samples |
| 频繁过热降频 | 散热不足 | 装风扇;jtop 看温度;sudo jetson_clocks 反而会更热,慎用 |
pip install torch 装完 import 报错 |
装了 x86 的 wheel | 必须用 NVIDIA 为 Jetson(aarch64) 编译的 whl |
| 系统启动但 USB/CSI 不工作 | 设备树/载板不匹配 | 刷机时选正确的载板配置(carrier board) |
七、通用调试工具
7.1 逻辑分析仪(查时序问题的唯一利器)
# ===== 软件:sigrok / PulseView(开源免费,支持 Saleae 克隆)=====
# Linux
sudo apt install -y pulseview sigrok-cli libsigrok libsigrokdecode
# macOS
brew install --cask pulseview sigrok-cli
# Windows:sigrok.org 下载 PulseView 安装包
# 命令行抓 I2C(快速验证,不用开 GUI)
sigrok-cli --driver fx2lafw --config samplerate=1M --time 5s -o capture.sr
# 直接解码并打印
sigrok-cli --driver fx2lafw --config samplerate=1M \
--time 5s --protocol-decoders i2c:scl=0:sda=1 -A i2c
# 解码 UART(自动测波特率)
sigrok-cli --driver fx2lafw --config samplerate=1M --time 10s \
--protocol-decoders uart:rx=0:baudrate=115200 -A uart
# 列出设备与可用驱动
sigrok-cli --scan
sigrok-cli --show
PulseView 图形界面使用要点:
- 采样率设为信号最高频率的 10 倍以上(100 kHz I2C 用 1 MHz 以上,1 MHz SPI 用 8 MHz 以上)。
- 抓前先设触发条件(如 SDA 下降沿),避免抓到一段空闲。
- 加协议解码器:
Decoders → I2C / SPI / UART,填对应通道(CH0/CH1…)。 - 解码器可以叠加:I2C 上再叠
SSD1306、SPI 上再叠MDIO等。
⚠️ 注意:廉价 24 MHz 8 通道逻辑分析仪(Cypress FX2LP 方案,约 ¥30–50)只能看不能产,且输入电平一般只支持 5 V tolerant 的 TTL,不要直接接 RS232 的 ±12 V 或 CAN 的差分信号,会烧。看 CAN/RS485 需要在收发器之后的 TTL 侧测。
| 设备 | 参考价格 | 采样率 | 通道 | 适合 |
|---|---|---|---|---|
| FX2LP 克隆("24MHz 8CH") | ¥30–50 | 24 MHz | 8 | I2C/SPI/UART 够用,入门首选 |
树莓派 Pico 自制(pico-logic-analyzer) |
¥25 | 最高可达 100 MHz 级(取决于实现) | 8–24 | 想玩开源固件 |
| DSLogic / Kingst LA 系列 | ¥300–1500 | 100–800 MHz | 16–32 | 高速 SPI、并行总线、时序余量分析 |
| Saleae Logic 8 / Pro | ¥2000+ | 100–500 MS/s | 8–16 | 专业,模拟+数字混合、协议触发强大 |
7.2 串口工具
# ===== Linux =====
sudo apt install -y minicom screen picocom tio
# minicom(功能全,需先配置)
sudo minicom -s # 进设置:Serial port setup → A 改设备 → E 改波特率/流控
minicom -D /dev/ttyUSB0 -b 115200 # 直接指定
# 退出:Ctrl+A 然后 X
# screen(最省事,但退出键位容易忘)
screen /dev/ttyUSB0 115200
# 退出:Ctrl+A 然后 k → y
# picocom(推荐,退出最直观)
picocom -b 115200 /dev/ttyUSB0 --echo
# 退出:Ctrl+A 然后 Ctrl+X
# tio(现代替代品,自动重连)
tio /dev/ttyUSB0 -b 115200 --log
# 退出:Ctrl+T 然后 q
# 只看输出不交互(抓日志到文件)
cat /dev/ttyUSB0 # 需先 stty 设波特率
stty -F /dev/ttyUSB0 115200 raw -echo && cat /dev/ttyUSB0 | tee boot.log
Windows:PuTTY(Serial 模式,填 COM 口与波特率)、MobaXterm、SSCOM/串口助手(国产,带十六进制收发与自动应答,适合调试 Modbus 之类二进制协议)。
Windows 首推:ATK-XCOM(正点原子串口调试助手)
上面 PuTTY 那类"终端"工具看日志够用,但调试单片机的双向通信(发指令、看回包、调试协议)时,图形化的串口调试助手效率高一个量级。Windows 上首推正点原子的 ATK-XCOM(免费,v3.0),理由不是它功能最多,而是每一项功能都对准了嵌入式调试的真实痛点:
| 痛点 | XCOM 对应功能 | 实战用法 |
|---|---|---|
| 设备吐的是二进制不是文本 | 16 进制收发(发送区字节自动拆分,不用手动加空格) | 调 Modbus/自定义帧协议时勾上 HEX,直接看原始字节 |
| 一条条敲 AT 指令太慢 | 多条发送(4 页 × 多条,支持导入导出) | 调蓝牙/GSM/WiFi 模块时把整套 AT 指令预先编排好,点击发送 |
| 要按键实时控车/飞行器 | 关联数字键盘(条目 0-9 绑数字键) | 智能车方向控制按数字键即发,小键盘当方向键用 |
| 帧对不上不知道哪次发的 | 时间戳显示(按 \r\n 断帧) |
注意:数据里没有换行符时时间戳无效——这是它断帧的机制决定的 |
| 传输丢包没感知 | 协议模式(Modbus RTU 变种 + 自动重发 + CRC8/16 校验) | IAP 串口升级/可靠传输场景用,可配重发次数与帧周期 |
| 高波特率芯片 | 自定义波特率(需关串口后改) | 硬件支持时突破 1382400bps 上限 |
两个上手就能踩的点提前说:一是16 进制多条发送不做校验——条目里混进非 HEX 字符会静默发送失败,排查"为什么这条没反应"先检查有没有输错;二是时间戳依赖换行回车断帧,如果你的设备固件 printf 不带 \r\n,时间戳和自动换行都会失效,先修固件输出格式。
类似的还有 SSCOM、友善串口调试助手,功能大同小异;选哪个看顺手,关键是十六进制收发、多条发送、定时发送这三个能力必须有——它们覆盖了 90% 的串口调试场景。
macOS:screen /dev/cu.usbserial-xxx 115200、minicom、CoolTerm(GUI)。
波特率自动探测(拿到一块未知板子时):
# 思路:依次尝试常见波特率,看哪个能出可读文本
for b in 9600 19200 38400 57600 74880 115200 230400 460800 921600; do
echo "--- trying $b ---"
timeout 3 picocom -b $b /dev/ttyUSB0 --imap lfcrlf -q 2>/dev/null | head -5
done
# 或者用逻辑分析仪/示波器量一位的时长:波特率 ≈ 1 / 位宽
# 更系统的做法见《硬件模块与通信协议基础》的 UART 一节
7.3 万用表实用技巧
| 档位 | 典型用途 | 注意事项 |
|---|---|---|
| 蜂鸣档(通断) | 查杜邦线是否断、焊点是否虚焊、GND 是否共地 | 必须在断电下测;阻值 < 几十欧才响,长线也可能误判 |
| 直流电压 | 确认 3.3 V / 5 V 是否存在、电池电量、分压中点 | 表笔别同时碰两个相邻引脚,容易短路 |
| 直流电流 | 测整机功耗、睡眠电流 | 必须串联进回路,且先插 mA 孔(大电流孔保险丝不同);测完立刻换回电压孔,否则下次测电压会短路 |
| 电阻 | 确认上拉电阻值(如 I2C 的 4.7 kΩ)、判断 LED 好坏 | 断电测,且电容要放电 |
| 电容 | 判断去耦电容是否虚标、滤波电容容量 | 需从电路上焊下一端再测,否则受并联电路影响 |
| 二极管档 | 判断二极管/LED 极性、三极管管型 | LED 正向压降约 1.8–3.3 V(不同颜色不同),表会显示压降 |
几个高频实用场景:
1) 判断杜邦线好坏:蜂鸣档测两端,不通就换。面包板项目一半的玄学故障源于此。
2) 判断板子是否真上电:黑表笔接 GND,红表笔量 VCC 引脚(不是电源输入端)。
3) 找短路:断电,蜂鸣档测 VCC 与 GND。导通说明有短路,逐段断开外围模块定位。
4) 测功耗:电源 → 万用表(电流档串联)→ 板子 VCC。注意 ESP32 峰值 300 mA+,
万用表 mA 档保险丝可能扛不住,用带 10 A 孔的档位或专用 USB 电流表。
5) 判断 IO 是否真在翻转:电压档测引脚,若一直在 0 V 或 3.3 V 说明代码没跑;
若在 1.6 V 左右波动说明在翻转(PWM/通信中)。
7.4 电源:3.3 V 与 5 V、电流不足的排查
| 参数 | 5 V 系统(Arduino Uno、舵机、多数传感器模块) | 3.3 V 系统(ESP32、STM32、RP2040、树莓派 GPIO) |
|---|---|---|
| 逻辑高电平阈值 | 约 > 3.0 V(TTL) / > 0.7×VCC(CMOS) | 约 > 2.0 V(具体看芯片数据手册) |
| 常见供电方式 | USB 5 V、DC 圆口 7–12 V 经板载 LDO | USB 5 V 经板载 AMS1117 转 3.3 V |
| 直接接 5 V 的后果 | — | ESP32/STM32/RP2040/树莓派 GPIO 大概率永久损坏 |
| 3.3 V 信号给 5 V 输入 | 通常能识别为高电平(临界,不可靠) | 需电平转换(见第 3 篇) |
电流不足导致反复重启的排查流程:
现象:程序下载正常,但一启动 WiFi / 一转舵机 / 一亮灯就重启;
串口日志里出现 "Brownout detector was triggered"(ESP32)
或树莓派右上角出现闪电图标 / dmesg 报 under-voltage
排查顺序:
1) 换 USB 口:台式机后置口 > 前置口 > 笔记本口 > USB HUB(HUB 最差)
2) 换线:细长线压降大;数据线要 28AWG/24AWG 以上(越粗越好)
3) 换电源:ESP32 项目按 5 V/1 A 起步,WiFi 发射峰值留 300 mA 余量
4) 外接供电:电机/舵机/继电器/大屏**必须独立供电**,只把 GND 与 MCU 共地
5) 加电容:在 MCU 的 VCC-GND 间并联 100 µF 电解 + 0.1 µF 陶瓷,吸收瞬时跌落
6) 测:万用表电压档量 MCU 的 VCC 引脚(不是电源输出端),
触发负载的瞬间若跌到 3.0 V 以下,就是电源问题
7) 软件侧确认:ESP32 可在 menuconfig 里看 Brownout 阈值;
树莓派用 vcgencmd get_throttled 确认是否欠压过
推荐电源配置:
| 场景 | 推荐 | 说明 |
|---|---|---|
| ESP32 开发(无大负载) | USB 5 V/1 A 充电头 | 够用且便宜 |
| ESP32 + 摄像头/大屏 | 5 V/2 A 以上 | 峰值电流大 |
| 树莓派 4B | 5 V/3 A 官方电源 | 低于 3 A 会随机重启、SD 卡损坏 |
| 树莓派 5 | 5 V/5 A USB-PD | 官方 PD 电源,普通充电头会限流警告 |
| 多舵机(4 个 SG90 以上) | 单独 5 V/2–3 A 开关电源 | 绝不能从 MCU 的 3.3 V 取电 |
| 步进电机 + A4988 | 12 V/2 A(按电机额定) | 电机电源与逻辑电源分开,只共地 |
| 桌面调试通用 | 可调稳压电源(0–30 V、0–3 A) | 带限流保护,烧片子前能救命 |
7.5 通用故障排查表
| 现象 | 可能原因(按概率排序) | 快速判定方法 |
|---|---|---|
| 电脑完全认不到设备 | 数据线没数据功能 → 驱动 → 板子坏 | 换线 → 换口 → 换电脑 |
| 能识别但下载失败 | 波特率过高 → 没进下载模式 → strapping 引脚被占用 | 降波特率 → 按键进模式 → 拔掉外设再试 |
| 下载成功不运行 | BOOT 跳线 → 复位电路 → 程序本身崩 | 看串口日志 → 手动复位 |
| 串口全乱码 | 波特率不对 → 晶振配置错 → 电平不匹配 | 换常见波特率 → 逻辑分析仪量位宽 |
| 运行几分钟后死机 | 电源跌落 → 过热 → 内存泄漏 / 栈溢出 | 摸芯片温度 → 量 VCC → 看日志 |
| 通信类外设无响应 | 接线错(TX/RX 未交叉)→ 未共地 → 地址错 → 上拉缺失 | 逻辑分析仪看波形 → I2C 地址扫描 |
| 时好时坏 | 杜邦线接触不良 / 面包板氧化 / 虚焊 | 重新插拔 → 换线 → 补焊 |
| 烧了芯片 | 电压接错 / VCC 与 GND 反接 / IO 直驱大电流 | 断电闻味摸温;下次接线前先量一遍 |
八、环境可复现:把"能跑"固化下来
# ===== 环境版本留档(排查"我这能跑你那不能跑"的关键)=====
pio --version && pio pkg list
idf.py --version
arm-none-eabi-gcc --version
xtensa-esp32s3-elf-gcc --version
openocd --version
python3 --version
cat /etc/os-release | head -3
# 写进项目 README 或 env-info.txt,比记忆可靠
{
echo "date: $(date -I)"
echo "os: $(uname -a)"
echo "pio: $(pio --version 2>/dev/null)"
echo "idf: $(idf.py --version 2>/dev/null)"
echo "gcc: $(arm-none-eabi-gcc --version 2>/dev/null | head -1)"
} | tee env-info.txt
# Docker 化(团队协作 / CI 最彻底的方案)
docker run --rm -it -v "$PWD":/project -w /project \
python:3.11-slim bash -c "pip install platformio && pio run"
九、本系列导航
- 本系列第 1 篇:《常见开发板选型与信息速查》——买什么板、各板规格与坑
- 本系列第 3 篇:《硬件模块与通信协议基础》——GPIO/UART/I2C/SPI/PWM/ADC/CAN 精讲 + 模块速查 + 电源与焊接
一个通用原则:环境配好后,立刻做一个"最小可验证闭环"(点灯 / 打印 Hello World / 读芯片 ID),把它跑通再往下走。90% 的"环境问题"都能在这一步暴露,而不是等到写了几百行代码才发现串口根本连不上。