linux基于串口API函数的串口程序开发介绍

前言

    本文介绍了linux上用串口api进行软件开发的步骤,其大致分为4步:(1)打开串口(2)配置串口(3)串口通讯(4)关闭串口

1 打开串口

1.1 代码示例

int libtty_open(const char *devname)
{
	int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY); 
	int flags = 0;
	
	if (fd == -1) {                        
		perror("open device failed");
		return -1;            
	}
	
	flags = fcntl(fd, F_GETFL, 0);
	flags &= ~O_NONBLOCK;
	if (fcntl(fd, F_SETFL, flags) < 0) {
		printf("fcntl failed.\n");
		return -1;
	}
		
	if (isatty(fd) == 0)
	{
		printf("not tty device.\n");
		return -1;
	}
	else
		printf("tty device test ok.\n");
	
	return fd;
}

1.2 参数详解

调用open()函数来打开串口,函数执行成功返回句柄,出错返回-1。
函数原型:int open(const char *pathname, int flags);
pathname:表示要打开的串口文件的路径
flag:表示打开方式和读写方式,这里需要设置为O_RDWR| O_NOCTTY| O_NDELAY

调用fcntl()函数来设置串口的访问模式和状态标志。
函数原型:int fcntl(int fd, int cmd, ... /* arg */ );
fd:表示打开的串口文件的句柄
cmd:决定了要进行何种操作,设为F_GETFL时用来获取文件访问模式和状态标志,设为F_SETFL时用来设置文件的文件访问模式和状态标志
arg :arg的取值与cmd的设定值有关,当cmd取值为F_GETFL时,arg的值可以忽略,当cmd取值为F_SETFL时,arg为对文件访问模式和状态标志的设定值。先将cmd设为F_GETFL调用该函数,返回值为当前串口文件的访问模式和状态标志,然后将获取到的文件访问模式和状态标志进行修改,再将cmd设置为F_SETFL调用该函数,将修改后的状态标志写入arg来对串口文件进行设置。

调用isatty()函数来测试打开的文件是否指向一个终端,返回1表示指向一个终端,返回0表示未指向一个终端
函数原型:int isatty(int fd);
fd:打开的串口文件的句柄

2 配置串口

2.1 代码示例

int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity, char hardflow)
{
	struct termios newtio;
	struct termios oldtio;
	int i;
	
	bzero(&newtio, sizeof(newtio));
	bzero(&oldtio, sizeof(oldtio));
	
	if (tcgetattr(fd, &oldtio) != 0) {
		perror("tcgetattr");    
		return -1; 
	}
	newtio.c_cflag |= CLOCAL | CREAD;
	newtio.c_cflag &= ~CSIZE;
 
	/* set tty speed */
	for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
		if (speed == name_arr[i]) {      
			cfsetispeed(&newtio, speed_arr[i]); 
			cfsetospeed(&newtio, speed_arr[i]);   
		} 
	}
	
	/* set data bits */
	switch (databits) {
	case 5:                
		newtio.c_cflag |= CS5;
		break;
	case 6:                
		newtio.c_cflag |= CS6;
		break;
	case 7:                
		newtio.c_cflag |= CS7;
		break;
	case 8:    
		newtio.c_cflag |= CS8;
		break;  
	default:   
		fprintf(stderr, "unsupported data size\n");
		return -1; 
	}
	
	/* set parity */
	switch (parity) {  
	case 'n':
	case 'N':
		newtio.c_cflag &= ~PARENB;    /* Clear parity enable */
		newtio.c_iflag &= ~INPCK;     /* Disable input parity check */
		break; 
	case 'o':  
	case 'O':    
		newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */
		newtio.c_iflag |= INPCK;     /* Enable input parity check */
		break; 
	case 'e': 
	case 'E':  
		newtio.c_cflag |= PARENB;    /* Enable parity */   
		newtio.c_cflag &= ~PARODD;   /* Even parity instead of odd */  
		newtio.c_iflag |= INPCK;     /* Enable input parity check */
		break;
	default:  
		fprintf(stderr, "unsupported parity\n");
		return -1; 
	} 
	
	/* set stop bits */ 
	switch (stopbits) {  
	case 1:   
		newtio.c_cflag &= ~CSTOPB; 
		break;
	case 2:   
		newtio.c_cflag |= CSTOPB; 
		break;
	default:   
		perror("unsupported stop bits\n"); 
		return -1;
	}
 
	if (hardflow)
		newtio.c_cflag |= CRTSCTS;
	else
		newtio.c_cflag &= ~CRTSCTS;
 
	newtio.c_cc[VTIME] = 0;	  /* Time-out value (tenths of a second) [!ICANON]. */
	newtio.c_cc[VMIN] = 0;    /* Minimum number of bytes read at once [!ICANON]. */
	
	tcflush(fd, TCIOFLUSH);  
	
	if (tcsetattr(fd, TCSANOW, &newtio) != 0)  
	{
		perror("tcsetattr");
		return -1;
	}
	return 0;
}
int libtty_tiocmget(int fd)
{
	unsigned long modembits = 0;
	int ret;

	ret = ioctl(fd, TIOCMGET, &modembits);
	if (modembits & TIOCM_DSR)       //表示modem方式为DSR
		printf("DSR Active!\n");
	if (modembits & TIOCM_CTS)			//表示modem方式为CTS
		printf("CTS Active!\n");
	if (modembits & TIOCM_CD)			//表示modem方式为CD
		printf("DCD Active!\n");
	if (modembits & TIOCM_RI)			//表示modem方式为RI
		printf("RI Active!\n");
	
	if (ret)
		return ret;
	else
		return modembits;
}

