千家信息网

虚拟机串口与主机串口通信·小程序(上)

发表于:2025-12-01 作者:千家信息网编辑
千家信息网最后更新 2025年12月01日,主机串口用到的工具是SSCOM32,虚拟机串口工具是VSPD。即通过VSPD工具,可以将二者的串口相连,可以想象成有一根串口线连接了主机和虚拟机。第一步 确定端口打开VSPD,如下图,点击"Port
千家信息网最后更新 2025年12月01日虚拟机串口与主机串口通信·小程序(上)

主机串口用到的工具是SSCOM32,虚拟机串口工具是VSPD。即通过VSPD工具,可以将二者的串口相连,可以想象成有一根串口线连接了主机和虚拟机。

第一步 确定端口
打开VSPD,如下图,点击"Port pairs"-"create pair"。我们要用到的就是COM1和COM2。此时,二者状态都是:close。



然后,打开虚拟机,"虚拟机"-"设置"-"串口"-选择端口号,并开启,确认。


如下图,这样代表该串口已打开。


再打开SSCOM,选择"串口2",打开串口。


同样,在VSPD中观察到串口已经被打开。


第二步 测试
虚拟机串口向主机发:


可以看到,主机串口收到了"hello"。


说到这里,复习一下"终端"。
1、控制台终端:tty0~tty6,也叫虚拟终端。(tty0是当前正在使用的虚拟终端的别名)
2、伪终端:pty(图形终端,远程控制终端)
3、串口终端:ttyS0~ttyS4
控制终端:tty。即当前正在使用的终端(以上的任何一种都有可能是控制终端)。
这个,自己输入输出重定向练习练习就清楚了~


同时,在vspd中也能看到二者传输的信息:

第三步 初始化串口配置信息
一般来说,我们可以手动修改虚拟机和主机的串口配置信息(波特率、校验位、停止位等等),但是每次都要修改是不是很麻烦呢。所以,可以写一个初始化串口配置信息的文件,在通信之前执行就可以了。

初始化串口的代码如下:
port.h

#ifndef _PORT_H_#define _PORT_H_#include #include < termios.h>#include < unistd.h>#include < sys/types.h>#include < sys/stat.h>#include < fcntl.h>/** struct termios{           tcflag_t c_iflag;             // input modes            tcflag_t c_oflag;             // output modes            tcflag_t c_cflag;             // control modes            tcflag_t c_lflag;             // local modes            cc_t     c_cc[NCCS];      // control chars  };*///设置波特率void setSpeed(struct termios *ptio,int speed);//设置数据位void setData(struct termios *ptio,int data);//设置奇偶校验void setParity(struct termios *ptio,int flag);  //0-忽略奇偶校验  1-设置奇校验  2-设置偶校验//设置停止位void setStop(struct termios *ptio, int stop);//初始化串口 返回值:fdint portInit(char devname[],int speed,int data,int flag,int stop);#endif

port.c

