主机串口用到的工具是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 <stdio.h>
#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);
//初始化串口 返回值:fd
int 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;
}
}
//初始化串口 返回值:fd
int 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了。明天再写~
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/183016.html