Termios作为POSIX标准的一部分,定义了对于终端操作的相关接口,其中串行端口作为其组成的一部分,其相关操作也被封装在该文件中。
在嵌入式开发中,往往需要使用串行端口进行上位机与下位机之间的通讯,而现代开发中,诸如Python,提供了十分方便的用以调用串行端口的封装接口与类库。
即使是C/C++的上位机开发,也往往结合诸如Qt等图形库,而这类图形库往往提供了串口操作的封装。而当我们想要在诸如Linux中开发小体量的控制台程序,或者有和我一样的洁癖,或者精简主义者。
那调用POSIX的标准接口用以实现串行端口通讯是不需要安装额外依赖的好方法。
简单例子
/*
* 初始化配置rs232串口设备
* 参数 fd: 设备文件描述符
* speed: rs232串口设备波特率
* databits: rs232串口设备数据位位数
* parity: rs232串口设备数据奇偶校验设置
* stopbits: rs232串口设备数据停止位位数
* 返回值: 配置成功返回零, 否则返回非零.
*
* */
int serial_port_init(int fd,int speed,int databits,char parity,int stopbits)
{
struct termios oldtio,newtio;
int status,i;
#ifdef DEBUG
printf("%s fd = %d\n speed = %d\tdatabits = %d\tparity = %c\tstopbits = %d\n", __func__, fd, speed, databits, parity, stopbits);
#endif
#if 1
/* 保存之前的串口配置 */
bzero(&newtio,sizeof(struct termios));
if(tcgetattr(fd,&oldtio)!=0){
perror("tcgetattr ");
return -1;
}
newtio=oldtio;
#endif
newtio.c_cflag |= CLOCAL;
newtio.c_cflag |= CREAD;
newtio.c_cflag &= ~ISTRIP;
#ifdef RAW_UART
cfmakeraw(&newtio);
#endif
/* 设置数据位数 */
switch (databits){
case 7: /* 七位数据位 */
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS7;
break;
case 8: /* 八位数据位 */
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
break;
default:
printf("%s unsupported data size\n", __func__);
return -1;
}
/*设置校验位*/
switch(parity){
case 'n': /* 无校验位 */
case 'N': /* 无校验位 */
newtio.c_cflag &= ~PARENB;
//newtio.c_cflag &= ~INPCK;
break;
case 'o':
case 'O':
newtio.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
newtio.c_iflag |= INPCK;
break;
case 'e':
case 'E':
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD; /* 转换为偶效验*/
newtio.c_iflag |= INPCK;
break;
case 's':
case 'S':
newtio.c_cflag &= ~PARENB;
newtio.c_cflag &= ~CSTOPB;
break;
default:
printf("%s unsupported parity\n", __func__);
return -1;
}
/* 设置停止位 */
switch (stopbits)
{
case 1: /* 一个停止位 */
newtio.c_cflag &= ~CSTOPB;
break;
case 2: /* 两个停止位 */
newtio.c_cflag |= CSTOPB;
break;
default:
printf("%s Unsupported stop bits\n", __func__);
return -1;
}
/* 設置波特率 */
switch(speed){
case 9600:
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
break;
case 460800:
cfsetispeed(&newtio,B460800);
cfsetospeed(&newtio,B460800);
break;
case 115200:
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
break;
default:
printf("%s not set speed fo serial port\n", __func__);
}
newtio.c_cc[VTIME]=0; /* 等待時間 */
newtio.c_cc[VMIN]=1;
tcdrain(fd); /* 使程序阻塞,直到缓冲区数据发送完 */
tcflush(fd,TCIOFLUSH); /* 更新配置 */
if ((tcsetattr(fd,TCSANOW,&newtio)) != 0){
perror("tcsetattr ");
return -1;
}
return 0;
}
/*
* 打开rs232串口设备
* 参数: device: rs232设备
* speed: rs232设备的波特率
* databits: rs232设备的数据位位数
* parity: rs232设备的奇偶校验设置
* stopbits: rs232设备的停止位位数
* 返回值: fd: 设备文件描述符
*
* */
int serial_port_open(char *device,
int speed, int databits,
int parity, int stopbits)
{
/* 打开rs232串口设备 */
int fd = open(device, O_RDWR|O_NOCTTY|O_NDELAY);
if(fd<=0){
printf("open %s error !\n", device);
exit(1);
}
/* 测试rs232设备 */
if(fcntl(fd,F_SETFL,0)<0){
perror("fcntl F_SETFL\n");
}
/* 测试rs232设备 */
if(isatty(fd) == 0){
perror("this is not a terminal device \n");
}
/* 初始化配置rs232串口设备 */
if(serial_port_init(fd, speed, databits, parity, stopbits)!=0){
printf("serial port init %s fail !\n", device);
close(fd);
exit(1);
}
return fd;
}
/*
* 关闭rs232串口设备
*
* */
int serial_port_close(int fd)
{
return close(fd);
}
/*
* 从rs232串口设备读数据
* 参数: fd: 设备文件描述符
* buf: 数据将存到的缓冲区指针
* len: 缓冲区的大小
*
* 返回值: num: 实际从rs232读到的数据长度
*
* */
int serial_port_read(int fd, char *buf, int len)
{
int num = 0;
/* 判断fd和buf的合法性 */
if((fd > 0) && (buf != NULL)){
/* 从rs232串口设备读数据 */
num = read(fd, buf, len);
}
return num;
}
/*
* 向rs232串口设备写数据
* 参数: fd: 设备文件描述符
* buf: 待写入的数据的数据缓冲区指针
* len: 缓冲区的大小
*
* 返回值: num: 实际写入到rs23设备的数据长度
* */
int serial_port_write(int fd, char *buf, int len)
{
int num = 0;
/* 判断fd和buf的合法性 */
if((fd > 0) && (buf != NULL)){
/* 向s232串口设备写数据 */
num = write(fd, buf, len);
}
return num;
}