Luat Air720S系列模块_CMUX_使用说明

                        <pre><span style="font-weight: 700;"><span style="font-family: 宋体; font-size: 22pt;">1.</span></span><b style="color: inherit; font-family: inherit;"><span style="font-family: 宋体; font-size: 22pt;"><font face="宋体">启动</font>CMUX<font face="宋体">模式</font></span></b></pre><p class="MsoNormal"><span style="font-family: 宋体; color: rgb(77, 77, 77); font-size: 11pt; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><font face="宋体">通常模块发送</font>AT+CMUX命令来激活多路复用,该命令格式为:</span><span style="font-family: 宋体; color: rgb(77, 77, 77); font-size: 11pt; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><o:p></o:p></span></p><table class="MsoNormalTable" border="1" cellspacing="0" style="width: 489.05pt; margin-left: 5.4pt; border: none;"><tbody><tr><td width="94" valign="top" style="width:56.7000pt;padding:0.0000pt 5.4000pt 0.0000pt 5.4000pt ;border-left:1.0000pt solid rgb(178,161,199);

mso-border-left-alt:0.5000pt solid rgb(178,161,199);border-right:1.0000pt solid rgb(178,161,199);mso-border-right-alt:0.5000pt solid rgb(178,161,199);
border-top:1.0000pt solid rgb(178,161,199);mso-border-top-alt:0.5000pt solid rgb(178,161,199);border-bottom:1.0000pt solid rgb(178,161,199);
mso-border-bottom-alt:0.5000pt solid rgb(178,161,199);background:rgb(192,192,192);”>

命令类型<o:p>

语法<o:p>

返回<o:p>

设置命令<o:p>

AT+CMUX= [[,[,[,[,[,[,[,[,]]]]]]]]]<o:p>

<o:p> 

OK<o:p>

查询命令<o:p>

AT+CMUX?<o:p>

+CMUX:[[,[,[,[,[,[,[,[,]]]]]]]]]<o:p>

<o:p> 

OK<o:p>

测试命令<o:p>

AT+CMUX=?<o:p>

+CMUX:(),(s),( s),(s),(s),(s),(s),(s),(s)  <o:p>

<o:p> 

OK<o:p>

 

参数定义:<o:p>

参数<o:p>

定义<o:p>

取值<o:p>

对取值的说明<o:p>

<o:p>

复用透传机制<o:p>

0<o:p>

基本选择{gw:这里改成基本模式更合适些吧}<o:p>

<o:p>

控制通道的复用器建立的方式<o:p>

0<o:p>

仅使用UIH <o:p>

<o:p>

传输速率,单位为bits/s<o:p>

1<o:p>

9600 <o:p>

2<o:p>

19200<o:p>

3<o:p>

38400<o:p>

4<o:p>

57600<o:p>

5<o:p>

115200<o:p>

6<o:p>

230400<o:p>

7<o:p>

460800<o:p>

<o:p>

最大报文大小<o:p>

1255<o:p>

默认:127<o:p>

<o:p>

接收timer10ms为单位<o:p>

1-255<o:p>

默认:10(100 ms)<o:p>

<o:p>

重传的最大数量<o:p>

0-100<o:p>

默认:3<o:p>

<o:p>

复用器控制通道的响应定时器,以10ms 为单位<o:p>

2-255<o:p>

默认:30<o:p>

<o:p>

唤醒响应定时器,以秒为单位<o:p>

1-255<o:p>

默认:10<o:p>

<o:p>

窗口大小,适用于有错误恢复选项的高级操作<o:p>

1-7<o:p>

默认:2<o:p>


2.CMUX 帧结构 

多路复用有三种操作模式:基本模式、带错误恢复功能的高级模式、不带错误恢复功能的高级模式(具体可以参见GSM 07.10协议)。不同的GPRS模块也支持不同的模式。<o:p>

<o:p> 

其中,帧类型可分控制帧和信息帧。 <o:p>

CMUX双方通过控制帧协商用以构建、拆除虚拟链路,控制帧又为以下几种: <o:p>

