Linux下串口的配置

一、串口属性
(标题一部分内容摘录自原文地址
1.termios结构体
串口属于终端设备,其接口属性用termios结构体来描述

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

1>c_cflag成员
c_cflag成员控制波特率、数据位、奇偶校验、停止位、流控制
【注】CREAD和CLOCAL选项通常要启用,这俩个选项使驱动程序启动接收字符装置,忽略串口信号线的状态。
在这里插入图片描述
2>c_iflag
c_iflag成员负责控制串口输入数据的处理
在这里插入图片描述
<1>设置输入校验
当c_flag成员的PARENB(奇偶校验)选项启用时,c_iflag的也应该启用奇偶校验选项。

struct termios options;
options.c_iflag |= (INPCK | ISTRIP);
【注】IGNPAR选项在一些场合的应用带有一定的危险性,它指示串口驱动程序忽略奇偶校验错误,也就是说,IGNPAR使奇偶校验出错的字符也通过输入。这在测试通信链路的质量时也许有用,但在通常的数据通信应用中不应使用。

<2>设置软件流控制

使用软件流控制是启用IXON、IXOFF和IXANY选项:
options.c_iflag |= (IXON | IXOFF | IXANY);
禁用软件流控制是禁止上面的选项:
options.c_iflag &= ~(IXON | IXOFF | IXANY);

3>输出标志c_oflag成员
在这里插入图片描述
<1>启用输出处理

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

<2>使用原始输出

使用原始输出,就是禁用输出处理,使数据能不经过处理、过滤地完整地输出到串口接口。
当OPOST被禁止,c_oflag其它选项也被忽略,其操作方法如下:
options.c_oflag &= ~OPOST; //取反求与

4>本地标志c_lflag成员
在这里插入图片描述
<1>选择规范模式

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

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

<2>选择原始模式

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

5>控制字符组c_cc成员
c_cc数组的长度是NCCS,一般介于15-20之间。c_cc数组的每个成员的下标都用一个宏表示,表13.8列出了c_cc的部分下标标志名及其对应说明。
在这里插入图片描述
【注】在规范模式下,调用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字节数据或者计时器超时后即返回,
如果是计时器超时,则返回04)当MIN = 0,TIME = 0时
如果有数据可用,则read最多返回所要求的字节数,如果无数据可用,则read立即返回0

2.属性设置
使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性。

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

int tcgetattr(int fd, struct termios *termios_p);
参数:1:fd为串口设备文件描述符
	 2:用于存放串口设置的termios结构体
	 
int tcsetattr(int fd, int optional_actions,
              const struct termios *termios_p);
参数:1:fd为串口设备文件描述符
	 2:opt是整形变量{
	 1)TCSANOW:更改立即发生;
	 2)TCSADRAIN:发送了所有输出后更改才发生,若更改输出参数则应用此选项;
	 3)TCSAFLUSH:发送了所有输出后更改才发生,更进一步,
	    在更改发生时未读的所有输入数据被删除(Flush)。
	  }
	 3:用于存放串口设置的termios结构体

【注】在串口驱动程序里,有输入缓冲区和输出缓冲区。在改变串口属性时,缓冲区中的数据可能还存在,
	  这时需要考虑到更改后的属性什么时候起作用。tcsetattr的参数opt可以指定在什么时候新的串口属性才起作用。

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

1>设置波特率

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

int cfsetispeed(struct termios *termptr, speed_t speed);
参数1:用于存放串口设置的termios结构体
   2:波特率
int cfsetospeed(struct termios *termptr, speed_t speed);
参数1:用于存放串口设置的termios结构体
   2:波特率
返回值:这两个函数若执行成功返回0,若出错则返回-1。

【注】使用这两个函数时,应当理解输入、输出波特率是存在串口设备termios结构中的。
	 在调用任一cfset函数之前,先要用tcgetattr获得设备的termios结构。
	 与此类似,在调用任一cfset函数后,波特率都被设置到termios结构中。

波特率设置示例

struct termios opt;

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

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

int set_bsize(struct termios *s, int bsize)
{
	if(s){
		options.c_cflag &= ~CSIZE;/* 先把数据位清零*/
		switch (bsize) 
		{
			case 5:
				s->c_cflag |= CS5;//5位数据位 
				break;
			case 6:
				s->c_cflag |= CS6;//6位数据位 
				break;
			case 7:
				s->c_cflag |= CS7;//7位数据位 
				break;
			case 8:
				s->c_cflag |= CS8;//8位数据位 
				break;
		}
	}
}

3>设置奇偶校验
正如设置数据位一样,设置奇偶校验是在直接在cflag成员上设置。

1.无奇偶校验(8位数据)
options.c_cflag &= ~PARENB; //取消进行奇偶校验
options.c_cflag &= ~CSTOPB; //不设置<2位停止位,否则为1位>
options.c_cflag &= ~CSIZE; //取消数据位屏蔽
options.c_cflag |= CS8; //8位数据

