导读
在串口通信开发中,数据错乱是常见问题。本文将快速介绍串口标志位的作用及配置方法,帮助解决数据传输错误。
这是一个真实案例,用户反馈“串口向另外的设备发送数据,发现运行一段时间后,发送的消息会阻塞很久才会发出来,一下子出来很多数据”。经过帮客户检查应用程序源码,发现应用程序在串口阻塞方面没有做正确的处理,修改后解决。
非阻塞打开串口
open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NONBLOCK);
设置串口成阻塞方式可用fcntl设置串口的阻塞/非阻塞。1. 阻塞:fcntl(fd, F_SETFL, 0)fcntl中的F_SETFL只可以更改标志O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC;而 0 则表示清空这几个标志,其中O_NONBLOCK也没了,所以就变成了阻塞。2. 非阻塞:fcntl(fd, F_SETFL, O_NONBLOCK)检测打开的文件描述符是否连接到一个终端设备,进一步确认串口是否正确打开。
获取和设置termios1. 获取termios结构体(串口属性)
2. 保存先前的串口配置int tcsetattr(int fd, int opt, const struct termios *termptr); 3. 设置串口属性3.1 opt:在串口驱动程序里有输入缓冲区和输出缓冲区。在改变串口属性时,缓冲区可能有数据存在,如何处理缓冲区中的数据,可通过opt 参数实现。
3.2 成功:0。3.2 失败:-1。
设置波特率1. 设置输入波特率
int cfsetispeed(struct termios *termptr, speed_t speed);
2. 设置输出波特率
int cfsetospeed(struct termios *termptr, speed_t speed);
设置数据位(也称设置字符大小)通过 c_cflag 设置。
CSIZE //数据位屏蔽CS5 //5个数据位CS6 //6个数据位CS7 //7个数据位CS8 //8个数据位
例如,设置串口的数据位为 8 位:
c_cflag &= ~CSIZE; //清除CSIZEc_cflag |= CS8; //设置CS8
设置奇偶校验位设置串口的奇偶校验是在 c_cflag 设置。
1. 无校验
c_cflag &= ~PARENB;
2. 偶校验
c_cflag |= PARENB;c_cflag &= ~PARODD;
3. 奇校验
c_cflag |= PARENB;c_cflag |= ~PARODD;
设置停止位设置串口停止位是在 c_cflag 设置。1. 设置 1 位停止位
c_cflag &= ~CSTOPB; //清除CSTOPB标志位
2. 设置 2 位停止位
c_cflag |= CSTOPB; //设置CSTOPB标志位
设置最少字符和等待时间c_cc[VTIME] 和c_cc[VMIN] 设置最少字符和等待时间,针对 read 而言。如果设置为0的话,则在任何情况下read()函数立即返回:
c_cc[VTIME] = 0;c_cc[VMIN] = 0;
清除串口缓冲由于串口在重新设置之后,需要对当前的串口设备进行适当的处理,通常使用tcflush实现。
int tcdrain(int fd); //使程序阻塞,直到输出缓冲区的数据全部发送完毕。int tcflow(int fd, int action); // 用于暂停或重新开始输出。int tcflush(int fd, int queue_selector); //用于清空输入/输出缓冲区。
使用tcflush()函数,对于在缓冲区中的尚未传输的数据,或者收到的,但是尚未读取的数据进行处理。queue_selector设置:
TCIOFLUSH:即对尚未处理的输入输出数据进行清空处理。
激活选项CLOCAL 和 CREAD 分别用于本地连接和接收使能。激活这两个选项:
c_cflag |= CLOCAL | CREAD;
激活串口配置(属性)
在完成全部串口配置之后, 要激活刚才的配置并使配置生效。使用属性设置函数 tcsetattr() ,前面已有其说明。
向串口写数据
直接调用wtrite()即可。
从串口读数据调用read()读取串口数据,但在非规范模式/原始模式下需要设置VMIN和VTIME。
VMIN和VTIME组合有四种情况:
VMIN>0,VTIME>0: 读取VMIN个字符或前后两个字符的输入间隔超过VTIME个1/10秒后返回,因为在输入第一个字符之后系统才会启动定时器,所以在这种情况下,read至少读取一个字符。
串口操作顺序
全部0条评论
快来发表一下你的评论吧 !