
大家好,我是情报小哥~
第N次scp驱动模块到板子上后,串口又Oops了。手忙脚乱地在板子上用vim看日志,切回本地改代码,再scp,再重启,再看串口……循环往复,一上午就这么没了。
确实有点难忍受这种"原始人"式的开发方式了,于是准备了这篇文章。作为一名写了8年嵌入式Linux的老兵,我曾经也是vim+Source Insight+Eclipse的重度用户,直到我把VScode打造成了一体化开发环境。
今天分享11个非显而易见的高级技巧,覆盖交叉编译、远程开发、内核调试、设备树等全流程。每一个都直击痛点,配置完立刻能用,让你的开发效率至少提升200%,上干货~
解决痛点:每次连接开发板或服务器都要输密码,内网设备无法直接访问,代码同步慢如蜗牛。
ssh-keygen -t ed25519 -C "your_email@example.com"# 一路回车,不要设置密码# 直接连接的情况ssh-copy-id root@192.168.1.100# 需要跳板机的情况ssh-copy-id -o ProxyCommand="ssh -W %h:%p jump_user@jump_server" root@192.168.1.100~/.ssh/config):# 跳板机Host jump-server HostName 123.45.67.89 User jump_user Port 22 IdentityFile ~/.ssh/id_ed25519# 目标开发板(通过跳板机访问)Host imx6ull-board HostName 192.168.1.100 User root IdentityFile ~/.ssh/id_ed25519 ProxyCommand ssh -W %h:%p jump-server # 关键:保持连接不断开 ServerAliveInterval 60 ServerAliveCountMax 30Ctrl+Shift+P,输入Remote-SSH: Connect to Host,选择imx6ull-board即可连接。连接后,VScode会自动在远程机器上安装服务器端,你可以直接在本地编辑远程文件,完全不需要scp同步。所有操作都是"无感"的,就像在本地编辑一样。
坑点:如果连接失败,检查远程机器的/etc/ssh/sshd_config是否允许密码登录和公钥认证。
解决痛点:内核源码太大,VScode默认索引不全,到处都是红色波浪线,函数跳转经常跳错。
bear工具(用于生成编译命令数据库):# Ubuntu/Debiansudo apt install bear# CentOS/RHELsudo yum install bearcompile_commands.json:# 清理之前的编译产物make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- clean# 用bear包装make命令,生成编译命令数据库bear -- make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfigbear --append -- make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8c_cpp_properties.json:{"configurations": [ {"name": "Linux-ARM64","includePath": ["${workspaceFolder}/**","${workspaceFolder}/arch/arm64/include","${workspaceFolder}/include" ],"defines": ["__KERNEL__","CONFIG_ARM64" ],"compilerPath": "/usr/bin/aarch64-linux-gnu-gcc","cStandard": "c11","cppStandard": "c++17","intelliSenseMode": "linux-gcc-arm64",// 关键:引入编译命令数据库"compileCommands": "${workspaceFolder}/compile_commands.json" } ],"version": 4}配置完成后,VScode的IntelliSense会精准解析内核的所有宏定义、结构体和函数。你可以:
F12跳转到任意函数或宏的定义Shift+F12查看所有引用坑点:每次修改内核配置(make menuconfig)后,都需要重新生成compile_commands.json。
解决痛点:每次编译都要输入一长串环境变量和make命令,容易出错,效率低下。
在.vscode目录下创建tasks.json:
{"version": "2.0.0","tasks": [ {"label": "编译内核镜像","type": "shell","command": "make","args": ["ARCH=arm64","CROSS_COMPILE=aarch64-linux-gnu-","-j8" ],"group": {"kind": "build","isDefault": true },"problemMatcher": "$gcc","presentation": {"clear": true } }, {"label": "编译设备树","type": "shell","command": "make","args": ["ARCH=arm64","CROSS_COMPILE=aarch64-linux-gnu-","dtbs" ],"problemMatcher": "$gcc" }, {"label": "编译并安装模块","type": "shell","command": "make","args": ["ARCH=arm64","CROSS_COMPILE=aarch64-linux-gnu-","modules","INSTALL_MOD_PATH=./modules_install","modules_install" ],"problemMatcher": "$gcc" }, {"label": "清理编译产物","type": "shell","command": "make","args": ["ARCH=arm64","CROSS_COMPILE=aarch64-linux-gnu-","clean" ],"problemMatcher": [] } ]}Ctrl+Shift+B直接执行默认的"编译内核镜像"任务Ctrl+Shift+P输入Tasks: Run Task选择其他任务小技巧:绑定快捷键。打开键盘快捷键设置(Ctrl+K Ctrl+S),搜索"运行任务",为常用任务设置快捷键,比如F5编译内核,F6编译设备树。
解决痛点:在目标板上用gdb调试应用程序太麻烦,没有图形界面,查看变量和调用栈不方便。
# 以Debian/Ubuntu为例apt install gdbserver在VScode中安装C/C++插件。
配置.vscode/launch.json:
{"version": "0.2.0","configurations": [ {"name": "远程调试应用","type": "cppdbg","request": "launch","program": "${workspaceFolder}/build/hello_world","args": [],"stopAtEntry": false,"cwd": "${workspaceFolder}","environment": [],"externalConsole": false,"MIMode": "gdb","miDebuggerPath": "/usr/bin/aarch64-linux-gnu-gdb","miDebuggerServerAddress": "192.168.1.100:1234","setupCommands": [ {"description": "为gdb启用整齐打印","text": "-enable-pretty-printing","ignoreFailures": true } ] } ]}gdbserver :1234 ./hello_worldF5开始调试。你可以在VScode中设置断点、单步执行、查看变量、查看调用栈,所有操作都和本地调试一样。调试嵌入式应用程序从未如此简单。
坑点:确保本地的交叉gdb版本和目标板上的gdbserver版本一致,否则可能出现兼容性问题。
解决痛点:内核调试一直是嵌入式Linux开发的噩梦,printk调试效率极低,JTAG调试器又贵又难用。
make ARCH=arm64 menuconfig# 开启以下选项:# CONFIG_KGDB=y# CONFIG_KGDB_SERIAL_CONSOLE=y# CONFIG_DEBUG_INFO=y# CONFIG_DEBUG_INFO_DWARF4=yqemu-system-aarch64 \ -machine virt \ -cpu cortex-a57 \ -smp 2 \ -m 1024M \ -kernel arch/arm64/boot/Image \ -append "root=/dev/vda console=ttyAMA0 kgdboc=ttyAMA0,115200 kgdbwait" \ -drive file=rootfs.ext4,if=virtio,format=raw \ -nographic \ -gdb tcp::1234launch.json:{"name": "调试Linux内核(QEMU)","type": "cppdbg","request": "launch","program": "${workspaceFolder}/vmlinux","args": [],"stopAtEntry": false,"cwd": "${workspaceFolder}","environment": [],"externalConsole": false,"MIMode": "gdb","miDebuggerPath": "/usr/bin/aarch64-linux-gnu-gdb","miDebuggerServerAddress": "localhost:1234","setupCommands": [ {"text": "set architecture aarch64" }, {"text": "target remote localhost:1234" } ]}你可以在VScode中调试内核的任何部分,包括驱动程序。设置断点、单步执行、查看内核变量和寄存器,甚至可以在中断处理函数中打断点。
小技巧:如果有JTAG/SWD调试器,可以安装Cortex-Debug插件,实现硬件级别的内核调试,速度更快,功能更强大。
解决痛点:设备树文件没有语法高亮,容易写错,编译和拷贝到目标板的过程繁琐。
安装DeviceTree插件。
配置tasks.json添加设备树编译和部署任务:
{"label": "编译并部署设备树","type": "shell","command": "make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs && scp arch/arm64/boot/dts/freescale/imx6ull-myboard.dtb root@192.168.1.100:/boot/","problemMatcher": "$gcc"}settings.json):{"files.associations": {"*.dts": "devicetree","*.dtsi": "devicetree" }}坑点:设备树修改后必须重启目标板才能生效。
解决痛点:Linux内核源码有几十万个文件,搜索一个函数要等半天,而且会搜到很多无关架构的代码。
在.vscode/settings.json中添加搜索排除规则:
{"files.exclude": {"**/.git": true,"**/arch/alpha": true,"**/arch/arc": true,"**/arch/arm": true, // 如果你只开发ARM64"**/arch/csky": true,"**/arch/h8300": true,"**/arch/hexagon": true,"**/arch/ia64": true,"**/arch/m68k": true,"**/arch/microblaze": true,"**/arch/mips": true,"**/arch/nds32": true,"**/arch/nios2": true,"**/arch/openrisc": true,"**/arch/parisc": true,"**/arch/powerpc": true,"**/arch/riscv": true,"**/arch/s390": true,"**/arch/sh": true,"**/arch/sparc": true,"**/arch/um": true,"**/arch/x86": true,"**/arch/xtensa": true,"**/drivers/**": false, // 保留驱动目录"**/sound/**": true,"**/tools/**": true,"**/samples/**": true },"search.exclude": {"**/build": true,"**/modules_install": true }}配置完成后,VScode只会索引你关心的架构和目录,搜索速度会提升10倍以上。按Ctrl+P可以秒搜任何文件,按Ctrl+Shift+F可以在整个工程中搜索字符串。
小技巧:使用工作区(Workspace)管理多个相关项目,比如内核、U-Boot和应用程序,可以在一个窗口中切换,非常方便。
解决痛点:需要在多个窗口之间切换,一会儿看串口输出,一会儿编译代码,一会儿拷贝文件,手忙脚乱。
在VScode中按Ctrl+打开内置终端。
点击终端右上角的"+"号新建终端,然后点击"拆分终端"按钮,将终端分成左右两部分。
在左侧终端运行串口工具:
# 使用picocom(推荐)picocom -b 115200 /dev/ttyUSB0# 或者使用minicomminicom -D /dev/ttyUSB0 -b 115200Ctrl+Shift+可以快速切换终端小技巧:配置快捷键一键发送文件到目标板。在keybindings.json中添加:
{"key": "ctrl+alt+s","command": "workbench.action.terminal.sendSequence","args": {"text": "scp ${file} root@192.168.1.100:/root/\u000D" }}按Ctrl+Alt+S即可将当前编辑的文件发送到目标板的/root目录。
解决痛点:每次写驱动都要重复写很多模板代码,比如字符设备驱动、platform_driver等,浪费时间。
按Ctrl+Shift+P输入Preferences: Configure User Snippets,选择c.json。
添加以下代码片段:
{"字符设备驱动框架": {"prefix": "chardev","body": ["#include <linux/module.h>","#include <linux/fs.h>","#include <linux/cdev.h>","#include <linux/device.h>","","#define DEVICE_NAME \"${1:mydev}\"","#define CLASS_NAME \"${2:myclass}\"","","static dev_t dev_num;","static struct cdev cdev;","static struct class *dev_class;","static struct device *dev_device;","","static int ${1:mydev}_open(struct inode *inode, struct file *file)","{"," printk(KERN_INFO \"${1:mydev} opened\\n\");"," return 0;","}","","static int ${1:mydev}_release(struct inode *inode, struct file *file)","{"," printk(KERN_INFO \"${1:mydev} closed\\n\");"," return 0;","}","","static struct file_operations fops = {"," .owner = THIS_MODULE,"," .open = ${1:mydev}_open,"," .release = ${1:mydev}_release,","};","","static int __init ${1:mydev}_init(void)","{"," int ret;",""," // 分配设备号"," ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);"," if (ret < 0) {"," printk(KERN_ERR \"Failed to allocate device number\\n\");"," return ret;"," }",""," // 初始化cdev"," cdev_init(&cdev, &fops);"," cdev.owner = THIS_MODULE;",""," // 添加cdev"," ret = cdev_add(&cdev, dev_num, 1);"," if (ret < 0) {"," printk(KERN_ERR \"Failed to add cdev\\n\");"," goto unregister_chrdev;"," }",""," // 创建类"," dev_class = class_create(THIS_MODULE, CLASS_NAME);"," if (IS_ERR(dev_class)) {"," printk(KERN_ERR \"Failed to create class\\n\");"," ret = PTR_ERR(dev_class);"," goto del_cdev;"," }",""," // 创建设备"," dev_device = device_create(dev_class, NULL, dev_num, NULL, DEVICE_NAME);"," if (IS_ERR(dev_device)) {"," printk(KERN_ERR \"Failed to create device\\n\");"," ret = PTR_ERR(dev_device);"," goto destroy_class;"," }",""," printk(KERN_INFO \"${1:mydev} module loaded\\n\");"," return 0;","","destroy_class:"," class_destroy(dev_class);","del_cdev:"," cdev_del(&cdev);","unregister_chrdev:"," unregister_chrdev_region(dev_num, 1);"," return ret;","}","","static void __exit ${1:mydev}_exit(void)","{"," device_destroy(dev_class, dev_num);"," class_destroy(dev_class);"," cdev_del(&cdev);"," unregister_chrdev_region(dev_num, 1);"," printk(KERN_INFO \"${1:mydev} module unloaded\\n\");","}","","module_init(${1:mydev}_init);","module_exit(${1:mydev}_exit);","","MODULE_LICENSE(\"GPL\");","MODULE_AUTHOR(\"Your Name\");","MODULE_DESCRIPTION(\"${1:mydev} character device driver\");" ],"description": "生成字符设备驱动框架" }}在C文件中输入chardev,按Tab键,3秒就能生成一个完整的字符设备驱动框架。你还可以添加platform_driver、i2c_driver、spi_driver等常用代码片段。
解决痛点:内核日志密密麻麻,很难一眼找到Oops、panic等关键信息。
安装Log File Highlighter插件。
配置settings.json添加自定义高亮规则:
{"logFileHighlighter.customPatterns": [ {"pattern": "Oops","foreground": "#ff0000","fontWeight": "bold" }, {"pattern": "BUG","foreground": "#ff0000","fontWeight": "bold" }, {"pattern": "panic","foreground": "#ff0000","fontWeight": "bold" }, {"pattern": "ERROR","foreground": "#ff0000" }, {"pattern": "WARNING","foreground": "#ff9900" }, {"pattern": "INFO","foreground": "#0099ff" }, {"pattern": "\\[\\s*\\d+\\.\\d+\\]","foreground": "#00cc00" } ]}一眼就能找到关键信息,大大提高问题定位效率。
解决痛点:新成员入职要花好几天搭建开发环境,每个人的环境都不一样,经常出现"在我电脑上能跑"的问题。
安装Dev Containers插件。
在工程根目录创建.devcontainer目录,添加Dockerfile:
FROM ubuntu:22.04# 安装必要的工具RUN apt update && apt install -y \ build-essential \ gcc-aarch64-linux-gnu \ g++-aarch64-linux-gnu \ gdb-multiarch \ make \ cmake \ git \ bear \ picocom \ ssh \ && rm -rf /var/lib/apt/lists/*# 设置工作目录WORKDIR /workspacedevcontainer.json:{"name": "嵌入式Linux开发环境","build": {"dockerfile": "Dockerfile" },"customizations": {"vscode": {"extensions": ["ms-vscode.cpptools","ms-vscode.cpptools-extension-pack","ms-vscode-remote.remote-ssh","ms-vscode-remote.remote-containers","plorefice.devicetree","emilast.logfilehighlighter" ] } },"forwardPorts": [1234],"postCreateCommand": "git config --global --add safe.directory /workspace"}Ctrl+Shift+P输入Dev Containers: Reopen in Container,VScode会自动构建Docker镜像并启动容器。以上11个技巧,覆盖了嵌入式Linux开发的全流程。从代码编辑、智能感知、交叉编译,到远程调试、设备树开发、日志分析,再到团队协作的环境统一,VScode都能完美胜任。
我已经用这套工作流开发了5年多,彻底告别了vim、Source Insight和Eclipse。现在我的开发效率比以前提高了至少3倍,再也不用在多个工具之间来回切换了。
持续获取嵌入式实战干货,关注、标星 公众号不错过每一篇技术解析~
推荐好文点击蓝色字体即可跳转
☞专辑|Linux应用程序编程大全 ☞ 专辑|学点网络知识 ☞ 专辑|手撕C语言 ☞ 专辑|手撕C++语言
☞ 专辑|经验分享 ☞ 专辑|从单片机到Linux ☞ 专辑|电能控制技术 ☞ 专辑|嵌入式必备数学知识 ☞ MCU进阶专辑
☞ 经验分享