Linux c下串口使用相关函数

① 串口使用相关头文件

#include<stdio.h>         /*标准输入输出的定义*/

#include<stdlib.h>        /*标准函数库定义*/

#include<unistd.h>       /*UNIX标准函数定义*/

#include<sys/types.h>  /*基本系统数据类型*/

#include<sys/stat.h>      /*文件状态*/

#include<fcntl.h>         /*文件控制定义*/

#inlcude<error.h>          /*错误号定义*/

#include<assert.h>

#define FALSE -1

#define TRUE 0

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400,B1200, B300,B38400, B19200, B9600, B4800, B2400,B1200, B300, };

int name_arr[] ={38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,19200, 9600, 4800, 2400, 1200, 300, };

② 打开串口设备函数

static int fd;
int OpenDev(int fd, const char *pathname)
{
       assert(pathname);          //检测串口路径pathname是否存在
[c1]
       if(-1==fd)
  {
         perror(“Can’t Open Serial Port”);
         return -1;
  }
  else
         return fd;
}
/*

[c1]O_RDWR:以读写的方式打开;

O_NOCTTY:如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。

O_NONBLOCK: 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。

*/

Linux 串口 编程 python linux串口函数_数据

Linux 串口 编程 python linux串口函数_Linux 串口 编程 python_02

 

③ 设置串口参数(串口波特率、数据流控制、帧的格式(即数据位的个数,停止位,校验位[c1]

int SerialPort_Config(int fd,int baude,int c_flow,int bits,char parity,int stop)
{
       struct termios options;
       /*获得当前设备模式,与终端相关的参数,fd=0标准输入*/
       if(tcgetattr(fd,&options)<0)
       {
              perror(“tcgetarrt error”);
              return-1;
  }
 
void set_speed(int fd,int speed)
{
       int i;
       int status;
       for(i=0;i<sizeof(speed_attr)/sizeof(int);i++)
       {
              if(speed==name_attr[i])
    {
         tcflush(fd,TCIOFLUSH);//设置当前flush
         cfsetispeed(&options,speed_attr[i]);
         cfsetospeed(&options,speed_attr[i]);
    }
  }
}
}
/*

[c1]串口的设置主要是设置 struct termios 结构体的各成员值。

struct termio
{
  unsigned short c_iflag; /* 输入模式标志 */
  unsigned short c_oflag; /* 输出模式标志 */
  unsigned short c_cflag; /* 控制模式标志*/
  unsigned short c_lflag; /* local mode flags */
  unsigned char c_line; /* line discipline */
  unsigned char c_cc[NCC]; /* control characters */
};
 */

 串口属性设置

1. 属性描述

串口属于终端设备,其接口属性用termios结构描述,如程序清单13.9所示。

程序清单13.9termios结构

struct termios {
tcflag_t  c_cflag/* 控制标志*/
tcflag_t  c_iflag;/* 输入标志*/
tcflag_t  c_oflag;/* 输出标志*/
tcflag_t  c_lflag;/* 本地标志*/
tcflag_t  c_cc[NCCS];/* 控制字符*/
};

粗略而言,控制标志影响到RS-232串行线(如:忽略调制解调器的状态线、每个字符需要一个或两个停止位等),输入标志由终端设备驱动程序用来控制字符的输入(如:剥除输入字节的第8位,允许输入奇偶校验等),输出控制则控制驱动程序输出(如:执行输出处理、将换行符映射为CR/LF等),本地标志影响驱动程序和用户之间的接口(如:本地回显的开和关等),c_cc数组则包含了所有可以更改的特殊字符。

(1)控制标志

c_cflag成员控制着波特率、数据位、奇偶校验、停止位以及流控制,表13.4列出了c_cflag可用的部分选项。

标志

说明

标志

说明

CBAUD

波特率位屏蔽

CSIZE

数据位屏蔽

B0

0位/秒(挂起)

CS5

5位数据位

B110

100位/秒

CS6

6位数据位

B134

134位/秒

CS7

7位数据位

B1200

1200位/秒

CS8

8位数据位

表13.4c_cflag部分可用选项

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选项通常是要启用的,这两个选项使驱动程序启动接收字符装置,同时忽略串口信号线的状态。

(2)输入标志

c_iflag成员负责控制串口输入数据的处理,表13.5所示是c_iflag的部分可用标志。

表13.5c_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);

 

(3)输出标志

c_oflag成员管理输出过滤,如表13.6所示是c_oflag成员的部分选项标志。

表13.6c_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;

 

(4)本地标志

本地标志c_lflag控制着串口驱动程序如何管理输入的字符,如表13.7所示是c_lflag的部分可用标志。

表13.7c_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);

 

(4)控制字符组

c_cc数组的长度是NCCS,一般介于15-20之间。c_cc数组的每个成员的下标都用一个宏表示,表13.8列出了c_cc的部分下标标志名及其对应说明。

表13.8c_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。

 

