打通嵌入式与 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界面罢了。

相关推荐
TG_yunshuguoji2 小时前
亚马逊云代理商:CloudWatch 日志查询实战 5 步精准定位 AWS 故障
服务器·云计算·aws
susu10830189112 小时前
Ubuntu 离线环境 安装 Docker Compose
运维·docker·容器
要开心吖ZSH2 小时前
(三)OpenClaw 云端服务器控制本地 Windows 浏览器完整配置指南(SSH方式)
运维·服务器·windows·openclaw
XMYX-02 小时前
Zabbix 7.0 自定义 Linux 监控模板(Agent Active版,支持CPU/内存/磁盘/网卡自动发现)
linux·zabbix
研究点啥好呢2 小时前
3月10日GitHub热门项目推荐|自动化的浪潮
运维·人工智能·ai·自动化·github
fengyehongWorld2 小时前
docker compose的使用
运维·docker·容器
进击切图仔2 小时前
linux 虚拟文件系统简介和详解
linux·运维·服务器
跟着珅聪学java2 小时前
Electron 精美菜单设计
运维·前端·数据库
AIwenIPgeolocation3 小时前
基于昇腾算力+鲲鹏服务器实现国产化环境的OpenClaw高效应用,解锁AI自动化新体验
服务器·人工智能·自动化