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

相关推荐
高梦轩20 小时前
MySQL高可用
android·运维·数据库
孟章豪1 天前
《SQL拼接 vs 参数化,为什么公司禁止拼接SQL?(附真实案例)》
服务器·数据库·sql
hsjcjh1 天前
Nodemailer使用教程:在Node.js中发送电子邮件
linux·运维·node.js
没有梦想的咸鱼185-1037-16631 天前
北斗高精度数据解算:破解城市峡谷/长基线/无网区难题,从毫米级定位到自动化交付——(GAMIT/GLOBK底层核心解算技术方法)
运维·arcgis·数据分析·自动化
不怕犯错,就怕不做1 天前
linux 如何查看自己的帐号密码及samba的帐号和密码
linux·运维·服务器
实在智能RPA1 天前
Agent 在物流行业能实现哪些自动化?——深度拆解 AI Agent 驱动的智慧物流新范式
运维·人工智能·ai·自动化
地下核武1 天前
Ubuntu 24.04 在线安装 Qt 6.10.2 后 Qt Creator 无法启动问题记录与解决
linux·qt·ubuntu
张3231 天前
Linux 启动过程
linux·运维
三万棵雪松1 天前
【Linux 物联网网关主控系统-Linux主控部分(二)】
linux·嵌入式linux
chinesegf1 天前
ubuntu建虚拟环境制作docker容器
linux·ubuntu·docker