打通嵌入式与 Linux:USB 转串口通信实战

目录

一、如何让虚拟机获取USB控制权?

二、如何使用VSCode,而不是vim?

(1)查看虚拟机的ssh状态

(2)查看虚拟机IP地址

(3)vscode连接到虚拟机Ubuntu

三、测试Linux与串口通信


在学习嵌入式开发时,单纯的操作寄存器模拟串口通信的实验未免太过容易,于是我选择了一个经典的场景:让 51 单片机通过 USB 转串口,与 Ubuntu 虚拟机进行数据通信。这个过程看似简单,却串联起了虚拟机配置、SSH 远程开发、termios 串口编程、权限管理等多个知识点。本文就是我在实践中踩坑、排错、总结的完整记录,希望能为同样在入门阶段的同学提供一份清晰的实践指南,让串口通信不再是 "玄学",而是可以被精准控制的技术环节。

一、如何让虚拟机获取USB控制权?

首先进入根目录/下的dev目录。由于linux一切皆文件的思想,每一个外设都会被封装成文件,而dev表示的就是外设文件目录。

一般来说,"USB转串口"设备在Linux中的名字叫ttyUSB*或者ttyACM*,其中*代表编号。

可是这里面似乎并没有找到,这意味着目前的USB并没有被虚拟机识别到。这是因为虚拟机是运行在主机上的一个软件,此时的控制权依然还在主机(物理机),所以需要在VMWare中进行一些配置,将控制权交给虚拟机。不过值得注意的是:控制权在同一时刻只能由一台机器占用,虚拟机用了主机就无法使用了。

从此以后,你想从上位机Linux访问到底层单片机,就只需要直接读写这个/dev/ttyUSB0文件即可,而这个操作与以前学过的文件操作完全相同,所有的适配工作已经被Linux内核完成了。当然如果你以后成为一名驱动工程师,这些适配工作可能就得你自己写咯。

二、如何使用VSCode,而不是vim?

由于直接在Linux虚拟机中编程需要用到古老的vim和丑陋的ui界面,不方便函数查看、报错检测,于是我选择使用VSCode编写代码,直接避免了上述问题。

(1)查看虚拟机的ssh状态

检查ssh服务是否正确运行:

发现仅仅是安装了ssh却没有启动,于是使用systemctl start ssh命令运行,然后可以设置开机自启动等,这过程中每个人肯定会遇到不同的问题,用ai查一查基本都能解决。

(2)查看虚拟机IP地址

命令:ip addr

发现ip地址是192.168.202.132,将其记录下来并打开vscode。

(3)vscode连接到虚拟机Ubuntu

后续可能会要求你输入密码,按照要求即可。于是就发现你现在可以使用Windos下的vscode来编写linux代码了。只要你不关闭该虚拟机,就完全可以不用理会他,直接使用更加方便的vscode编辑器即可。

后续你可能遇到各种各样的问题,比如没有代码补全提醒,没有各种头文件等,这些自己上网络查询,然后在vscode中下载、配置一下即可,不再赘述。

三、测试Linux与串口通信

以下代码是AI生成的,因为本人还未曾学习Linux驱动开发,对于这一块不熟悉。主要是一些关于ttyUSB文件的波特率、形式等配置。

cpp 复制代码
#include<fcntl.h>  //open等文件相关的头文件

#include<unistd.h>
#include<stdio.h>
#include<errno.h>

#include<termios.h>


#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
#include<termios.h>
// 移除未使用的<string>,避免冗余

void init_USB(int fd)
{
    struct termios opt;
    // 【修改1】增加tcgetattr容错:防止获取配置失败导致程序崩溃
    if (tcgetattr(fd, &opt) != 0) {
        perror("获取串口配置失败");
        return;
    }

    cfsetispeed(&opt, B57600);
    cfsetospeed(&opt, B57600);

    opt.c_cflag |= CLOCAL | CREAD;
    opt.c_cflag &= ~CSIZE;
    opt.c_cflag |= CS8;
    opt.c_cflag |= PARENB;
    opt.c_cflag |= CMSPAR;
    opt.c_cflag &= ~PARODD;
    opt.c_cflag &= ~CSTOPB;
    opt.c_cflag &= ~CRTSCTS;

    opt.c_iflag &= ~(IXON | IXOFF | IXANY);
    opt.c_iflag &= ~(INPCK | ISTRIP);
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    opt.c_oflag &= ~OPOST;

    // 【修改2】增加tcsetattr容错:配置失败时提示
    if (tcsetattr(fd, TCSANOW, &opt) != 0) {
        perror("配置串口参数失败");
    }
}

int main()
{
    int fd=open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);
    if(fd==-1)
    {
        perror("打开串口失败");
        // 【修改3】打开失败后直接退出,避免后续操作无效fd
        return -1;
    }

    init_USB(fd);

    // 读取串口信息:原代码仅读1次,无阻塞/循环,大概率读不到数据
    char buffer[100];
    // 补充:打印提示,告知用户程序在等待数据
    printf("等待51单片机发送数据...\n");
    int len=read(fd,buffer,16);  // 读16字节(匹配buffer大小,无问题)
    
    if(len>0)
    {
        buffer[len]='\0';
        // 优化:打印原始十六进制,便于核对51的SBUF数据(字符串可能有不可见字符)
        printf("读取到 %d 字节数据:\n", len);
        //printf("字符串形式:%s\n", buffer);
        printf("十六进制形式:");
        for(int i=0; i<len; i++) {
            printf("0x%02X ", (unsigned char)buffer[i]);
        }
        printf("\n");
    }
    else if(len==0)
    {
        printf("未读取到数据(串口无数据发送)\n");
    }
    else
    {
        // 优化:打印具体错误原因,便于排查
        perror("读取串口失败");
    }
    
    close(fd);
    return 0;
}

最后结果发现与上一篇文章的结果完全一致,这说明我们已经成功的从51单片机中读取到消息了!

关于这篇文章,其实没有什么亮眼的操作,就干了一件事情:打通了从底层硬件到上位机Linux的链路,基于这个基础,后续我们可以在Linux上进行更为复杂的操作,比如增加数据链路层协议、唤醒缓冲区等等,就可以成为了一个较为工业化的项目了。

而这个过程,其实就是串口助手干的事情,只不过他更加完善、且富含方便操作的UI界面罢了。

相关推荐
mN9B2uk1722 分钟前
大数据量高并发的数据库优化
服务器·数据库·oracle
starvapour24 分钟前
Ubuntu部署gitlab频繁出现502的问题
linux·ubuntu·gitlab
jinglong.zha30 分钟前
LScript-从零基础到商业变现的AI自动化学习平台
运维·学习·自动化
土星云SaturnCloud32 分钟前
边缘计算赋能智慧工地:从“看得见“到“管得住“的智能化升级
服务器·人工智能·ai·边缘计算
Adorable老犀牛1 小时前
Telegraf:InfluxData 出品的指标采集代理
运维·telegraf
北塔软件1 小时前
北塔软件智能体平台 | 不只监控,更是AI时代的数据资产
运维·人工智能·知识库·北塔软件
zhangfeng11331 小时前
ps aux讲解,结合国家超算中心 hpc apptainer
linux·服务器·网络
夜月yeyue1 小时前
STM32 DMA 双缓冲采样
linux·stm32·单片机·嵌入式硬件·系统架构
ScilogyHunter1 小时前
Buildroot完全指南:从入门到实战
linux·嵌入式·buildroot
毕竟是shy哥1 小时前
Claude Code 接入 DeepSeek 保姆级教程,WSL/Linux 通用
linux·安装教程·codex·deepseek·claude code·openclaw