linux串口编程(termios结构体说明)

termios结构体说明

https://www.cnblogs.com/li-hao/archive/2012/02/19/2358158.html

termios结构体中,该结构体一般包括如下的成员:
tcflag_t       c_iflag;      
tcflag_t       c_oflag;      
tcflag_t       c_cflag;      
tcflag_t       c_lflag;     
cc_t            c_cc[NCCS];
  
 
 其具体意义如下
 
c_iflag:输入模式标志,控制终端输入方式,具体参数如下所示。

c_iflag参数表
键值说明
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在使用该参数而是认为该参数总是已经设置

c_oflag:       输出模式标志,控制终端输出方式,具体参数如下所示。
c_oflag参数
键值说明
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

c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如下所示。
c_oflag参数
键值说明
CBAUD             波特率(4+1位)(非POSIX)
CBAUDEX        附加波特率(1位)(非POSIX)
CSIZE              字符长度,取值范围为CS5、CS6、CS7或CS8
CSTOPB         设置两个停止位
CREAD           使用接收器
PARENB         使用奇偶校验
PARODD        对输入使用奇偶校验,对输出使用偶校验
HUPCL           关闭设备时挂起
CLOCAL         忽略调制解调器线路状态
CRTSCTS         使用RTS/CTS流控制


c_lflag:本地模式标志,控制终端编辑功能,具体参数如下所示。
c_lflag参数
键值说明
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信号

与此结构体相关的函数
(一)tcgetattr()      //   tc  get attr
1.原型
int tcgetattr  (int fd,struct termois & termios_p);
2.
功能 
取得终端介质(fd)初始值,并把其值 赋给temios_p;函数可以从后台进程中调用;

但是,终端属性可能被后来的前台进程所改变。


(二)tcsetattr()          //tc  set  attr
1.原型
int tcsetattr   (int fd,int actions,const struct    termios *termios_p);
2.功能
设置与终端相关的参数 (除非需要底层支持却无法满足),使用 termios_p 引用的 termios 结构。optional_actions (tcsetattr函数的第二个参数)指定了什么时候改变会起作用: 
TCSANOW:      改变立即发生  
TCSADRAIN:    改变在所有写入 fd 的输出都被传输后生效。这个函数应当用于修改影响输出的参数时使用。(当前输出完成时将值改变)  
TCSAFLUSH :   改变在所有写入 fd 引用的对象的输出都被传输后生效,所有已接受但未读入的输入都在改变发生前丢弃(同TCSADRAIN,但会舍弃当前所有值)。 


(三)tcsendbreak()    //  tc send break
  传送连续的 0 值比特流,持续一段时间,如果终端使用异步串行数据传输的话。如果 duration 是 0,它至少传输 0.25 秒,不会超过 0.5 秒。如果 duration 非零,它发送的时间长度由实现定义。 
如果终端并非使用异步串行数据传输,tcsendbreak() 什么都不做。


(四)tcdrain()            //tc drain
等待直到所有写入 fd 引用的对象的输出都被传输。


(五)tcflush()           //tc   flush
丢弃要写入 引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据,取决于 queue_selector 的值:

TCIFLUSH :   刷新收到的数据但是不读  
TCOFLUSH : 刷新写入的数据但是不传送  
TCIOFLUSH :同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送 

(六)tcflow()            //tc  flow  
挂起 fd 引用的对象上的数据传输或接收,取决于 action 的值:

TCOOFF :  挂起输出  
TCOON :   重新开始被挂起的输出  
TCIOFF :   发送一个 STOP 字符,停止终端设备向系统传送数据  
TCION :     发送一个 START 字符,使终端设备向系统传输数据  
打开一个终端设备时的默认设置是输入和输出都没有挂起。


(七)波特率函数 
被用来获取和设置 termios 结构中,输入和输出波特率的值。新值不会马上生效,直到成功调用了 tcsetattr() 函数。
设置速度为 B0 使得 modem "挂机"。与 B38400 相应的实际比特率可以用 setserial(8) 调整。 
输入和输出波特率被保存于 termios 结构中。 
cfmakeraw 设置终端属性如下: 

             //输入模式标志
termios_p->c_iflag &= ~( IGNBRK | BRKINT  | PARMRK | ISTRIP | INLCR | IGNCR  | ICRNL | IXON   );

//  输出模式标志
termios_p->c_oflag &= ~OPOST;

//  本地模式标志 
termios_p->c_lflag &= ~(   ECHO   |   ECHONL   |   ICANON   |    ISIG   |   IEXTEN   );
             
 //  控制模式标志
termios_p->c_cflag &= ~(CSIZE   |   PARENB);

termios_p->c_cflag |= CS8;

cf get o speed

cf  set o speed 

cf  get i speed

cf set i  speed