2.7位数据位奇偶校验
options.c_cflag |= PARENB; //进行奇偶校验
options.c_cflag &= ~PARODD; //取反偶检验,<奇校验,否则为偶校验>
options.c_cflag &= ~CSTOPB;//取反1位停止位<2位停止位,否则为1位>
options.c_cflag &= ~CSIZE;//取消数据位屏蔽
options.c_cflag |= CS7;//8位数据

3.奇校验
options.c_cflag|= PARENB; //进行奇偶校验
options.c_cflag |= PARODD; //奇校验,否则为偶校验
options.c_cflag &= ~CSTOPB; //取反1位停止位<2位停止位,否则为1位>
options.c_cflag &= ~CSIZE; //取消数据位屏蔽
options.c_cflag |= CS7; //7位数据

串口初始化示例

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


int fd = -1;

//初始化串口
int init_uart(char *uart);

//启动接收 
int enable_read(struct termios *s);


//查看串口属性
void show_termios(const struct termios *s);

//获取串口属性
int get_termios(int fd, struct termios *s);

//波特率设置
int set_speed(struct termios *s, int speed);

//设置数据位
int set_bsize(struct termios *s, int bsize);

//设置停止位 
int set_stop(struct termios *s, int stop);

//不启用RTS/CTS(硬件)流控制
int disable_flow_control(struct termios *s);

//奇偶校验 
int set_parity(struct termios *s, int parity);

//设置控制字符 
int set_c_cc(struct termios *s, int vtime, int vmin);

//配置串口属性
int uart_set(int fd, int baud, char parity, int bsize, int stop)int get_speed(const struct termios *s);

char get_parity(const struct termios *s);

int get_bsize(const struct termios *s);

int get_stop(const struct termios *s);

int main()
{
	int ret;
	char *uart = "/dev/ttyS1"; //默认打开的串口,字符设备文件
	ret = init_uart(uart);
	
	...
	
	return 0;
}

/**********************************
 *功能:从串口读取数据到buf中
 *参数1:@fd:串口描述符
 *参数2:@buf:存放数据的内存地址
 *参数3:@len:读取的长度
 *Return : -1失败
 *************************************/
int uart_read(int fd, unsigned char *buf, int len)
{

	if (fd < 0 || 0 == buf || len < 1) {
		return -1;
	}
	return read(fd, buf, len);
}

/**********************************
 *Describe:把数据写入到串口中
 *参数1:@fd:串口描述符
 *参数2:@buf:要写入的数据村的地址
 *参数3:@len:要写入的长度
 *Return : -1失败
*************************************/
int uart_write(int fd, const unsigned char *data, int len)
{
	if (fd < 0 || 0 == data || len < 1) {
		return -1;
	}

	return write(fd, data, len);
}

/**********************
**关闭串口
**********************/
int uart_close(int fd)
{
	return close(fd);
}


int init_uart(char *uart)
{
		int value;
		struct termios old; //设置前的结构
		struct termios new; //设置后的新结构,打印对比设置是否成功
		
		if(NULL == uart)
		{
			printf("uart is NULL!");
			return -1;
		}
		
//1.打开一个串口
/*************************************************************************************
**O_NOCTTY:如果path指的是终端设备,则不将此设备分配作为此进程的控制终端。
**O_NONBLOCK:如果path指的是一个FIFO,一个块特殊文件或一个字符特殊文件,
**            则此选择项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。
**************************************************************************************/
		fd = open(path, O_RDWR | O_NOCTTY); 
		if( 1 > fd)
		{
			printf("open error");
			return -1;
		}
	  
//2.F_SETFL:设置文件flag为0,即默认,即阻塞状态
		if(-1 == fcntl(fd,F_SETFL,0))
		{
			printf("fcntl error");
			return -1;
		}
		
		//获取与终端相关的参数
		if(-1 == get_termios(g_fd, &old))
		{
			printf("get termios error");
			return -1;
		}
		
		show_termios(&old);
	
//3.串口配置	
		value = uart_config(g_fd, 57600, 0, 8, 1);
		if (0 != value)
		{
			printf("config error");
			return -1;
		}
		
		printf("57600 , 0 ,8,  1");
		value = get_termios(g_fd, &new);

		if (-1 == value)
		{
			printf("get termiso");
			return -1;
		}

		printf("config ok,fd is %d\n", g_fd);
		show_termios(&new);

		return 0;
}

									
}



int get_termios(int fd, struct termios *s)
{
	if (-1 == fd || 0 == s) {
		return -1;
	}

//tcgetattr函数用于获取与终端相关的参数
	return tcgetattr(fd, s);
}

void show_termios(const struct termios *s)
{
	if (s) {
		printf("speed=%d", get_speed(s));
		printf(" parity=%c", get_parity(s));
		printf(" bsize=%d", get_bsize(s));
		printf(" stop=%d\n", get_stop(s));
	}
}

/*****************************************
 *函数功能:配置串口
 *参数1:fd:串口的文件描述符
 *参数2:baud:串口的波特率
 *参数3:parity:串口的奇偶
 *参数4:bsize:数据的位数
 *参数5:stop:停止位
 *Return : -1失败
 ****************************************/
