新闻资讯

串口属性设置

串口属性设置

上一节的串口基本操作虽然可以进行基本的串口数据收发,但只能使用串口驱动默认的属性(9600,8n1,无流控),而在实际应用中,往往要设置串口属性如波特率、数据位、奇偶校验、停止位等。

接上篇《【Linux公开课】Linux串口编程》!

16.2 串口属性设置

上一节的串口基本操作虽然可以进行基本的串口数据收发,但只能使用串口驱动默认的属性(9600,8n1,无流控),而在实际应用中,往往要设置串口属性如波特率、数据位、奇偶校验、停止位等。

16.2.1 终端属性描述

前面提到过,进行串口编程时需要包含<termios.h>头文件。该文件包含了POSIX终端属性描述结构struct termios,该结构如程序清单16.5所示。


程序清单16.5 termios结构

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



tcflag_t的定义为:


typedef unsigned int tcflag_t;


下面对termios结构各成员进行简单介绍。


1、控制标志


通过termios结构的c_cflag成员可设置串口的波特率、数据位、奇偶校验、停止位以及流控制,详见后续对应部分的描述。


2、输入标志


c_iflag成员负责控制串口输入数据的处理,它的部分可用标志如表16.1所列。


表16.1 c_iflag标志

标志

说明

标志

说明

INPCK

打开输入奇偶校验

IXOFF

启用/停止输入控制流起作用

IGNPAR

忽略奇偶错字符

IGNBRK

忽略BREAK条件

PARMRK

标记奇偶错

INLCR

将输入的NL转换为CR

ISTRIP

剥除字符第8位

IGNCR

忽略CR

IXON

启用/停止输出控制流起作用

ICRNL

将输入的CR转换为NL


使用软件流控制是启用IXON、IXOFF和IXANY选项:


options.c_iflag |= (IXON | IXOFF | IXANY);


相反,要禁用软件流控制是禁止上面的选项:


options.c_iflag &= ~(IXON | IXOFF | IXANY);


3、输出标志


termios结构的c_oflag成员管理输出过滤,它的部分选项标志如表16.2所列。


表16.2 c_oflag标志

标志

说明

标志

说明

BSDLY

退格延迟屏蔽

OLCUC

将输出的小写字符转换为大写字符

CMSPAR

标志或空奇偶性

ONLCR

将NL转换为CR-NL

CRDLY

CR延迟屏蔽

ONLRET

NL执行CR功能

FFDLY

换页延迟屏蔽

ONOCR

在0列不输出CR

OCRNL

将输出的CR转换为NL

OPOST

执行输出处理

OFDEL

填充符为DEL,否则为NULL

OXTABS

将制表符扩充为空格

OFILL

对于延迟使用填充符


  • 启用输出处理


启用输出处理需要在c_oflag成员中启用OPOST选项,其操作方法如下:


options.c_oflag |= OPOST;


  • 使用原始输出


使用原始输出,就是禁用输出处理,使数据能不经过处理、过滤地完整地输出到串口。当OPOST被禁止,c_oflag其它选项也被忽略,其操作方法如下:


options.c_oflag &= ~OPOST;


4、本地标志


termios结构的c_lflag成员影响驱动程序和用户之间的接口,它的部分可用标志如表16.3所列。


表16.3 c_lflag标志

标志

说明

标志

说明

ISIG

启用终端产生的信号

NOFLSH

在中断或退出键后禁用刷清

ICANON

启用规范输入

IEXTEN

启用扩充的输入字符处理

XCASE

规范大/小写表示

ECHOCTL

回送控制字符为(char)

ECHO

进行回送

ECHOPRT

硬拷贝的可见擦除方式

ECHOE

可见擦除字符

ECHOKE

Kill的可见擦除

ECHOK

回送kill符

PENDIN

重新打印未决输入

ECHONL

回送NL

TOSTOP

对于后台输出发送SIGTTOU


  • 选择规范模式


规范模式是行处理的。调用read读取串口数据时,每次返回一行数据。当选择规范模式时,需要启用ICANON、ECHO和ECHOE选项:

options.c_lflag |= (ICANON | ECHO | ECHOE);