1.cfgetospeed()         返回 termios_p 指向的 termios 结构中存储的输出波特率 
2.cfsetospeed()         设置 termios_p 指向的 termios 结构中存储的输出波特率为 speed。取值必须是以下常量之一: 
B0        B50        B75        B110        B134        B150        B200        B300        B600        B1200        B1800        B2400        B4800        B9600        B19200        B38400        B57600        B115200        B230400
其中:零值 B0 用来中断连接。如果指定了 B0,不应当再假定存在连接。通常,这样将断开连接。CBAUDEX 是一个掩码,指示高于 POSIX.1 定义的速度的那一些 (57600 及以上)。因此,B57600 & CBAUDEX 为非零。 
3.cfgetispeed() 返回 termios 结构中存储的输入波特率。 
4.cfsetispeed() 设置 termios 结构中存储的输入波特率为 speed。如果输入波特率被设为0,实际输入波特率将等于输出波特率。

 
RETURN VALUE 返回值
1.cfgetispeed()      返回 termios 结构中存储的输入波特率。 
2.cfgetospeed()     返回 termios 结构中存储的输出波特率。 
3.其他函数返回: 
  (1)0:成功 
  (2)  -1:失败,
    并且为 errno 置值来指示错误。 

tcsetattr()   任何一个设置属性设置成功,都会返回成功,但因为要设置几个属性,但并不确定每个个都成成功,因此,要调用tcgetattr 来读取验证。


注意 tcsetattr() 返回成功,如果任何所要求的修改可以实现的话。因此,当进行多重修改时,应当在这个函数之后再次调用 tcgetattr() 来检测是否所有修改都成功实现

#if 0

#include <termios.h>
#include <unistd.h>

获取 fd 串口终端的属性,保存到 tremios_p
int tcgetattr(int fd, struct termios *termios_p);

设置 tremios_p 串口终端属性到 fd 中,使用 optional_action
int tcsetattr(int fd, int optional_actions,
			const struct termios *termios_p);
optional_actions 可取值:
TCSANOW   立即生效
TCSADRAIN 所有写到 fd 的输出已完成后才生效
TCSAFLUSH 所有写到 fd 的输出已完成,并且输入已接收完成,
但还未被读取

int tcsendbreak(int fd, int duration);

int tcdrain(int fd);

刷新串口终端 fd。queue_selector 可取值:
TCIFLUSH   刷新接收的数据而不读
TCOFLUSH   刷新写的数据而不发送
TCIOFLUSH  刷新接收和写的数据而不读和发送
int tcflush(int fd, int queue_selector);

int tcflow(int fd, int action);

void cfmakeraw(struct termios *termios_p);

获取输入波特率
speed_t cfgetispeed(const struct termios *termios_p);

获取输出波特率
speed_t cfgetospeed(const struct termios *termios_p);

设置输入波特率
int cfsetispeed(struct termios *termios_p, speed_t speed);

设置输出波特率
int cfsetospeed(struct termios *termios_p, speed_t speed);

设置输入/输出波特率
int cfsetspeed(struct termios *termios_p, speed_t speed);


struct termios 结构体至少包含以下成员:

tcflag_t c_iflag;      /* input modes */
tcflag_t c_oflag;      /* output modes */
tcflag_t c_cflag;      /* control modes */
tcflag_t c_lflag;      /* local modes */
cc_t     c_cc[NCCS];   /* special characters */

非规范模式下,输入立即可用,对 read 来说,c_cc[NCSS] 数组的
c_cc[VMIN] 和 c_cc[VTIME] 有以下四种情况:

c_cc[VMIN] == 0, c_cc[VTIME] == 0:
轮询读,无数据立即返回,read 返回 0

c_cc[VMIN] >  0, c_cc[VTIME] == 0:
阻塞读,直到读到请求的字节数(即 c_cc[VMIN] 个字节)

c_cc[VMIN] == 0, c_cc[VTIME] >  0:
带超时的读,如果在超时时间内有数据,则返回数据,如果超时到期,
没有数据,则返回 0

c_cc[VMIN] >  0, c_cc[VTIME] >  0:
带超时及请求字节的读,在超时期内等待请求的字节数,超时到期,
不管请求的数据是否满足都会返回,如果请求的数据满足,会立即返回。


原始模式的典型配置:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
			| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;

以上函数的返回值:

除 cfgetispeed 和 cfgetospeed 返回波特率外,其它的函数:
成功返回 0,失败返回 -1
注意:tcsetattr 函数只有请求的设置有一个生效,就会返回成功,
因此要验证是否全部设置成功,应该使用 tcgetattr 获取
属性后,与设置的属性进行对比来验证是否全部设置成功。

#endif

编程:

打开串口

/*

在 Linux 下串口文件是位于 /dev 下的

打开串口是通过使用标准的文件打开函数open操作:


*/
#include     <stdio.h>      
#include     <stdlib.h>    
#include     <unistd.h>    
#include     <sys/types.h>  
#include     <sys/stat.h>   
#include     <fcntl.h>     
#include     <termios.h>    
#include     <errno.h>   

static int fd;

int uart_open(int fd,const char *pathname)
{
    assert(pathname);

    fd = open(pathname,O_RDWR|O_NOCTTY| O_NONBLOCK);
    if(fd == -1)
    {
        perror("Open UART failed!");
        return -1;
    }
    return fd;
}

其中:

O_NOCTTY

如果路径名指向终端设备,不要把这个设备用作控制终端

O_NONBLOCK

如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)

对于串口的打开操作,必须使用O_NOCTTY参数,它表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,任务的一个输入都会影响进程。如键盘上过来的Ctrl+C中止信号等都将影响进程。

设置串口

串口初始化需要设置串口波特率,数据流控制,帧的格式(即数据位个数,停止位,校验位,数据流控制)

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;

}

读写串口

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;
}

关闭串口

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>
#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);
}

猜你喜欢

转载自blog.csdn.net/h490516509/article/details/85047477
今日推荐