博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
虚拟机串口与主机串口通信·小程序(上)
阅读量:6292 次
发布时间:2019-06-22

本文共 8226 字,大约阅读时间需要 27 分钟。

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

<br>第一步 确定端口
打开VSPD,如下图,点击“Port pairs”-“create pair“。我们要用到的就是COM1和COM2。此时,二者状态都是:close。
虚拟机串口与主机串口通信·小程序(上)虚拟机串口与主机串口通信·小程序(上)
虚拟机串口与主机串口通信·小程序(上)
<br>然后,打开虚拟机,“虚拟机”-“设置”-“串口”-选择端口号,并开启,确认。
虚拟机串口与主机串口通信·小程序(上)
<br>如下图,这样代表该串口已打开。
虚拟机串口与主机串口通信·小程序(上)
<br>再打开SSCOM,选择“串口2”,打开串口。
虚拟机串口与主机串口通信·小程序(上)
<br>同样,在VSPD中观察到串口已经被打开。
虚拟机串口与主机串口通信·小程序(上)
<br>第二步 测试
虚拟机串口向主机发:
虚拟机串口与主机串口通信·小程序(上)
<br>可以看到,主机串口收到了“hello”。
虚拟机串口与主机串口通信·小程序(上)
<br>说到这里,复习一下“终端”。
1、控制台终端:tty0~tty6,也叫虚拟终端。(tty0是当前正在使用的虚拟终端的别名)
2、伪终端:pty(图形终端,远程控制终端)
3、串口终端:ttyS0~ttyS4
控制终端:tty。即当前正在使用的终端(以上的任何一种都有可能是控制终端)。
这个,自己输入输出重定向练习练习就清楚了~

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

虚拟机串口与主机串口通信·小程序(上)
第三步 初始化串口配置信息
一般来说,我们可以手动修改虚拟机和主机的串口配置信息(波特率、校验位、停止位等等),但是每次都要修改是不是很麻烦呢。所以,可以写一个初始化串口配置信息的文件,在通信之前执行就可以了。
<br>初始化串口的代码如下:
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;}

<br>如何查看串口配置是否成功?要查看某个串口的波特率等信息,可以在控制台输入命令: stty -F /dev/ttyS0 -a #ttyS0为要查看的串口。

第四步 读取配置信息
这个,在上一篇文章中已经展示过了,为了练手,可以从配置文件中读取配置信息,再利用以上代码进行初始化,当然也可以直接初始化啦~
<br>以下是从文件中获取配置信息(基于上一篇文章内容)
配置信息:
虚拟机串口与主机串口通信·小程序(上)
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

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

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

转载于:https://blog.51cto.com/13097817/2049080

你可能感兴趣的文章
从源码剖析useState的执行过程
查看>>
地包天如何矫正?
查看>>
中间件
查看>>
Android SharedPreferences
查看>>
css面试题
查看>>
Vue组建通信
查看>>
用CSS画一个带阴影的三角形
查看>>
前端Vue:函数式组件
查看>>
程鑫峰:1.26特朗.普力挺美元力挽狂澜,伦敦金行情分析
查看>>
safari下video标签无法播放视频的问题
查看>>
01 iOS中UISearchBar 如何更改背景颜色,如何去掉两条黑线
查看>>
对象的继承及对象相关内容探究
查看>>
Spring: IOC容器的实现
查看>>
Serverless五大优势,成本和规模不是最重要的,这点才是
查看>>
Nginx 极简入门教程!
查看>>
iOS BLE 开发小记[4] 如何实现 CoreBluetooth 后台运行模式
查看>>
Item 23 不要在代码中使用新的原生态类型(raw type)
查看>>
为网页添加留言功能
查看>>
JavaScript—数组(17)
查看>>
Android 密钥保护和 C/S 网络传输安全理论指南
查看>>