Linux串口编程教程(三)——串口编程详解

来源:互联网 发布:91毁了多少家庭知乎 编辑:程序博客网 时间:2024/06/05 01:08

Linux串口编程教程(三)——串口编程详解

前言:本章将正式讲解串口编程技术,利用一个串口收发数据的程序,来分步讲解。

注意:您可以下载我的源代码进行参考。

打开串口

大家都知道,在Linux系统中设备被以作文件形式存在,所以我们以打开文件的方式访问设备。这里要注意的是普通用户一般不能直接访问设备,需要root权限。
有3个方法可以解决这个问题:

  1. 以root超级用户的身份运行。(常用)
  2. 改变设备文件的访问权限。
  3. 在程序中使用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;}

说明:

  1. O_NOCTTY:表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,键盘上过来的Ctrl+C中止信号等都将影响进程。
  2. 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