本文共 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