ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Zynq-7000上RS422通信测试:从设备树到应用层排障实践

Zynq-7000上RS422通信测试:从设备树到应用层排障实践 1. 项目背景与测试目标1.1 这块板子为什么要测RS422先说下我为什么折腾这件事。手头这块Zynq-7000板卡是客户定制的板子上有4路隔离RS422接口用来和工业现场的伺服驱动器、PLC控制器做长距离数据传输。Zynq-7000这颗芯片很有意思——双核ARM Cortex-A9配上可编程逻辑PL等于一颗芯片同时干了两件事ARM核跑Linux系统负责逻辑控制FPGA逻辑负责高速数据采集和协议解析。RS422这种工业现场常用的差分串口正好需要这种异构架构来对接。之前团队在这块板子上已经跑通了千兆网口和SD卡读写但RS422一直是“硬件焊上去、驱动没联调”的状态。客户调试现场反馈说数据偶发乱码、丢字节得先在Linux下走一遍完整的通信测试流程确认到底是硬件问题、驱动问题还是上层的串口配置参数没写对。这次测试的最终目的有三个一是验证4路RS422在Linux下能否被正确识别并稳定收发二是测出每路口的实际波特率误差和误码率给客户一个可接受的数据三是固化一套测试步骤后续产线烧完系统可以直接执行脚本验收不用每次手动敲命令。1.2 测试环境与硬件资源清单设备清单这块得先说清楚因为后面所有操作都依赖这套环境主控板Zynq-7000系列具体型号XC7Z020双核ARM Cortex-A9跑在667MHz操作系统PetaLinux 2018.3构建的嵌入式Linux内核版本4.14RS422接口芯片板载4路使用ADI的ADM2682隔离收发器支持全双工辅助调试板另一块USB转RS422模块用于和被测板对发数据串口调试助手PC端用secureCRT截取收发数据做比对这里有个关键点Zynq-7000的PS端ARM系统端自带的UART控制器只有两个而且是通过MIO引脚直接引出的其中UART0默认绑定到调试串口UART1是空闲的。如果要扩展多路RS422通常有两个选择一是用PS端的UART1配合外部收发器芯片二是把UART控制器做成IP核放在PL端再通过AXI总线挂到PS端。我这边用的是第二种方案因为客户要求4路RS422PS端本来就只有两个UART不够用。硬件连线方面RS422是4线制的差分信号A同相端、B反相端两对线一对发送一对接收。接法上务必注意板卡上的发送A要接对端设备的接收A发送B接对端接收B不能把同一个设备端的T和R短接那是RS232的半双工玩法。如果手头只有DB9头子一般2脚是发送A、7脚是接收B但不同厂家的DB9引脚定义偶尔有差异最好对着原理图确认一遍再动手。2. 底层链路核验与驱动配置2.1 为什么先查设备树而不是直接写应用层代码很多人拿到板子的第一个动作就是写个open(/dev/ttyPS1)的测试程序去收发然后发现收不到数据就开始怀疑这怀疑那。以我做嵌入式Linux这几年踩坑的经验必须先确认底层链路通不通再往上走应用层。因为RS422在Linux下的串口驱动框架里和普通RS232复用同一套UART驱动机制你没有直接在应用层配置差分信号的能力能不能正常工作完全取决于设备树里有没有正确描述这路UART。Zynq-7000的PL端挂UART设备树里通常是这样的结构axi_uart422_0 { compatible xlnx,xps-uartlite-1.00.a; reg 0x42C00000 0x10000; interrupts 0 29 4; clock-names s_axi_aclk; clocks clkc 15; current-speed 115200; device_type serial; port-number 2; status okay; };注意compatible字段用的是“xlnx,xps-uartlite”这是Xilinx官方提供的UART Lite IP核驱动在Linux内核里是现成的文件名叫uartlite.c。如果你用的是UART 16550 IP核compatible就得改成“xlnx,xps-uart16550-2.00.a”对应驱动是8250系列。这两个驱动在内核配置里对应不同的选项别搞混了——我遇到过有人把UART Lite的设备树配成16550的结果驱动怎么Load都失败。设备树还有一种写法就是用aliases直接指定端口号aliases { serial0 uart0; serial1 axi_uart422_0; };这种写法可以把PL端的UART固定映射到/dev/ttyS1否则系统会按探测顺序自动分配重启几次之后设备节点可能会漂移测试脚本里写死的设备路径就失效了。工业设备上电重启之后节点变了是非常头疼的事情所以建议直接在家目录写个udev规则根据/sys/class/tty/ttyS1/device/of_node/compatible来固定设备名。2.2 内核配置确认UART驱动已编入设备树写好了驱动还得在内核里开着才行。PetaLinux工程目录下执行petalinux-config -c kernel进入菜单找Device Drivers → Character devices → Serial drivers确保下面这几个选项是开启的Xilinx UART Lite serial port supportXilinx UART 16550 serial port supportSupport for console on AMBA serial port如果你是直接从Xilinx官方仓库拉的内核这几个选项默认是打开的但架不住有人手欠关掉过。确认完之后重新编译内核并打包petalinux-build petalinux-package --boot --fsbl zynq_fsbl.elf --fpga system_wrapper.bit --u-boot这里有个容易忽略的点如果PL端的UART IP核是这次新加的比特流bit文件里面必须包含这个IP的实例化否则设备树里描述得再完美硬件上根本没有对应的逻辑电路驱动加载时也是“设备不存在”的错误。所以每次改完PL工程记得重新生成比特流并重新打包BOOT.bin。2.3 查看设备节点是否生成烧录系统后启动到Linux命令行第一步就是验证内核有没有识别到串口设备。我习惯用这组命令dmesg | grep -i tty ls -l /dev/tty* cat /proc/tty/drivers如果一切正常dmesg里能看到类似[ 1.456789] xuartps 42C00000.serial: ttyS2 at MMIO 0x42C00000 (irq 29) is a XUARTPS注意看结尾的XUARTPS还是uartlite这个标识符能确认驱动绑定的类型。我这边4路PL端UART在/dev/ttyS1/dev/ttyS4其中ttyS1是UART Lite IP核0号实例其余类推。如果/dev下面没有对应节点多半是设备树没配对或者驱动没编进去。排查手段是从/proc/device-tree下手直接把设备树子节点导出来看ls /proc/device-tree/axi0/serial42C00000/ cat /proc/device-tree/axi0/serial42C00000/compatible直接直视内核视角下的设备树内容比自己翻dts源码更快定位问题。3. RS422通信测试工具与链接操作3.1 测试环境里有哪几种可用工具Linux下的串口测试工具常用的就这几个microcom、minicom、picocom、putty再加一个没有交互界面的stty。嵌入式板子上空间有限我一般用microcom因为它只依赖一个动态库体积够小而且支持直接指定波特率、数据位、校验位。如果没有现成的工具busybox里通常会带一个缩水版的microcom命令格式是microcom -s 115200 -t 5000 /dev/ttyS2-s指定波特率-t指定超时毫秒数不加-t的话就一直挂在那里等数据。但注意busybox的microcom功能和完整版有差距有些参数不支持比如数据位和校验位只能在stty里先设置好microcom不会帮你改。完整工具链部署到嵌入式板子上的思路是先在PC上交叉编译好静态链接的二进制再扔到板子的/usr/bin或/opt目录。下面是我的交叉编译流程wget https://github.com/ravynsoft/microcom/archive/refs/tags/v1.0.tar.gz tar xf v1.0.tar.gz cd microcom-1.0 export CROSS_COMPILEarm-linux-gnueabihf- make CC${CROSS_COMPILE}gcc因为嵌入式Linux动态库版本可能和PC不完全兼容我倾向于用-static参数编译make CC${CROSS_COMPILE}gcc LDFLAGS-static这样生成的microcom在板子上就能直接用不会出现“libncurses.so.5找不到”这种尴尬事。3.2 回环测试验证本板收发通路我把RS422的回环测试放在所有联调测试的第一步因为它能最快验证板卡自身的UART控制器和收发器芯片是否工作正常。回环测试有两种做法一种是直接把发送端的A接到本板接收端的A发送端B接到本板接收端的B用杜邦线或者短接帽在接线端子上面短接另一种是利用RS422收发器芯片的“自动回环”功能把DE/RE引脚拉高让收发器处在自发自收模式不过这种方式没法验证外部接线问题我建议还是老老实实短接端子。短接好之后用stty配置串口参数stty -F /dev/ttyS2 115200 cs8 -cstopb -parenb raw参数含义逐个解释115200是波特率cs8表示8位数据位-cstopb表示1位停止位减号表示否定即关掉2位停止位选项-parenb表示无校验。raw这个参数很重要它让驱动程序不做任何行处理比如把\n自动变成\r\n或者把收到的\r自动忽略这样才能保证收发内容字节完全一致。配好之后再验证一下stty -F /dev/ttyS2 -a然后往里写测试数据echo hello rs422 /dev/ttyS2因为发送端和接收端在板卡内部被短接在一起理论上一毫秒后数据就会原封不动地回来用cat命令应该能读到timeout 1 cat /dev/ttyS2如果回显了你刚写的内容说明UART控制器数据通路OK。如果没回显先别急着怀疑驱动确认一下短接的针脚是不是真的短接到了正确的接收差分对上——RS422的A/B极性接反是完全静默的什么数据都不会有不像RS232那样至少还有个电压信号。3.3 与PC端USB转RS422模块对发回环测试通过后可以开始和PC端模块对发测试了。这里的接法是板卡RS422端子USB转RS422模块端子说明TX_ARX_A板卡发送接到对端接收TX_BRX_B板卡发送-接到对端接收-RX_ATX_A板卡接收A接到对端发送ARX_BTX_B板卡接收B接到对端发送BGNDGND公共地必须连很多人连接RS422时不接地线觉得差分信号不需要公共地这个认知是错的。RS422虽然抑制共模干扰能力强但它并非完全隔离——收发器芯片内部的ESD保护二极管总得有泄放路径悬空的参考地会让共模电压漂移长时间运行之后偶尔出现一个乱码字节就是这个原因。尤其是在实验室里台式PC和设备不共地的情况特别常见接上GND之后稳定性会明显改善。PC端的secureCRT新建一个串口会话选择USB转RS422对应的COM口波特率115200、8位数据位、1位停止位、无校验、无硬件流控。然后开始双向测试。板卡发、PC收的命令for i in $(seq 1 100); do echo test_$i; sleep 0.1; done /dev/ttyS2在secureCRT里应该能连续看到100条test记录每一条都不会丢。如果中途有乱码或者漏数据先看是不是线缆太长——RS422理论上可以到1200米但这里有个前提是波特率越低距离越远在115200波特率下超过100米就开始有衰减风险了。实验室环境一般不超过5米线缆长度基本可以排除。PC发、板卡收的命令timeout 5 cat /dev/ttyS2然后用secureCRT的“发送文件”功能发一个包含“0123456789abcdef”循环的文本文件大小建议控制在10KB以内太大会让嵌入式板子上的缓冲区溢出。板卡终端打印出来的内容应该和发送的完全一致。3.4 老化测试与误码统计单条数据对发成功之后还不能立刻给客户交差得做一轮持续性的老化测试来验证稳定性。我之前遇到过一种“薛定谔的乱码”——单帧数据收发正常跑个半小时就随机蹦出一个错字节这种偶发故障最让人头疼必须靠长时规避性测试暴露它。老化测试我习惯用脚本来做统计核心思路是构造一个带序号的数据包发送对端收到后检查序号是否连续。板卡端脚本#!/bin/sh count0 while true do count$((count1)) echo PACKET_${count}_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 /dev/ttyS2 sleep 0.01 donePC端用Python写个监听脚本统计接收结果import serial import time ser serial.Serial(COM3, 115200, timeout1) last_seq 0 error_count 0 total_count 0 start_time time.time() while time.time() - start_time 3600: line ser.readline().decode(utf-8, errorsignore).strip() if not line: continue total_count 1 try: seq int(line.split(_)[1]) if seq ! last_seq 1: print(fseq jump: {last_seq} - {seq}) error_count 1 last_seq seq except (IndexError, ValueError): print(fparse error: {line}) error_count 1 print(ftotal: {total_count}, errors: {error_count}, error_rate: {error_count/total_count*100:.4f}%)这个脚本够简单也够实用。跑1小时如果有跳序号或者解析错误直接看总数和误码率比人肉盯屏幕靠谱得多。间隙我会顺便测测不同波特率下的表现常用挡位是9600、19200、38400、57600、115200这5档。4. 排障实录与常见问题速查4.1 端口节点不开或者权限不够我手里这块板子跑完PetaLinux后偶发过一次/dev/ttyS2节点消失的情况多方排查后定位是设备树里中断号冲突了。PL端UART的中断号是在Vivado里给IP核分配的呢如果两个IP核用了同一个中断号Linux中断子系统会拒掉后注册的那个驱动对应的tty设备也就创建不出来。排查方法先看dmesg | grep -i irq确认有没有IRQ request failed字样然后在Vivado里打开Address Editor逐个核对中断号。比中断冲突更常见的是权限问题。非root用户访问/dev/ttyS2会提示Permission denied多数人第一反应是chmod 666 /dev/ttyS2——能用但一重启就没了。正确姿势是加udev规则echo KERNELttyS[0-9]*, MODE0666 /etc/udev/rules.d/99-rs422.rules重新插拔或者重启之后普通用户就能直接访问了。4.2 收不到数据时的三级排查路径如果在回环模式下都收不到数据按这个顺序排查能省很多冤枉时间第一级查物理层。用万用表量RS422收发器的A-B引脚之间电压正常空闲状态应该在2V到6V之间。如果量出来是0V大概率发送端没使能检查发送使能引脚有没有被硬件拉高。第二级查驱动层。执行cat /proc/tty/driver/serial看串口状态0: uart:XTUARTPS port:00000000 irq:0 tx:0 rx:0 1: uart:XTUARTPS port:00000000 irq:0 tx:0 rx:0 2: uart:XTUARTPS port:42C00000 irq:29 tx:0 rx:0如果tx和rx计数始终是0说明驱动层面就没有数据流经过问题往内核外设方向查如果tx计数在涨但rx是0说明发送通路OK、接收通路有问题重点查接线和收发器的RE引脚。第三级查设备树。确认设备树里UART节点的interrupts属性和Vivado里配置的保持一致这个排查在4.1里已经说过了不再赘述。4.3 乱码和奇偶校验问题RS422乱码的情况比收不到数据更磨人。有一次客户报障说“通信不稳定大概一分钟出一个错码”我用示波器抓A-B差分波形才定位——发送端信号幅值只剩1.8V了衰减严重。查了一圈是板卡到设备的电缆中间过了一个转接端子那个转接端子的焊点虚焊了接触电阻变大导致信号幅值跌落。如果你用示波器量波形没问题那大概率是波特率不准或者奇偶校验配置不对。RS422的标准帧格式是“起始位 8数据位 校验位(可选) 停止位”如果一端开了校验另一端没开接收端会把校验位当成数据位来读出来的内容必然错位乱码。排查方法是两端都改成无校验、8位数据位、1位停止位最通用的配置先跑通再精细化调整。另外一个隐藏很深的坑某些工控设备在上电瞬间会往总线上吐一段初始化数据如果这段数据和业务数据混在一起接收端的逻辑控制不好就会整体错位。通俗讲就是一个包含5个字节的数据包回来了但你只收到3个字节剩下2个字节被当成下一包来解析了。这种情况软件上要做的是启停位和超时判断硬件上无解。4.4 速率上不去与缓冲区溢出我测试时还遇到过一种情况波特率115200、每包256字节、每秒发100包结果PC端频繁丢弃数据。这不是波特率不够而是嵌入式Linux的tty缓冲区在驱动层有上限。Linux内核里tty_flip_buffer默认的缓冲区大小大约是64KB如果应用层读数据的速度跟不上数据到达的速度缓冲区满了之后新数据直接被丢弃。优化思路有三个方向应用层改用pollread的非阻塞方式加大每次读取的字节数及时把数据从内核态搬到用户态内核配置中调整/proc/sys/kernel/printk减少内核打印抢占CPU的时间如果还不行就要考虑在PL端加FIFO缓存把UART收到的数据先暂存到FPGA内部的Block RAM里然后由DMA批量搬运到内存这已经是高性能方案了。4.5 常见问题速查表故障现象可能原因处理方法设备节点找不到设备树未描述该UART / 驱动未编入dmesg看报错核对设备树compatible字段设备节点在但打不开权限问题加udev规则改文件权限能写入但收不到接线错误 / 收发器未使能万用表量AB差分电压检查DE/RE引脚偶发乱码线缆过长 / 焊接不良 / 共地问题检查线缆和接插件确保GND相连波特率漂移晶振精度不足换更高精度晶振或软件校准数据丢包tty缓冲区溢出应用层及时读取 / PL端加FIFO5. 实操总结与几点个人经验来回折腾了两天总算把这块板子上的4路RS422接口全部调通了。回顾整个测试过程最深的感受就是串口通信调试不存在“一步到位”它极其依赖你把底层链路一步步踩实设备树描述对不对、驱动有没有绑上、硬件接线有没有接反、参数配置是否一致每一步都会让你前面的假设全部推倒重来。我个人在实际操作中还有一个习惯所有测试脚本和命令都沉淀成一份shell脚本放到板子的/home/root目录下包含自检、双向对发、老化统计三个子命令。产线同事拿到板子之后不需要理解任何RS422原理跑一条./rs422_test.sh loopback就知道硬件通不通跑一条./rs422_test.sh aging 3600就能拿误码率报告这才是嵌入式测试应该有的交付形态 —— 把复杂链路固化成一条可重复执行的命令比写十页测试文档有用得多。最后再分享一个小技巧调试RS422的时候在示波器上同时抓A线和B线不要只看单端波形——差分信号的真谛在于A-B的差值只有一对线放在一起观察才能准确判断共模干扰和幅值衰减是否正常。很多人一开始只挂A线到示波器上看到正弦波还觉得没问题结果其实共模噪声早就超标了这一条经验至少帮我省了三个小时的排障时间今天一起整理出来希望对后来人能少踩一些坑。
返回列表