#include " port.h"//设置波特率void setSpeed(struct termios *ptio,int speed){    switch(speed)    {        case 9600:            cfsetispeed(ptio,B9600);            cfsetospeed(ptio,B9600);            break;        case 14400:            break;        case 19200:            cfsetispeed(ptio,B19200);            cfsetospeed(ptio,B19200);            break;        case 38400:            cfsetispeed(ptio,B38400);            cfsetospeed(ptio,B38400);            break;        case 115200:            cfsetispeed(ptio,B115200);            cfsetospeed(ptio,B115200);            break;        default:            break;    }}//设置数据位void setData(struct termios *ptio,int data){    ptio->c_cflag &= ~CSIZE;    switch(data)    {        case 5:            ptio->c_cflag |= CS5;            break;        case 6:            ptio->c_cflag |= CS6;            break;        case 7:            ptio->c_cflag |= CS7;            break;        case 8:            ptio->c_cflag |= CS8;            break;        default:            break;    }}//设置奇偶校验   0-忽略奇偶校验  1-设置奇校验  2-设置偶校验void setParity(struct termios *ptio,int flag)   {    switch(flag)    {        case 0:            ptio->c_cflag &= ~PARENB;            break;        case 1:            ptio->c_cflag |= PARENB;            ptio->c_cflag |= PARODD;            ptio->c_iflag |= (INPCK | ISTRIP);            break;        case 2:            ptio->c_iflag |= (INPCK | ISTRIP);            ptio->c_cflag |= PARENB;            ptio->c_cflag &= ~PARODD;            break;        default:            break;    }}//设置停止位 (若停止位为1,则清除CSTOPB;若停止位为2,则激活CSTOPB)void setStop(struct termios *ptio, int stop){    switch(stop)    {        case 1:            ptio->c_cflag &= ~CSTOPB;            break;        case 2:            ptio->c_cflag |= CSTOPB;            break;        default:            break;    }}//初始化串口 返回值:fdint portInit(char devname[],int speed,int data,int flag,int stop){    int fd;    struct termios tio = {0};    //打开串口设备    fd = open(devname,O_RDWR);    if(fd == -1)    {        printf("open port : %s failed.\n",devname);        return;    }    //获取原有串口配置    tcgetattr(fd,&tio);    //激活选项有CLOCAL和CREAD,用于本地连接和接收使能    tio.c_cflag |= CLOCAL | CREAD;    //设置波特率    setSpeed(&tio,speed);    //设置数据位,需要使用掩码设置    setData(&tio,data);    //设置奇偶校验    setParity(&tio,flag);    //设置停止位    setStop(&tio, stop);    //设置最少字符和等待时间    tio.c_cc[VTIME] = 0;        tio.c_cc[VMIN] = 1; //最小字符,缓冲区里达到数量时才返回    //设置不采用流控制    tio.c_cflag &= ~CRTSCTS;    //清除(输入)缓存    tcflush(fd,TCIFLUSH);    //设置串口默认为堵塞模式    fcntl(fd,F_SETFL,0);    //激活配置    tcsetattr(fd,TCSANOW,&tio);    return fd;}


如何查看串口配置是否成功?要查看某个串口的波特率等信息,可以在控制台输入命令: stty -F /dev/ttyS0 -a #ttyS0为要查看的串口。
第四步 读取配置信息
这个,在上一篇文章中已经展示过了,为了练手,可以从配置文件中读取配置信息,再利用以上代码进行初始化,当然也可以直接初始化啦~

以下是从文件中获取配置信息(基于上一篇文章内容)
配置信息:

readConfig.h

#ifndef _READCONFIG_H_#define _READCONFIG_H_#include < stdio.h>#include < string.h>#include < sys/types.h>#include < sys/stat.h>#include < fcntl.h>/*串口属性结构体*/struct t_port{    char devname[20];    int speed;    int data;    int parity;    int stop;};//去空格 void rm_space(char *pStr);//去注释 void rm_annotation(char *pStr);//获取配置项信息void getMsg(char filename[],struct t_port *port);#endif


readConfig.c

#include " readConfig.h"int icount = 0;//去空格 void rm_space(char *pStr){    char *pos = pStr;    pos = strchr(pStr,' ');    while(pos != NULL)    {        strcpy(pos,pos+1);        pos = strchr(pos,' ');    }}//去注释 void rm_annotation(char *pStr){    icount++;    char *pos = pStr;    char *end = NULL;    if(icount == 1) //第一行有可能顶格    {        pos = strchr(pStr,'#');    }    else    {        pos = strstr(pStr,"\n#");    }    while(pos != NULL)    {        end = strchr(pos+1,'\n');        strcpy(pos,end);        pos = strstr(pStr,"\n#");    }   }//获取配置项信息void getMsg(char filename[],struct t_port *port){    int fd;    int readByte;    char buf[512] = "";    char *pos = NULL;    char *end = NULL;    char key[20] = " ";    char value[20] = " ";    char keys[10][20] = {""};    char values[10][20] = {""};    int flag = 0;    int i = 0;    //打开配置文件    fd = open(filename, O_RDWR);    readByte = read(fd,buf,512);    //处理数据      rm_space(buf);    rm_annotation(buf);    pos = strchr(buf,'\n');    end = strchr(pos,'=');    while(end != NULL)    {        memset(key,0,sizeof(key));        memset(value,0,sizeof(value));        memcpy(key,pos+1,end - (pos + 1));        pos = end;        end = strchr(pos,'\r');        if(end == NULL) //if the final data        {            flag = 1;            break;        }        memcpy(value,pos+1,end - (pos + 1));        //存key value        memcpy(keys[i],key,strlen(key));        memcpy(values[i],value,strlen(value));        i++;        pos = end + 1;        end = strchr(pos,'=');    }    if(flag)        {        end = strchr(pos,'\0');        memcpy(value,pos+1,end - (pos + 1));        memcpy(keys[i],key,strlen(key));        memcpy(values[i],value,strlen(value));    }    //进行匹配    for(i = 0; i < 10 ;i++)    {        if(strcmp(keys[i],"dev") == 0)        {            strcpy(port->devname,values[i]);        }        else if(strcmp(keys[i],"speed") == 0)        {            port->speed = atoi(values[i]);        }        else if(strcmp(keys[i],"data") == 0)        {            port->data = atoi(values[i]);        }        else if(strcmp(keys[i],"parity") == 0)        {            port->parity = atoi(values[i]);        }        else if(strcmp(keys[i],"stop") == 0)        {            port->stop = atoi(values[i]);        }    }}

