加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
登录/注册
立即注册,领取新人专属福利!
  • 正文
    •   非阻塞打开串口
    •   设置串口成阻塞方式
    •   获取和设置termios
    •   设置波特率
    •   设置奇偶校验位
    •   设置最少字符和等待时间
  • 相关推荐
申请入驻 产业图谱

ZLG嵌入式笔记(连载24) | “串口阻塞”你真的会用吗?

02/13 15:21
1081
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

串口通信开发中,数据错乱是常见问题。本文将快速介绍串口标志位的作用及配置方法,帮助解决数据传输错误。

这是一个真实案例,用户反馈“串口向另外的设备发送数据,发现运行一段时间后,发送的消息会阻塞很久才会发出来,一下子出来很多数据”。经过帮客户检查应用程序源码,发现应用程序在串口阻塞方面没有做正确的处理,修改后解决。

  非阻塞打开串口

open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NONBLOCK);
    O_NOCTTY:如果打开的是一个终端设备,这个程序不会成为对应这个端口的控制终端,如果没有该标志,任何一个输入,例如键盘中止信号等,都将影响进程。
    O_NONBLOCK:该标志与早期使用的O_NDELAY标志作用差不多。程序不关心DCD信号线的状态,也就是不关心端口另一端是否已经连接。如果不指定该标志,进程将一直在休眠状态,直到DCD信号线为0。简单点就是以非阻塞方式打开串口。

  设置串口成阻塞方式

可用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)

检测打开的文件描述符是否连接到一个终端设备,进一步确认串口是否正确打开。

  获取和设置termios

1. 获取termios结构体(串口属性)

    int tcgetattr(int fd, struct termios *termptr);termptr:接收返回的termios,成功:0,失败:-1。

2. 保存先前的串口配置

int tcsetattr(int fd, int opt, const struct termios *termptr);

3. 设置串口属性

3.1 opt:在串口驱动程序里有输入缓冲区和输出缓冲区。在改变串口属性时,缓冲区可能有数据存在,如何处理缓冲区中的数据,可通过opt 参数实现。

    TCSANOW:更改立即发生;TCSADRAIN:发送了所有输出后更改才发生,若更改输出参数则应用此选项;TCSAFLUSH:发送了所有输出后更改才发生,在更改发生时未读的所有输入数据被删除(Flush)。

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 设置。

    PARENB     进行奇偶校验。PARODD    奇校验,否则为偶校验。

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设置:

    • TCIFLUSH: 对接收到而未被读取的数据进行清空处理。TCOFLUSH: 对尚未传送成功的输出数据进行清空处理。

TCIOFLUSH:即对尚未处理的输入输出数据进行清空处理。

激活选项

CLOCAL 和 CREAD 分别用于本地连接和接收使能。激活这两个选项:

c_cflag |= CLOCAL | CREAD;

激活串口配置(属性)

在完成全部串口配置之后, 要激活刚才的配置并使配置生效。使用属性设置函数 tcsetattr() ,前面已有其说明。

向串口写数据

直接调用wtrite()即可。

从串口读数据

调用read()读取串口数据,但在非规范模式/原始模式下需要设置VMIN和VTIME。

    VMIN:非规范模式下读取的最小字符数。VTIME:非规范模式下读数据时的延时,VTIME个1/10秒。

VMIN和VTIME组合有四种情况:

    • VMIN=0,VTIME=0:  读取的最少字符数为0,延时时间为0,read立即返回。VMIN>0,VTIME=0:  read阻塞到读取VMIN个字符才返回。VMIN=0,VTIME>0:  有数据就返回,无数据等待VTIME个1/10秒返回。

VMIN>0,VTIME>0:  读取VMIN个字符或前后两个字符的输入间隔超过VTIME个1/10秒后返回,因为在输入第一个字符之后系统才会启动定时器,所以在这种情况下,read至少读取一个字符。

串口操作顺序

    保存原有串口属性(可选);设置波特率;设置激活选项,如c_cflag |= CLOCAL | CREAD;设置数据位大小;设置奇偶校验位;设置停止位;设置输出(可选),如c_oflag=0;0是清空标志;c_oflag&=~OPOST;设置输入(可选);设置c_lflag,如原始模式cfmakeraw(&termios);设置读取特性,c_cc[VTIME]和c_cc[VMIN];刷新缓冲区,tcflush();设置串口属性,tcsetattr()。

M3562 Cortex®-A53核心板

四核Cortex-A53

1.8GHz主频

低成本3568方案

参考价格:288元起

致远电子

致远电子

广州致远电子股份有限公司成立于2001年,注册资金5000万元,国家级高新技术认证企业,广州市高端工控测量仪器工程技术研究开发中心,Intel ECA全球合作伙伴和微软嵌入式系统金牌合作伙伴。

广州致远电子股份有限公司成立于2001年,注册资金5000万元,国家级高新技术认证企业,广州市高端工控测量仪器工程技术研究开发中心,Intel ECA全球合作伙伴和微软嵌入式系统金牌合作伙伴。收起

查看更多
点赞
收藏
评论
分享
加入交流群
举报

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录