分类: LINUX
2013-03-11 13:45:33
原文地址:串口多路复用收发数据 作者:dengwei3010
串口多路复用收发数据
1.1 设计目的
具有GSM和GPRS 功能的设备需要同时处理(发送和接收)各种数据流,例如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函数来虚拟出两个接口mux0和mux1,
在代码中用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);