Android USB通讯(host转串口)

打印 上一主题 下一主题

主题 996|帖子 996|积分 2988

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
前言:公司属于北斗通讯行业,项目大多都需要和各式各样的硬件装备相结合来满足项目需求,因此所涉及到的各种技能也相对比较冷门。前段时间有个项目用到了一款定制 Android 装备,此中有多个接口,包罗两个 USB 接口和一个 RS232 串口,需要用到此中一个 USB 毗连北斗装备实现指令互通,经过探索现在也大抵相识了 Android USB(host)通讯流程后续还有另一个项目则用到了 USB(accessory)有空再写一篇。在这里记录和分享一下,有错漏或可优化之处欢迎大家留言。
一、导入模块

工具基于 github 上的串口工具库:usb-serial-for-android
1. 在项目级 build.gradle 文件添加 jitpack.io 库

  1. maven { url 'https://jitpack.io' }
复制代码
2. 添加依赖

  1. implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
  2. 可能有更新的版本自行修改
复制代码
3. 本地

2023年11月30日更新:近来把代码拉抵家里的电脑准备在家办公时恰恰停网了导致第三方库导入不了,于是就到工具库源码上把代码都拉到了本地,如果你也恰恰遇到了这种环境也可以如许处理。这里随手打包了一份,本身重新导入一下即可:https://download.csdn.net/download/lxt1292352578/88565529

二、上代码