SABM:建立DLC <o:p>

UA:响应SABM帧或DISC<o:p>

DM:链路未成功建立时,对收到的DISC命令的响应 <o:p>

DISC:通知对端拆除链接,在DLC0发送DISC帧,等于退出MUX功能<o:p>

<o:p> 

信息帧: <o:p>

UIH\UI\I:这三种是信息帧,也就是携带要传输数据的帧。

3.LINUX下的CMUX使用

3.1 挂载USB设备<o:p>

在可移动设备,勾选QinHeng USB2.0-Serial

<o:p>

然后打开终端控制台,ls /dev/ttyUSB*查看USB设备,如下表示挂载成功。


<o:p>

<o:p>

3.2 CMUX虚拟串口生成<o:p>

参考用例:https://www.kernel.org/doc/html/latest/driver-api/serial/n_gsm.html


<o:p>

3.2.1 测试实例gsmmux.h源代码

#ifndef _LINUX_GSMMUX_H

#define _LINUX_GSMMUX_H

 

struct gsm_config

{

unsigned int adaption;

unsigned int encapsulation;

unsigned int initiator;

unsigned int t1;

unsigned int t2;

unsigned int t3;

unsigned int n2;

unsigned int mru;

unsigned int mtu;

unsigned int k;

unsigned int i;

unsigned int unused[8]; /* Padding for expansion without

   breaking stuff /

};

 

#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)

#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)

 

struct gsm_netconfig {

unsigned int adaption;  / Adaption to use in network mode /

unsigned short protocol;/ Protocol to use - only ETH_P_IP supported /

unsigned short unused2;

char if_name[IFNAMSIZ]; / interface name format string /

__u8 unused[28];        / For future use /

};

 

#define GSMIOC_ENABLE_NET      _IOW('G', 2, struct gsm_netconfig)

#define GSMIOC_DISABLE_NET     _IO('G', 3)

 

 

#endif

3.2.2 测试实例cmux.c 源代码

#include <stdlib.h>

#include <stdio.h>

#include <stdarg.h>

#include <string.h>

#include <sys/ioctl.h>

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

/**

gsmmux.h provides n_gsm line dicipline structures and functions.

It should be kept in sync with your kernel release.

/

#include "gsmmux.h"

 

/* n_gsm ioctl /

#ifndef N_GSM0710

# define N_GSM0710 21

#endif

 

/ attach a line discipline ioctl /

#ifndef TIOCSETD

# define TIOCSETD 0x5423

#endif

 

/ serial port of the modem /

//#define SERIAL_PORT "/dev/ttyUSB0"

 

/ line speed /

#define LINE_SPEED B115200

 

/ maximum transfert unit (MTU), value in bytes /

#define MTU 512

 

/**

whether or not to create virtual TTYs for the multiplex

0 : do not create

1 : create

/

#define CREATE_NODES 1

 

/ number of virtual TTYs to create (most modems can handle up to 4) /

#define NUM_NODES 4

 

/ name of the virtual TTYs to create /

#define BASENAME_NODES "/dev/ttyGSM"

 

/ name of the driver, used to get the major number /

#define DRIVER_NAME "gsmtty"

 

/**

whether or not to print debug messages to stderr

0 : debug off

1 : debug on

/

#define DEBUG 1

 

/**

whether or not to detach the program from the terminal

0 : do not daemonize

1 : daemonize

/

#define DAEMONIZE 1

 

 / size of the reception buffer which gets data from the serial line /

#define SIZE_BUF 256

 

 

/**

Prints debug messages to stderr if debug is wanted

/

static void dbg(char fmt, ...) {

va_list args;

 

if (DEBUG) {

fflush(NULL);

va_start(args, fmt);

vfprintf(stderr, fmt, args);

va_end(args);

fprintf(stderr, "\n");

fflush(NULL);

}

return;

}

 

/

Sends an AT command to the specified line and gets its result

Returns  0 on success

-1 on failure

/

int send_at_command(int serial_fd, char command, int max_retry) {

char buf[SIZE_BUF];

int r;

int retry = 0;

 

do{

/ write the AT command to the serial line /

if (write(serial_fd, command, strlen(command)) <= 0)

err(EXIT_FAILURE, "Cannot write to ttyUSB");

/ wait a bit to allow the modem to rest /

sleep(1);

 

/ read the result of the command from the modem /

memset(buf, 0, sizeof(buf));

r = read(serial_fd, buf, sizeof(buf));

if (r == -1)

{

dbg("read errno %d", errno);

retry++;

dbg("retry write time: %d", retry);

}

/ if there is no result from the modem, return failure /

if (r == 0) {

dbg("%s\t: No response", command);

retry++;

//return -1;

}

if(retry!= 0 && retry >= max_retry)

{

err(EXIT_FAILURE, "Cannot read ttyUSB");

}

else

{

break;

}

}while(1);

 

 

 

/ if we have a result and want debug info, strip CR & LF out from the output /

if (DEBUG) {

int i;

char bufp[SIZE_BUF];

memcpy(bufp, buf, sizeof(buf));

for(i=0; i

if (bufp[i] == '\r' || bufp[i] == '\n')

bufp[i] = ' ';

}

dbg("%s\t: %s", command, bufp);

}

 

/ if the output shows "OK" return success /

if (strstr(buf, "OK\r") != NULL) {

return 0;

}

return -1;

 

}

 

 

/**

Gets the major number of the driver device

Returns  the major number on success

-1 on failure

/

int get_major(char driver) {

 

FILE *fp;

char *line = NULL;

size_t len = 0;

ssize_t read;

char device[20];

int major = -1;

/ open /proc/devices file /

if ((fp = fopen("/proc/devices", "r")) == NULL)

err(EXIT_FAILURE, "Cannot open /proc/devices");

 

/ read the file line by line /

while ((major == -1) && (read = getline(&line, &len, fp)) != -1) {

/ if the driver name string is found in the line, try to get the major /

if (strstr(line, driver) != NULL) {

if (sscanf(line,"%d %s\n", &major, device) != 2)

major = -1;

}

/ free the line before getting a new one /

if (line) {

free(line);

line = NULL;

}

 

}

 

/ close /proc/devices file /

fclose(fp);

 

return major;

}

 

/

Creates nodes for the virtual TTYs

Returns the number of nodes created

/

int make_nodes(int major, char basename, int number_nodes) {

 

int minor, created = 0;

dev_t device;

char node_name[15];

mode_t oldmask;

 

/ set a new mask to get 666 mode and stores the old one /

oldmask = umask(0);

 

for (minor=1; minor

 

/ append the minor number to the base name /

sprintf(node_name, "%s%d", basename, minor);

/ store a device info with major and minor /

device = makedev(major, minor);

/ create the actual character node /

if (mknod(node_name, S_IFCHR | 0666, device) != 0) {

warn("Cannot create %s", node_name);

} else {

created++;

dbg("Created %s", node_name);

}

 

}

 

/ revert the mask to the old one /

umask(oldmask);

 

return created;

}

 

/

Removes previously created TTY nodes

Returns nothing, it doesn't really matter if it fails

/

void remove_nodes(char basename, int number_nodes) {

 

int node;

char node_name[15];

 

for (node=1; node

 

/ append the minor number to the base name /

sprintf(node_name, "%s%d", basename, node);

/ unlink the actual character node /

dbg("Removing %s", node_name);

if (unlink(node_name) == -1)

warn("Cannot remove %s", node_name);

 

}

 

return;

}

 

/

Function raised by signal catching

/

void signal_callback_handler(int signum) {

remove_nodes(BASENAME_NODES, NUM_NODES);

return;

}

int main(int argc, char** argv) {

 

int serial_fd, major;

struct termios tio;

int ldisc = N_GSM0710;

struct gsm_config gsm;

char atcommand[40];

 

char* port;

 

if(argc != 2)

{

printf("usage\n ./cmux /dev/ttyUSBx\n");

exit(-1);

}

port = argv[1];

 

/* print global parameters /

dbg("SERIAL_PORT = %s", port);

 

/ open the serial port /

serial_fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

if (serial_fd == -1)

{

dbg("openport ret = %d\n", errno);

err(EXIT_FAILURE, "Cannot open %s", port);

}

/ get the current attributes of the serial port /

if (tcgetattr(serial_fd, &tio) == -1)

err(EXIT_FAILURE, "Cannot get line attributes");

/ set the new attrbiutes */

tio.c_iflag = 0;

tio.c_oflag = 0;

tio.c_cflag = CS8 | CREAD | CLOCAL;

tio.c_cflag &= (CRTSCTS);<o:p>

tio.c_lflag = 0;<o:p>

tio.c_cc[VMIN] = 1;<o:p>

tio.c_cc[VTIME] = 0;<o:p>

<o:p>

/* write the speed of the serial line /<o:p>

if (cfsetospeed(&tio, LINE_SPEED) < 0 || cfsetispeed(&tio, LINE_SPEED) < 0)<o:p>

err(EXIT_FAILURE, “Cannot set line speed”);<o:p>

<o:p>

/ write the attributes /<o:p>

if (tcsetattr(serial_fd, TCSANOW, &tio) == -1)<o:p>

err(EXIT_FAILURE, “Cannot set line attributes”);<o:p>

<o:p> 

/**<o:p>

Send AT commands to put the modem in CMUX mode.<o:p>

This is vendor specific and should be changed<o:p>

to fit your modem needs.<o:p>

The following matches Quectel M95.<o:p>

/<o:p>

//if (send_at_command(serial_fd, “AT+IFC=2,2\r”) == -1)<o:p>

// errx(EXIT_FAILURE, “AT+IFC=2,2: bad response”);<o:p>

if (send_at_command(serial_fd, “AT+CGMM\r”, 3) == -1)<o:p>

warnx(“AT+GMM: bad response”);<o:p>

if (send_at_command(serial_fd, “AT\r”, 0) == -1)<o:p>

warnx(“AT: bad response”);<o:p>

sprintf(atcommand, “AT+CMUX=0,0,5\r”);<o:p>

if (send_at_command(serial_fd, atcommand, 0) == -1)<o:p>

errx(EXIT_FAILURE, “Cannot enable modem CMUX”);<o:p>

<o:p> 

/* use n_gsm line discipline /<o:p>

sleep(2);<o:p>

if (ioctl(serial_fd, TIOCSETD, &ldisc) < 0)<o:p>

err(EXIT_FAILURE, “Cannot set line dicipline. Is ‘n_gsm’ module registred?”);<o:p>

<o:p> 

/ get n_gsm configuration /<o:p>

if (ioctl(serial_fd, GSMIOC_GETCONF, &gsm) < 0)<o:p>

err(EXIT_FAILURE, “Cannot get GSM multiplex parameters”);<o:p>

<o:p> 

/ set and write new attributes /<o:p>

gsm.initiator = 1;<o:p>

gsm.encapsulation = 0;<o:p>

gsm.mru = MTU;<o:p>

gsm.mtu = MTU;<o:p>

gsm.t1 = 10;<o:p>

gsm.n2 = 3;<o:p>

gsm.t2 = 30;<o:p>

gsm.t3 = 10;<o:p>

<o:p> 

if (ioctl(serial_fd, GSMIOC_SETCONF, &gsm) < 0)<o:p>

err(EXIT_FAILURE, “Cannot set GSM multiplex parameters”);<o:p>

dbg(“Line dicipline set”);<o:p>

<o:p>

/ create the virtual TTYs /<o:p>

if (CREATE_NODES) {<o:p>

int created;<o:p>

if ((major = get_major(DRIVER_NAME)) < 0)<o:p>

errx(EXIT_FAILURE, “Cannot get major number”);<o:p>

if ((created = make_nodes(major, BASENAME_NODES, NUM_NODES)) < NUM_NODES)<o:p>

warnx(“Cannot create all nodes, only %d/%d have been created.”, created, NUM_NODES);<o:p>

}<o:p>

<o:p> 

/ wait to keep the line discipline enabled, wake it up with a signal /<o:p>

signal(SIGINT, signal_callback_handler);<o:p>

signal(SIGTERM, signal_callback_handler);<o:p>

<o:p>

/ detach from the terminal if needed /<o:p>

if (DAEMONIZE) {<o:p>

dbg(“Going to background”);<o:p>

if (daemon(0,0) != 0)<o:p>

err(EXIT_FAILURE, “Cannot daemonize”);<o:p>

}<o:p>

<o:p> 

dbg(“catching sig”);<o:p>

<o:p> 

pause();<o:p>

<o:p>

/ remove the created virtual TTYs /<o:p>

if (CREATE_NODES) {<o:p>

remove_nodes(BASENAME_NODES, NUM_NODES);<o:p>

}<o:p>

<o:p> 

/ close the serial line */<o:p>

close(serial_fd);<o:p>

<o:p> 

return EXIT_SUCCESS;<o:p>

}