int libtty_tiocmset(int fd, char bDTR, char bRTS)
{
	unsigned long controlbits = 0;
	
	if (bDTR)
		controlbits |= TIOCM_DTR;    		//设置modem状态为DTR
	if (bRTS)
		controlbits |= TIOCM_RTS;			//设置modem状态为RTS
	
	return ioctl(fd, TIOCMSET, &controlbits);
}

2.2 参数详解

首先要创建一个newtio结构体并清空,然后才可以通过将串口的一些配置参数填入newtio结构体。

2.2.1 忽略modem控制线

然后通过更改newtio结构体中的c_cflag来忽略modem控制线
newtio.c_cflag |= CLOCAL; //CLOCAL参数的作用是忽略modem控制线

2.2.2 使能串口接收

newtio.c_cflag |= CREAD; //CREAD参数的作用是使能串口接收

2.2.3 配置串口波特率

我们可以使用cfsetispeed()和cfsetosspeed()来分别配置串口的输入输出波特率
函数原型:
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);

termios_p是存放串口配置的结构体的指针
speed的值为期望设定的波特率

2.2.4 设置数据位长度

在设置数据位长度之前先清空newtio结构体的c_cflag中的数据位长度设置,
然后通过修改c_cflag来修改修改数据位长度。
newtio.c_cflag &= ~CSIZE; //CSIZE是用来设置数据位长度的, &= ~CSIZE表示清空数据位

newtio.c_cflag |= CS5; //设置数据位长度为5
newtio.c_cflag |= CS6; //设置数据位长度为6
newtio.c_cflag |= CS7; //设置数据位长度为7
newtio.c_cflag |= CS8; //设置数据位长度为8

2.2.5 设置校验方式

我们可以修改newtio结构体的c_cflag和c_iflag来设置校验方式
需要使用到三个参数:
PARENB:用来使能校验
INPCK:用来使能输入校验检查
PAROOD:用来设定校验方式为奇校验,反之为偶校验

2.2.6 设置停止位

通过设置结构体的c_cflag来设置停止位
需要使用到一个参数:
CSTOPB:用来设置为2个校验位,反之为一个校验位

2.2.7 设置流控

通过设置newtio结构体的c_cflag来设置停止位
需要使用到一个参数:
CRTSCTS:用来使能流控,反之清除流控使能

2.2.8 设置最小读取字节数和超时时间

通过修改newtio结构体中的c_cc[VTIME]和c_cc[VMIN]来修改最小读取字节和超时时间
可以分为四类情况:
(1)MIN=0,TIME=0(轮训读,即不用等待,直接读)
    如果数据可获取,read立刻返回,读取可获得的最少字节数或请求的字节数,如果没有可以获得的数据,则返回0