DEMO:https://github.com/LXTTTTTT/USBtoSerialPortDemo
源码资源:https://download.csdn.net/download/lxt1292352578/88717549
复制再把报红的部分直接去掉或者换成本身的就能直接利用
  1. package com.example.SecondProject.Utils.Transfer.USB;
  2. import android.app.PendingIntent;
  3. import android.content.BroadcastReceiver;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.IntentFilter;
  7. import android.hardware.usb.UsbDeviceConnection;
  8. import android.hardware.usb.UsbManager;
  9. import android.os.Build;
  10. import android.util.Log;
  11. import android.widget.Toast;
  12. import com.example.SecondProject.Base.MainApplication;
  13. import com.example.SecondProject.BuildConfig;
  14. import com.example.SecondProject.Global.Constant;
  15. import com.example.SecondProject.Global.Variable;
  16. import com.example.SecondProject.Utils.DataUtil;
  17. import com.example.SecondProject.Utils.NotificationCenter;
  18. import com.example.SecondProject.Utils.ProtocolUtil;
  19. import com.hoho.android.usbserial.driver.UsbSerialDriver;
  20. import com.hoho.android.usbserial.driver.UsbSerialPort;
  21. import com.hoho.android.usbserial.driver.UsbSerialProber;
  22. import com.hoho.android.usbserial.util.SerialInputOutputManager;
  23. import java.io.IOException;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. // usb 数据传输工具
  27. public class USBTransferUtil {
  28.     private String TAG = "USBTransferUtil";
  29.     private MainApplication APP = MainApplication.getInstance();  // 主程序,替换为你自己的
  30.     private UsbManager manager = (UsbManager) APP.getSystemService(Context.USB_SERVICE);  // usb管理器
  31.     private BroadcastReceiver usbReceiver;  // 广播监听:判断usb设备授权操作
  32.     private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".INTENT_ACTION_GRANT_USB";  // usb权限请求标识
  33.     private final String IDENTIFICATION = " USB-Serial Controller D";  // 目标设备标识
  34.     private List<UsbSerialDriver> availableDrivers = new ArrayList<>();  // 所有可用设备
  35.     private UsbSerialDriver usbSerialDriver;  // 当前连接的设备
  36.     private UsbDeviceConnection usbDeviceConnection;  // 连接对象
  37.     private UsbSerialPort usbSerialPort;  // 设备端口对象,通过这个读写数据
  38.     private SerialInputOutputManager inputOutputManager;  // 数据输入输出流管理器
  39. // 连接参数,按需求自行修改 ---------
  40.     private int baudRate = 115200;  // 波特率
  41.     private int dataBits = 8;  // 数据位
  42.     private int stopBits = UsbSerialPort.STOPBITS_1;  // 停止位
  43.     private int parity = UsbSerialPort.PARITY_NONE;// 奇偶校验
  44. // 单例 -------------------------
  45.     private static USBTransferUtil usbTransferUtil;
  46.     public static synchronized USBTransferUtil getInstance() {
  47.         if(usbTransferUtil == null){
  48.             usbTransferUtil = new USBTransferUtil();
  49.         }
  50.         return usbTransferUtil;
  51.     }
  52.     public void connect(){
  53.         // “Variable.isConnectUSB” 我的变量标识,自行删除或修改
  54.         if(!Variable.isConnectUSB){
  55.             registerReceiver();  // 注册广播监听
  56.             refreshDevice();  // 拿到已连接的usb设备列表
  57.             connectDevice();  // 建立连接
  58.         }
  59.     }
  60.     // 注册usb授权监听广播
  61.     public void registerReceiver(){
  62.         usbReceiver = new BroadcastReceiver() {
  63.             @Override
  64.             public void onReceive(Context context, Intent intent) {
  65.                 if(INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
  66.                     // 授权操作完成,连接
  67. //                    boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);  // 不知为何获取到的永远都是 false 因此无法判断授权还是拒绝
  68.                     connectDevice();
  69.                 }
  70.             }
  71.         };
  72.         APP.registerReceiver(usbReceiver,new IntentFilter(INTENT_ACTION_GRANT_USB));
  73.     }
  74.     // 刷新当前可用 usb设备
  75.     public void refreshDevice(){
  76.         availableDrivers.clear();
  77.         availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
  78.         Log.e(TAG, "当前可用 usb 设备数量: " + availableDrivers.size() );
  79.         // 有设备可以连接
  80.         if(availableDrivers.size() != 0){
  81.             // 当时开发用的是定制平板电脑有 2 个usb口,所以搜索到两个
  82.             if(availableDrivers.size()>1){
  83.                 for (int i = 0; i < availableDrivers.size(); i++) {
  84.                     UsbSerialDriver availableDriver = availableDrivers.get(i);
  85.                     // 我是通过 ProductName 这个参数来识别我要连接的设备
  86.                     if(availableDriver.getDevice().getProductName().equals(IDENTIFICATION)){
  87.                         usbSerialDriver = availableDriver;
  88.                     }
  89.                 }
  90.             }
  91.             // 通常手机只有充电口 1 个
  92.             else {
  93.                 usbSerialDriver = availableDrivers.get(0);
  94.             }
  95.             usbSerialPort = usbSerialDriver.getPorts().get(0);  // 一般设备的端口都只有一个,具体要参考设备的说明文档
  96.             // 同时申请设备权限
  97.             if(!manager.hasPermission(usbSerialDriver.getDevice())){
  98.                 int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0;
  99.                 PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(APP, 0, new Intent(INTENT_ACTION_GRANT_USB), flags);
  100.                 manager.requestPermission(usbSerialDriver.getDevice(), usbPermissionIntent);
  101.             }
  102.         }
  103.         // 没有设备
  104.         else {
  105.             APP.showToast("请先接入设备",0);
  106.         }
  107.     }
  108.     // 连接设备
  109.     public void connectDevice(){
  110.         if(usbSerialDriver == null || inputOutputManager != null){return;}
  111.         // 判断是否拥有权限
  112.         boolean hasPermission = manager.hasPermission(usbSerialDriver.getDevice());
  113.         if(hasPermission){
  114.             usbDeviceConnection = manager.openDevice(usbSerialDriver.getDevice());  // 拿到连接对象
  115.             if(usbSerialPort == null){return;}
  116.             try {
  117.                 usbSerialPort.open(usbDeviceConnection);  // 打开串口
  118.                 usbSerialPort.setParameters(baudRate, dataBits, stopBits, parity);  // 设置串口参数:波特率 - 115200 , 数据位 - 8 , 停止位 - 1 , 奇偶校验 - 无
  119.                 startReceiveData();  // 开启读数据线程
  120.             } catch (IOException e) {
  121.                 e.printStackTrace();
  122.             }
  123.         }else {
  124.             APP.showToast("请先授予权限再连接",0);
  125.         }
  126.     }
  127.     // 开启数据接收监听
  128.     public void startReceiveData(){
  129.         if(usbSerialPort == null || !usbSerialPort.isOpen()){return;}
  130.         inputOutputManager = new SerialInputOutputManager(usbSerialPort, new SerialInputOutputManager.Listener() {
  131.             @Override
  132.             public void onNewData(byte[] data) {
  133.                 // 在这里处理接收到的 usb 数据
  134.                 String data_str = DataUtil.bytes2string(data).toUpperCase();
  135.                 Log.e(TAG, "收到 usb 数据: " + data_str);
  136.             }
  137.             @Override
  138.             public void onRunError(Exception e) {
  139.                 Log.e(TAG, "usb 断开了" );
  140.                 disconnect();
  141.                 e.printStackTrace();
  142.             }
  143.         });
  144.         inputOutputManager.start();
  145.         Variable.isConnectUSB = true;  // 修改连接标识
  146.         NotificationCenter.standard().postNotification(Constant.CONNECT_USB);  // 发送全局广播
  147.         APP.showToast("连接成功" ,Toast.LENGTH_SHORT);
  148.     }
  149.     // 下发数据
  150.     public void write(String data_hex){
  151.         if(usbSerialPort != null){
  152.             Log.e(TAG, "当前usb状态: isOpen-" + usbSerialPort.isOpen() );
  153.             // 当串口打开时再下发
  154.             if(usbSerialPort.isOpen()){
  155.                 byte[] data_bytes = DataUtil.hex2bytes(data_hex);  // 将字符数据转化为 byte[]
  156.                 if (data_bytes == null || data_bytes.length == 0) return;
  157.                 try {
  158.                     usbSerialPort.write(data_bytes,100);  // 写入数据
  159.                 } catch (IOException e) {
  160.                     e.printStackTrace();
  161.                 }
  162.             }else {
  163.                 APP.showToast(" usb 未连接" ,Toast.LENGTH_SHORT);
  164.             }
  165.         }
  166.     }
  167.     // 断开连接
  168.     public void disconnect(){
  169.         try{
  170.             // 停止数据接收监听
  171.             if(inputOutputManager != null){
  172.                 inputOutputManager.stop();
  173.                 inputOutputManager = null;
  174.             }
  175.             // 关闭端口
  176.             if(usbSerialPort != null){
  177.                 usbSerialPort.close();
  178.                 usbSerialPort = null;
  179.             }
  180.             // 关闭连接
  181.             if(usbDeviceConnection != null){
  182.                 usbDeviceConnection.close();
  183.                 usbDeviceConnection = null;
  184.             }
  185.             // 清除设备
  186.             if(usbSerialDriver != null){
  187.                 usbSerialDriver = null;
  188.             }
  189.             // 清空设备列表
  190.             availableDrivers.clear();
  191.             // 注销广播监听
  192.             APP.unregisterReceiver(usbReceiver);
  193.             Variable.isConnectUSB = false;  // 修改标识
  194.             NotificationCenter.standard().postNotification(Constant.DISCONNECT_USB);  // 发送广播
  195.             APP.showToast("断开连接",0);
  196.         }catch (Exception e){
  197.             e.printStackTrace();
  198.         }
  199.     }
  200.     // 下发初始化指令
  201.     public void init_device(){
  202.         try {
  203.             Thread.sleep(500);
  204.         } catch (InterruptedException e) {
  205.             e.printStackTrace();
  206.         }
  207.         write(ProtocolUtil.CCICR(0,"00"));  // 查询 IC 信息
  208.     }
  209. }
