ARTICLE DETAIL

资讯详情

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

Android串口测试工具开发:底层原理与实战指南

Android串口测试工具开发:底层原理与实战指南 简介面向Android嵌入式、物联网及硬件调试开发者的串口测试工具资源围绕串口通信基础、权限声明、第三方SerialPort库使用、串口参数配置与数据收发流程展开。资源提供了CommManager类实现涵盖打开串口、设置波特率/数据位/停止位/校验位、发送与监听接收数据、异常处理及释放资源等完整逻辑同时结合简洁友好的界面设计思路方便开发者根据实际协议进行二次扩展。压缩包约2.62MB轻量便于快速部署已有765人学习。通过该资源可系统掌握Android串口调试工具的实现要点缩短从原理理解到编码落地的时间适合正在开发串口助手或需要对设备进行联调测试的Android工程师。 最近在调一块嵌入式Linux主板手边没有电脑只带着手机突然意识到一个被低估很久的东西android串口测试工具。说得直白点就是把手机和平板变成一个小型串口调试助手用来连开发板、单片机、PLC这类设备。很多人觉得串口调试必须抱着笔记本实际上在产线、现场、出差路上Android工具能省太多事。这篇文章我不打算只做App推荐而是结合自己从查资料到写工具的过程把Android串口通信的底层逻辑、常见芯片驱动、关键代码、实测记录和踩坑经验一次讲清楚。适合嵌入式开发、硬件测试和Android开发同学参考。1. 为什么我会自己做一个android串口测试工具1.1 硬件调试中的尴尬时刻以前调试串口我都是老老实实打开电脑插上USB转串口线开一个PC端的串口调试助手。但这套流程在真实场景里经常卡壳有时候是产线工位上根本没有电脑只有一台测试平板有时候是出差路上接到现场电话想远程看一眼设备日志身边只有手机。这个时候最需要的就是一个能直接装在Android设备上的串口测试工具。市面上现成的Android串口助手不少但真正用起来总有各种膈应。有些工具只支持USB转串口不支持设备自带的原生串口节点有些必须root普通手机根本跑不了还有一些UI做得花里胡哨却连最基本的hex显示和定时发送都没有。再加上我经常要测RS485、Modbus这类协议现成工具的命令模板和日志功能往往不够灵活。折腾几次之后我决定自己写一个顺便把Android串口通信这套东西彻底搞清楚。1.2 需求拆解一个趁手的串口助手要解决哪些问题写工具之前我先列了一份需求清单核心就几条能自动发现串口设备手动填节点路径也行不能只靠root后手动敲命令。波特率、数据位、停止位、校验位可配置最好常见波特率一键选。接收区支持ASCII和hex两种显示带时间戳能滚动查看大日志。发送区要支持字符串、hex两种模式还要有定时发送和命令模板。所有收发记录能保存成文件方便事后分析和归档。列完清单才发现这几条其实就是PC端串口助手的核心功能一点不多。做工具最忌讳堆功能能把上面这些做稳定已经能覆盖90%的日常调试场景。后面我实际用下来发现“定时发送”和“hex显示”这两个功能使用频率最高遇到Modbus调试时尤其依赖。2. Android串口通信基础绕不开的节点、权限和驱动2.1 串口节点和权限问题Android底层是Linux内核所以它眼里的串口设备同样是一个文件节点常见的有/dev/ttyS0、/dev/ttyMT0、/dev/ttyUSB0还有一些厂商定制节点如/dev/ttyHSL0。普通APP如果想打开这些节点必须面对两层限制第一层是文件权限默认情况下只有root和特定系统组能读写串口第二层是SELinux策略就算你拿到root权限SELinux也可能拦一道。所以原生串口调试方案通常绕不开root。我见过不少工具的做法是直接执行su命令然后chmod 666 /dev/ttyS0这种方式简单粗暴但需要设备已经root。如果是在定制ROM或者开发板上做集成更规范的做法是把串口设备权限放进SELinux policy或者直接把APP提成系统应用这样就不需要root。2.2 USB转串口芯片CH340、FTDI怎么选大多数手机和平板没有原生串口这时候就要用OTG线外接USB转串口模块。市面上常见的转接芯片有CH340、CH341、CP2102、FT232、PL2303它们的驱动情况和支持度差别不小。我自己用得最多的是CH340和FTDI下面这个表是我在实际项目中的感受芯片常见模块Android驱动支持典型场景CH340/CH341CH340 USB转TTL模块usb-serial支持较好山寨芯片要注意VID/PIDArduino、STM32、51调试CP2102CP2102模块识别稳定功耗低工业传感器、GPS模块FT232/FTDIFT232RL模块老牌厂商兼容性最好专业调试、多路串口PL2303早期USB转串口线Android端兼容性一般老设备维护在Android上USB转串口不需要root但需要APP通过USB Host API拿到设备访问权限。这个过程和PC上装驱动类似只是Android把驱动层做成了用户态库常见的有usb-serial-for-android它对CH340、FTDI、CP2102、PL2303都有现成支持。2.3 两种工作模式对比结合我自己的项目我把串口访问分成两种模式原生串口模式和USB转串口模式。它们解决的问题不一样不能互相替代。模式访问方式是否需要root适用场景原生串口直接打开/dev/ttyS*通常需要Android板卡调试、定制系统日志USB转串口USB Host API 驱动库不需要外接单片机、PLC、传感器所以我在工具里设计了两个访问后端一个负责原生串口节点一个负责USB转串口设备。上层界面完全复用底层通过统一接口封装这样不管用户手里是root过的开发板还是普通手机加OTG线都能正常工作。3. 用Android Studio从零搭建串口调试助手3.1 项目结构与关键依赖开发环境是Android Studio最低支持API 21语言用Java。项目结构上我按功能拆成三层设备层封装原生串口和USB转串口的打开、关闭、参数设置。通信层负责读写线程、数据转换、发送队列。界面层设备选择、参数配置、收发显示、日志保存。依赖库方面USB转串口我用的是usb-serial-for-android原生串口部分参考Google旧的android-serialport-api自己编译了一个精简版so库。这里有个小建议如果只做USB转串口完全可以不碰JNI但想调试Android主板自带串口原生模块还是得准备。3.2 核心代码打开串口并配置波特率原生串口打开的核心是JNI调用底层就是Linux的open函数。这段代码虽然古老但非常稳定public class SerialPort { static { System.loadLibrary(serial_port); } private native int open(String path, int baudrate, int flags); private native void close(); private int mFd; private FileDescriptor mFileDescriptor; private FileInputStream mInputStream; private FileOutputStream mOutputStream; public SerialPort(File device, int baudrate, int flags) throws IOException { mFd open(device.getAbsolutePath(), baudrate, flags); if (mFd -1) { throw new IOException(打开串口失败); } mFileDescriptor new FileDescriptor(); mInputStream new FileInputStream(mFileDescriptor); mOutputStream new FileOutputStream(mFileDescriptor); } }打开成功后读取和写入就变成普通的文件流操作。上位机配置波特率通过JNI传给内核常见波特率直接传115200、9600这类数值如果是非标准波特率如1500000部分内核也支持但要看驱动和硬件。USB转串口的打开方式不太一样需要先获取USB设备权限再通过驱动库打开UsbManager usbManager (UsbManager) getSystemService(Context.USB_SERVICE); UsbSerialPort port UsbSerialProber.getDefaultProber().findDevice(usbManager, device); if (port ! null) { UsbDeviceConnection connection usbManager.openDevice(device); port.open(connection); port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); }需要注意UsbSerialProber默认能识别大部分常见芯片但遇到山寨CH340时可能找不到设备这时手动添加Vendor ID和Product ID即可。3.3 接收和发送数据的线程模型串口读取是阻塞IO绝对不能放在主线程否则界面会卡死甚至ANR。我单独开了一个读线程不断从输入流中读数据new Thread(() - { byte[] buffer new byte[1024]; int len; while (!stopRead) { try { len inputStream.read(buffer); if (len 0) { byte[] data Arrays.copyOf(buffer, len); handler.obtainMessage(MSG_RECEIVE, data).sendToTarget(); } } catch (IOException e) { break; } } }).start();读取到的原始字节会通过Handler发给主线程然后根据用户选择的显示模式转成ASCII字符串或者hex文本。发送数据正好反过来直接把字符串或hex转成字节数组写入输出流byte[] data HexUtils.hexStringToBytes(hexString); outputStream.write(data); outputStream.flush();因为串口速度相比网络慢很多所以发送大批量数据时要注意节奏。像我调试Modbus协议一条报文最多就16个字节直接发没问题。但如果是往设备烧写配置或者导日志动辄几百KB就必须在发送线程里加延时或者按帧拆分不然容易把设备的接收缓冲区塞爆。3.4 界面与日志让调试更顺手界面我做得比较克制顶部是设备路径和波特率下拉框中间是收发显示区底部是输入框和发送按钮。关键功能都用长按和侧滑收纳起来保证主界面一屏放得下。接收区我用的是RecyclerView每条数据按时间戳分组支持点击复制也支持一键清空。日志保存这一块容易被忽略但实际用起来非常重要。我的做法是把所有收发原始数据写到sdcard/Documents/serial_log/目录下文件名包含日期和设备名格式是CSV每一行有时间、方向、hex数据、ASCII数据。这样现场调试完回头整理报告或者排查问题都有据可查。4. 实测记录从开发板日志到Modbus总线4.1 连接开发板读内核日志第一次实测我选了一块RK3288开发板板子上带调试串口焊盘标注是TX、RX、GND。用USB转TTL模块连上打开我写的工具选择USB设备设置波特率1500000接收区立刻开始滚动输出内核日志。这个场景其实已经完全替代了PC端的SecureCRT。驱动加载、串口初始化、应用崩溃等内核打印都能实时看到比用adb shell dmesg方便在不用连电脑。我还专门测试了长时间挂机跑了一整晚接收区没有乱码也没有内存暴涨这个结果让我对工具的信心一下就有了。4.2 USB转串口直连STM32开发板测完我又拿了一套STM32F103的工装板通过CH340模块连接手机。设备端的协议是自定义帧格式本质上是Modbus RTU的子集。在工具里选择波特率115200数据位8停止位1无校验然后在hex发送框输入一条读保持寄存器指令01 03 00 00 00 02 C4 0B点击发送返回区立刻收到设备回帧。没有出现乱码字节长度也完全符合预期。这条指令读的是两个寄存器的值后面我再把解析逻辑写在笔记里测试工具本身只保证底层收发稳定不做协议层解析这样工具更通用。4.3 数据吞吐和稳定性表现我还专门做了个小压力测试连续发送100条不同的测试帧每帧16字节总数据不到2KB接收端全部收到一条没丢。后来我把这个工具顺手接到RS485转串口模块上跑Modbus RTU轮询连续两个小时收发正常。但也有意外发现一些山寨CH340模块在Android上表现不稳定拔掉USB线再插上后设备节点偶尔不会重新出现必须把OTG线重新插一次。这个问题不是工具能解决的只能尽量在界面里增加“重新扫描设备”和“重新打开串口”的按钮让用户多一个恢复手段。5. 常见问题与排查技巧实录5.1 典型问题速查表用这套工具跑了几个月我在各种群和现场也帮别人看过类似问题整理了一个排查表现象可能原因解决办法设备列表为空没有USB OTG权限或节点路径不对检查OTG线重新扫描USB设备用ls /dev/ttyS*确认节点打开串口失败文件权限不足SELinux拦截root后执行chmod 666 /dev/ttyS0或者修改SELinux策略接收全乱码波特率不匹配或数据位/校验位错误确认两端都是8N1波特率重新选择发送后设备无响应TX和RX接反或没有共地交换TX/RX线接好GND地线偶尔丢包读线程缓冲区太小把buffer从256加大到1024或者用环形缓冲区USB设备识别不了山寨芯片VID/PID不在默认列表中手动添加设备的Vendor ID和Product ID5.2 我踩过的坑和最终解决思路第一个坑是主线程直接读串口。最开始我为了提高效率在Activity里直接调inputStream.read()结果数据稍微多一点就直接ANR。后来才老老实实把读取放到子线程用Handler更新UI这个坑几乎每个刚做串口App的人都会踩。第二个坑是串口关闭。有段时间设备明明没被占用但重新打开时一直报open failed最后发现是关闭时没有先停掉读线程导致文件描述符没有真正释放。现在我的关闭流程固定是先置stopRead true再关闭输入流再关输出流最后调用底层close()顺序不能乱。第三个坑和日志编码有关。很多设备输出的是GBK编码的中文日志如果统一按UTF-8转字符串显示出来就是乱码。我的解决方式是在字符转换时增加编码选择默认是UTF-8但可以手动切到GBK。这一点在做国内工控设备时非常常见尤其是老式PLC和仪表。第四个坑是山寨芯片的兼容性。usb-serial-for-android默认能识别CH340的常规VID/PID是1A86:7523但有些山寨模块用的不是这个VID导致识别不到。最后我加了一个自定义设备列表让用户可以把检测到的USB设备信息直接填进来问题就解决了。最后再分享一个小技巧如果你要调试的设备波特率是1500000这种非标准值部分Android平板在USB转串口模式下会不稳定这时候优先用设备自带的原生串口节点稳定性好很多。我自己实测同一个开发板原生串口跑1500000很稳定但走USB转CH340就只能稳定到115200。这个差异和内核驱动实现有关工具本身能做的就是把每种模式的参数配置得足够透明让使用者心里有数。本文还有配套的精品资源点击获取
返回列表