<o:p>

<o:p>

3.2.3 生成cmux可执行文件

通执行gcc cmux.c -o cmux cmux.c编译成可执行文件

<o:p>

3.2.4 虚拟串口生成

通执行 ./cmux /dev/ttyUSB0 就会创建几个虚拟串口

<o:p>

下图中的ttyGSM1,ttyGSM2,ttyGSM3,ttyGSM4及为虚拟出的串口

<o:p>

<h1 style=”font-family: “ helvetica=”” neue”,=”” helvetica,=”” arial,=”” “pingfang=”” sc”,=”” “hiragino=”” sans=”” gb”,=”” “wenquanyi=”” micro=”” hei”,=”” “microsoft=”” yahei”,=”” sans-serif;=”” color:=”” rgb(0,=”” 0,=”” 0);”=””>4.常见问题处理<h3 style=”font-family: “ helvetica=”” neue”,=”” helvetica,=”” arial,=”” “pingfang=”” sc”,=”” “hiragino=”” sans=”” gb”,=”” “wenquanyi=”” micro=”” hei”,=”” “microsoft=”” yahei”,=”” sans-serif;=”” color:=”” rgb(0,=”” 0,=”” 0);”=””>4.1 常见AT 错误处理<h3 style=”font-family: “ helvetica=”” neue”,=”” helvetica,=”” arial,=”” “pingfang=”” sc”,=”” “hiragino=”” sans=”” gb”,=”” “wenquanyi=”” micro=”” hei”,=”” “microsoft=”” yahei”,=”” sans-serif;=”” color:=”” rgb(0,=”” 0,=”” 0);”=””>

命令<o:p>

错误情况<o:p>

错误原因<o:p>

处理<o:p>

AT+CMUX<o:p>

+CME ERROR: 980<o:p>

参数输入错误<o:p>

检查一下输入参数是否正确<o:p>

4.2 VMware无法识别USB设备的解决方法

我的虚拟机上认不到USB设备,发现是VMware USB Arbitration Service服务没有启动,手动启动VMware USB Arbitration Service服务,再重启VMware,虚拟机就能识别出USB设备了。<o:p>

具体步骤:
1.点击开始->运行,在对话框中输入”services.msc”,确定,打开windows服务管理器。<o:p>

2.在服务列表中选中”VMware USB Arbitration Service”,双击打开属性对话框,再选择”启动”,就能启动VMware USB Arbitration Service服务了。<o:p>

3.关闭VMware软件,并重新打开,启动一下虚拟机。

4.3 虚拟串口生成失败

如下图所示

<o:p>

出现原因是没有选择正确的USB口,我们720SL开发板上的开关要拨到UART2一端,出现上面的错误的原因是因为开关要拨到UART1一端了,把开关拨到UART2一端,重启一下模块,然后在做上述操作。<o:p>

<o:p>


<o:p>

上次更新 2021-01-28