Linux之4G模块串口通信

在使用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;
    }
}
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值