当串口设备作为用户终端时,通常要把串口设备配置成规范模式。


  • 选择原始模式


在原始模式下,串口输入数据是不经过处理的,在串口接口接收的数据被完整保留。要使串口设备工作在原始模式,需要关闭ICANON、ECHO、ECHOE和ISIG选项,其操作方法如下:


options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);


5、控制字符组


termios结构的c_cc成员是一个数组,其长度是NCCS,一般介于15-20之间。c_cc数组的每个元素的下标都用一个宏表示,它的部分下标标志名及说明如表16.4所列。


表16.4 c_cc标志

标志

说明

标志

说明

VINTR

中断

VEOL

行结束

VQUIT

退出

VMIN

需读取的小字节数

VERASE

擦除

VTIME

与“VMIN”配合使用,是指限定的传输或等待的长时间

VEOF

行结束



16.2.2 获取和设置终端属性


使用函数tcgetattr()可以获取串口设备的termios结构。该函数原型如下:


int tcgetattr(int fd, struct termios *termptr);


函数执行成功返回0,串口设备的termios结构由temptr参数返回;若出错则返回-1。

获得termios结构后,可以把串口的属性设置到termios结构中。串口属性设置完成后,可通过tcsetattr()函数把新的属性设置应用到串口中。tcsetattr()函数原型如下:


int tcsetattr(int fd, int opt, const struct termios *termptr);


在串口驱动程序里有输入缓冲区和输出缓冲区。在改变串口属性时,缓冲区可能有数据存在,如何处理缓冲区中的数据,可通过opt参数实现:


  • TCSANOW:更改立即发生;

  • TCSADRAIN:发送了所有输出后更改才发生,若更改输出参数则应用此选项;

  • TCSAFLUSH:发送了所有输出后更改才发生,在更改发生时未读的所有输入数据被删除(Flush)。


上述两函数执行时,若成功则返回0,若出错则返回-1。


16.2.3 设置波特率


串口的波特率分输入波特率和输出波特率,可分别通过cfsetispeed()和cfsetospeed()函数设置。这两个函数原型为:


int cfsetispeed(struct termios *termptr, speed_t speed);
int cfsetospeed(struct termios *termptr, speed_t speed);


这两个函数若执行成功返回0,若出错则返回-1。speed参数为需要设置的波特率,可选择的常量如表16.5所列。


表16.5 波特率常量

标志

说明

标志

说明

B0

0位/秒(挂起)

B9600

9600位/秒

B110

100位/秒

B19200

19200位/秒

B134

134位/秒

B57600

57600位/秒

B1200

1200位/秒

B115200

115200位/秒

B2400

2400位/秒

B460800

460800位/秒

B4800

4800位/秒



通常来说,串口的输入和输出波特率都设置为同一个值,如将波特率设置为115200的代码为:


cfsetispeed(&opt, B115200);
cfsetospeed(&opt, B115200);


程序清单16.6所示的set_baudrate()函数实现了波特率设置操作。该函数将串口输入/输出设置为相同的波特率,使用时只需填写所需波特率即可。


程序清单16.6 set_baudrate()函数

static void set_baudrate (struct termios *opt, unsigned int baudrate)
{
cfsetispeed(opt, baudrate);
cfsetospeed(opt, baudrate);
}


使用set_baudrate()函数设置串口输入/输出波特率为115200的代码为:


set_baudrate(&opt, B115200));


16.2.4 设置数据位


设置串口数据位是在termios结构的c_cflag成员上设置,可用的选项标志如表16.6所列。


表16.6 设置数据位可用的标志选项

标志

说明

标志

说明

CSIZE

数据位屏蔽

CS7

7位数据位

CS5

5位数据位

CS8

8位数据位

CS6

6位数据位


设置串口的数据位为8位的代码为:


opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;

在该代码中,把CS8改成CS5、CS6或CS7,分别可以把串口的数据位设置为5位、6位或7位。

程序清单16.7所示的set_data_bit()函数实了串口数据位的设置。


程序清单16.7 set_data_bit函数

