Linux 串口编程

在linux中,所有的设备文件一般位于"/dev"目录下,其中串口对应的名称为"/dev/ttySx",(因驱动不同,该设备名也会有所不同),可也查看在"/dev"下的文件以确认。在linux下对设备的操作方法与对文件操作方法一样,因此,对串口的读写就可以使用简单的read,write等函数来完成,所不同的只是需要对串口的其他参数另做配置。

       串口的设置主要是设置structtermios结构体中的成员,如下所示.

struct termios

{

  tcflag_t  c_iflag;       //输入模式标志

   tcflag_t  c_oflag;       //输出模式标志

  tcflag_t  c_cflag;       //控制模式标志

  tcflag_t  c_lflag;       //本地模式标志

   cc_t   c_line;           //线路规则

   cc_t   c_cc[NCC];        //控制特性

}

       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支持的常量名称如下表所示,其用于控制终端端口发送出去的字符处理。

键    值

说    明

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_cflag中的成员不能直接对其初始化,需要将其通过“或”、“与”操作使用其中的某些选项。

键    值

说    明

CBAUD

波特率(4+1位)(非POSIX)

CBAUDEX

附加波特率(1位)(非POSIX)

CSIZE

字符长度,取值范围为CS5、CS6、CS7或CS8

CSTOPB

设置两个停止位

CREAD

使用接收器

PARENB

使用奇偶校验

PARODD

对输入使用奇偶校验,对输出使用偶校验

HUPCL

关闭设备时挂起

CLOCAL

忽略调制解调器线路状态

CRTSCTS

使用RTS/CTS流控制

 

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信号

 

C_cc[nccs]支持的常量名称如下表所示

说    明

说    明

VINTR

Interrupt字符 ,对应键 “ctrl+c”

VEOL

附加的End-of-file字符

VQUIT

Quit字符 ,”ctrl+z”

VTIME

非规范模式读取时的超时时间

VERASE

Erase字符 , “backspace”

VSTOP

Stop字符

VKILL

Kill字符 ,”ctrl+u”

VSTART

Start字符

VEOF

End-of-file字符 ,”ctrl+d”

VSUSP

Suspend字符

VMIN

非规范模式读取时的最小字符数

操作串口的控制函数

函数名

说明

Tcgetattr

获取属性(termios结构)

Tcsetattr

设置属性(termios结构)

Tcsendbreak

发送break字符

Tcdrain

等待所有输出都被传输(使程序阻塞,直到输出缓冲区中的数据全部发送完毕)

tcflow

挂起传输或接收(用于暂停或重新开始输出)

Tcflush

用于清空输入/输出缓冲区

cfmakeraw

设置串口模式为原始模式

Cfgetispeed

获取输入波特率

Cfgetospeed

获取输出波特率

Cfsetispeed

设置输入波特率

Cfsetospeed

设置输出波特率

cfsetspeed

设置波特率

Linux串口实现-uart.c

/************************************************************************************************************* 
 * 文件名: 	  		uart.c 
 * 功能:      		Linux串口操作函数
 * 作者:      		[email protected] 
 * 创建时间:    	2014年12月31日 
 * 最后修改时间:	2014年12月31日  
 * 详细:      		无
*************************************************************************************************************/  
#include          
#include         
#include        
#include 
#include 
#include         
#include      
#include         
#include 

static int SerialPortFd = 0; //保存串口的文件描述符

/*************************************************************************************************************************
*函数        :	int SerialIsValid(void)
*功能        : 检查串口文件描述符的有效性
*参数        : 无
*返回        : 0:有效
			  -1:无效
*依赖        : 无
*作者        : edward
*时间        : 20141231
*最后修改时间: 20141231
*说明        : 无
*************************************************************************************************************************/
static int SerialIsValid(void)
{
	//SerialPortFd 安全检查
	if(0 == SerialPortFd)
	{
		perror("SerialPortFd is invalid");
		return -1;
	}
	return 0;
}
/*************************************************************************************************************************
*函数        :	int SerialIsValid(void)
*功能        : 检查串口文件描述符的有效性
*参数        : 无
*返回        : 0:有效
			  -1:无效
*依赖        : 无
*作者        : edward
*时间        : 20141231
*最后修改时间: 20141231
*说明        : 无
*************************************************************************************************************************/

