Linux串口编程教程(三)——串口编程详解
来源:互联网 发布:91毁了多少家庭知乎 编辑:程序博客网 时间:2024/06/05 01:08
Linux串口编程教程(三)——串口编程详解
前言:本章将正式讲解串口编程技术,利用一个串口收发数据的程序,来分步讲解。
注意:您可以下载我的源代码进行参考。
打开串口
大家都知道,在Linux系统中设备被以作文件形式存在,所以我们以打开文件的方式访问设备。这里要注意的是普通用户一般不能直接访问设备,需要root权限。
有3个方法可以解决这个问题:
- 以root超级用户的身份运行。(常用)
- 改变设备文件的访问权限。
- 在程序中使用setuid,以串口设备所有者的身份运行程序。
代码:
#include<stdio.h>#include<fcntl.h>#include<assert.h>static int fd;int uart_open(int fd,const char *pathname){ assert(pathname); /*打开串口*/ fd = open(pathname,O_RDWR|O_NOCTTY|O_NDELAY); if(fd == -1) { perror("Open UART failed!"); return -1; } /*清除串口非阻塞标志*/ if(fcntl(fd,F_SETFL,0) < 0) { fprintf(stderr,"fcntl failed!\n"); return -1; } return fd;}
说明:
- O_NOCTTY:表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,键盘上过来的Ctrl+C中止信号等都将影响进程。
- O_NDELAY:表示不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。
关闭串口
关闭串口操作很简单,不过需要注意在关闭后是不是需要做一些清理操作。
代码:
#include<unistd.h>#include<assert.h>static int fd;int uart_close(int fd){ assert(fd); close(fd); /*可以在这里做些清理工作*/ return 0;}
配置串口
串口初始化需要设置串口波特率,数据流控制,帧的格式(即数据位个数,停止位,校验位,数据流控制)。
代码:
#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<assert.h>#include<termios.h>#include<string.h>static int ret;static int fd;int uart_set(int fd,int baude,int c_flow,int bits,char parity,int stop){ struct termios options; /*获取终端属性*/ if(tcgetattr(fd,&options) < 0) { perror("tcgetattr error"); return -1; } /*设置输入输出波特率,两者保持一致*/ switch(baude) { case 4800: cfsetispeed(&options,B4800); cfsetospeed(&options,B4800); break; case 9600: cfsetispeed(&options,B9600); cfsetospeed(&options,B9600); break; case 19200: cfsetispeed(&options,B19200); cfsetospeed(&options,B19200); break; case 38400: cfsetispeed(&options,B38400); cfsetospeed(&options,B38400); break; default: fprintf(stderr,"Unkown baude!\n"); return -1; } /*设置控制模式*/ options.c_cflag |= CLOCAL;//保证程序不占用串口 options.c_cflag |= CREAD;//保证程序可以从串口中读取数据 /*设置数据流控制*/ switch(c_flow) { case 0://不进行流控制 options.c_cflag &= ~CRTSCTS; break; case 1://进行硬件流控制 options.c_cflag |= CRTSCTS; break; case 2://进行软件流控制 options.c_cflag |= IXON|IXOFF|IXANY; break; default: fprintf(stderr,"Unkown c_flow!\n"); return -1; } /*设置数据位*/ switch(bits) { case 5: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS5; break; case 6: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS6; break; case 7: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS7; break; case 8: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS8; break; default: fprintf(stderr,"Unkown bits!\n"); return -1; } /*设置校验位*/ switch(parity) { /*无奇偶校验位*/ case 'n': case 'N': options.c_cflag &= ~PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag &= ~INPCK;//INPCK:使奇偶校验起作用 break; /*设为空格,即停止位为2位*/ case 's': case 'S': options.c_cflag &= ~PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag &= ~CSTOPB;//CSTOPB:使用两位停止位 break; /*设置奇校验*/ case 'o': case 'O': options.c_cflag |= PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag |= PARODD;//PARODD:若设置则为奇校验,否则为偶校验 options.c_cflag |= INPCK;//INPCK:使奇偶校验起作用 options.c_cflag |= ISTRIP;//ISTRIP:若设置则有效输入数字被剥离7个字节,否则保留全部8位 break; /*设置偶校验*/ case 'e': case 'E': options.c_cflag |= PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag &= ~PARODD;//PARODD:若设置则为奇校验,否则为偶校验 options.c_cflag |= INPCK;//INPCK:使奇偶校验起作用 options.c_cflag |= ISTRIP;//ISTRIP:若设置则有效输入数字被剥离7个字节,否则保留全部8位 break; default: fprintf(stderr,"Unkown parity!\n"); return -1; } /*设置停止位*/ switch(stop) { case 1: options.c_cflag &= ~CSTOPB;//CSTOPB:使用两位停止位 break; case 2: options.c_cflag |= CSTOPB;//CSTOPB:使用两位停止位 break; default: fprintf(stderr,"Unkown stop!\n"); return -1; } /*设置输出模式为原始输出*/ options.c_oflag &= ~OPOST;//OPOST:若设置则按定义的输出处理,否则所有c_oflag失效 /*设置本地模式为原始模式*/ options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* *ICANON:允许规范模式进行输入处理 *ECHO:允许输入字符的本地回显 *ECHOE:在接收EPASE时执行Backspace,Space,Backspace组合 *ISIG:允许信号 */ /*设置等待时间和最小接受字符*/ options.c_cc[VTIME] = 0;//可以在select中设置 options.c_cc[VMIN] = 1;//最少读取一个字符 /*如果发生数据溢出,只接受数据,但是不进行读操作*/ tcflush(fd,TCIFLUSH); /*激活配置*/ if(tcsetattr(fd,TCSANOW,&options) < 0) { perror("tcsetattr failed"); return -1; } return 0;}
读写串口
代码:
#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<assert.h>#include<termios.h>#include<string.h>#include<sys/time.h>#include<sys/types.h>#include<errno.h>static int ret;static int fd;/* * 安全读写函数 */ssize_t safe_write(int fd, const void *vptr, size_t n){ size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while(nleft > 0) { if((nwritten = write(fd, ptr, nleft)) <= 0) { if(nwritten < 0&&errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return(n);}ssize_t safe_read(int fd,void *vptr,size_t n){ size_t nleft; ssize_t nread; char *ptr; ptr=vptr; nleft=n; while(nleft > 0) { if((nread = read(fd,ptr,nleft)) < 0) { if(errno == EINTR)//被信号中断 nread = 0; else return -1; } else if(nread == 0) break; nleft -= nread; ptr += nread; } return (n-nleft);}int uart_read(int fd,char *r_buf,size_t len){ ssize_t cnt = 0; fd_set rfds; struct timeval time; /*将文件描述符加入读描述符集合*/ FD_ZERO(&rfds); FD_SET(fd,&rfds); /*设置超时为15s*/ time.tv_sec = 15; time.tv_usec = 0; /*实现串口的多路I/O*/ ret = select(fd+1,&rfds,NULL,NULL,&time); switch(ret) { case -1: fprintf(stderr,"select error!\n"); return -1; case 0: fprintf(stderr,"time over!\n"); return -1; default: cnt = safe_read(fd,r_buf,len); if(cnt == -1) { fprintf(stderr,"read error!\n"); return -1; } return cnt; }}int uart_write(int fd,const char *w_buf,size_t len){ ssize_t cnt = 0; cnt = safe_write(fd,w_buf,len); if(cnt == -1) { fprintf(stderr,"write error!\n"); return -1; } return cnt;}
完整程序
具体读写命令请读者自行编写。
/************************************************************************* > File Name: uart_operation.c > Author: AnSwEr > Mail: 1045837697@qq.com > Created Time: 2015年09月02日 星期三 12时45分48秒 ************************************************************************/#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<assert.h>#include<termios.h>#include<string.h>#include<sys/time.h>#include<sys/types.h>#include<errno.h>static int ret;static int fd;/* * 安全读写函数 */ssize_t safe_write(int fd, const void *vptr, size_t n){ size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while(nleft > 0) { if((nwritten = write(fd, ptr, nleft)) <= 0) { if(nwritten < 0&&errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return(n);}ssize_t safe_read(int fd,void *vptr,size_t n){ size_t nleft; ssize_t nread; char *ptr; ptr=vptr; nleft=n; while(nleft > 0) { if((nread = read(fd,ptr,nleft)) < 0) { if(errno == EINTR)//被信号中断 nread = 0; else return -1; } else if(nread == 0) break; nleft -= nread; ptr += nread; } return (n-nleft);}int uart_open(int fd,const char *pathname){ assert(pathname); /*打开串口*/ fd = open(pathname,O_RDWR|O_NOCTTY|O_NDELAY); if(fd == -1) { perror("Open UART failed!"); return -1; } /*清除串口非阻塞标志*/ if(fcntl(fd,F_SETFL,0) < 0) { fprintf(stderr,"fcntl failed!\n"); return -1; } return fd;}int uart_set(int fd,int baude,int c_flow,int bits,char parity,int stop){ struct termios options; /*获取终端属性*/ if(tcgetattr(fd,&options) < 0) { perror("tcgetattr error"); return -1; } /*设置输入输出波特率,两者保持一致*/ switch(baude) { case 4800: cfsetispeed(&options,B4800); cfsetospeed(&options,B4800); break; case 9600: cfsetispeed(&options,B9600); cfsetospeed(&options,B9600); break; case 19200: cfsetispeed(&options,B19200); cfsetospeed(&options,B19200); break; case 38400: cfsetispeed(&options,B38400); cfsetospeed(&options,B38400); break; default: fprintf(stderr,"Unkown baude!\n"); return -1; } /*设置控制模式*/ options.c_cflag |= CLOCAL;//保证程序不占用串口 options.c_cflag |= CREAD;//保证程序可以从串口中读取数据 /*设置数据流控制*/ switch(c_flow) { case 0://不进行流控制 options.c_cflag &= ~CRTSCTS; break; case 1://进行硬件流控制 options.c_cflag |= CRTSCTS; break; case 2://进行软件流控制 options.c_cflag |= IXON|IXOFF|IXANY; break; default: fprintf(stderr,"Unkown c_flow!\n"); return -1; } /*设置数据位*/ switch(bits) { case 5: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS5; break; case 6: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS6; break; case 7: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS7; break; case 8: options.c_cflag &= ~CSIZE;//屏蔽其它标志位 options.c_cflag |= CS8; break; default: fprintf(stderr,"Unkown bits!\n"); return -1; } /*设置校验位*/ switch(parity) { /*无奇偶校验位*/ case 'n': case 'N': options.c_cflag &= ~PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag &= ~INPCK;//INPCK:使奇偶校验起作用 break; /*设为空格,即停止位为2位*/ case 's': case 'S': options.c_cflag &= ~PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag &= ~CSTOPB;//CSTOPB:使用两位停止位 break; /*设置奇校验*/ case 'o': case 'O': options.c_cflag |= PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag |= PARODD;//PARODD:若设置则为奇校验,否则为偶校验 options.c_cflag |= INPCK;//INPCK:使奇偶校验起作用 options.c_cflag |= ISTRIP;//ISTRIP:若设置则有效输入数字被剥离7个字节,否则保留全部8位 break; /*设置偶校验*/ case 'e': case 'E': options.c_cflag |= PARENB;//PARENB:产生奇偶位,执行奇偶校验 options.c_cflag &= ~PARODD;//PARODD:若设置则为奇校验,否则为偶校验 options.c_cflag |= INPCK;//INPCK:使奇偶校验起作用 options.c_cflag |= ISTRIP;//ISTRIP:若设置则有效输入数字被剥离7个字节,否则保留全部8位 break; default: fprintf(stderr,"Unkown parity!\n"); return -1; } /*设置停止位*/ switch(stop) { case 1: options.c_cflag &= ~CSTOPB;//CSTOPB:使用两位停止位 break; case 2: options.c_cflag |= CSTOPB;//CSTOPB:使用两位停止位 break; default: fprintf(stderr,"Unkown stop!\n"); return -1; } /*设置输出模式为原始输出*/ options.c_oflag &= ~OPOST;//OPOST:若设置则按定义的输出处理,否则所有c_oflag失效 /*设置本地模式为原始模式*/ options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* *ICANON:允许规范模式进行输入处理 *ECHO:允许输入字符的本地回显 *ECHOE:在接收EPASE时执行Backspace,Space,Backspace组合 *ISIG:允许信号 */ /*设置等待时间和最小接受字符*/ options.c_cc[VTIME] = 0;//可以在select中设置 options.c_cc[VMIN] = 1;//最少读取一个字符 /*如果发生数据溢出,只接受数据,但是不进行读操作*/ tcflush(fd,TCIFLUSH); /*激活配置*/ if(tcsetattr(fd,TCSANOW,&options) < 0) { perror("tcsetattr failed"); return -1; } return 0;}int uart_read(int fd,char *r_buf,size_t len){ ssize_t cnt = 0; fd_set rfds; struct timeval time; /*将文件描述符加入读描述符集合*/ FD_ZERO(&rfds); FD_SET(fd,&rfds); /*设置超时为15s*/ time.tv_sec = 15; time.tv_usec = 0; /*实现串口的多路I/O*/ ret = select(fd+1,&rfds,NULL,NULL,&time); switch(ret) { case -1: fprintf(stderr,"select error!\n"); return -1; case 0: fprintf(stderr,"time over!\n"); return -1; default: cnt = safe_read(fd,r_buf,len); if(cnt == -1) { fprintf(stderr,"read error!\n"); return -1; } return cnt; }}int uart_write(int fd,const char *w_buf,size_t len){ ssize_t cnt = 0; cnt = safe_write(fd,w_buf,len); if(cnt == -1) { fprintf(stderr,"write error!\n"); return -1; } return cnt;}int uart_close(int fd){ assert(fd); close(fd); /*可以在这里做些清理工作*/ return 0;}int main(void){ const char *w_buf = "something to write"; size_t w_len = sizeof(w_buf); char r_buf[1024]; bzero(r_buf,1024); fd = uart_open(fd,"/dev/ttyS0");/*串口号/dev/ttySn,USB口号/dev/ttyUSBn*/ if(fd == -1) { fprintf(stderr,"uart_open error\n"); exit(EXIT_FAILURE); } if(uart_set(fd,9600,0,8,'N',1) == -1) { fprintf(stderr,"uart set failed!\n"); exit(EXIT_FAILURE); } ret = uart_write(fd,w_buf,w_len); if(ret == -1) { fprintf(stderr,"uart write failed!\n"); exit(EXIT_FAILURE); } while(1) { ret = uart_read(fd,r_buf,1024); if(ret == -1) { fprintf(stderr,"uart read failed!\n"); exit(EXIT_FAILURE); } } ret = uart_close(fd); if(ret == -1) { fprintf(stderr,"uart_close error\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS);}
总结
好了,以上这些就是读写串口的一些基本实现。当然,也许你copy代码后发现无法实现,那请你依据具体的情况对串口进行配置,对读写命令进行编写。
反馈与建议
- 微博:@AnSwEr不是答案
- github:AnSwErYWJ
- 博客:AnSwEr不是答案的专栏
0 0
- Linux串口编程教程(三)——串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- Linux串口编程详解
- NYOJ+最长上升子序列动态规划+数组因为输入数据时从位置0开始后面dp也是从0开始
- redhat linux yum源修改
- 关于css的样式和jsp、jquery相关问题
- 在Eclipse中使用JUnit4进行单元测试(上)
- Eclipse 扩大内存
- Linux串口编程教程(三)——串口编程详解
- std空间和全局命名空间
- Connection is read-only. Queries leading to data modification are not allowed
- java系统学习(八) --------接口与内部类
- Ubuntu下忘记用户密码
- AsyncTask
- hdu3037 lucas 定理 组合数取模
- 在Eclipse中使用JUnit4进行单元测试(中)
- ppt转为pdf文档格式转换的方法讲解