(2)MIN>0,TIME=0(阻塞读)
     Read直到可以读到MIN数量的字节数可以读到才返回
(3)MIN=0,TIME>0
    TIME的值以1/10秒为单位,计时开始于read(2)被调用,当至少读到1个字节或TIME时间期满时,read才会被返回,如果时间期满没有任何可以获得的数据,则read返回0。
如果调用read的时候已经有可获得的数据,则调用的行为就像是在调用后立刻接收了数据。
(4)MIN>0,TIME>0
    与MIN=0,TIME>0不同的是,MIN>0,TIME>0的情况下timer是用来对字节间进行计时的,而MIN==0,TIME>0的情况下timer是用来对每次read进行计时的,一旦第一个字节是可获得的,在接收到之后的每一个字节后,定时器将会重启,在以下三种情况的任何一个满足的时候,read将会返回。
a)接收到了最小字节数
b)字节间的时间间隔达到了TIME
c)read请求的字节数已经被接收完了

2.2.9 清空读写缓存

调用tcflush()函数来清空读写缓存
函数原型:int tcflush(int fd, int queue_selector);
fd:打开的串口文件的句柄
queue_selector:用来选择清空什么缓存,可选的值有TCIFLUSH、TCOFLUSH、TCIOFLUSH,分别表示清空读缓存、清空写缓存、清空读写缓存,这里使用TCIOFLUSH

2.2.10 获取modem状态

可以根据需求获取modem状态
函数int libtty_tiocmget(int fd)是用来获取modem状态的
其中使用了ioctl()函数来获取modem状态
函数原型:int ioctl(int fd, unsigned long request, …);
fd:打开的串口文件的句柄
request:程序对串口驱动发的命令,这里使用TIOCMGET,表示获取modem状态
:该参数的意义由参数request决定,这里的该参数指向的内存用来存放从驱动发回来的值,以下4个值对应四个modem状态
TIOCM_DSR:表示获取到的modem状态为DSR
TIOCM_CTS:表示获取到的modem状态为CTS
TIOCM_CD:表示获取到的modem状态为CD
TIOCM_RI:表示获取到的modem状态为RI

2.2.11 设置modem状态

可以根据需求来设置modem状态
函数int libtty_tiocmset(int fd, char bDTR, char bRTS)是用来设置modem状态的
其中使用了ioctl()函数来设置modem状态
函数原型:int ioctl(int fd, unsigned long request, …);
fd:打开的串口文件的句柄
request:程序对串口驱动发的命令,这里使用TIOCMSET,表示设置modem状态
:该参数的意义由参数request决定,这里的该参数指向的内存用来存放想要设置的modem状态的执,以下2个值对应2个modem状态
TIOCM_DTR:表示设置modem状态为DTR
TIOCM_RTS:表示设置modem状态为RTS

3 读写串口

3.1 代码示例

void tty_test(int fd)
{
	int nwrite, nread;
	char buf[100];
	int i;
	
	memset(buf, 0x32, sizeof(buf));
	
	nwrite = write(fd, buf, sizeof(buf));
	printf("wrote %d bytes already.\n", nwrite);
	sleep(1);
	nread = read(fd, buf, sizeof(buf));
	printf("read %d bytes already.\n", nread);
	printf("*************************\n");
	for (i = 0; i < nread; i++)
		printf(" 0x%.2x", buf[i]);
	printf("\n*************************\n");
	sleep(1);
}

3.2 参数详解

完成配置后可以调用read()和write()对串口进行读写
函数原型:ssize_t read(int fd, void *buf, size_t count);
fd:串口文件句柄
buf:读缓冲指针
count:期望读取的字节数
返回值:成功返回读到的字节数,出错返回-1
函数原型:ssize_t write(int fd, const void *buf, size_t count);
fd:串口文件句柄
buf:写缓冲指针
count:期望写入的字节数
返回值:成功返回写入的字节数,出错返回-1

4 关闭串口

4.1 代码示例

int libtty_close(int fd)
{
	return close(fd);
}

4.2 参数详解

调用close()函数来关闭串口,函数执行成功返回0,出错返回-1。
函数原型:int close(int fd);
fd:打开的串口文件的句柄

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值