在使用AT指令给4G模块发信息的时候,我们需要借助到Linux中busybox的microcom工具,该工具用来实现与串口之间的通信,那怎么自己实现编程跟4G模块发信息,以及接收信息把想要的信息放入结构体呢?
编程思想:
在/dev下找到4G模块的设备描述符,然后按照“Linux一切皆文件”的思想,打开这个设备,初始化串口配置后,用write发送AT的指令给4G模块通信,然后用read接收,再把接收到想要的数据分割出来放到结构体里面去。
1.在/dev下找到4G模块的设备描述符,我的是/dev/ttyACM2
2.打开这个设备
//deviceName --/dev/ttyACM2
//O_NOCTTY 如果打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
//O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
gg_G4_INFO->deviceFd = open(gg_G4_INFO->deviceName,O_RDWR | O_NOCTTY | O_NONBLOCK);
if(gg_G4_INFO->deviceFd < 0)
{
printf("Open %s failed,fd=%d\n",gg_G4_INFO->deviceName,gg_G4_INFO->deviceFd);
goto EXIT;
}
// 检查串口是否处于阻塞态
if((retval = fcntl(gg_G4_INFO->deviceFd,F_SETFL,0)) < 0)
{
printf("%s,Fcntl check faile.\n",__func__);
goto EXIT;
}
}
3.初始化串口
一些函数介绍:
为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数。tcgetattr用于获取终端的相关参数,而tcsetattr函数用于设置终端参数。
<termios.h>
1.tcgetattr函数:
函数原型:int tcgetattr(int fd, struct termios *termios_p);
功能:
tcgetattr函数用于获取与终端相关的参数。参数fd为终端的文件描述符,返回的结果保存在termios 结构体中
其成员:
tcflag_t c_iflag; //输入模式标志,控制终端输入方式
tcflag_t c_oflag; //输出模式标志,控制终端输出方式
tcflag_t c_cflag; //控制模式标志,指定终端硬件控制信息
tcflag_t c_lflag; //本地模式标志,控制终端编辑功能
cc_t c_cc[NCCS]; //控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等
返回值:成功返回 0,失败返回 -1
2.tcsetattr函数:
函数原型:int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
功能:
tcsetattr函数用于设置终端参数。函数在成功的时候返回0,失败的时候返回-1,并设置errno的值。
参数fd为打开的终端文件描述符,参数optional_actions用于控制修改起作用的时间,而结构体termios_p中保存了要修改的参数。optional_actions可以取如下的值。
TCSANOW:不等数据传输完毕就立即改变属性。
TCSADRAIN:等待所有数据传输结束才改变属性。
TCSAFLUSH:等待所有数据传输结束,清空输入输出缓冲区才改变属性。
错误信息:
EBADF:非法的文件描述符。
EINTR:tcsetattr函数调用被信号中断。
EINVAL:参数optional_actions使用了非法值,或参数termios中使用了非法值。
ENOTTY:非终端的文件描述符。
3.strerror()函数
原型:char * strerror(int errnum);
函数说明:strerror()用来依参数errnum 的错误代码来查询其错误原因的描述字符串, 然后将该字符串指针返回.
返回值:返回描述错误原因的字符串指针.
功能:获取指向错误消息字符串的指针
4.isatty()函数
功能:检查设备类型,判断文件描述词是否为终端机
头文件:<io.h>
原型:int isatty(int handle);
参数:int handle 为要检查的设备文件句柄
返回值:如果参数desc所代表的文件描述词为一终端机则返回1,否则返回0
int decive_init(G4_INFO **g_G4_INFO)
{
G4_INFO *gg_G4_INFO = *g_G4_INFO;
if(!gg_G4_INFO )
{
printf("gg_G4_INFO is NULL\n");
goto EXIT;
}
int retval;
char baudrate[32] = {0};
struct termios NewTermios;
memset(&NewTermios,0,sizeof(struct termios));
memset(&(gg_G4_INFO->OldTermios),0,sizeof(struct termios));
if(tcgetattr(gg_G4_INFO->deviceFd,&(gg_G4_INFO->OldTermios)))
{
printf("%s,Get termios to OldTermios failure:%s\n",__func__,strerror(errno));
return -2;
}
//tcgetattr功能:获取文件描述符对应串口的原始属性,并保存在第二个参数中
//通常获取的原始属性需要进行备份,在程序退出之前要将其修改回来,否则无法继续使用串口。
if(tcgetattr(gg_G4_INFO->deviceFd,&NewTermios))
{
printf("%s,Get termios to NewTermios failure:%s\n",__func__,strerror(errno));
return -3;
}
/* 修改控制模式,保证程序不会占用串口 */
NewTermios.c_cflag |= CLOCAL;
/* 启动接收器,能够从串口中读取输入数据 */
NewTermios.c_cflag |= CREAD;
/* CSIZE字符大小掩码,将与设置databits相关的标致位置零 */
NewTermios.c_cflag &= ~CSIZE;
NewTermios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/*
* ICANON: 标准模式
* ECHO: 回显所输入的字符
* ECHOE: 如果同时设置了ICANON标志,ERASE字符删除前一个所输入的字符,WERASE删除前一个输入的单词
* ISIG: 当接收到INTR/QUIT/SUSP/DSUSP字符,生成一个相应的信号
*
* */
NewTermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/*
* BRKINT: BREAK将会丢弃输入和输出队列中的数据(flush),并且如果终端为前台进程组的控制终端,则BREAK将会产生一个SIGINT信号发送到这个前台进程组
* ICRNL: 将输入中的CR转换为NL
* INPCK: 允许奇偶校验
* ISTRIP: 剥离第8个bits
* IXON: 允许输出端的XON/XOF流控
*
* */
/* OPOST: 表示处理后输出,按照原始数据输出 */
NewTermios.c_oflag &= ~(OPOST);
if(gg_G4_INFO->BaudRate)
{
sprintf(baudrate,"B%d",gg_G4_INFO->BaudRate);
cfsetispeed(&NewTermios,(int)baudrate); //设置输入输出波特率
cfsetospeed(&NewTermios,(int)baudrate);
}
else
{
cfsetispeed(&NewTermios,B115200);
cfsetospeed(&NewTermios,B115200);
}
// 设置数据位
switch(gg_G4_INFO->DataBits)
{
case '5':
NewTermios.c_cflag |= CS5;
break;
case '6':
NewTermios.c_cflag |= CS6;
break;
case '7':
NewTermios.c_cflag |= CS7;
break;
case '8':
NewTermios.c_cflag |= CS8;
break;
default:
NewTermios.c_cflag |= CS8; //默认数据位为8
break;
}
// 设置校验方式
switch(gg_G4_INFO->Parity)
{
// 无校验
case 'n':
case 'N':
NewTermios.c_cflag &= ~PARENB;
NewTermios.c_iflag &= ~INPCK;
break;
// 偶校验
case 'e':
case 'E':
NewTermios.c_cflag |= PARENB;
NewTermios.c_cflag &= ~PARODD;
NewTermios.c_iflag |= INPCK;
break;
// 奇校验
case 'o':
case 'O':
NewTermios.c_cflag |= PARENB;
NewTermios.c_cflag |= PARODD;
NewTermios.c_iflag |= INPCK;
// 设置为空格
case 's':
case 'S':
NewTermios.c_cflag &= ~PARENB;
NewTermios.c_cflag &= ~CSTOPB;
/* 默认无校验 */
default:
NewTermios.c_cflag &= ~PARENB;
NewTermios.c_iflag &= ~INPCK;
break;
}
// 设置停止位
switch(gg_G4_INFO->StopBits)
{
case '1':
NewTermios.c_cflag &= ~CSTOPB;
break;
case '2':
NewTermios.c_cflag |= CSTOPB;
break;
default:
NewTermios.c_cflag &= ~CSTOPB;
break;
}
NewTermios.c_cc[VTIME] = 0; //最长等待时间
NewTermios.c_cc[VMIN] = 0; //最小接收字符
gg_G4_INFO->mSend_Len = 128; //若命令长度大于mSend_Len,则每次最多发送为mSend_Len
if(tcflush(gg_G4_INFO->deviceFd,TCIFLUSH))
{
printf("%s,Failed to clear the cache:%s\n",__func__,strerror(errno));
return -4;
}
if(tcsetattr(gg_G4_INFO->deviceFd,TCSANOW,&NewTermios) != 0)
{
printf("%s,tcsetattr failure:%s\n",__func__,strerror(errno));
return -5;
}
printf("decive_init Successfully......\n");
return 0;
}
4.用write和send发送和接收
fd_set rfd, wfd, efd;
while(1)
{
printf("FD_ISSET4\n");
sleep(2);
if(g_G4_INFO->deviceFd < 0)
{
printf("G4Handler again begin\n");
G4Handler(&g_G4_INFO);
printf("G4Handler again end\n");
if(g_G4_INFO->deviceFd < 0)
{
continue;
}
else
{
printf("G4Handler init again success\n");
}
}
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_ZERO(&efd);
FD_SET(g_G4_INFO->deviceFd, &rfd);
FD_SET(g_G4_INFO->deviceFd, &wfd);
FD_SET(g_G4_INFO->deviceFd, &efd);
iRet = select((g_G4_INFO->deviceFd)+1,&rfd,&wfd,&efd,NULL);
if(iRet > 0)
{
if(FD_ISSET(g_G4_INFO->deviceFd,&rfd))
{
nrecv = 0;
//memset(g_G4_INFO->readBuf,0,strlen(g_G4_INFO->readBuf));
nrecv = recvFromDevice(&g_G4_INFO);
if(nrecv < 0)
{
printf("recvFromDevice error\n");
printf("trying\n");
flag = 1;
continue;
}
else
{
printf("recv success\n");
}
}
else if(FD_ISSET(g_G4_INFO->deviceFd,&wfd))
{
//注意:AT指令集发送的实质,例如,当我们发送AT时,其实是发送了" AT\r "
if( sendToDevice(&g_G4_INFO) == -1)
{
printf("send error\n");
printf("trying...\n");
flag = 1;
continue;
}
else
{
printf("send trying success continue\n");
}
}
}
else
{
perror("select error\n");
goto EXIT;
}
}
EXIT:
if(g_G4_INFO)
{
free(g_G4_INFO);
// g_G4_INFO->deviceName = NULL;
}
if(g_G4_INFO->deviceFd >0)
{
close(g_G4_INFO->deviceFd);
g_G4_INFO->deviceFd = -1;
}
}