2. 属性设置

使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性,如程序清单13.10所示。

程序清单13.10设置和获取termios结构属性

#include <termios.h>/* 使用终端接口函数需要使用此头文件*/

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。

(1)设置波特率

串口的输入和输出波特率可分别用cfsetispeed()和cfsetospeed()函数来设置,如程序清单13.11所示。

程序清单13.11设置串口输入/输出波特率函数

#include <termios.h>

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函数。操作方法如程序清单13.12所示。

程序清单13.12设置波特率示例

if (tcgetattr(fd, &opt)< 0) {
return ERROR;
}
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
if (tcsetattr(fd,   TCSANOW,   &opt)<0) {
return   ERROR;
}

(2)设置数据位

设置数据位不需要专用的函数,只需要在设置数据位之前用数据位屏蔽标志(CSIZE)把对应数据位清零,然后再设置新的数据位即可,如下所示:

options.c_cflag &= ~CSIZE;/* 先把数据位清零*/

options.c_cflag |= CS8;/* 把数据位设置为8位*/

(3)设置奇偶校验

正如设置数据位一样,设置奇偶校验是在直接在cflag成员上设置。下面是各种类型的校验设置方法。

 

1)无奇偶校验(8N1):

options.c_cflag &= ~PARENB;

options.c_cflag &= ~CSTOPB;

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS8;

2)7位数据位奇偶校验(7E1):

options.c_cflag |= PARENB;

options.c_cflag &= ~PARODD;

options.c_cflag &= ~CSTOPB;

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS7;

3)奇校验(7O1):

options.c_cflag|= PARENB;

options.c_cflag |= PARODD;

options.c_cflag &= ~CSTOPB;

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS7;

 

串口设置示例:

static int UART2_Init(void)
{
    struct termios opt; //属性描述
    fdUart2 = open(DEV_UART2, O_RDWR | O_NOCTTY);             
    if(fdUart2 < 0) 
    {
        perror(DEV_UART2);
        return -1;
    }
    tcgetattr(fdUart2, &opt);     //获取串口属性结构体对象
    cfsetispeed(&opt, B38400);     //设置输入波特率
    cfsetospeed(&opt, B38400);     //设置输出波特率
 
    /* raw mode */   //偶校验
    opt.c_lflag   &=   ~(ECHO | ICANON | IEXTEN |ISIG); //设置本地标志:不进行回送,关闭规范输入,关闭扩充输入字符处理,关闭终端产生的信号
    opt.c_iflag   &=  ~(IXON | ISTRIP);       // 关闭输出流控制, 不剥除第8位
    opt.c_iflag   |= (ICRNL | BRKINT | INPCK);// 将输入的CR转换为NL,使得输入和输出队列被刷新,打开奇偶校验
    opt.c_oflag   &=   ~(OPOST);              // 设置输出标志:不执行输出处理
    opt.c_cflag   &=   ~(PARODD | CSIZE);     // 关闭输入输出是奇校验,使用屏蔽位
    opt.c_cflag   |=   (CS8 | CLOCAL | CREAD | PARENB);//8位数据位,保证程序不会占用串口,能够从串口读取输入数据,允许输出产生奇偶信息以及输入的奇偶校验
    /*'DATA_LEN' bytes can be read by serial*/
 
    opt.c_cc[VMIN]   =   DATA_LEN;   //读取字符的最少个数                                   
    opt.c_cc[VTIME]  =   1;        //读取一个字符等待1*(1/10)s
    
    tcflush(fdUart2,TCIOFLUSH);  //清空所有正在发生的IO数据
    
    if (tcsetattr(fdUart2, TCSANOW, &opt) < 0) //激活配置(将修改后的termios数据设置到串口)
    {
        return   -1;
    }
    return fdUart2;
}

1. 属性描述

串口属于终端设备,其接口属性用termios结构描述,如程序清单13.9所示。

程序清单13.9termios结构

struct termios {

tcflag_t  c_cflag/* 控制标志*/

tcflag_t  c_iflag;/* 输入标志*/

tcflag_t  c_oflag;/* 输出标志*/

tcflag_t  c_lflag;/* 本地标志*/

tcflag_t  c_cc[NCCS];/* 控制字符*/

};

粗略而言,控制标志影响到RS-232串行线(如:忽略调制解调器的状态线、每个字符需要一个或两个停止位等),输入标志由终端设备驱动程序用来控制字符的输入(如:剥除输入字节的第8位,允许输入奇偶校验等),输出控制则控制驱动程序输出(如:执行输出处理、将换行符映射为CR/LF等),本地标志影响驱动程序和用户之间的接口(如:本地回显的开和关等),c_cc数组则包含了所有可以更改的特殊字符。

(1)控制标志

c_cflag成员控制着波特率、数据位、奇偶校验、停止位以及流控制,表13.4列出了c_cflag可用的部分选项。

标志

说明

标志

说明