第五步 编写通信程序
那么,主函数只要调用以上功能能函数,再调用read\write系统函数,就可以了。

#include < stdio.h>#include " port.h"#include " readConfig.h" int main(){    int fd;    char filename[20] = "serial.cfg";    char recbuf[100] = "";    char sendbuf[100] = "";    struct t_port port = {0};    //从文件获取配置信息    getMsg(filename,&port);    //串口初始化    fd = portInit(port.devname,port.speed,port.data,port.parity,port.stop);    //主机与虚拟机串口通信    while(1)    {        memset(recbuf,0,100);        memset(sendbuf,0,100);        //虚拟机等待主机用户输入,接收消息        printf("please wait...\n");        read(fd,recbuf,100);        printf("receive msg: %s\n",recbuf);        //等待虚拟机用户输入,发送消息给主机        printf("send msg: ");        scanf("%s",sendbuf);        if(strcmp(sendbuf,"over") == 0)        {            break;        }        write(fd,sendbuf,sizeof(sendbuf));    }    close(fd);    return 0;}

第六步 编译运行
编译运行之后,主机(SSCOM32)虚拟机串口之间就可以通信了。如下图:

主机:hello
虚拟机:world
主机:12345
虚拟机:over

运行时,如果出现主机发消息,虚拟机接收不到的情况,是因为消息内容都还在缓冲区。比如,在用write发送数据时没有键入回车,信息就将发送不出去的情况,这主要是因为我们在输出输入时是按照 规范模式接受到回车或者换行才发送,而很多情况我们是不需要回车和换行的,这时,应当切换到行方式输入,tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);不经处理直接发送。
当然也可以直接在SSCOM的界面上勾选"发送新行"。


但是,可以发现,我们只能实现:主机发一句,虚拟机收到内容后才能发送,然后主机收到内容才可以继续发送。不能做到主机和虚拟机同时发送,那么如何解决呢?多进程就OK了。明天再写~

串口 主机 配置 信息 终端 输入 控制 奇偶 数据 文件 波特率 波特 通信 内容 消息 函数 工具 情况 激活 运行 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络技术研发的英文缩写 网信办网络技术保障岗 c语言软件开发实例基础篇 安徽网络技术咨询哪个正规 软件开发行业领域专家作用 win2008数据库安装 直播平台个性化软件开发支持 试叙述数据库的安全性完整性 上海联诚网络技术有限公司 mac 服务器设定 通讯技术网络技术电脑技术 日照网络安全平台登录 教师掌握网络技术的好处 棋牌类软件开发与测试 it软件开发哪个学校好 asp修改数据库名称 网络安全证书不能上网 网络安全手抄报一年级简单漂亮 asp同时两个数据库 深圳市精湛网络技术有限公司 万方数据库热点关键词 手机待机完后提示连接不上服务器 网络安全的日常隐患有多少 数据库不同库之间查询 《网络安全法》平台责任 云服务器双十一上线时间 微信永久封号网络安全法 网络安全部队发言材料 辽图手机数据库怎么看 泉州手机app软件开发
0