# serialPort **Repository Path**: jhonzhang/serialPort ## Basic Information - **Project Name**: serialPort - **Description**: Mac 串口工具 - **Primary Language**: Objective-C - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2021-11-16 - **Last Updated**: 2023-05-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: 串口 ## README # SerialPortGUI #### 介绍 + 一个用object-c编写的串口通信工具,包括GUI界面和串口通信模块. #### 软件架构 object-c 开发 #### 项目介绍 本项目基于xcode工具开发,主要是用object-c 结合c开发完成,是一个串口带图形界面应用。 1. 自动加载串口参数json文件和串口配置json文件。实现串口的波特率,数据位,停止位,校验位选择参数设置。 2. 可以自动扫描 dev目录下的可用串口设备,添加到界面的路径选择列表里面。 3. 可以在图形界面选择串口配置参数,也可以修改config.json默认加载配置参数。 4. 在图形界面可以从输入框输入数据,发送数据, 5. 在终端界面可以显示配置信息和发送的数据,读取的数据。 #### 使用说明 1. 通过串口参数设置,可以选择串口设备,选择波特率,选择数据位,选择停止位,选择校验位。 2. 点击打开串口,可以打开并设置串口参数。 3. 在输入框输入数据,可以发送串口数据。 4. 在显示窗口可以看到相关显示信息。 5. 自动保存log的路径:```/vault/Serialtool``` #### 串口配置参数 串口属于终端设备,其接口属性用termios结构描述, 使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性 tcgetattr 参数fd为终端的文件描述符,返回的结果保存在termios 结构体中 tcsetattr 设置串口属性包括波特率设置,效验位和停止位设置 串口的设置主要是设置 struct termios 结构体的各成员值 ``` struct termios { tcflag_t c_cflag/* 控制标志*/ tcflag_t c_iflag;/* 输入标志*/ tcflag_t c_oflag;/* 输出标志*/ tcflag_t c_lflag;/* 本地标志*/ cc_t c_line; /* 线控制 */ tcflag_t c_cc[NCCS];/* 控制字符*/ }; ``` ##### 控制标志 + c_cflag成员控制着波特率、数据位、奇偶校验、停止位以及流控制 ``` 标志 说明 标志 说明 CBAUD 波特率位屏蔽 CSIZE 数据位屏蔽 B0 0位/秒(挂起) CS5 5位数据位 B110 100位/秒 CS6 6位数据位 B134 134位/秒 CS7 7位数据位 B1200 1200位/秒 CS8 8位数据位 B2400 2400位/秒 CSTOPB 2位停止位,否则为1位 B4800 4800位/秒 CREAD 启动接收 B9600 9600位/秒 PARENB 进行奇偶校验 B19200 19200位/秒 PARODD 奇校验,否则为偶校验 B57600 57600位/秒 HUPCL 最后关闭时断开 B115200 115200位/秒 CLOCAL 忽略调制调解器状态行 B460800 460800位/秒 ``` + c_cflag成员的CREAD和CLOCAL选项通常是要启用的,这两个选项使驱动程序启动接收字符装置,同时忽略串口信号线的状态。 ##### 输出标志 + c_iflag成员负责控制串口输入数据的处理 c_iflag的部分可用标志。 ``` 标志 说明 INPCK 打开输入奇偶校验 IGNPAR 忽略奇偶错字符 PARMRK 标记奇偶错 ISTRIP 剥除字符第8位 IXON 启用/停止输出控制流起作用 IXOFF 启用/停止输入控制流起作用 IGNBRK 忽略BREAK条件 INLCR 将输入的NL转换为CR IGNCR 忽略CR ICRNL 将输入的CR转换为NL ``` ##### 设置输入校验 当c_cflag成员的PARENB(奇偶校验)选项启用时,c_iflag的也应启用奇偶校验选项。操作方法是启用INPCK和ISTRIP选项: options.c_iflag |= (INPCK | ISTRIP); 注意:IGNPAR选项在一些场合的应用带有一定的危险性,它指示串口驱动程序忽略奇偶校验错误,也就是说,IGNPAR使奇偶校验出错的字符也通过输入。这在测试通信链路的质量时也许有用,但在通常的数据通信应用中不应使用。 设置软件流控制 使用软件流控制是启用IXON、IXOFF和IXANY选项: options.c_iflag |= (IXON | IXOFF | IXANY); 相反,要禁用软件流控制是禁止上面的选项: options.c_iflag &= ~(IXON | IXOFF | IXANY); ##### 输出标志 c_oflag成员管理输出过滤, c_oflag成员的部分选项标志。 ``` 标志 说明 BSDLY 退格延迟屏蔽 CMSPAR 标志或空奇偶性 CRDLY CR延迟屏蔽 FFDLY 换页延迟屏蔽 OCRNL 将输出的CR转换为NL OFDEL 填充符为DEL,否则为NULL OFILL 对于延迟使用填充符 OLCUC 将输出的小写字符转换为大写字符 ONLCR 将NL转换为CR-NL ONLRET NL执行CR功能 ONOCR 在0列不输出CR OPOST 执行输出处理 OXTABS 将制表符扩充为空格 ``` ##### 启用输出处理 启用输出处理需要在c_oflag成员中启用OPOST选项,其操作方法如下: options.c_oflag |= OPOST; 使用原始输出 使用原始输出,就是禁用输出处理,使数据能不经过处理、过滤地完整地输出到串口接口。当OPOST被禁止,c_oflag其它选项也被忽略,其操作方法如下: options.c_oflag &= ~OPOST; #### 本地标志 本地标志c_lflag控制着串口驱动程序如何管理输入的字符 c_lflag的部分可用标志。 ``` 标志 说明 ISIG 启用终端产生的信号 ICANON 启用规范输入 XCASE 规范大/小写表示 ECHO 进行回送 ECHOE 可见擦除字符 ECHOK 回送kill符 ECHONL 回送NL NOFLSH 在中断或退出键后禁用刷清 IEXTEN 启用扩充的输入字符处理 ECHOCTL 回送控制字符为^(char) ECHOPRT 硬拷贝的可见擦除方式 ECHOKE Kill的可见擦除 PENDIN 重新打印未决输入 TOSTOP 对于后台输出发送SIGTTOU ``` 选择规范模式 规范模式是行处理的。调用read读取串口数据时,每次返回一行数据。当选择规范模式时,需要启用ICANON、ECHO和ECHOE选项: options.c_lflag |= (ICANON | ECHO | ECHOE); 当串口设备作为用户终端时,通常要把串口设备配置成规范模式。 选择原始模式 在原始模式下,串口输入数据是不经过处理的,在串口接口接收的数据被完整保留。要使串口设备工作在原始模式,需要关闭ICANON、ECHO、ECHOE和ISIG选项,其操作方法如下: options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); #### 控制字符组 c_cc数组的长度是NCCS,一般介于15-20之间。 c_cc数组的每个成员的下标都用一个宏表示, 列出了c_cc的部分下标标志名及其对应说明。 ``` 标 志 说 明 VINTR 中断 VQUIT 退出 VERASE 擦除 VEOF 行结束 VEOL 行结束 VMIN 需读取的最小字节数 VTIME 与“VMIN”配合使用,是指限定的传输或等待的最长时间 ``` 在规范模式下,调用read读取串口数据时,通常是返回一行数据。而在原始模式下,串口输入数据是不分行的。在原始模式下,返回读取数据的数量需要考虑两个变量:MIN和TIME。MIN和TIME在c_cc数组中的下标名为VMIN和VTIME。 MIN是指一次read调用期望返回的最小字节数。TIME与MIN组合使用,其具体含义分以下四种情形: 1)当MIN > 0,TIME > 0时 TIME为接收到第一个字节后允许的数据传输或等待的最长分秒数(1分秒= 0.1秒)。定时器在收到第一个字节后启动,在计时器超时之前,若已收到MIN个字节,则read返回MIN个字节,否则,在计时器超时后返回实际接收到的字节。 注意:因为只有在接收到第一个字节时才启动,所以至少可以返回1个字节。这种情形中,在接到第一个字节之前,调用者阻塞。如果在调用read时数据已经可用,则如同在read后数据立即被接到一样。 2)当MIN > 0,TIME = 0时 MIN个字节完整接收后,read才返回,这可能会造成read无限期地阻塞。 3)当MIN = 0, TIME > 0时 TIME为允许等待的最大时间,计时器在调用read时立即启动,在串口接到1字节数据或者计时器超时后即返回,如果是计时器超时,则返回0。 4)当MIN = 0,TIME = 0时 如果有数据可用,则read最多返回所要求的字节数,如果无数据可用,则read立即返回0。 ### 属性设置 使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性。 设置和获取termios结构属性 ``` #include /* 使用终端接口函数需要使用此头文件*/ int tcgetattr(int fd, struct termios *termptr); int tcsetattr(int fd, int opt, const struct termios *termptr); ``` 其中:fd为串口设备文件描述符,termptr参数在tcgetattr函数中是用于存放串口设置的termios结构体,opt是整形变量,使用方法如下: 1)TCSANOW:更改立即发生; 2)TCSADRAIN:发送了所有输出后更改才发生,若更改输出参数则应用此选项; 3)TCSAFLUSH:发送了所有输出后更改才发生,更进一步,在更改发生时未读的所有输入数据被删除(Flush)。 在串口驱动程序里,有输入缓冲区和输出缓冲区。在改变串口属性时,缓冲区中的数据可能还存在,这时需要考虑到更改后的属性什么时候起作用。tcsetattr的参数opt可以指定在什么时候新的串口属性才起作用。 上述两函数执行时,若成功则返回0,若出错则返回-1。 掌握了如何获取和设置串口的属性结构后,下面将介绍串口主要属性的修改,即修改termios结构体的成员。 termios结构体的各个成员的各个选项中除需要用屏蔽标志的选项外(如波特率选项、数据位选项等),都是按位表示的,对这些选项的设置或清除可以直接用“^”或“&”逻辑运算来完成。 需要用屏蔽标志的选项的话则需要先用“&”运算清除原设置,再用“^”运算设置新选项。例如,为了设置字符长度,需先用字符长度屏蔽标志CSIZE将表示字符长度的位清0,然后再将对应位设置为CS5、CS6、CS7或CS8。 ### 串口属性设置 #### 设置波特率 串口的输入和输出波特率可分别用cfsetispeed()和cfsetospeed()函数来设置 ``` #include int cfsetispeed(struct termios *termptr, speed_t speed); int cfsetospeed(struct termios *termptr, speed_t speed); ``` 这两个函数若执行成功返回0,若出错则返回-1。 使用这两个函数时,应当理解输入、输出波特率是存在串口设备termios结构中的。在调用任一cfset函数之前,先要用tcgetattr获得设备的termios结构。与此类似,在调用任一cfset函数后,波特率都被设置到termios结构中。为使这种更改影响到设备,应当调用tcsetattr函数。操作方法如下所示。 ``` if (tcgetattr(fd, &opt)< 0) { return ERROR; } cfsetispeed(&opt, B9600); cfsetospeed(&opt, B9600); if (tcsetattr(fd, TCSANOW, &opt)<0) { return ERROR; } ``` #### 设置数据位 设置数据位不需要专用的函数,只需要在设置数据位之前用数据位屏蔽标志(CSIZE)把对应数据位清零,然后再设置新的数据位即可,如下所示: options.c_cflag &= ~CSIZE;/* 先把数据位清零*/ options.c_cflag |= CS8;/* 把数据位设置为8位*/ #### 设置奇偶校验 正如设置数据位一样,设置奇偶校验是在直接在cflag成员上设置。 下面是各种类型的校验设置方法。 + 无奇偶校验(8N1): options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; + 7位数据位奇偶校验(7E1): options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS7; + 奇校验(7O1): options.c_cflag|= PARENB; options.c_cflag |= PARODD; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS7; #### 串口设置示例: ``` #include #include #include #include #include #include #include #include #include #include #include #define MCUUARTPORT "/dev/tty.usbserial-Fixture" //写入数据 ssize_t writedata(int fd,char *sendBUff){ printf("write data: %s\n",sendBUff); size_t len; ssize_t w_ok; strcat(sendBUff,"\r\n\0"); len=strlen(sendBUff); w_ok =write(fd, sendBUff, len); printf("w_ok:%zd\n",w_ok); if(w_ok<0) { printf("write failed:w_len:%zd\n",w_ok); } else{ printf("write fd: %d,str: %s len: %zd\n",fd,sendBUff,len); } return w_ok; } //读取数据 ssize_t readData(int fd,char *readBuff,size_t r_len){ ssize_t r_ok; r_ok= read(fd,readBuff,r_len); printf("r_ok %zd\n",r_ok); return r_ok; } int main(void) { int fd; /* ====================打开串口设备==================== */ printf(" init dev start\n"); //打开设备/dev/path fd = open(MCUUARTPORT, O_RDWR | O_NDELAY | O_NOCTTY|O_NONBLOCK); if (-1 == fd) { printf("open serial port %s fail %d\n", MCUUARTPORT, fd); return fd; } printf("fd:%d\n",fd); fcntl(fd, F_SETFL, 0);//设置文件状态标志值为0,阻塞模式 printf("open device success\n"); /* ====================设置串口属性==================== */ printf("set baudrate\n"); struct termios options; //串口属于终端设备,其接口属性用termios结构描述,定义结构体 memset(&options, 0x00, sizeof(options)); //memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作 //使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性。 if (0 != tcgetattr(fd, &options)) { //获得串口属性 printf("%s error\n", __FUNCTION__); return -1; } tcflush(fd, TCIOFLUSH);//刷新输入输出缓冲区 //设置波特率 cfsetispeed(&options, B115200);//设置输入速度为115200 cfsetospeed(&options, B115200);//设置输出速度为115200 //设置串口属性,TCSANOW:不等数据传输完毕就立即改变属性 if (0 != tcsetattr(fd, TCSANOW, &options)) { printf("---- tc setattr error\n"); return -1; } tcflush(fd, TCIOFLUSH);//刷新输入输出缓冲区 //串口属性配置 printf("set properties\n"); memset(&options, 0x00, sizeof(options)); if (0 != tcgetattr(fd, &options)) { //获得串口属性 printf("tcgetattr failed-->1\n"); return -1; } options.c_cflag |= (CLOCAL | CREAD);//这两个选项使驱动程序启动接收字符装置,同时忽略串口信号线的状态 options.c_cflag &= ~CSIZE;//先把数据位清零 options.c_cflag |= CS8;//设置数据位为8位 options.c_cflag &= ~CSTOPB;//设置停止位为1 options.c_cflag &= ~PARENB; //设置无校验 options.c_cflag &= ~CRTSCTS;//不启用 RTS/CTS (硬件) 流控制 options.c_iflag &= ~(IXON | IXOFF | IXANY); //禁用软件流控制 options.c_iflag &= IGNCR;//忽略CR options.c_oflag &= ~OPOST; // 设置输出标志:不执行输出处理 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);//设置本地标志:不进行回送,关闭规范输入,关闭扩充输入字符处理,关闭终端产生的信号 //如果有数据可用,则read最多返回所要求的字节数,如果无数据可用,则read立即返回0 options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 0; tcflush(fd, TCIFLUSH);//刷新输入缓冲区 if (0 != tcsetattr(fd, TCSANOW, &options)) { printf("tcgetattr failed-->2\n"); return -1; } printf("loand...\n"); printf("write and read...\n"); //================================ char buff[1024]; char readBuff[1024]; size_t readLengh=1024; ssize_t w_ok,r_ok; while(1) { printf("Please enter the command or press q to exit:\n"); scanf("%s",buff); char *sendBuff=buff; if ((strlen(buff) == 1) && (buff[0] == 'q'||buff[0] == 'Q')){ printf("Exit!\n"); break; } int flag=0; while(flag< 10) { //写数据 writedata(fd,sendBuff); flag++; sleep(2); //读数据 r_ok = read(fd,readBuff,readLengh); printf("r_ok %zd\n",r_ok); //r读取数据失败,继续读取 if (r_ok>0 ){ printf("Success!\n"); printf("read data:%s\n", readBuff); break; } } } return 0; } ``` #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)