Linux 485驱动通信异常
背景
前段时间接到一个项目,要求用主控用485和MCU通信。将代码调试好之后,验证没问题就发给测试了。测试测的也没问题。
但是,到设备量产时,发现有几台设备功能异常。将设备拿回来排查,发现是485通信有问题,偶现通信失败。
排查思路
复现问题
发给测试之前,功能都验证了很多次,但是并没有发现通信失败的问题。设备拿到手,第一时间就尝试复现通信失败的问题,也没有成功。
于是,写了一个脚本,不断的和MCU通信,看什么情况下会失败。
果然,在通信若干次后,发现日志异常,主控接收数据出现了错乱。
接着,继续跑脚本,想看下什么情况下会失败。但是,每次通信异常的时机都是随机的,没有规律。
观察了下错乱的数据,和正确的数据做了对比,也没有什么发现。
清空buf
接收的数据出现了异常,第一个想到的是,是不是接收buffer不干净,有其他数据干扰呢?
尝试在接收buffer和发送buffer之前,手动清空下buf。确保不会有其它数据干扰。
重新跑脚本和MCU 通信,但是仍会失败。
收发时序
光看是什么办法了。上示波器看下主控和MCU的时序的。
正常来讲,主控和MCU的485控制管脚应该是正好反向的电平。即主控485控制管脚高电平发送的时候,MCU的485控制管脚应该是低电平。
问题复现时,对比了管脚的电平,确实是反向的,没有问题。这也排除了收发时序对不上的问题。
(绿色的是MCU的485控制管脚,黄色的是主控的485控制管脚)

收发数据正确
小示波器没有解码的功能,只能找硬件来量下主控的RX和MCU的TX。确认下,到底是主控接收的不对,还是MCU发的不对。

显然,是主控接收的数据有问题了。
仔细观察会发现,绿色波形这里有个半高电平,覆盖了黄色的低电平。导致第一帧出错了,后面的数据也都错乱了。

又重新复现了几次,发现每次失败时都是这种现象。那为什么这里会有个半高电平呢?
确认问题
和硬件对着原理图经过一番讨论,硬件给到的结论是,485芯片的RX管脚接了3.3V的上拉,只有当485芯片的使能管脚拉高时,RX才会有3.3V的半高电平出现。硬件怀疑是485控制管脚和MCU的时序没对上。
不过,我之前也量了主控和MCU的485控制管脚的电平,看了是对的?难道是我看错了?
接着又重新量了主控和MCU的485控制管脚,发现确实有问题,具体如下图。两者有1.5ms的高电平是重合的,这或许就是问题所在!