复制代码
三、利用例子

1. 利用场景

原本项目装备利用的是左边这种标准的 USB 转串口线毗连北斗装备,但是现在手头没有当时的装备,恰恰有一条右图这种 type-c 转串口线,那就用手机和电脑毗连模仿利用场景吧

毗连之后是这个样子的

手机接口被占用了,这时可以利用无线调试,附个无线调试教程:https://blog.csdn.net/lxt1292352578/article/details/131954052
2. 利用案例

电脑需要准备一个串口调试工具,注意需要把串口参数设置为相同
附个串口调试工具:https://download.csdn.net/download/lxt1292352578/88595528

直接调用毗连装备方法:USBTransferUtil.getInstance().connect();

注意初次毗连需要手动授权

毗连成功后在电脑上利用串口调试助手发送数据模仿互通过程
下发数据方法:USBTransferUtil.getInstance().write("363636");
由于我下发北斗指令时利用的是16进制字符,有其他需求自行在方法里面修改

吸收数据的处理:直接在工具类内部处理,不知道是由于装备的不同照旧转接线的不同,这里吸收到的数据是碎片化的而不像我在开发时收到的是完备的数据,恰恰之前开发串口毗连北斗装备时也是这种碎片化数据,之后有空的时候顺便做一个北斗协议拼接/剖析教程吧

互通成功

3. 装备接入监听

Android体系在每次拔插 USB 装备时都会广播一个意图,如许如果我们需要在 USB 装备毗连时进行某种操纵只需要在 manifest 文件里面给对应的 activty 添加一个声明并指定过滤规则即可
<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" />

在xml资源文件夹中添加 device_filter 文件

