串行接口是一种可以将接收来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接收的串行数据流转换为并行的数据字符供给CPU的器件。一般完成这种功能的电路,我们称为串行接口电路。
串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节的通信方式。
struct termios {
tcflag_t c_iflag; /* 输入标志*/
tcflag_t c_oflag; /* 输出标志*/
tcflag_t c_cflag; /* 控制标志*/
tcflag_t c_lflag; /* 本地标志*/
tcflag_t c_cc[NCCS]; /* 控制字符*/
};
c_iflag:输入模式标志,控制终端输入方式,具体参数如下表所示。
标志 | 说明 |
---|---|
IGNBRK | 忽略BREAK键输入 |
BRKINT | 如果设置了IGNBRK,BREAK键输入将被忽略 |
IGNPAR | 忽略奇偶校验错误 |
PARMRK | 标识奇偶校验错误 |
INPCK | 允许输入奇偶校验 |
ISTRIP | 去除字符的第8个比特 |
INLCR | 将输入的NL(换行)转换成CR(回车) |
IGNCR | 忽略输入的回车 |
ICRNL | 将输入的回车转化成换行(如果IGNCR未设置的情况下) |
IUCLC | 将输入的大写字符转换成小写字符(非POSIX) |
IXON | 允许输入时对XON/XOFF流进行控制 |
IXANY | 输入任何字符将重启停止的输出 |
IXOFF | 允许输入时对XON/XOFF流进行控制 |
IMAXBEL | 当输入队列满的时候开始响铃 |
c_oflag:输出模式标志,控制终端输出方式,具体参数如下表所示。
标志 | 说明 |
---|---|
OPOST | 处理后输出 |
OLCUC | 将输入的小写字符转换成大写字符(非POSIX) |
ONLCR | 将输入的NL(换行)转换成CR(回车)及NL(换行) |
OCRNL | 将输入的CR(回车)转换成NL(换行) |
ONOCR | 第一行不输出回车符 |
ONLRET | 不输出回车符 |
OFILL | 发送填充字符以延迟终端输出 |
OFDEL | 以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NUL |
NLDLY | 换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s) |
CRDLY | 回车延迟,取值范围为:CR0、CR1、CR2和 CR3 |
TABDLY | 水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3 |
BSDLY | 空格输出延迟,可以取BS0或BS1 |
VTDLY | 垂直制表符输出延迟,可以取VT0或VT1 |
FFDLY | 换页延迟,可以取FF0或FF1 |
标志 | 说明 |
---|---|
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同时设置的情况下,终端只使用大写。 |
ECHO | 显示输入字符 |
ECHOE | 如果ICANON同时设置,ERASE将删除输入的字符 |
ECHOK | 如果ICANON同时设置,KILL将删除当前行 |
ECHONL | 如果ICANON同时设置,即使ECHO没有设置依然显示换行符 |
ECHOPRT | 如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX) |
TOSTOP | 向后台输出发送SIGTTOU信号 |
c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。c_cc中定义了如下表所示的控制字符。
宏 | 说 明 |
---|---|
VINTR | Interrupt字符 |
VEOL | 附加的End-of-file字符 |
VQUIT | Quit字符 |
VTIME | 非规范模式读取时的超时时间 |
VERASE | Erase字符 |
VSTOP | Stop字符 |
VKILL | Kill字符 |
VSTART | Start字符 |
VEOF | End-of-file字符 |
VSUSP | Suspend字符 |
VMIN | 非规范模式读取时的最小字符数 |
说明:使用函数tcgetattr和tcsetattr可以获取和设置串口termios结构属性。
int tcgetattr(int fd, struct termios *termptr);
1. fd:串口设备文件描述符。
2. termptr:在tcgetattr函数中是用于获取串口的termios结构体。
3. 返回值:成功则返回0,若出错则返回-1。
1. int cfsetispeed(struct termios *termptr, speed_t speed);//设置串口输入波特率
2. int cfsetospeed(struct termios *termptr, speed_t speed);//设置串口输出波特率
3. int tcflush(int fd,int );
TCIFLUSH 刷新收到的数据但是不读
TCOFLUSH 刷新写入的数据但是不传送
TCIOFLUSH 同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送
输出缓冲器清空,输入缓冲区清空。清空缓冲区里的数据。
termptr.c_cflag &= ~CSIZE;/* 先把数据位清零*/
termptr.c_cflag |= CS8;/* 把数据位设置为8位*/
termptr.c_cflag &= ~PARENB;
termptr.c_cflag &= ~CSTOPB;
termptr.c_cflag &= ~CSIZE;
termptr.c_cflag |= CS8;
termptr.c_cflag |= PARENB;
termptr.c_cflag &= ~PARODD;
termptr.c_cflag &= ~CSTOPB;
termptr.c_cflag &= ~CSIZE;
termptr.c_cflag |= CS7;
termptr.c_cflag|= PARENB;
termptr.c_cflag |= PARODD;
termptr.c_cflag &= ~CSTOPB;
termptr.c_cflag &= ~CSIZE;
termptr.c_cflag |= CS7;
int tcsetattr(int fd, int opt, const struct termios *termptr);
1. fd为串口设备文件描述符。
2. opt是整形变量,使用方法如下:
TCSANOW:更改立即发生;
TCSADRAIN:发送了所有输出后更改才发生,若更改输出参数则应用此选项;
TCSAFLUSH:发送了所有输出后更改才发生,更进一步,在更改发生时未读的所有输入数据被删除(Flush)。
参数opt说明:
在串口驱动程序里,有输入缓冲区和输出缓冲区。在改变串口属性时,缓冲区中的数据可能还存在,这时需要考虑到更改后的属性什么时候起作用。tcsetattr的参数opt可以指定在什么时候启动新的串口属性。
3. termptr参数在tcgetattr函数中是用于存放串口设置的termios结构体。
4. 返回值:成功则返回0,若出错则返回-1。
#include
#include
#include
#include
#include
#include
#define DATA_LEN 5
static int UART2_init(void)
{
//打开串口
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
if(fd < 0)
{
perror("提示错误!");
return -1;
}
struct termios opt; //终端属性结构体
tcgetattr(fd, &opt); //获取串口属性结构体对象
cfsetispeed(&opt, B115200); //设置输入波特率
cfsetospeed(&opt, B115200); //设置输出波特率
/* raw mode */ //偶校验
opt.c_lflag &= ~(ECHO | ICANON | IEXTEN |ISIG); //设置本地标志:不进行回送,关闭规范输入,关闭扩充输入字符处理,关闭终端产生的信号
opt.c_iflag &= ~(IXON | ISTRIP); // 关闭输出流控制, 不剥除第8位
opt.c_iflag |= (ICRNL | BRKINT | INPCK);// 将输入的CR转换为NL,使得输入和输出队列被刷新 ,打开奇偶校验
opt.c_oflag &= ~(OPOST); // 设置输出标志:不执行输出处理
opt.c_cflag &= ~(PARODD | CSIZE); // 关闭输入输出是奇校验 ,使用屏蔽位
opt.c_cflag |= (CS8 | CLOCAL | CREAD | PARENB);//8位数据位,保证程序不会占用串口,能够从串口读取输入数据,允许输出产生奇偶信息以及输入的奇偶校验
/*'DATA_LEN' bytes can be read by serial*/
opt.c_cc[VMIN] = DATA_LEN; //读取字符的最少个
opt.c_cc[VTIME] = 1; //读取一个字符等待1*(1/10)s
tcflush(fd,TCIOFLUSH); //清空所有正在发生的IO数据
if (tcsetattr(fd, TCSANOW, &opt) < 0) //激活配置(将修改后的termios数据设置到串口)
{
return -1;
}
return fd;
}
int main()
{
UART2_init();
return 0;
}
/***********************************************************
Description:
设置串口波特率
************************************************************/
static int tc_set_speed(int fd,int baud_rate)
{
int status;
int i;
struct termios opt;
int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300 };
int name_arr[] = { 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300 };
if(fd == -1)
return 1;
//set baud_rate
if (tcgetattr(fd, &opt) != 0)
{
printf("tc_set_speed fail !\n");
return 0 ;
}
for (i=0; i < sizeof(speed_arr)/sizeof(int); i++)
{
if (baud_rate == name_arr[i])
{
cfsetispeed(&opt, speed_arr[i]);
cfsetospeed(&opt, speed_arr[i]);
tcflush(fd,TCIOFLUSH);
status = tcsetattr(fd, TCSANOW, &opt);
if (status != 0)
{
printf("tcsetattr fail\n");
return 0;
}
break;
}
}
return 1;
}
/***********************************************************
Description:
设置串口数据位、停止位、校验位、流控
************************************************************/
static int tc_set_parity(int fd, int databits,int stopbits,int parity,int flowctrl)
{
struct termios options;
if(fd == -1)
return 1;
if (tcgetattr(fd,&options) != 0)
{
printf("tcgetattr ERROR");
return 0 ;
}
/*set flow control*/
switch (flowctrl)
{
case 0: /* 无流控制 */
options.c_cflag &= ~CRTSCTS;
options.c_iflag &= ~(IXON | IXOFF | IXANY );
break;
case 1: /* 硬件流控制 */
options.c_cflag |= CRTSCTS; /* also called CRTSCTS */
options.c_iflag &= ~(IXON | IXOFF | IXANY );
break;
case 2: /* 软件流控制 */
options.c_iflag |= (IXON | IXOFF | IXANY );
options.c_cflag &= ~CRTSCTS;
break;
default: /* 无流控制 */
options.c_cflag &= ~CRTSCTS;
options.c_iflag &= ~(IXON | IXOFF | IXANY );
printf("Default No flow control");
break;
}
options.c_cflag &= ~CSIZE;
/*set databit*/
switch (databits)
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
printf("Unsupported data size");
return 0;
}
/*set parity*/
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
printf("Unsupported parity");
return 0;
}
/*set stopbits*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
printf("Unsupported stop bits");
return 0 ;
}
/* Set input parity option */
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
//RAM MODE
#ifdef _BSD_SOURCE
cfmakeraw( &options );
#else
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
#endif
tcflush(fd,TCIFLUSH);
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
printf("tcsetattr ERROR");
return 0;
}
return 1;
}