如前所述,设置串口中最基本的包括波特率设置,校验位和停止位设置。串口的设置主
要是设置struct termios结构体的各成员值,如下所示:
#include
struct termio
{
unsigned short c_iflag; /* 输入模式标志 */
unsigned short c_oflag; /* 输出模式标志 */
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /*本地模式标志 */
unsigned char c_line; /* line discipline */
unsigned char c_cc[NCC]; /* control characters */
};
在这个结构中最为重要的是c_cflag,通过对它的赋值,用户可以设置波特率、字符大小、
数据位、停止位、奇偶校验位和硬件流控等。另外c_iflag 和c_cc 也是比较常用的标志。在
此主要对这3 个成员进行详细说明。
c_cflag支持的常量名称
CBAUD 波特率的位掩码
B0 0波特率(放弃DTR)
B1800 1800波特率
B2400 2400波特率
B4800 4800波特率
B9600 9600波特率
B19200 19200波特率
B38400 38400波特率
B57600 57600波特率
B115200 115200波特率
EXTA 外部时钟率
EXTB 外部时钟率
CSIZE 数据位的位掩码
CS5 5个数据位
CS6 6个数据位
CS7 7个数据位
CS8 8个数据位
CSTOPB 2个停止位(不设则是1个停止位)
CREAD 接收使能
PARENB 校验位使能
PARODD 使用奇校验而不使用偶校验
HUPCL 最后关闭时挂线(放弃DTR)
CLOCAL 本地连接(不改变端口所有者)
LOBLK 块作业控制输出
CNET_CTSRTS 硬件流控制使能
c_iflag支持的常量名称
INPCK 奇偶校验使能
IGNPAR 忽略奇偶校验错误
PARMRK 奇偶校验错误掩码
ISTRIP 除去奇偶校验位 ,剥除输入字符的第8位。
IXON 启动出口硬件流控
IXOFF 启动入口软件流控
IXANY 允许字符重新启动流控
IGNBRK 忽略中断情况
BRKINT 当发生中断时发送SIGINT信号
INLCR 将NL映射到CR
IGNCR 忽略CR
ICRNL 将CR映射到NL
IUCLC 将高位情况映射到低位情况
IMAXBEL 当输入太长时回复ECHO
c_cc 支持的常量名称
VINTR 中断控制,对应键为CTRL+C
VQUIT 退出操作,对应键为CRTL+Z
VERASE 删除操作,对应键为Backspace(BS)
VKILL 删除行,对应键为CTRL+U
VEOF 位于文件结尾,对应键为CTRL+D
VEOL 位于行尾,对应键为Carriage return(CR)
VEOL2 位于第二行尾,对应键为Line feed(LF)
VMIN 指定了最少读取的字符数
VTIME 指定了读取每个字符的等待时间
串口控制函数
Tcgetattr 取属性(termios结构)
Tcsetattr 设置属性(termios结构)
cfgetispeed 得到输入速度
Cfgetospeed 得到输出速度
Cfsetispeed 设置输入速度
Cfsetospeed 设置输出速度
Tcdrain 等待所有输出都被传输
tcflow 挂起传输或接收
tcflush 刷清未决输入和/或输出
Tcsendbreak 送BREAK字符
tcgetpgrp 得到前台进程组ID
tcsetpgrp 设置前台进程组ID
串口配置流程
(1)保存原先串口配置使用tcgetattr(fd,&oldtio)函数
struct termios newtio,oldtio;tcgetattr(fd,&oldtio);
(2)激活选项有CLOCAL和CREAD,用于本地连接和接收使能。newtio.c_cflag|=CLOCAL|CREAD;
(3)设置波特率,使用函数cfsetispeed、cfsetospeed
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
(4)设置数据位,需使用掩码设置。
newtio.c_cflag&=~CSIZE;
newtio.c_cflag|=CS8;
(5)设置奇偶校验位,使用c_cflag和c_iflag。
设置奇校验:
newtio.c_cflag|=PARENB;
newtio.c_cflag|=PARODD;
newtio.c_iflag|=(INPCK | ISTRIP);
设置偶校验:
newtio.c_iflag|=(INPCK|ISTRIP);
newtio.c_cflag|=PARENB;
newtio.c_cflag&=~PARODD;
(5)设置停止位,通过激活c_cflag中的CSTOPB实现,若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB。
newtio.c_cflag&=~CSTOPB;
(6)设置最少字符和等待时间,对于接收字符和等时间没有特别要求时,可设为0。
newtio.c_cc[VTIME]=0;
newtio.c_cc[VMIN]=0;
(7)处理要写入的引用对象tcflush函数刷清(抛弃)输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)。
int tcflush(int filedes,int queue)
queue数应当是下列三个常数之一:
TCIFLUSH 刷清输入队列
TCOFLUSH 刷清输出队列。
TCIOFLUSH 刷清输入、输出队列。
如:tcflush(fd,TCIFLUSH);
(8)激活配置。在完成配置后,需激活配置使其生效。使用
tsettattr()函数。原型:
int tcgetattr(int filedes,struct termios *termptr);
int tcsetattr(int filedes, int opt, const struct termios
*termptr);
tcsetattr的参数opt使我们可以指定在什么时候新的终端属性才起作用。opt可以指定为下列常数中的一个:
TCSANOW更改立即发生。
TCSADRAIN发送了所有输出后更改才发生。若更改输出参数则应使用此选择项。
TCSAFLUSH发送了所有输出后更改才发生。更进一步,在列改发生时未读的所有输入数据都被删除(刷清)使用如:tcsetattr(fd,TCSANOW,&newtio)
打开串口
fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
Open函数中除普通参数外,另有两个参数O_NOCTTY和O_NDELAY。
O_NOCTTY:通知linux系统,这个程序不会成为这个端口的控制终端。
O_NDELAY:通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。
在标准模式下,除非设置了O_NONBLOCK选项,否则只有当遇到文件结束符或各行的字符都已经编辑完毕后才返回,即默认的读操作是阻塞的。
fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
Open函数中除普通参数外,另有两个参数O_NOCTTY和O_NDELAY。
O_NOCTTY:通知linux系统,这个程序不会成为这个端口的控制终端。
O_NDELAY:通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。
在标准模式下,除非设置了O_NONBLOCK选项,否则只有当遇到文件结束符或各行的字符都已经编辑完毕后才返回,即默认的读操作是阻塞的。
例子:
//#include <linux/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#define RS485_NAME "/dev/ttyS2"
#define IOC_SET_SEND _IOW(RTS_MAJOR, 1, unsigned long)
#define IOC_SET_RECEIVE _IOW(RTS_MAJOR, 2, unsigned long)
#define IOC_SET_RECEIVE _IOW(RTS_MAJOR, 2, unsigned long)
#define BAUDRATE 19200
int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,};
int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300,};
int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,};
int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300,};
/*******************************************************************
@brief 设置串口通信速率
*@param fd 类型 int 打开串口的文件句柄
*@param speed 类型 int 串口速度
*@return void
********************************************************************/
int set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
return 0;
}
@brief 设置串口通信速率
*@param fd 类型 int 打开串口的文件句柄
*@param speed 类型 int 串口速度
*@return void
********************************************************************/
int set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
return 0;
}
/***************************************************************************
*@brief 设置串口数据位,停止位和效验位
*@param fd 类型 int 打开的串口文件句柄
*@param databits 类型 int 数据位 取值 为 7 或者8
*@param stopbits 类型 int 停止位 取值为 1 或者2
*@param parity 类型 int 效验类型 取值为N,E,O,,S
*****************************************************************************/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0) {
perror("SetupSerial 1");
return(-1);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n"); return (-1);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Clear parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* enable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* enable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;break;
default:
fprintf(stderr,"Unsupported parity\n");
return (-1);
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (-1);
}
/* Set input parity option */ /*have set it befor*/
//if (parity != 'n')
// options.c_iflag |= INPCK;
*@brief 设置串口数据位,停止位和效验位
*@param fd 类型 int 打开的串口文件句柄
*@param databits 类型 int 数据位 取值 为 7 或者8
*@param stopbits 类型 int 停止位 取值为 1 或者2
*@param parity 类型 int 效验类型 取值为N,E,O,,S
*****************************************************************************/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0) {
perror("SetupSerial 1");
return(-1);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n"); return (-1);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Clear parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* enable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* enable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;break;
default:
fprintf(stderr,"Unsupported parity\n");
return (-1);
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (-1);
}
/* Set input parity option */ /*have set it befor*/
//if (parity != 'n')
// options.c_iflag |= INPCK;
// options.c_iflag=0;/*直接设置为0最方便;否则以前的设置会影响接收*/
// options.c_cflag |= (CLOCAL | CREAD);
// options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// options.c_oflag &= ~OPOST; /*Output*/
// options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// options.c_oflag &= ~OPOST; /*Output*/
tcflush(fd,TCIFLUSH);
//设置最少字符和等待时间,对于接收字符和等时间没有特别要求时,可设为0
options.c_cc[VTIME] = 50; /* 设置超时15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (-1);
}
return (0);
}
/*==========================================================================*
int uart_test(void)
{
int iRtn,iRS485,status;
char rxbuf[100]={0};
int iRes=0;
iRS485= open(RS485_NAME, O_RDWR);
if (iRS485 < 0)
{
printf("Can't open device %s.\n", RS485_NAME);
goto err;
}
set_speed(iRS485,BAUDRATE);
set_Parity(iRS485,8,1,'o');
int uart_test(void)
{
int iRtn,iRS485,status;
char rxbuf[100]={0};
int iRes=0;
iRS485= open(RS485_NAME, O_RDWR);
if (iRS485 < 0)
{
printf("Can't open device %s.\n", RS485_NAME);
goto err;
}
set_speed(iRS485,BAUDRATE);
set_Parity(iRS485,8,1,'o');
ioctl(iRS485, TIOCSERGETLSR, &iRtn);
//printf("\nThe 1 MODEM status bit is 0x%x\n",iRtn);
ioctl(iRS485,TIOCMGET,&status);
// status |=TIOCM_DTR;
status |=TIOCM_RTS;
ioctl(iRS485,TIOCMSET,&status); //set RTS in write mode
write(iRS485, "RS485 test data!!", 10);
iRtn = 0;
while(!iRtn)
{
ioctl(iRS485, TIOCSERGETLSR, &iRtn);
// printf("%d",iRtn);
}
ioctl(iRS485,TIOCMGET,&status);
status &=~ TIOCM_RTS;
ioctl(iRS485,TIOCMSET,&status); //set RTS in receive mode
// getchar();
iRtn = read(iRS485, rxbuf, sizeof(rxbuf));
if(iRtn > 0) printf("RS485 receive data is :%s\n",rxbuf);
err:
close(iRS485);
return iRS485;
}
status &=~ TIOCM_RTS;
ioctl(iRS485,TIOCMSET,&status); //set RTS in receive mode
// getchar();
iRtn = read(iRS485, rxbuf, sizeof(rxbuf));
if(iRtn > 0) printf("RS485 receive data is :%s\n",rxbuf);
err:
close(iRS485);
return iRS485;
}