static void set_data_bit (struct termios *opt, unsigned int databit)
{
opt->c_cflag &= ~CSIZE;
switch (databit) {
case 8:
opt->c_cflag |= CS8;
break;
case 7:
opt->c_cflag |= CS7;
break;
case 6:
opt->c_cflag |= CS6;
break;
case 5:
opt->c_cflag |= CS5;
break;
default:
opt->c_cflag |= CS8;
break;
}
}

在set_data_bit()函数中,databit参数可以取值为8、7、6、5,分别表示把数据位设置为8位、7位、6位、5位。

使用set_data_bit()函数设置8位数据位的代码如下:

set_data_bit(8)

16.2.5 设置奇偶校验

设置串口的奇偶校验是在termios结构的c_cflag成员上设置,可用的选项标志如表16.7所列。

表16.7 奇偶校验标志

标志

说明

PARENB

进行奇偶校验

PARODD

奇校验,否则为偶校验

Linux的串口驱动支持无校验(‘N’)、偶校验(‘E’)和奇校验(‘O’)。

  • 设置无校验的方法为:

opt->c_cflag &= ~PARENB;

  • 设置偶校验的方法为:

opt->c_cflag |= PARENB;
opt->c_cflag &= ~PARODD;

  • 设置奇校验的方法为:

opt->c_cflag |= PARENB;
opt->c_cflag |= ~PARODD;

程序清单16.8所示的set_parity()函数实现了串口奇偶校验设置。

程序清单16.8 set_parity函数

static void set_parity (struct termios *opt, char parity)
{
switch (parity) {
case 'N': /* 无校验 */
case 'n':
opt->c_cflag &= ~PARENB;
break;
case 'E': /* 偶校验 */
case ‘e‘:
opt->c_cflag |= PARENB;
opt->c_cflag &= ~PARODD;
break;
case 'O': /* 奇校验 */
case ‘o‘:
opt->c_cflag |= PARENB;
opt->c_cflag |= ~PARODD;
break;
default: /* 其它选择为无校验 */
opt->c_cflag &= ~PARENB;
break;
}
}

在set_parity函数中,parity参数可以取值为:‘N’和‘n’(无奇偶校验)、‘E’和‘e’(表示偶校验)、‘O’和‘o’(表示奇校验)。

设置串口为无校验的代码如下:

static void set_parity (&opt, ‘N’);

static void set_parity (&opt, ‘n’);

16.2.6 设置停止位

设置串口停止位是在termios对象的c_cflag成员上设置,需要用到的选项标志为CSTOPB(2位停止位,否则为1位)。

例如,设置1位停止位的方法为:

opt->c_cflag &= ~CSTOPB;

程序清单16.9所示的set_stopbit()函数实现串口停止位的设置。

程序清单16.9 set_parity函数

static void set_stopbit (struct termios *opt, const char *stopbit)
{
if (0 == strcmp (stopbit, "1")) {
opt->c_cflag &= ~CSTOPB; /* 1位停止位t */
} else if (0 == strcmp (stopbit, "1.5")) {
opt->c_cflag &= ~CSTOPB; /* 1.5位停止位 */
}else if (0 == strcmp (stopbit, "2")) {
opt->c_cflag |= CSTOPB; /* 2 位停止位 */
}else {
opt->c_cflag &= ~CSTOPB; /* 1 位停止位 */
}
}

在set_stopbit()函数中,stopbit参数可以取值为:“1”(1位停止位)、“1.5”(1.5位停止位)和“2”(2位停止位)。

设置串口为1位停止位的代码如下:

set_stopbit(&opt, "1");

16.2.7 其它设置

调用read()函数读取串口数据时,返回读取数据的数量需要考虑两个变量:MIN和TIME。MIN和TIME在termios结构的c_cc成员的数组下标名为VMIN和VTIME。

MIN是指一次read调用期望返回的小字节数。VTIME说明等待数据到达的分秒数(秒的1/10为分秒)。TIME与MIN组合使用的具体含义分为以下四种情形:

  • 当MIN > 0,TIME > 0时

计时器在收到个字节后启动,在计时器超时之前(TIME的时间到),若已收到MIN个字节,则read返回MIN个字节,否则,在计时器超时后返回实际接收到的字节。