又重新复现了几次问题,发现每次通信失败时,都会有一段高电平是重合的。
到这里,基本也就明确了问题原因:主控和MCU的485控制管脚时序没对上!
寻找问题根因
从波形找出了问题所在,回归串口编程,继续看下代码吧。把重点放在了时序切换的代码上。
代码里面,在切换485管脚时有这样两段代码。
以下只是伪代码
代码一:
setdir(SEND) //切换为发送状态
write() //发送数据
tcdrain(fd) //判断是否写完
setdir(READ) //切换为读状态
代码二:
do
{
ioctl(fd,TIOCSERGETLSR,&lsr) //判断发送buffer是否写完
}while(!(lsr&TIOCSER_TEMT)) //如果TX为空,返回TIOCSER_TEMT
这两段代码,都是和485管脚切换相关的,根据不同情况走不同逻辑,出问题的代码走的是代码一片段。
tcdrain 和 TIOCSERGETLSR
那这两段代码有什么区别呢?
tcdrain是应用层控制tty的一个函数,调用 tcdrain()函数后会使得应用程序阻塞, 直到串口输出缓冲区中的数据全部发送完毕为止。
ioctl(fd,TIOCSERGETLSR,&lsr)是获取tty 设备的线路状态寄存器( LSR )的值。
这两段代码最大区别就是延时不同!
tcdrain()是等待fd所引用的串口设备数据传输完毕。虽然在物理上数据已传输完毕时,但Linux对硬件实时性高,对于用户请求的实时性较低。所以操作系统会有延时,导致tcdrain()多停留几ms,从而导致数据发送完后,485管脚的控制方向不能及时切换。
如果对接的485设备,接收和应答的延迟小于tcdrain()的延时,那方向切换不及时将导致数据接收丢失。这就是问题根因所在。
那为什么操作系统会有延时呢?
这个得说说Linux工作队列相关机制,对于硬件操作Linux处理的很及时,但是对于数据Linux可能将其交给系统的下半部的内核线程去处理,这就可能导致用户的系统调用存在一定的延时,而485通信对时间要求又很严格,1ms的延时就会导致数据错乱。
总结
- 严谨细致。在问题发生时,我也去量过主控和和MCU 485控制管脚的电平,只看到了两者是反向的,但是并没有放大去看最后一段电平的细节。导致遗漏了解决问题的线索。
- 一切问题发生都是有原因的。偶现问题并不好排查,但是我们可以尝试制作偶现问题发生的条件,看有没有可能成为必现问题。如果不能必现,可尝试通过脚本去不断运行在问题发生的场景,使其出现的概率提升。
- 心态。放平心态,多看代码。认真分析。
Linux 485驱动通信异常的更多相关文章
- Linux内核驱动开发之KGDB原理介绍及kgdboe方式配置
接博文<Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)>.上篇博文中,仅简单介绍使用串口的Kgbd的流程(kgdboc方式),本文将重点介绍KGDB调试Linux内核的原 ...
- Linux内核中断和异常分析(中)
在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机 ...
- 【驱动】linux设备驱动·入门
linux设备驱动 驱动程序英文全称Device Driver,也称作设备驱动程序.驱动程序是用于计算机和外部设备通信的特殊程序,相当于软件和硬件的接口,通常只有操作系统能使用驱动程序. 在现代计算机 ...
- Linux设备驱动那些事
目的 初步了解 linux 设备驱动框架模型 初步了解设备驱动模型有哪些元素 设备驱动模型元素的说明及解释 设备驱动模型元素的工作原理 设备驱动模型的小例子 对整体有个粗略的了解,设备驱动类型种类太多 ...
- Linux网络驱动--snull
snull是<Linux Device Drivers>中的一个网络驱动的例子.这里引用这个例子学习Linux网络驱动. 因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是 ...
- Linux设备驱动模型之I2C总线
一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...
- linux设备驱动概述,王明学learn
linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...
- linux网卡驱动移植
这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...
- linux设备驱动归纳总结(六):1.中断的实现【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-90740.html linux设备驱动归纳总结(六):1.中断的实现 xxxxxxxxxxxxxxxx ...
- linux设备驱动归纳总结(三):6.poll和sellct【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-61749.html linux设备驱动归纳总结(三):6.poll和sellct xxxxxxxxxx ...
随机推荐
- Django框架项目之git笔记——版本控制器、git介绍、git使用
文章目录 版本控制器 git 简介 git与svn比较 git的工作流程 版本库间的通信 git分支管理 git使用 流程(核心总结) 安装 基础命令 将已有的文件夹 - 初始化为git仓库 在指定目 ...
- 第一个 Go 程序"hello,world" 与 main 函数和Go常用基本命令
第一个 Go 程序"hello,world" 与 main 函数和Go常用基本命令 目录 第一个 Go 程序"hello,world" 与 main 函数和Go ...
- EMC ndmp NAS
NAS 是带存储系统的专用高性能文件服务器.它可提供文件级数据访问和共享. NAS 使用网络和文件共享协议,包括用于数据传输的 TCP/IP 以及用于远程文件服务的 CIFS 和 NFS. 最简单的备 ...
- CF1526C1
题目简化和分析: 给您一个数组,在其中选择若干个数使得: 任意前缀和 \(\ge 0\) 数量尽可能的大 我们可以使用贪心策略,策略如下: 如果当前数为非负,必喝. 而毒药尽可能的多喝,如果喝没了,就 ...
- 网络基础-OSI七层vsTCP/UDP四层 五层 数据封装
1.0 网络基础 1.1 网络是什么? 网络是信息传输.接收.共享的虚拟平台,通过它把各个点.面.体的信息联系到一起,从而实现这些资源的共享 网络分类:局域网 ,城域网,广域网 1.2 数据通信方式 ...
- 【KMP】border 题解
题目描述 输入 输出 样例输入 abaabaa 样例输出 17 样例解释: f[2][a] = 1 f[3][a] = 1 f[4][a] = 1 f[4][b] = 2 f[5][a] = 1 f[ ...
- Unity学习笔记--入门
Unity引擎学习 入门: Unity中的坐标系: 左手坐标系(z轴正方向向里面) x轴:平行屏幕向右正方向 y轴:平行屏幕向上正方向 Z轴:垂直屏幕向内为正方向 [补]openGL是右手坐标系 Di ...
- 实战攻防演练-Linux写入ssh密钥,利用密钥登录
前言 密钥形式登录的原理是利用密钥生成器制作一对密钥,一只公钥和一只私钥.将公钥添加到服务器的某个账户上,然后在客户端利用私钥即可完成认证并登录.这样一来,没有私钥,任何人都无法通过 SSH 暴力破解 ...
- c#享元模式详解
基本介绍: 享元模式的定义:运用共享技术有效地支持大量细粒度的对象重复使用.适用于大量小粒度的对象造成的运行效率和内存使用效率低下的情况. "享元"顾名思义,"享 ...
- spring---面向切面(AOP @Pointcut 表达式篇)
AOP(面向切面编程),可以说是OOP(面向对象编程)的补充和完善.OOP引入封装.继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合. 当我们需要为分散的对象引入公共行为的时候,O ...