int SerialOpen(const char *devicePath)
{
	SerialPortFd = open(devicePath,O_RDWR | O_NOCTTY | O_NDELAY);
	if(SerialPortFd < 0)
	{
		perror(devicePath);
		printf("%s open failed\n",devicePath);
		return -1;
	}
	return 0;
}
/*************************************************************************************************************************
*函数        :	int SerialConfig(int nSpeed,int nBits,char nEvent,int nStop)
*功能        : 串口参数设置
*参数        : nSpeed:波特率
			   nBits :数据位
			   nEvent:奇偶校验位
			   nStop :停止位
*返回        : 0:设置成功
			  -1:设置失败
*依赖        : 无
*作者        : edward
*时间        : 20141231
*最后修改时间: 20141231
*说明        : 无
*************************************************************************************************************************/

int SerialConfig(int nSpeed,int nBits,char nEvent,int nStop)
{
	int rtn = 0;
	int speed = 0;
	struct termios NewConfig,OldConfig;
	
	//SerialPortFd 安全检查
	if(SerialIsValid())
		return -1;
	
	
	//1、保存原串口配置
	/*tcgetattr(serialPortFd,&options)得到与SerialFd指向对象的相关参数,
	并将它们保存于oldtio,该函数,还可以测试配置是否正确,该串口是否
	可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.*/
	rtn = tcgetattr(SerialPortFd,&OldConfig);
	if(0 != rtn)
	{
		perror("Error on tcgetattr");
		return -1;
	}
	
	bzero(&NewConfig,sizeof(NewConfig));
	
	//2、激活选项
	//CLOCAL:修改控制模式,保证程序不会占用串口
	//CREAD:修改控制模式,接收使能,使得能够从串口中读取输入数据
	NewConfig.c_cflag |= CLOCAL | CREAD;
	
	//3、设置数据位
	//在设置数据位时,需要先使用CSIZE位清空数据位设置,然后再设置相应的数据位
	NewConfig.c_cflag &= ~CSIZE;
	switch(nBits)
	{
		case 8: NewConfig.c_cflag |= CS8;break;
		case 7: NewConfig.c_cflag |= CS7;break;
		case 6: NewConfig.c_cflag |= CS6;break;
		case 5: NewConfig.c_cflag |= CS5;break;
		default:
			printf("Error on nBits\n");
			return -1;
	}
	
	//4、设置奇偶校验位
	//设置奇偶校验需要使用到termiso中的两个成员:c_cflag和c_iflag,
	//首先激活c_cflag中的校验位使能标志PARENB,这样会对输出数据产生校验位,
	//而输入数据进行校验检测。同时还要激活c_iflag中的对于输入数据的奇偶校验使能INPCK
	//c_cflag中的PARODD为1时使用奇校验,为0时使用偶校验
	switch(nEvent)
	{
		case 'o':
		case 'O':  //设置为奇校验
			NewConfig.c_cflag |=(PARODD|PARENB);
			NewConfig.c_iflag |= INPCK;
		break;
		case 'e':
		case 'E':  //设置为偶校验
			NewConfig.c_cflag |=PARENB;
			NewConfig.c_cflag &= ~PARODD;
			NewConfig.c_iflag |= INPCK;
		break;
		case 's':
		case 'S':	/*as no parity*/
			NewConfig.c_cflag &=~PARENB;
			NewConfig.c_cflag &= ~CSTOPB;
		break;
		case 'n':
		case 'N': //设置无奇偶校验位
			NewConfig.c_cflag &= ~PARENB;
			NewConfig.c_iflag &= ~INPCK;
		break;
		default:	
			printf("Error on nEvent\n");
			return -1;
	}
	
	//5、设置波特率
	switch(nSpeed)
	{
		case 2400: speed = B2400; break;
		case 4800: speed = B4800; break;
		case 9600: speed = B9600; break;
		case 19200: speed = B19200; break;
		case 38400: speed = B38400; break;
		case 115200: speed = B115200; break;
		default:
			printf("Error on nSpeed\n");
			return -1;
	}
	cfsetispeed(&NewConfig,speed);
	cfsetospeed(&NewConfig,speed);
	
	//6、设置停止位
	//设置停止位是通过激活c_cflag中的CSTOPB而实现的。若停止位为1个,这清除CSTOPB,若停止位为2个,则激活CSTOPB
	switch(nStop)
	{
		case 1: NewConfig.c_cflag &= ~CSTOPB;break;
		case 2: NewConfig.c_cflag |=  CSTOPB;break;
		default:
			printf("Error on nStop\n");
			return;
	}
	
	//7、设置等待时间和最小接收字符
	NewConfig.c_cc[VTIME] = 0;//读取一个字符等待1*(1/10)s
	NewConfig.c_cc[VMIN] = 0;//读取字符的最少个数

	
	//8、清除串口缓冲
	//在串口重新设置后,需要对当前的串口设备进行适当的处理,
	//这时可以调用tcdrain,tcflow,tcflush等函数来处理目前串口缓冲区中的数据
	//tcdrain 函数使程序阻塞,直到输出缓冲区中的数据全部发送完毕
	//tcflow  函数用于暂停或重新开始输出
	//tcflush 函数用于清空输入/输出缓冲区
	
	//如果发生数据溢出,接收数据,但是不再读取
	tcflush(SerialPortFd,TCIFLUSH);
	
	//9、激活配置
	//在串口配置完成后,需要使用tcsetattr函数激活配置。
	rtn = tcsetattr(SerialPortFd,TCSANOW,&NewConfig);
	if(0 != rtn)
	{
		perror("Error on tcsetattr");
		return -1;
	}
	
	printf("Serial Set Done\n");
	return 0;
}
/*************************************************************************************************************************
*函数        :	int SerialReceive(unsigned char *buf,int size)
*功能        :  串口接收数据
*参数        : buf: 接收缓冲区
			   size:缓冲区大小
*返回        :错误-1,返回接收数据的大小
*依赖        : 无
*作者        : edward
*时间        : 20141231
*最后修改时间: 20141231
*说明        : 无
*************************************************************************************************************************/