注意:因为只有在接收到个字节时才开始计时,所以至少可以返回1个字节。这种情形中,在接到个字节之前,调用者阻塞。如果在调用read时数据已经可用,则如同在read后数据立即被接到一样。

  • 当MIN > 0,TIME = 0时

MIN个字节完整接收后,read才返回,这可能会造成read无限期地阻塞。

  • 当MIN = 0, TIME > 0时

TIME为允许等待的大时间,计时器在调用read时立即启动,在串口接到1字节数据或者计时器超时后即返回,如果是计时器超时,则返回0。

  • 当MIN = 0,TIME = 0时

如果有数据可用,则read多返回所要求的字节数,如果无数据可用,则read立即返回0。

设置TIME为150、MIN为255的方法如下:

opt.c_cc[VTIME] = 150;

opt.c_cc[VMIN] = 255;

16.2.8 串口属性设置函数

程序清单16.10所示的set_port_attr函数实现了串口属性的设置。

程序清单16.10 终端属性设置函数

int set_port_attr (int fd,int baudrate, int databit, const char *stopbit, char parity, int vtime,int vmin )
{
struct termios opt;
tcgetattr(fd, &opt);
set_baudrate(&opt, baudrate);
opt.c_cflag |= CLOCAL | CREAD; /* | CRTSCTS */
set_data_bit(&opt, databit);
set_parity(&opt, parity);
set_stopbit(&opt, stopbit);
opt.c_oflag = 0;
opt.c_lflag |= 0;
opt.c_oflag &= ~OPOST;
opt.c_cc[VTIME] = vtime;
opt.c_cc[VMIN] = vmin;
tcflush (fd, TCIFLUSH);
return (tcsetattr (fd, TCSANOW, &opt));
}

set_port_attr函数调用成功时,返回0;调用失败时,返回-1。

设置串口属性为“115200、8n1”的代码如下:

ret = set_port_attr (fd, B115200, 8, "1", 'N', 150, 255 );
if(ret < 0) {
printf("set uart arrt faile \n");
exit(-1);
}

16.2.9 串口范例2

程序清单16.11所示代码可以设置串口属性后,作数据收发操作。在该代码中,程序在打开串口设备后,把串口属性设置为“115200 8n1”;然后在串口发送“hello ZLG!”的字符串,然后准备接收字符串;在接收了字符串之后,把接收的字符打印出来。

程序清单16.11 串口操作示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm/termios.h>
#include "serial.h"
#define DEV_NAME "/dev/ttyS1"
int main (int argc, char *argv[])
{
int fd;
int len, i,ret;
char buf[] = "hello ZLG!";
fd = open(DEV_NAME, O_RDWR | O_NOCTTY);
if(fd < 0) {
perror(DEV_NAME);
return -1;
}
ret = set_port_attr (fd, B115200, 8, "1", 'N',150,255 ); /* 115200 8n1 */
if(ret < 0) {
printf("set uart arrt faile \n");
exit(-1);
}
len = write(fd, buf, sizeof(buf)); /* 向串口发送字符串 */
if (len < 0) {
printf("write data error \n");
return -1;
}
len = read(fd, buf, sizeof(buf)); /* 在串口读取字符串 */
if (len < 0) {
printf("read error \n");
return -1;
}
printf("%s \n", buf); /* 打印在串口读取的字符串 */
return(0);
}

串口范例2代码可用的测试方法如下:

1、把示例代码在Linux主机上作本地编译,生成test_uart_2程序文件;

2、使用另外一台Windows电脑和Linux主机建立串口连接;

3、在Windows电脑打开串口助手软件,设置串口属性为“15200,8n1,无流控”;

4、在Linux主机运行test_uart_2程序:

vmuser@Linux-host:~/uart_test$ sudo ./test_uart_2

这时串口助手软件接收到“hello ZLG!”字符串,如图16.4所示。

图16.4 test_uart_2程序发送数据成功

5、在串口助手的发送“hello ZLG!”的字符串;

这时Linux主机的test_uart_2程序打印从串口接收的字符串如图16.5所示。

 


图16.5 test_uart_2接收数据成功

友荐云推荐
技术文章
最新产品
热卖产品
友荐云推荐

粤公网安备 44010602004346号