一、简介
1.1 Linux串口编程主要是设置structtermios结构体的个成员值。Termios是在POSIX规范中定义的标准接口,表示终端设备(包括虚拟终端丶串口等),串口是一种终端设备,一般通过终端编程接口对其进行配置和控制。
串口的配置最重要的是以下结构体定义中标明红色的条目,配置方法如下:
某几位清0:struct_name.flag&= ~(MASK1 | MASK 2….)
某几位置1:struc_name.flag|= (MASK1 | MASK2…)
1.2例如:设置115200的波特率:
Structtermios new_cfg;
New_cfg.c_cflag|= B115200
1.3本文写作目的旨在方便查阅串口设置结构体的组成,方便开发
二、结构体定义
#include<termios.h>
Struct termios{
Unsigned short c_iflag; //输入模式标志
Unsigned short c_oflag; //输出模式标志
Unsigned short c_cflag; //控制模式标志
Unsigned short c_lflag; //本地模式标志
Unsigned char c_line; //线路规程
Unsigned char c_cc[NCC]; //控制特性
Speed_t c_ispeed; //输入速度
Speed_t c_ospreed; //输出速度
}
2.1 输入模式标志(c_iflag:16bit)
INPCK | 奇偶校验使能 |
IGNPAR | 忽略奇偶校验错误 |
PARMRK | 奇偶校验错误掩码 |
ISTRIP | 裁剪掉第8比特 |
IXON | 启动输出软件控制流 |
IXOF | 启动输入软件控制流 |
IXANY | 输入任意字符可以重新启动输入(默:起始字符) |
IGNBRK | 忽略输入终止条件 |
BRKINT | 当检测到输入终止条件时发送SIGINT信号 |
INLCR | 当收到NL(换行符)转换CR(回车符) |
IGNCR | 忽略收到的CR |
ICRNL | 当收到CR转换为NL |
IUCLC | 讲接收到的大写字符映射为小写字符 |
IMAXBEL | 当输入队列满时响铃 |
2.2输出模式标志(c_oflag :16bit)
OPOST | 启动输出处理功能,如果不设置,其他位忽略 |
OLCUC | 将输出中的大写字符转换成小写字符 |
ONLCR | 将输出中的换行符(‘\n’)转换成回车符(‘\r’) |
ONOCR | 如果当前列号为0,则不输出回车字符 |
OCRNL | 将输出中的回车符转换成换行 |
ONLRET | 不输出回车符 |
OFILL | 发送填充字符以提供延时 |
OFDEL | 如果设置该标志,则表示填充字符为DEL字符,否则为NUL |
NLDLY | 换行延时掩码 |
CRDLY | 回车延时掩码 |
TABDLY | 制表符延时掩码 |
BSDLY | 水平退格符延时掩码 |
VTDLY | 垂直退格符延时掩码 |
FFLDY | 换页符延时掩码 |
2.3控制模式标志(c_cflag:16bit)
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个停止位(不设置则是一个) |
GREAD | 接收使能 |
PARENB PARODD | 校验使能位 使用奇校验而不是偶校验 |
HUPCL | 最后关闭时挂线(放弃DTR) |
CLOCAL | 本地连接(不改变端口所有者) |
CRTSCTS | 硬件流控 |
2.4本地模式标志(c_lflag:16bit)
ISIG | 若收到信号字符(INTR,QUIT等)则会产生相应的信号 |
ICANON | 启动规范模式 |
ECHO | 启动本地回显功能 |
ECHOE | 若设置ICANON则允许退格操作 |
ECHOK | 若设置ICANON,则KILL字符会删除当前行 |
ECHONL | 若设置ICANON,则允许回显换行符 |
ECHOCTL | 若设置ECHO,则控制字符会显示成^x,其中x的ASCII码等于给相应的控制字符的ASCII码加上0x40. |
ECHOPRT | 若设置ICANON和IECHO,则删除字符和被删除的字符都会被显示 |
ECHOKE | 若设置ICANON,则允许回显在ECHOE和ECHOPRT中设定的KILL字符 |
NOFLASH | 在通常情况下,当接收到INTR,QUIT,SUSP控制字符时,会清空输入和输出队列。如果设置改标志,则所有的队列不会被清空 |
TOSTOP | 若一个后台进程师徒向他的控制终端进行写操作,则系统向改后台进程的进程组发送SIGTTOU信号。该信号通常终止进程的执行 |
IEXTEN | 启动输入处理功能 |
2.5控制特性(c_cc[])
VINTR | 中断控制字符,对应的键位ctrl+c |
VQUIT | 退出操作符,对应的键为ctrl+z |
VERASE | 删除操作符,对应Backspace |
VKILL | 删除行符,对应的键为ctrl+u |
VEOF | 文件结尾符,对应的键为ctrl+d |
VEOL | 附加行结尾符,对应的键为carriage return |
VEOL2 | 第二行结尾符,对应的键为line feed |
VMIN | 指定最少读取的字符数 |
VTIME | 指定读取的每个字符之间的超时时间 |
三、串口配置基本流程
3.1保存串口配置
int tcgetattr(int fd, struct termios *termios_p);
3.2激活选项
CLOCAL和CREAD分别用于本地连接和接收使能,因此要先通过位掩码的方式激活这两个选项:
newtio.c_cflag |=CLOCAL |CREAD;
3.3设置波特率
波特率的设置不能直接通过掩码来设定,有提供设置函数:
cfsetispeed(&new_cfg,B115200);
cfsetospeed(&new_cfg,B115200);
3.4设置数据位
首先去掉数据位中的位掩码,再重新按要求设置:
New_cfg.c_cflag &=~CSIZE;
New_cfg.c_cflag |=CS8;
3.5设置奇偶校验位
New_cfg.c_cflag|=(PARODD | PARENB) //激活校验位使能
New_cfg.c_iflag //激活对输入数据的奇偶校验使能
3.6设置停止位
New_cfg.c_cflag &= ~CSTOPB //讲奇偶校验位设置一个比特
New_cfg.c_cflag |=CSTOPB //讲奇偶校验位设置两个比特
3.7设置最少字符和等待时间
New_cfg.c_cc[VTIME]=0;
New_cfg.c_cc[VMIN]=0;
3.8清除串口缓存
由于串口在重新设置之后,需要对当前的串口设备进行适当的处理,这时就可以调用在<termios.h>中声明的tcdrain(),tcflow(),tcflush()等函数来处理目前串口缓冲中的数据,格式如下:
int tcdrain(int fd) //使程序阻塞,直到输出缓冲区的数据全部发送完毕
int tcflow(int fd,int action) //用于暂停或者重新开始输出
int tcflush(int fd,int queue_selector) //用于清空输入输出缓冲区
3.9激活配置
tcsetattr(int fd,int option_action,conststruct terios *termrios_p)
Param :
Optional_options:
TCSCANOW:配置的修改立即生效
TCSADRAIN:配置的修改在所有写入fd的输出都传输完毕之后生效
TCSAFLUSH:所有已接受但未读入的输出都将在修改生效之前被丢弃
Return:
0:成功
-1:失败
3.10使用串口
串口配置完成后就可以像文件一样读写数据,使用read(),write()函数
四、串口配置实例(通用函数封装)
#include <stdio.h>#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include<stdbool.h>
#include <termios.h>
/*@ Fn uartOpen
@param uartDevicePath 设备文件
@param speed 波特率,默认115200
@param dataWidth 数据位宽,默认8bit
@param stopBit 停止位,true:2bit; false:1bit;
@return fd 打开文件描述符
*/
int uartOpen(char *uartDevicePath,int speed,int dataWidth,bool stopBit){
//Info
int fd=-1;
struct termios new_cfg,old_cfg;
printf("uart path=%s\n",uartDevicePath);
//open device
fd=open(uartDevicePath,O_RDWR | O_NOCTTY );
if(fd==-1)
printf("open failed!\n");
//preserve the old configuration
if(tcgetattr(fd,&old_cfg)!=0)
printf("Old config preserve failed!\n");
//set mode
new_cfg.c_cflag |=CLOCAL |CREAD;
//set baudrate
switch(speed){
case 1800:{
cfsetispeed(&new_cfg,B1800);
cfsetospeed(&new_cfg,B1800);
}break;
case 2400:{
cfsetispeed(&new_cfg,B2400);
cfsetospeed(&new_cfg,B2400);
}break;
case 4800:{
cfsetispeed(&new_cfg,B4800);
cfsetospeed(&new_cfg,B4800);
}break;
case 9600:{
cfsetispeed(&new_cfg,B9600);
cfsetospeed(&new_cfg,B9600);
}break;
case 19200:{
cfsetispeed(&new_cfg,B19200);
cfsetospeed(&new_cfg,B19200);
}break;
case 38400:{
cfsetispeed(&new_cfg,B38400);
cfsetospeed(&new_cfg,B38400);
}break;
case 57600:{
cfsetispeed(&new_cfg,B57600);
cfsetospeed(&new_cfg,B57600);
}break;
case 115200:{
cfsetispeed(&new_cfg,B115200);
cfsetospeed(&new_cfg,B115200);
}break;
default:{
cfsetispeed(&new_cfg,B115200);
cfsetospeed(&new_cfg,B115200);
}break;
}
//set size of data
new_cfg.c_cflag &=~CSIZE;
switch(dataWidth){
case 5:{
new_cfg.c_cflag |=CS5;
}break;
case 6:{
new_cfg.c_cflag |=CS6;
}break;
case 7:{
new_cfg.c_cflag |=CS7;
}break;
case 8:{
new_cfg.c_cflag |=CS8;
}break;
default :{
new_cfg.c_cflag |=CS8;
}break;
}
//set stopbit:true 2 stopbit,false 1 stopbit
if(stopBit)
new_cfg.c_cflag |=(CSTOPB);
else
new_cfg.c_cflag &=~(CSTOPB);
//set others
new_cfg.c_cc[VTIME]=0;
new_cfg.c_cc[VMIN]=0;
//start
if(-1==tcsetattr(fd,TCSANOW,&new_cfg))
printf("Uart setting failed!\n");
return fd;
}
void main()
{
int fd=uartOpen("/dev/ttyUSB0",115200,8,true);
while(1){
;
//handle here
}
}