CBAUD

波特率位屏蔽

CSIZE

数据位屏蔽

B0

0位/秒(挂起)

CS5

5位数据位

B110

100位/秒

CS6

6位数据位

B134

134位/秒

CS7

7位数据位

B1200

1200位/秒

CS8

8位数据位

表13.4c_cflag部分可用选项

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选项通常是要启用的,这两个选项使驱动程序启动接收字符装置,同时忽略串口信号线的状态。

(2)输入标志

c_iflag成员负责控制串口输入数据的处理,表13.5所示是c_iflag的部分可用标志。

表13.5c_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);

 

(3)输出标志

c_oflag成员管理输出过滤,如表13.6所示是c_oflag成员的部分选项标志。

表13.6c_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;

 

(4)本地标志

本地标志c_lflag控制着串口驱动程序如何管理输入的字符,如表13.7所示是c_lflag的部分可用标志。

表13.7c_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);

 

(4)控制字符组

c_cc数组的长度是NCCS,一般介于15-20之间。c_cc数组的每个成员的下标都用一个宏表示,表13.8列出了c_cc的部分下标标志名及其对应说明。

表13.8c_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。

 

2. 属性设置

使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性,如程序清单13.10所示。

程序清单13.10设置和获取termios结构属性

#include <termios.h>/* 使用终端接口函数需要使用此头文件*/

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。

(1)设置波特率

串口的输入和输出波特率可分别用cfsetispeed()和cfsetospeed()函数来设置,如程序清单13.11所示。

程序清单13.11设置串口输入/输出波特率函数

#include <termios.h>

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函数。操作方法如程序清单13.12所示。

程序清单13.12设置波特率示例

if (tcgetattr(fd, &opt)< 0) {
return ERROR;
}
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
if (tcsetattr(fd,   TCSANOW,   &opt)<0) {
return   ERROR;
}

(2)设置数据位

设置数据位不需要专用的函数,只需要在设置数据位之前用数据位屏蔽标志(CSIZE)把对应数据位清零,然后再设置新的数据位即可,如下所示:

options.c_cflag &= ~CSIZE;/* 先把数据位清零*/

options.c_cflag |= CS8;/* 把数据位设置为8位*/

(3)设置奇偶校验

正如设置数据位一样,设置奇偶校验是在直接在cflag成员上设置。下面是各种类型的校验设置方法。

 

1)无奇偶校验(8N1):

options.c_cflag &= ~PARENB;

options.c_cflag &= ~CSTOPB;

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS8;

2)7位数据位奇偶校验(7E1):

options.c_cflag |= PARENB;

options.c_cflag &= ~PARODD;

options.c_cflag &= ~CSTOPB;

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS7;

3)奇校验(7O1):

options.c_cflag|= PARENB;

options.c_cflag |= PARODD;

options.c_cflag &= ~CSTOPB;

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS7;

 

串口设置示例:

static int UART2_Init(void)
{
    struct termios opt; //属性描述
    fdUart2 = open(DEV_UART2, O_RDWR | O_NOCTTY);             
    if(fdUart2 < 0) 
    {
        perror(DEV_UART2);
        return -1;
    }
    tcgetattr(fdUart2, &opt);     //获取串口属性结构体对象
    cfsetispeed(&opt, B38400);     //设置输入波特率
    cfsetospeed(&opt, B38400);     //设置输出波特率
 
    /* raw mode */   //偶校验
    opt.c_lflag   &=   ~(ECHO | ICANON | IEXTEN |ISIG); //设置本地标志:不进行回送,关闭规范输入,关闭扩充输入字符处理,关闭终端产生的信号
    opt.c_iflag   &=  ~(IXON | ISTRIP);       // 关闭输出流控制, 不剥除第8位
    opt.c_iflag   |= (ICRNL | BRKINT | INPCK);// 将输入的CR转换为NL,使得输入和输出队列被刷新,打开奇偶校验
    opt.c_oflag   &=   ~(OPOST);              // 设置输出标志:不执行输出处理
    opt.c_cflag   &=   ~(PARODD | CSIZE);     // 关闭输入输出是奇校验,使用屏蔽位
    opt.c_cflag   |=   (CS8 | CLOCAL | CREAD | PARENB);//8位数据位,保证程序不会占用串口,能够从串口读取输入数据,允许输出产生奇偶信息以及输入的奇偶校验
    /*'DATA_LEN' bytes can be read by serial*/
 
    opt.c_cc[VMIN]   =   DATA_LEN;   //读取字符的最少个数                                   
    opt.c_cc[VTIME]  =   1;        //读取一个字符等待1*(1/10)s
    
    tcflush(fdUart2,TCIOFLUSH);  //清空所有正在发生的IO数据
    
    if (tcsetattr(fdUart2, TCSANOW, &opt) < 0) //激活配置(将修改后的termios数据设置到串口)
    {
        return   -1;
    }
    return fdUart2;
}