赞
踩
在拿到我的EC20 4G模块后,可谓是迫不及待的去办了一张新的电话卡,可是在插上卡以后,登录我的树莓派,却始终存在一个问题,插上卡以后,使用AT命令
AT+CSQ
查看信号强度,一切正常,
AT+CPIN?
一切也都就绪,但是在使用
AT+CREG?
时,却出现了(0,2)的错误,我上一篇博客有提到,使用这个命令第二个参数出现2说明卡还没有注册上,但是处于正在注册状态,可是无论我怎么等,仍然处于这个状态,换了几张卡依然如此,能检查出卡,却大不了电话发不了短信,在网上找了各种解决方法,csdn,找了EC20 4G模块的淘宝客户,提供的方法都解决不了,在经过一天努力未果后,我决定放弃了,于是我拔下了模块的天线,可就是这时候,我看到了天线插口写着一个DIV,另一个插口写着一个MAIN(超级小),内心一颤,心想会不会是天线的原因,果不其然,在我跟换天线的插口位置以后,一切的问题全都解决了…
原来模块分为两个天线口,一个是主集天线,一个是分集天线,后者只能用来接收数据,所以在查看信号时时没问题的,让我压根没想到这俩天线还有差别,所以如果你也遇到AT+CREG?返回 +CREG: 0,2 不妨也试试 ……
废话不多说,这次要写的,是一个用于串口通信的程序,类似于我们Linux中busybox的microcom工具,该工具用来实现与串口之间的通信,如图:
因为的我们在使用microcom的时候,接收到的数据与答复,都是通过肉眼来观察判断的,但如果通过程序实现,我们可以将接收到的数据进行处理并利用进程间通信或是socket发送至其他地方,又或者可以设置每隔多少时间发送一条短信,并且,在学习这个程序的编程中,将会慢慢的对串口通信更加了解,我之前的博客介绍了串口通信的基本知识,在掌握这些后,我们一起来看程序吧~
通过termios结构体,我们可以更好地控制串口的属性,例如输入,输出或者一些特殊的要求我们都可以通过设置这个值来实现:
struct termios
{
tcflag_t c_iflag; //输入模式标志
tcflag_t c_oflag; //输出模式标志
tcflag_t c_cflag; //控制模式标志
tcflag_t c_lflag; //本地模式标志
cc_t c_cc[NCCS]; //控制字符
speed_t c_isspeed; //输入波特率
speed_t c_ospedd; //输出波特率
}
通过设定对应功能的结构体成员以达到控制串口属性的目的,属性的设置是通过标志位来实现的,通过与,或和取反等方式,来将对应的功能模块的标志位置0或者置1,从而告诉系统是否要有此功能,关于属性设置,分为输入,输出,控制等属性,下面一一来看他们都有哪些键值设置吧:
IGNBRK
忽略BREAK键输入
BRKINT
如果设置了IGNBRK,BREAK键的输入将被忽略,如果设置了BRKINT ,将产生SIGINT中断
IGNPAR
忽略奇偶校验错误
PARMRK
标识奇偶校验错误
INPCK
允许输入奇偶校验
ISTRIP
去除字符的第8个比特
INLCR
将输入的NL(换行)转换成CR(回车)
IGNCR
忽略输入的回车
ICRNL
将输入的回车转化成换行(如果IGNCR未设置的情况下)
IUCLC
将输入的大写字符转换成小写字符(非POSIX)
IXON
允许输入时对XON/XOFF流进行控制
IXANY
输入任何字符将重启停止的输出
IXOFF
允许输入时对XON/XOFF流进行控制
IMAXBEL
当输入队列满的时候开始响铃,Linux在使用该参数而是认为该参数总是已经设置
OPOST
处理后输出
OLCUC
将输入的小写字符转换成大写字符(非POSIX)
ONLCR
将输入的NL(换行)转换成CR(回车)及NL(换行)
OCRNL
将输入的CR(回车)转换成NL(换行)
ONOCR
第一行不输出回车符
ONLRET
不输出回车符
OFILL
发送填充字符以延迟终端输出
OFDEL
以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符将是NUL(‘\0’)(非POSIX)
NLDLY
换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s)
CRDLY
回车延迟,取值范围为:CR0、CR1、CR2和 CR3
TABDLY
水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3
BSDLY
空格输出延迟,可以取BS0或BS1
VTDLY
垂直制表符输出延迟,可以取VT0或VT1
FFDLY
换页延迟,可以取FF0或FF1
CBAUD
波特率(4+1位)(非POSIX)
CBAUDEX
附加波特率(1位)(非POSIX)
CSIZE
字符长度,取值范围为CS5、CS6、CS7或CS8
CSTOPB
设置两个停止位
CREAD
使用接收器
PARENB
使用奇偶校验
PARODD
对输入使用奇偶校验,对输出使用偶校验
HUPCL
关闭设备时挂起
CLOCAL
忽略调制解调器线路状态
CRTSCTS
使用RTS/CTS流控制
ISIG
当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
ICANON
使用标准输入模式
XCASE
在ICANON和XCASE同时设置的情况下,终端只使用大写。如果只设置了XCASE,则输入字符将被转换为小写字符,除非字符使用了转义字符(非POSIX,且Linux不支持该参数)
ECHO
显示输入字符
ECHOE
如果ICANON同时设置,ERASE将删除输入的字符,WERASE将删除输入的单词
ECHOK
如果ICANON同时设置,KILL将删除当前行
ECHONL
如果ICANON同时设置,即使ECHO没有设置依然显示换行符
ECHOPRT
如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX)
TOSTOP
向后台输出发送SIGTTOU信号
调用read()函数读取串口数据时,返回读取数据的数量需要考虑两个变量: MIN和TIME。MIN和TIME在termios结构的c_ cc成员的数组下标名为VMIN和VTIME。
MIN是指一次read调用期望返回的最小字节数。VTIME说明等待数据到达的分秒数(秒的1/10为分秒)。TIME与MIN组合使用的具体含义分为以下四种情形:
● 当MIN>0,TIME>0时
计时器在收到第-一个字节后启动,在计时器超时之前(TIME的时间到),若己收到MIN个字节,则read返回MIN个字节,否则,在计时器超时后返回实际接收到的字节。
注意:因为只有在接收到第一个字节时才开始计时,所以至少可以返回1个字节。这种情形中,在接到第一个字节之前,调用者阻塞。如果在调用read时数据已经可用,则如同在read后数据立即被接到一样。
● 当MIN>0,TIME=0时
MIN个字节完整接收后,read 才返回,这可能会造成read无限期地阻塞。
● 当MIN=0,TIME>0时
TIME为允许等待的最大时间,计时器在调用read时立即启动,在串口接到1字节数据或者计时器超时后即返回,如果是计时器超时,则返回0。
● 当MIN=0,TIME= 0时
如果有数据可用,则read最多返回所要求的字节数,如果无数据可用,则read立即返回0。
函数原型:
int tcgetattr(int fd,struct termios &termios_p);
int tcsetattr(int fd,int actions,const struct termios *termios_p);
参数
int fd: 打开串口文件后,获取到的文件描述符
struct termios &termios_p: termios 类型的结构体,包含在 <termios.h> 头文件中,这里需要传地址或指针
返回值:成功返回 0,失败返回 -1
函数功能: 获取文件描述符对应串口的原始属性,并保存在第二个参数中,通常获取的原始属性需要进行备份,在程序退出之前要将其修改回来,否则无法继续使用串口。
函数原型
int tcflush(int fd,int quene)
在打开串口后,串口其实已经可以开始读取 数据了 ,这段时间用户如果没有读取,将保存在缓冲区里,如果用户不想要开始的一段数据,或者发现缓冲区数据有误,可以使用这个函数清空缓冲
需要注意,如果是在任务中,需要不停地写入数据到串口设备,千万不能在每次写入数据到设备前,进行flush以前数据的操作,因为两次写入的间隔是业务控制的,内核不会保证在两次写入之间一定把数据发送成功。flush操作一般在打开或者复位串口设备时进行操作。
返回值:成返回 0 ,失败返回 -1
函数原型
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
我所写的程序包含了3个功能模块,
下面一起来看各个功能模块的流程图吧~
打开串口:
通过传入的文件名打开获取到文件描述符,最终成功后返回文件描述符。
关闭串口
关闭串口不能简单地close(fd),否则第二次运行程序将无法打开串口,需要重启模块才能再次使用。
ComportOpen.h
#ifndef _COMPORTOPEN_H_ #define _COMPORTOPEN_H_ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <termios.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #define SERIALNAME_LEN 128 typedef struct _st_MyAttr { int fd; //串口文件描述符 int BaudRate; //波特率 int DataBits; //数据位 char Parity; //奇偶校验位 int StopBits; //停止位 int mSend_Len; //单次最大发送长度 char SerialName[SERIALNAME_LEN]; //串口名称 struct termios OldTermios; //串口的原始属性 }MyAttr; int comport_open(MyAttr *attr); int comport_close(MyAttr *attr); #endif /* ----- #ifndef _COMPORTOPEN_H_ ----- */
我将所有需要用到的串口属性封装在了一个结构体中,这样,我在设计后面的函数时,就可以直接传结构体指针,再根据功能的实际要求,使用自己需要的成员即可。
ComportOpen.c
/********************************************************************************* * Copyright: (C) 2020 LuXiaoyang<920916829@qq.com> * All rights reserved. * * Filename: ComportOpen.c * Description: This file Open or close the serial port * * Version: 1.0.0(03/07/20) * Author: LuXiaoyang <920916829@qq.com> * ChangeLog: 1, Release initial version on "03/07/20 17:50:09" * ********************************************************************************/ #include "ComportOpen.h" int comport_open(MyAttr *attr) { int i; int retval = -1; if(NULL == attr) { printf("%s,Invalid parameter\n",__func__); return retval; } /* O_NOCTTY表示打开的是一个终端设备,程序不会成为该 * 端口的控制终端,O_NONBLOCK使得read处于非阻塞模式 */ attr->fd = open(attr->SerialName,O_RDWR | O_NOCTTY | O_NONBLOCK); if(attr->fd < 0) { printf("%s,Open %s failed:%s\n",__func__,attr->SerialName,strerror(errno)); return -1; } /* 检查串口是否处于阻塞态 */ if((retval = fcntl(attr->fd,F_SETFL,0)) < 0) { printf("%s,Fcntl check faile.\n",__func__); return -2; } printf("Starting serial communication process "); for(i = 0;i < 6;i++) { printf(" . "); fflush(stdout); sleep(1); } printf("\n"); //这部分纯属搞笑 if(0 == isatty(attr->fd)) //是否为终端设备 { printf("%s:[%d] is not a Terminal equipment.\n",attr->SerialName,attr->fd); return -3; } printf("Open %s successfully.\n",attr->SerialName); return 0; } int comport_close(MyAttr *attr) { if(tcflush(attr->fd,TCIOFLUSH)) //清零用于串口通信的缓冲区 { printf("%s,Tcflush faile:%s\n",__func__,strerror(errno)); return -1; } /* 将串口设置为原有属性 */ if(tcsetattr(attr->fd,TCSANOW,&(attr->OldTermios))) { printf("%s,Set old options failed:%s\n",__func__,strerror(errno)); return -2; } close(attr->fd); free(attr); return 0; }
根据思路来写函数
ComportInit.h
#ifndef _COMPORTINIT_H_
#define _COMPORTINIT_H_
#include "ComportOpen.h"
#include <string.h>
int comport_init(MyAttr *attr);
#endif /* ----- #ifndef _COMPORTINIT_H_ ----- */
因为我将串口设置的各项参数都进行了封装,所以函数的参数只用传一个结构体指针或者传结构体地址即可。
ComportInit.c
/********************************************************************************* * Copyright: (C) 2020 LuXiaoyang<920916829@qq.com> * All rights reserved. * * Filename: ComportInit.c * Description: This file is Init COm Port * * Version: 1.0.0(03/07/20) * Author: LuXiaoyang <920916829@qq.com> * ChangeLog: 1, Release initial version on "03/07/20 17:50:09" * ********************************************************************************/ #include "ComportInit.h" int comport_init(MyAttr *attr) { int retval; char baudrate[32] = {0}; struct termios NewTermios; memset(&NewTermios,0,sizeof(struct termios)); memset(&(attr->OldTermios),0,sizeof(struct termios)); if(!attr) { printf("Invalid parameter.\n"); return -1; } if(tcgetattr(attr->fd,&(attr->OldTermios))) { printf("%s,Get termios to OldTermios failure:%s\n",__func__,strerror(errno)); return -2; } if(tcgetattr(attr->fd,&NewTermios)) { printf("%s,Get termios to NewTermios failure:%s\n",__func__,strerror(errno)); return -3; } /* 修改控制模式,保证程序不会占用串口 */ NewTermios.c_cflag |= CLOCAL; /* For example: * * c_cflag: 0 0 0 0 1 0 0 0 * CLOCAL: | 0 0 0 1 0 0 0 0 * -------------------- * 0 0 0 1 1 0 0 0 * * Finally: * * c_flag = 0 0 0 1 1 0 0 0; * * */ /* 启动接收器,能够从串口中读取输入数据 */ NewTermios.c_cflag |= CREAD; /* CSIZE字符大小掩码,将与设置databits相关的标致位置零 */ NewTermios.c_cflag &= ~CSIZE; /* For example: * * CSIZE = 0 1 1 1 0 0 0 0 ---> ~CSIZE = 1 0 0 0 1 1 1 1 * * c_cflag: 0 0 1 0 1 1 0 0 * ~CSIZE: & 1 0 0 0 1 1 1 1 * ----------------------- * 0 0 0 0 1 1 0 0 * * Finally: * * c_cflag = 0 0 0 0 1 1 00 * * */ 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(attr->BaudRate) { sprintf(baudrate,"B%d",attr->BaudRate); cfsetispeed(&NewTermios,(int)baudrate); //设置输入输出波特率 cfsetospeed(&NewTermios,(int)baudrate); } else { cfsetispeed(&NewTermios,B115200); cfsetospeed(&NewTermios,B115200); } /* 设置数据位 */ switch(attr->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(attr->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(attr->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; //最小接收字符 attr->mSend_Len = 128; //若命令长度大于mSend_Len,则每次最多发送为mSend_Len if(tcflush(attr->fd,TCIFLUSH)) { printf("%s,Failed to clear the cache:%s\n",__func__,strerror(errno)); return -4; } if(tcsetattr(attr->fd,TCSANOW,&NewTermios) != 0) { printf("%s,tcsetattr failure:%s\n",__func__,strerror(errno)); return -5; } printf("Comport Init Successfully......\n"); return 0; }
和串口通信其实就是write 和read,通过获取到的文件描述符,在加上一些串口所特有的属性,从而完成通信。
发送:
这里要注意了,我在前面一篇博客总结说明了AT指令集发送的实质,例如,当我们发送AT时,其实是发送了" AT\r ",所以,当我们获取到要发送的指令时,需要在指令的最后面加上一个 \r(CR) ,串口才能接收到信息,后面的讨论会提到一个问题。
接收的程序较为简单,这里就直接贴代码了。
ComportSwap.h
#ifndef _COMPORTSWAP_H_
#define _COMPORTSWAP_H_
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "ComportOpen.h"
int comport_send(MyAttr *attr,char *sbuf,int sbuf_len);
int comport_recv(MyAttr *attr,char *rbuf,int rbuf_len,int timeout);
#endif /* ----- #ifndef _COMPORTSWAP_H_ ----- */
ComportSwap.c
/********************************************************************************* * Copyright: (C) 2020 LuXiaoyang<920916829@qq.com> * All rights reserved. * * Filename: ComporSwap.c * Description: This file is communicate with com port * * Version: 1.0.0(03/07/20) * Author: LuXiaoyang <920916829@qq.com> * ChangeLog: 1, Release initial version on "03/07/20 17:50:09" * ********************************************************************************/ #include "ComportSwap.h" int comport_send(MyAttr *attr,char *sbuf,int sbuf_len) { char *ptr,*end; int retval; if(!attr || !sbuf || sbuf_len <= 0) { printf("%s,Invalid parameter.\n",__func__); return -1; } if(sbuf_len > attr->mSend_Len) { ptr = sbuf; end = sbuf + sbuf_len; do { if(attr->mSend_Len < (end - ptr)) { retval = write(attr->fd,ptr,attr->mSend_Len); if(retval <= 0 || retval != attr->mSend_Len) { printf("Write to com port[%d] failed:%s\n",attr->fd,strerror(errno)); return -2; } ptr += attr->mSend_Len; } else { retval = write(attr->fd,ptr,(end - ptr)); if(retval <= 0 || retval != (end - ptr)) { printf("Write to com port[%d] failed:%s\n",attr->fd,strerror(errno)); return -3; } ptr += (end - ptr); } }while(end > ptr); } else { retval = write(attr->fd,sbuf,sbuf_len); if(retval <= 0 || retval != sbuf_len) { printf("Write to com port[[%d] failed:%s\n",attr->fd,strerror(errno)); return -4; } } return retval; } int comport_recv(MyAttr *attr,char *rbuf,int rbuf_len,int timeout) { int retval; fd_set rset; struct timeval time_out; if(!rbuf || rbuf_len <= 0) { printf("%s,Invalid parameter.\n",__func__); return -1; } if(timeout) //指定延时等待 { time_out.tv_sec = (time_t)(timeout / 1000); time_out.tv_usec = 0; FD_ZERO(&rset); FD_SET(attr->fd,&rset); retval = select(attr->fd + 1,&rset,NULL,NULL,&time_out); if(retval < 0) { printf("%s,Select failed:%s\n",strerror(errno)); return -2; } else if(0 == retval) { printf("Time Out.\n"); return 0; } } usleep(1000); retval = read(attr->fd,rbuf,rbuf_len); if( retval <= 0) { printf("%s,Read failed:%s\n",__func__,strerror(errno)); return -3; } return retval; }
接下来就是写主程序了,因为要不断和串口通信,所以应该使用一个while循环,从标准输入获取命令,加上\r 发送,同时还要读fd,所以这里采用select多路复用来实现监听标准输入与用于与串口通信的fd。
comport.c
/********************************************************************************* * Copyright: (C) 2020 LuXiaoyang<920916829@qq.com> * All rights reserved. * * Filename: comport.c * Description: This file * * Version: 1.0.0(04/07/20) * Author: LuXiaoyang <920916829@qq.com> * ChangeLog: 1, Release initial version on "04/07/20 03:02:28" * ********************************************************************************/ #include <stdlib.h> #include <signal.h> #include <getopt.h> #include "ComportOpen.h" #include "ComportInit.h" #include "ComportSwap.h" int g_stop = 0; void sig_handler(int sig_num) { if(sig_num == SIGINT) g_stop = 1; } void adjust_buf(char* buf); void help_information(); int main(int argc, char *argv[]) { int retval; int ch; char sbuf[128] = {0}; char rbuf[128] = {0}; fd_set rset; MyAttr* attr = NULL; struct option options[] = { {"help",no_argument,NULL,'h'}, {"baudrate",required_argument,NULL,'b'}, {"databits",required_argument,NULL,'d'}, {"parity",required_argument,NULL,'p'}, {"stopbits",required_argument,NULL,'s'}, {"name",required_argument,NULL,'n'}, {NULL,0,NULL,0} }; attr = (MyAttr*)malloc(sizeof(MyAttr)); memset(attr,0,sizeof(MyAttr)); while((ch = getopt_long(argc,argv,"hb:d:p:s:n:",options,NULL)) != -1) { switch(ch) { case 'h': help_information(); return 0; case 'b': attr->BaudRate = atoi(optarg); break; case 'd': attr->DataBits = atoi(optarg); break; case 'p': attr->Parity = optarg[0]; break; case 's': attr->StopBits = atoi(optarg); break; case 'n': strncpy(attr->SerialName,optarg,SERIALNAME_LEN); break; } } if(strlen(attr->SerialName) == 0) { printf("Parameter warning:\n"); printf("\tAt least need to enter the serial port name,You can specify the serial port name with -n.\n"); return 0; } if(comport_open(attr) != 0) { printf("Open %s failed!\n",attr->SerialName); return -1; } retval = comport_init(attr); if(retval < 0) goto cleanup; signal(SIGINT,sig_handler); fflush(stdin); printf("Start to communicate with com port......\n"); while(!g_stop) { FD_ZERO(&rset); FD_SET(STDIN_FILENO,&rset); FD_SET(attr->fd,&rset); /* 使用多路复用监听标准输入和串口fd */ retval = select(attr->fd + 1,&rset,NULL,NULL,NULL); if(retval < 0) { printf("Program exit......\n"); break; } if(retval == 0) { printf("Time Out.\n"); goto cleanup; } if(FD_ISSET(STDIN_FILENO,&rset)) { memset(sbuf,0,sizeof(sbuf)); /* 从标准输入读取命令 */ fgets(sbuf,sizeof(sbuf),stdin); /* 处理要发送的数据 */ adjust_buf(sbuf); if(comport_send(attr,sbuf,strlen(sbuf)) < 0) { printf("Write failed.\n"); goto cleanup; } fflush(stdin); } if(FD_ISSET(attr->fd,&rset)) { memset(rbuf,0,sizeof(rbuf)); retval = comport_recv(attr,rbuf,sizeof(rbuf),0); if(retval <= 0) { printf("Read failed:%s\n",strerror(errno)); break; } printf("%s",rbuf); fflush(stdout); } } cleanup: comport_close(attr); return 0; } void adjust_buf(char *buf) { int i = strlen(buf); strcpy(&buf[i-1],"\r"); } void help_information() { printf("\t-b Set BaudRate\n"); printf("\t-d Set Databits\n"); printf("\t-p Set Parity,0 for no parity,1 for Odd parity,2 for Evev parity\n"); printf("\t-s Set StopBits\n"); printf("\t-n Set the name of the serial port you want to use\n"); printf("\t Ctrl + c to exit the program\n"); printf("\n\tIf you do not specify parameters,The default parameters of the program are as follows:\n"); printf("\tBaudRate: 1115200\n\tDatabits: 8bits\n\tParity: no parity\n\tStopBits: 1bits\n"); }
makefile
(CC) = gcc comport: comport.c ComportOpen.o ComportInit.o ComportSwap.o ComportSwap.o $(CC) comport.c ComportOpen.o ComportInit.o ComportSwap.o -o comport -Werror -Wall ComportOpen.o: ComportOpen.c $(CC) -c ComportOpen.c ComportInit.o: ComportInit.c $(CC) -c ComportInit.c ComportSwap.o: ComportSwap.h $(CC) -c ComportSwap.c clear: rm *.o comport
help_msg:
通过参数传入设置:
参考:
https://blog.csdn.net/onion_lwl/article/details/81293266
https://blog.csdn.net/morixinguan/article/details/80898172
https://blog.csdn.net/wumenglu1018/article/details/53098794/
2020.7.5 持续更新…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。