一、串口属性
(标题一部分内容摘录自原文地址)
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字节数据或者计时器超时后即返回,
如果是计时器超时,则返回0。
4)当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;
}
/*************************************************************/