Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805231
  • 博文数量: 106
  • 博客积分: 1250
  • 博客等级: 少尉
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-09 09:38
文章分类

全部博文(106)

文章存档

2014年(1)

2013年(13)

2012年(92)

分类: LINUX

2013-03-11 13:45:33

串口多路复用收发数据

1.1 设计目的

具有GSMGPRS 功能的设备需要同时处理(发送和接收)各种数据流,例如AT指令流(AT Command)GPRS 数据流,GSM线路交接数据流(circuit switched data) 等,这些数据流彼此独立。 

对于不使用多路复用的设备来说,在某个时间段只能处理一种数据流或者一路数据流,这样,设备的使用率不高,因此设计采用GSM0710规范定义的标准多路复用协议,将传输设备划分成多个逻辑链路通道(Channel 或者DLC),来同时传输这些数据流,每个逻辑通道都具备独立的缓冲区(buffer)和流量控制。

本文来自实际应用,在集中器浙江版本中,需要同时支持短信和GPRS网络。只有一个串口给GPRS模块。一个接口,多个描述符来收发数据。在unix系统中,有典型的实例。I/O多路转接。通过select来实现I/O的多路复用。

1.2 具体应用分析

通过一个守护进程mux来实现串口的多路复用。如下图:



首先从main函数进入通过系统函数getopt()来处理main的输入参数。然后在openDevicesAndMuxMode函数两次调用open_pty函数来虚拟出两个接口mux0mux1
在代码中用
ussp_fd[] 来表示。open_serialport函数打开串口ttys0

int openDevicesAndMuxMode ()

{

int     i = 0;

int     ret = -1;

maxfd = 0;

for (i = 0; i < numOfPorts; i++) {

remaining[i] = 0;

if ((ussp_fd[i] = open_pty (ptydev[i], i)) < 0) {

syslog (LOG_ERR, "Can't open %s. %s (%d).\n", ptydev[i], strerror (errno), errno);

return -1;

}

else if (ussp_fd[i] > maxfd)

maxfd = ussp_fd[i];

cstatus[i].opened = 0;

cstatus[i].v24_signals = S_DV | S_RTR | S_RTC | EA;

}

cstatus[i].opened = 0;

// open the serial port

if ((serial_fd = open_serialport (serportdev)) < 0) {

syslog (LOG_ALERT, "Can't open %s. %s (%d).\n", serportdev, strerror (errno), errno);

return -1;

}

      ........

return ret;

}

然后在while中用用selelct 实现 i/o 多路复用。这个过程主要分为几个部分,

l 调用系统接口select,将接口描述符加入到select的描述符集中。

FD_ZERO (&rfds);

FD_SET (serial_fd, &rfds);

for (i = 0; i < numOfPorts; i++)

FD_SET (ussp_fd[i], &rfds);

timeout.tv_sec = 1;

timeout.tv_usec = 0;

l 通过select函数,来检测是否指定的条件发生。

sel = select (maxfd + 1, &rfds, NULL, NULL, &timeout);

if (faultTolerant) {

// get the current time

time (¤tTime);

}

if (sel > 0) {

……

}

l 读物理接口串口

当串口有数据时,首先read读取数据,将该帧读取到本地buffer中。extract函数解析帧,调用write_frame函数将解析出来的数据发送到相应的虚拟接口。

if (FD_ISSET (serial_fd, &rfds)) 

{

if ((size = gsm0710_buffer_free (in_buf)) > 0

&& (len = read (serial_fd, buf, min (size, sizeof (buf)))) > 0) {

//sleep (1);

gsm0710_buffer_write (in_buf, buf, len);  // 读取数据到本地buffer

//  解析数据帧,发送到相应的虚拟串口

if (extract_frames (in_buf) > 0 && faultTolerant) {

frameReceiveTime = currentTime;

pingNumber = 1;

}

}

}

l 依次读虚拟接口数据,read读取ussp_ff[]中的数据,通过ussp_recv_data向串口ttys0发送数据。对两个虚拟的接口的数据流分析如下:

/dev/ttyS0 <---> /dev/pts/1| <----> SMS

/dev/ttyS0 <---> /dev/pts/0 | <----> gprs

for (i = 0; i < numOfPorts; i++)

if (FD_ISSET (ussp_fd[i], &rfds)) {   // USSP_fd[] = ttys[]

if (remaining[i] > 0) {

memcpy (buf, tmp[i], remaining[i]);

free (tmp[i]);

}

if ((len = read (ussp_fd[i], buf + remaining[i], sizeof (buf) - remaining[i])) > 0)

{

remaining[i] = ussp_recv_data (buf, len + remaining[i], i);

}

if (len < 0) {

            ……

}

}



1.3 Gprs模块下,将串口虚拟成两个接口的数据流程图如下:


其中涉及的控制的帧的分析参考:《华为串口多路复用.pdf


ussp_fd[]    // 虚拟接口名称mux[]

serial_fd[]   // 物理接口名称ttys0


fd = open("/dev/pts/1", O_RDWR | O_NOCTTY);

阅读(2112) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~