基于UsbSerialForAndroid的USB主机功能无法接收串口数据问题求助
问题描述
我用OTG线让搭载Android 9系统的moto e6 play作为USB主机对接AdaFruit Trinket M0。Trinket出厂默认上电后会循环发送串口文本字符串,我在Windows PC端用Putty连接该设备已经验证该功能正常。
我在手机端基于Xamarin移植的UsbSerialForAndroid编写代码,代码可以成功识别设备并打开端口,但收不到任何设备发来的串口数据。具体表现为UsbSerialPort.Read方法内的UsbDeviceConnection.RequestWait调用会永久阻塞,如果把RequestWait替换为1秒休眠操作,缓冲区里也没有任何数据。我也试过UsbSerialForAndroid支持的所有协议(包括CdcAcmSerialDriver等),都没有效果。另外手机会把Trinket识别为键盘设备,我原本认为这种情况不会影响其他代码访问该设备。
以下是简化后去掉错误检查的Xamarin代码:
usbManager = GetSystemService(Context.UsbService) as UsbManager; var usbDevice = usbManager.DeviceList.ToList()[0].Value; if (!usbManager.HasPermission(usbDevice)) usbManager.RequestPermission(usbDevice, null); var table = UsbSerialProber.DefaultProbeTable; var type = typeof(Hoho.Android.UsbSerial.Driver.CdcAcmSerialDriver); table.AddProduct(0x239A, 0x801F, type); var prober = new UsbSerialProber(table); var drivers = prober.FindAllDrivers(usbManager); var driver = drivers[0]; UsbDeviceConnection connection = usbManager.OpenDevice(driver.GetDevice()); var ports = driver.GetPorts(); // 多数设备只有一个端口(端口0) var port = ports[0]; // 多数设备只有一个端口(端口0) port.Open(connection); // 以下串口参数和我在Windows上用Putty成功连接的参数完全一致 port.SetParameters(9600, 8, Hoho.Android.UsbSerial.Driver.StopBits.One, UsbSerialPort.PARITY_NONE); var bytes = new byte[32]; var len = port.Read(bytes, 32); // <-- 代码执行到这里会卡在RequestWait调用上
排查与解决方案
- 处理系统设备占用问题:Android识别Trinket为键盘属于HID设备默认挂载,系统会优先持有该设备的访问权限,普通应用无法直接抢占,需要在
port.Open(connection)之后添加代码强制抢占设备接口:
// 强制抢占设备所有接口,第二个参数设为true代表忽略系统内核驱动的占用 for (int i = 0; i < usbDevice.InterfaceCount; i++) { connection.ClaimInterface(usbDevice.GetInterface(i), true); }
- 调整读操作超时配置:
UsbSerialPort.Read方法默认无超时参数时会永久阻塞,给读调用添加超时参数即可避免卡死:
// 第二个参数为超时时间,单位毫秒,这里设置1000毫秒超时 var len = port.Read(bytes, 1000);
- 修正端点识别配置:AdaFruit Trinket M0的USB串口使用TinyUSB实现的CDC-ACM协议,部分旧版UsbSerialForAndroid的CdcAcmSerialDriver对该设备的端点识别存在问题,可手动指定bulk in端点:
// 打开端口后手动设置输入端点,Trinket M0的CDC数据输入端点通常为0x81 var usbInterface = usbDevice.GetInterface(1); var inEndpoint = usbInterface.GetEndpoint(0); port.SetInEndpoint(inEndpoint);
- 禁用系统HID输入拦截:在AndroidManifest.xml中添加USB设备访问过滤配置,避免系统将设备识别为输入设备后分发给输入服务占用:
<activity ...> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity>
在res/xml目录下新建device_filter.xml,添加对应设备ID配置:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="9114" product-id="32799" /> <!-- 对应十六进制0x239A、0x801F --> </resources>
内容的提问来源于stack exchange,提问作者Digiproc
相关产品推荐
相关产品推荐