附上过滤规则文件代码:这里包含了大部分USB装备的厂商的厂商ID和产品ID,如有别的需求自行增长
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources xmlns:tools="http://schemas.android.com/tools"
  3.     tools:ignore="MissingDefaultResource">
  4.     <!-- 0x0403 / 0x60??: FTDI -->
  5.     <usb-device vendor-id="1027" product-id="24577" /> <!-- 0x6001: FT232R -->
  6.     <usb-device vendor-id="1027" product-id="24592" /> <!-- 0x6010: FT2232H -->
  7.     <usb-device vendor-id="1027" product-id="24593" /> <!-- 0x6011: FT4232H -->
  8.     <usb-device vendor-id="1027" product-id="24596" /> <!-- 0x6014: FT232H -->
  9.     <usb-device vendor-id="1027" product-id="24597" /> <!-- 0x6015: FT230X, FT231X, FT234XD -->
  10.     <!-- 0x10C4 / 0xEA??: Silabs CP210x -->
  11.     <usb-device vendor-id="4292" product-id="60000" /> <!-- 0xea60: CP2102 and other CP210x single port devices -->
  12.     <usb-device vendor-id="4292" product-id="60016" /> <!-- 0xea70: CP2105 -->
  13.     <usb-device vendor-id="4292" product-id="60017" /> <!-- 0xea71: CP2108 -->
  14.     <!-- 0x067B / 0x23?3: Prolific PL2303x -->
  15.     <usb-device vendor-id="1659" product-id="8963" /> <!-- 0x2303: PL2303HX, HXD, TA, ... -->
  16.     <usb-device vendor-id="1659" product-id="9123" /> <!-- 0x23a3: PL2303GC -->
  17.     <usb-device vendor-id="1659" product-id="9139" /> <!-- 0x23b3: PL2303GB -->
  18.     <usb-device vendor-id="1659" product-id="9155" /> <!-- 0x23c3: PL2303GT -->
  19.     <usb-device vendor-id="1659" product-id="9171" /> <!-- 0x23d3: PL2303GL -->
  20.     <usb-device vendor-id="1659" product-id="9187" /> <!-- 0x23e3: PL2303GE -->
  21.     <usb-device vendor-id="1659" product-id="9203" /> <!-- 0x23f3: PL2303GS -->
  22.     <!-- 0x1a86 / 0x?523: Qinheng CH34x -->
  23.     <usb-device vendor-id="6790" product-id="21795" /> <!-- 0x5523: CH341A -->
  24.     <usb-device vendor-id="6790" product-id="29987" /> <!-- 0x7523: CH340 -->
  25.     <!-- CDC driver -->
  26.     <usb-device vendor-id="9025" />                   <!-- 0x2341 / ......: Arduino -->
  27.     <usb-device vendor-id="5824" product-id="1155" /> <!-- 0x16C0 / 0x0483: Teensyduino  -->
  28.     <usb-device vendor-id="1003" product-id="8260" /> <!-- 0x03EB / 0x2044: Atmel Lufa -->
  29.     <usb-device vendor-id="7855" product-id="4"    /> <!-- 0x1eaf / 0x0004: Leaflabs Maple -->
  30.     <usb-device vendor-id="3368" product-id="516"  /> <!-- 0x0d28 / 0x0204: ARM mbed -->
  31.     <usb-device vendor-id="1155" product-id="22336" /><!-- 0x0483 / 0x5740: ST CDC -->
  32.     <usb-device vendor-id="11914" product-id="5"   /> <!-- 0x2E8A / 0x0005: Raspberry Pi Pico Micropython -->
  33.     <usb-device vendor-id="11914" product-id="10"  /> <!-- 0x2E8A / 0x000A: Raspberry Pi Pico SDK -->
  34.     <usb-device vendor-id="6790" product-id="21972" /><!-- 0x1A86 / 0x55D4: Qinheng CH9102F -->
  35. </resources>
复制代码
通过声明以上的intent-filter和meta-data,表明它是一个能够处理USB装备毗连事故的Activity,而且根据res/xml/device_filter.xml中的规则对毗连的USB装备进行过滤和处理。如许,在Android装备毗连USB装备时,体系就会将相应的事故发送给Activity,从而实现对USB装备的交互和数据处理。比如我在这个 activity 的 onResume 生命周期添加毗连USB装备装备操纵,如许当体系发送了USB装备接入通知并授权成功后就能直接跳转到这个 activity 并进行后续操纵,简朴粗暴。
体系监测到 USB 装备接入并发送通知,授权并跳转至对应 activity :

四、小结

整个毗连流程大抵是如许的:
获取当前体系可用的 USB 装备列表 → 选中对应的USB装备并申请权限(初次)→ 获取装备端口(通常只有一个)→ 按照特定参数打开端口
具体操纵参考代码,参考的项目是上文提到的工具库官方demo:
https://github.com/mik3y/usb-serial-for-android/tree/master
我的demo:https://github.com/LXTTTTTT/USBtoSerialPortDemo
源码资源:https://download.csdn.net/download/lxt1292352578/88717549
原工具库官方demo代码比较复杂,我只是按照本身的需求把对应的部分整理出来以是比较简朴。由于这个功能用的不多以是也没有做过多的封装几乎所有的毗连/通讯操纵都直接写在工具类里面,如果有需求自行改造即可。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表