int uart_set(int fd, int baud, char parity, int bsize, int stop)
{
	if (fd < 0) {
		printf("param_errror");
		return -1;
	} else {
		struct termios new = {0};

		bzero(&new, sizeof(new));

		//设置波特率
		if (set_speed(&new, baud)) {
			return -2;
		}

		//设置奇偶校验
		if (set_parity(&new, parity)) {
			return -3;
		}

		//设置数据位
		if (set_bsize(&new, bsize)) {
			return -4;
		}

		//设置停止位
		if (set_stop(&new, stop)) {
			return -5;
		}

		//控制字符组设置
		set_c_cc(&new, 5 * 10 * 60, 0);  //如果是阻塞模式,30s唤醒read一次
		enable_read(&new); //启动接收
		disable_flow_control(&new);//禁用流控制

		if (set_termios(fd, &new)) {
			return -6;
		}
	}

	return 0;
}

/******************* 波特率设置 start***************************/
int speed_to_baud(int speed)
{
	switch (speed) {
	case 1200:
		return B1200;

	case 2400:
		return B2400;

	case 4800:
		return B4800;

	case 9600:
		return B9600;

	case 19200:
		return B19200;

	case 38400:
		return B38400;

	case 57600:
		return B57600;

	case 115200:
		return B115200;
	}

	return B9600;
}

int set_speed(struct termios *s, int speed)
{
	if (s) {
		s->c_cflag &= ~CBAUD;
		s->c_cflag |= speed_to_baud(speed);
		return 0;
	}

	return -1;
}
/******************* 波特率设置 end***************************/


/******************** 奇偶校验 start ************************/
int set_parity(struct termios *s, int parity)
{
	if (s) {
		switch (parity) {
		case 0:
				break;
		case 1:
			s->c_cflag &= ~PARENB;//取消进行奇偶校验
			break;

		case 2:
			s->c_cflag |= PARENB;//进行奇偶校验
			s->c_cflag |= PARODD;//奇校验,否则为偶校验
			break;

		case 3:
			s->c_cflag |= PARENB;//进行奇偶校验
			s->c_cflag &= ~PARODD;//偶校验
			break;
		default:
			break;
		}
		return 0;
	}
	return -1;
}
/******************** 奇偶校验 end ***********************************/


/******************** 设置数据位 start ******************************/
int set_bsize(struct termios *s, int bsize)
{
	if (s) {
		s->c_cflag &= ~CSIZE;

		switch (bsize) {
		case 5:
			s->c_cflag |= CS5;
			break;

		case 6:
			s->c_cflag |= CS6;
			break;

		case 7:
			s->c_cflag |= CS7;
			break;

		case 8:
			s->c_cflag |= CS8;
			break;
		}

		return 0;
	}

	return -1;
}
/******************** 设置数据位 end ******************************/


/*************** 设置停止位 start *****************************/
int set_stop(struct termios *s, int stop)
{
	if (s) {
		if (1 == stop) {
			s->c_cflag &= ~CSTOPB; //1位停止位
		} else {
			s->c_cflag |= CSTOPB; //2位停止位
		}

		return 0;
	}

	return -1;
}
/****************** 设置停止位 end *****************************/


/******************设置控制字符 start ******************************/
int set_c_cc(struct termios *s, int vtime, int vmin)
{
	if (s) {
		s->c_cc[VTIME] = vtime; //时间
		s->c_cc[VMIN] = vmin; //位数
		return 0;
	}
	return -1;
}
/******************设置控制字符 end ******************************/


/*******************启动接收 start ******************************/
int enable_read(struct termios *s)
{
	if (s) {
		s->c_cflag |= CREAD;
		return 0;
	}

	return -1;
}

//不启用接收数据功能
int disable_read(struct termios *s)
{
	if (s) {
		s->c_cflag &= ~CREAD;
		return 0;
	}

	return -1;
}
/*******************启动接收 end ******************************/


/*********** 不启用RTS/CTS(硬件)流控制 ************/
int disable_flow_control(struct termios *s)
{
	if (s) {
		s->c_cflag &= ~CRTSCTS;
		return 0;
	}

	return -1;
}
/**************************************/



/*************** display 相关 *****************/
int get_speed(const struct termios *s)
{
	if (s) {
		return baud_to_speed(s->c_cflag & CBAUD);
	}

	return -1;
}

char get_parity(const struct termios *s)
{
	if (s) {
		if (s->c_cflag & PARENB) {
			if (s->c_cflag & PARODD) {
				return 'O';
			} else {
				return 'E';
			}
		}

		return 'N';
	}

	return -1;
}

int get_bsize(const struct termios *s)
{
	if (s) {
		switch (s->c_cflag & CSIZE) {
		case CS5:
			return 5;

		case CS6:
			return 6;

		case CS7:
			return 7;

		case CS8:
			return 8;
		}
	}

	return -1;
}

int get_stop(const struct termios *s)
{
	if (s) {
		if (s->c_cflag & CSTOPB) {
			return 2;
		}

		return 1;
	}

	return -1;
}
/*************************************************************/
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值