int SerialReceive(unsigned char *buf,int size)
{
	int rtn ;
	//SerialPortFd 安全检查
	if(SerialIsValid())
		return -1;
	rtn = read(SerialPortFd,buf,size);
//	printf("ReadLength = %d\n",rtn);
	return rtn;
}
/*************************************************************************************************************************
*函数        : int SerialSend(unsigned char *buf,int DataLength)
*功能        : 串口发送数据
*参数        : buf:发送缓冲区
			   DataLength:需要发送的字节数
*返回        : rtn:成功发送的字节数
			  -1:发送失败
*依赖        : 无
*作者        : edward
*时间        : 20141231
*最后修改时间: 20141231
*说明        : 无
*************************************************************************************************************************/

int SerialSend(unsigned char *buf,int DataLength)
{
	int rtn ;
	//SerialPortFd 安全检查
	if(SerialIsValid())
		return -1;
#if 0
	int remain = DataLength;
	int offset = 0;
	int sub = 0;
	while(remain > 0 )
	{
		sub = (remain >= 8? 8:remain);
		write(SerialPortFd,buf+offset,sub);
		tcflush(SerialPortFd,TCOFLUSH);
		remain -= 8;
		offset += 8;
	}
#else
	rtn = write(SerialPortFd,buf,DataLength);
#endif
	return rtn;	
}
/*************************************************************************************************************************
*函数        : int SerialIsValid(void)
*功能        : 关闭串口
*参数        : 无
*返回        : 0:关闭成功
*依赖        : 无
*作者        : edward
*时间        : 20141231
*最后修改时间: 20141231
*说明        : 无
*************************************************************************************************************************/
int SerialClose(void)
{
	//SerialPortFd 安全检查
	if(SerialIsValid())
			return -1;
	close(SerialPortFd);
	return 0;
}


uart.h头文件

#ifndef __UART_H__
#define __UART_H__
int SerialOpen(const char *devicePath);
int SerialConfig(int nSpeed,int nBits,char nEvent,int nStop);
int SerialReceive(unsigned char *buf,int size);
int SerialSend(unsigned char *buf,int DataLength);
int SerialClose(void);
#endif

uart-test测试文件,固定发送字符,并接收,硬件上将串口的RX与TX相连接

#include 
#include 
#include "uart.h"
int main(int argc,char**argv)
{
	int rtn;
	int Count = 0;
	unsigned char *send = "Hello Uart";
	unsigned char buf[1024] ={0};
	if(2 > argc){
		printf("Input error\n");
		printf("Usage: %s \n",argv[0]);
		printf("Eample: %s /dev/ttyS0\n",argv[0]);
		return -1;
	}
	//打开串口
	rtn = SerialOpen(argv[1]);
	if(0 > rtn)
		return -1;
	//配置串口
	rtn = SerialConfig(115200,8,'n',1);
	if(0 > rtn)
		return -1;
	//循环发送与接收
	while(1)
	{	//发送数据
		rtn = SerialSend(send,strlen(send));
		if(0 < rtn){
			printf("[Send]: %s\n",send);
		}
		sleep(1);
		//接收数据
		rtn = SerialReceive(buf,sizeof(buf));
		if(0 < rtn){
			printf("[Recv]: %s\n",buf);
		}
	}
}







你可能感兴趣的:(Linux)