# CH34 Flutter 插件使用手册 > 版本: 1.0.0 > 支持的芯片: CH340/CH341/CH342/CH343/CH344/CH347/CH9101/CH9102/CH9103/CH9104/CH9143 --- ## 目录 1. [简介](#一简介) 2. [安装与配置](#二安装与配置) 3. [快速开始](#三快速开始) 4. [API 参考](#四api-参考) 5. [类型定义](#五类型定义) 6. [完整示例](#六完整示例) 7. [常见问题](#七常见问题) --- ## 一、简介 `ch34` 是一个 Flutter 插件,为 WCH CH34X 系列 USB 转串口芯片提供 Flutter 接口支持。通过该插件,Flutter 应用可以与 CH34X 芯片进行串口通信。 ### 支持的芯片型号 | 芯片型号 | 说明 | |---------|------| | CH340 | 单串口 USB 转串口芯片 | | CH341 | 并口/串口 USB 转换芯片 | | CH342 | 双串口 USB 转串口芯片 | | CH343 | 增强型单串口芯片 | | CH344 | 四串口 USB 转串口芯片 | | CH347 | 高速 USB 转串口芯片 | | CH9101 | 单串口 USB 转串口芯片 | | CH9102 | 双串口 USB 转串口芯片 | | CH9103 | 四串口 USB 转串口芯片 | | CH9104 | 八串口 USB 转串口芯片 | | CH9143 | 多串口 USB 转串口芯片 | ### 系统要求 - Android 4.4 及以上版本 - 支持 USB Host 或 OTG 功能的 Android 设备 --- ## 二、安装与配置 ### 2.1 添加依赖 在 `pubspec.yaml` 中添加: ```yaml dependencies: ch34: ^1.0.0 ``` ### 2.2 导入插件 ```dart import 'package:ch34/ch34.dart'; ``` ### 2.3 初始化配置 在应用启动时进行初始化配置: ```dart // 开启调试模式(可选,默认关闭) await Ch34Manager.setDebug(true); // 设置全局读取超时时间(可选,默认 0 表示异步传输) await Ch34Manager.setReadTimeout(1000); // 1000ms ``` --- ## 三、快速开始 ### 3.1 基本使用流程 ```dart // 1. 枚举可用设备 final devices = await Ch34Manager.enumDevice(); if (devices.isEmpty) { print('未找到 CH34X 设备'); return; } // 2. 选择设备 final device = devices.first; print('找到设备: ${device.deviceName}, 芯片: ${device.chipType}'); // 3. 打开设备 final opened = await Ch34Manager.openDevice(device.deviceName); if (!opened) { print('打开设备失败'); return; } // 4. 设置串口参数 await Ch34Manager.setSerialParameter( device.deviceName, 0, // 串口号(从 0 开始) const SerialParameter( baud: 9600, dataBits: DataBits.bits8, stopBits: StopBits.one, parity: Parity.none, hardwareFlowControl: false, ), ); // 5. 注册数据回调 Ch34Manager.registerDataCallback( device.deviceName, 0, (data) { print('收到数据: $data'); }, ); // 6. 发送数据 await Ch34Manager.writeData( device.deviceName, 0, Uint8List.fromList([0x01, 0x02, 0x03]), ); // 7. 使用完毕后断开 await Ch34Manager.disconnect(device.deviceName); ``` ### 3.2 权限处理 ```dart // 请求 USB 设备权限 final granted = await Ch34Manager.requestPermission(device.deviceName); if (!granted) { print('USB 权限被拒绝'); return; } ``` --- ## 四、API 参考 ### 4.1 基础方法 #### getPlatformVersion 获取平台版本。 ```dart static Future getPlatformVersion() ``` **返回值**: 平台版本字符串 --- ### 4.2 设备枚举与识别 #### enumDevice 枚举当前所有可用的 USB 设备。 ```dart static Future> enumDevice() ``` **返回值**: 可用 USB 设备列表 **异常**: `Ch34Exception` - 枚举失败时抛出 #### getChipType 获取指定设备的芯片型号。 ```dart static Future getChipType(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: 芯片型号字符串,`null` 表示无法识别 --- ### 4.3 设备打开与权限 #### openDevice 打开 USB 设备。 ```dart static Future openDevice(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: `true` 成功,`false` 失败 #### requestPermission 申请 USB 设备的权限。 ```dart static Future requestPermission(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: `true` 已授权,`false` 被拒绝 --- ### 4.4 USB 状态监听 #### setUsbStateListener 注册 USB 设备插拔状态监听。 ```dart static void setUsbStateListener( void Function(String deviceName, bool connected) onStateChanged, ) ``` **参数**: - `onStateChanged` - 状态变化回调函数 #### removeUsbStateListener 移除 USB 状态监听。 ```dart static void removeUsbStateListener() ``` --- ### 4.5 串口信息 #### getSerialCount 获取设备的串口数目。 ```dart static Future getSerialCount(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: 串口数目,`-1` 表示读取芯片型号失败 #### getSerialBaud 获取串口波特率(仅 CH9114 系列有效)。 ```dart static Future getSerialBaud(String deviceName, int serialNumber) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 **返回值**: 大于 0 表示波特率,小于 0 表示出错 #### getChipMasterFrequency 获取芯片主频(仅 CH9114 系列有效)。 ```dart static Future getChipMasterFrequency(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: 芯片主频信息对象 #### enableSerial 打开或关闭串口(仅 CH9114 系列有效)。 ```dart static Future enableSerial( String deviceName, int serialNumber, bool enable, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `enable` - `true` 打开,`false` 关闭 **返回值**: `true` 设置成功,`false` 设置失败 --- ### 4.6 串口参数设置 #### setSerialParameter 设置串口参数。 ```dart static Future setSerialParameter( String deviceName, int serialNumber, SerialParameter parameter, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号(从 0 开始) - `parameter` - 串口参数配置 **返回值**: `true` 设置成功,`false` 设置失败 --- ### 4.7 数据读写 #### writeData 发送串口数据(同步发送)。 ```dart static Future writeData( String deviceName, int serialNumber, Uint8List data, { int timeout = 0, }) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `data` - 要发送的数据 - `timeout` - 超时时间(毫秒),0 表示不超时 **返回值**: 实际发送的字节数 #### asyncWriteData 发送串口数据(异步发送)。 ```dart static Future asyncWriteData( String deviceName, int serialNumber, Uint8List data, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `data` - 要发送的数据 **说明**: 将数据加入缓存持续发送,不返回状态和结果 #### readData 阻塞读取串口数据。 ```dart static Future readData( String deviceName, int serialNumber, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 **返回值**: 读取到的数据 #### readDataWithTimeout 主动读取串口数据(带超时参数)。 ```dart static Future readDataWithTimeout( String deviceName, int serialNumber, int vTime, int vMin, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `vTime` - 等待时间(毫秒) - `vMin` - 读取的最小字节数 **返回值**: 读取到的数据 **读取行为说明**: - `vTime>0, vMin>0`: 阻塞直到读取到第一个字符后开始计时,时间到或已读够 vMin 个字符则返回 - `vTime>0, vMin=0`: 读到数据立即返回,否则最多等待 vTime - `vTime=0, vMin>0`: 一直阻塞直到读到 vMin 个字符后返回 #### registerDataCallback 注册串口数据回调。 ```dart static Future registerDataCallback( String deviceName, int serialNumber, void Function(Uint8List data) onData, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `onData` - 数据接收回调函数 **说明**: 注册后数据自动推送,不需要主动调用 `readData`。**推荐使用此方式接收数据**。 #### removeDataCallback 取消注册串口数据回调。 ```dart static void removeDataCallback(String deviceName) ``` **参数**: - `deviceName` - 设备名称 --- ### 4.8 连接状态 #### isConnected 判断 USB 设备是否已经连接。 ```dart static Future isConnected(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: `true` 已连接,`false` 未连接 #### getConnectedDevices 获取当前已经打开的设备列表。 ```dart static Future> getConnectedDevices() ``` **返回值**: 已打开的设备名称列表 --- ### 4.9 断开与关闭 #### disconnect 断开 USB 设备的连接。 ```dart static Future disconnect(String deviceName) ``` **参数**: - `deviceName` - 设备名称 #### close 释放资源,关闭所有串口设备。 ```dart static Future close() ``` --- ### 4.10 GPIO 功能 #### isSupportGpio 查询设备是否支持 GPIO 功能。 ```dart static Future isSupportGpio(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: `true` 支持,`false` 不支持 #### queryGpioCount 查询该 USB 设备的 GPIO 数目。 ```dart static Future queryGpioCount(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: GPIO 数目 #### queryGpioStatus 查询指定 GPIO 的状态。 ```dart static Future queryGpioStatus( String deviceName, int gpioIndex, ) ``` **参数**: - `deviceName` - 设备名称 - `gpioIndex` - GPIO 编号(从 0 开始) **返回值**: GPIO 状态 #### queryAllGpioStatus 查询所有 GPIO 状态。 ```dart static Future> queryAllGpioStatus(String deviceName) ``` **参数**: - `deviceName` - 设备名称 **返回值**: 全部 GPIO 状态列表 #### enableGpio 使能指定 GPIO。 ```dart static Future enableGpio( String deviceName, int gpioIndex, bool enable, GpioDirection direction, ) ``` **参数**: - `deviceName` - 设备名称 - `gpioIndex` - GPIO 编号 - `enable` - `true` 使能,`false` 关闭 - `direction` - GPIO 方向 **返回值**: `true` 使能成功,`false` 使能失败 #### setGpioVal 设置指定 GPIO 的电平值。 ```dart static Future setGpioVal( String deviceName, int gpioIndex, GpioValue value, ) ``` **参数**: - `deviceName` - 设备名称 - `gpioIndex` - GPIO 编号 - `value` - GPIO 电平值 **返回值**: `true` 设置成功,`false` 设置失败 #### getGpioVal 获取指定 GPIO 的电平值。 ```dart static Future getGpioVal( String deviceName, int gpioIndex, ) ``` **参数**: - `deviceName` - 设备名称 - `gpioIndex` - GPIO 编号 **返回值**: GPIO 电平值 --- ### 4.11 信号控制 #### setDtr 设置 DTR 信号。 ```dart static Future setDtr( String deviceName, int serialNumber, bool valid, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `valid` - 是否有效(低电平有效) **返回值**: `true` 设置成功,`false` 设置失败 #### setRts 设置 RTS 信号。 ```dart static Future setRts( String deviceName, int serialNumber, bool valid, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `valid` - 是否有效(低电平有效) **返回值**: `true` 设置成功,`false` 设置失败 #### setBreakSignal 设置 Break 信号。 ```dart static Future setBreakSignal( String deviceName, int serialNumber, bool valid, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `valid` - 是否有效(低电平有效) **返回值**: `true` 设置成功,`false` 设置失败 --- ### 4.12 Modem 状态回调 #### registerModemStatusCallback 注册 Modem 控制信号状态回调。 ```dart static Future registerModemStatusCallback( String deviceName, void Function(ModemStatus status) onModemStatus, ) ``` **参数**: - `deviceName` - 设备名称 - `onModemStatus` - Modem 状态变化回调 #### removeModemStatusCallback 移除 Modem 状态回调。 ```dart static void removeModemStatusCallback(String deviceName) ``` **参数**: - `deviceName` - 设备名称 --- ### 4.13 错误查询 #### querySerialErrorCount 查询串口错误状态。 ```dart static Future querySerialErrorCount( String deviceName, int serialNumber, SerialErrorType errorType, ) ``` **参数**: - `deviceName` - 设备名称 - `serialNumber` - 串口号 - `errorType` - 错误类型 **返回值**: 该种错误出现的次数 --- ### 4.14 全局配置 #### setReadTimeout 设置读取超时时间。 ```dart static Future setReadTimeout(int timeout) ``` **参数**: - `timeout` - 超时时间(毫秒) **说明**: 全局有效,应在 APP 初始化时调用 #### addNewHardware 添加自定义硬件 VID/PID。 ```dart static Future addNewHardware( int vid, int pid, { String? chipType, }) ``` **参数**: - `vid` - 硬件 VID - `pid` - 硬件 PID - `chipType` - 芯片类型(可选) #### setDebug 设置调试模式。 ```dart static Future setDebug(bool enabled) ``` **参数**: - `enabled` - `true` 开启,`false` 关闭 #### isDebugMode 返回当前是否处于调试模式。 ```dart static Future isDebugMode() ``` **返回值**: `true` 处于调试模式,`false` 不处于 --- ## 五、类型定义 ### 5.1 枚举类型 #### DataBits - 数据位 ```dart enum DataBits { bits5(5), // 5 位数据位 bits6(6), // 6 位数据位 bits7(7), // 7 位数据位 bits8(8); // 8 位数据位(默认) } ``` #### StopBits - 停止位 ```dart enum StopBits { one(1), // 1 位停止位(默认) two(2), // 2 位停止位 } ``` #### Parity - 校验位 ```dart enum Parity { none(0), // 无校验(默认) odd(1), // 奇校验 even(2), // 偶校验 mark(3), // 标记校验 space(4), // 空格校验 } ``` #### GpioDirection - GPIO 方向 ```dart enum GpioDirection { inDir, // 输入方向 outDir, // 输出方向 } ``` #### GpioValue - GPIO 电平值 ```dart enum GpioValue { low, // 低电平 high, // 高电平 } ``` #### SerialErrorType - 串口错误类型 ```dart enum SerialErrorType { framingError, // 帧错误 parityError, // 校验错误 overrunError, // 溢出错误 breakInterrupt, // 中断错误 } ``` ### 5.2 数据类 #### SerialParameter - 串口参数 ```dart class SerialParameter { const SerialParameter({ this.baud = 115200, // 波特率 this.dataBits = DataBits.bits8, // 数据位 this.stopBits = StopBits.one, // 停止位 this.parity = Parity.none, // 校验位 this.hardwareFlowControl = false, // 硬件流控 }); } ``` **常用波特率**: 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 #### UsbDeviceInfo - USB 设备信息 ```dart class UsbDeviceInfo { final String deviceName; // 设备名称 final int productId; // 产品 ID (PID) final int vendorId; // 厂商 ID (VID) final int serialCount; // 串口数量 final String? chipType; // 芯片型号(如果识别) } ``` #### GpioStatus - GPIO 状态 ```dart class GpioStatus { final int index; // GPIO 编号 final GpioDirection direction; // 方向 final GpioValue value; // 电平值 final bool enabled; // 是否已使能 } ``` #### ChipMasterFrequency - 芯片主频 ```dart class ChipMasterFrequency { final int frequency; // 芯片主频(Hz) final bool switchEnable; // 是否允许切换主频 final int coStatus; // 晶振状态 } ``` #### ModemStatus - Modem 状态 ```dart class ModemStatus { final bool cts; // Clear To Send final bool dsr; // Data Set Ready final bool ri; // Ring Indicator final bool dcd; // Data Carrier Detect } ``` --- ## 六、完整示例 ### 6.1 设备扫描与管理 ```dart import 'package:ch34/ch34.dart'; import 'dart:typed_data'; class SerialDeviceManager { static List _devices = []; /// 扫描并连接设备 static Future scanAndConnect() async { try { // 获取已连接设备 final connected = await Ch34Manager.getConnectedDevices(); print('已连接设备: $connected'); // 枚举所有可用设备 _devices = await Ch34Manager.enumDevice(); print('发现 ${_devices.length} 个设备'); for (final device in _devices) { print('设备: ${device.deviceName}'); print(' VID: 0x${device.vendorId.toRadixString(16).toUpperCase().padLeft(4, '0')}'); print(' PID: 0x${device.productId.toRadixString(16).toUpperCase().padLeft(4, '0')}'); print(' 芯片: ${device.chipType ?? "未知"}'); print(' 串口数: ${device.serialCount}'); } } on Ch34Exception catch (e) { print('设备扫描失败: $e'); } } } ``` ### 6.2 串口通信封装 ```dart class SerialPortService { String? _deviceName; int _serialNumber = 0; bool _isConnected = false; /// 打开串口 Future open(String deviceName, {int baudRate = 9600}) async { try { // 请求权限 final granted = await Ch34Manager.requestPermission(deviceName); if (!granted) { return false; } // 打开设备 final opened = await Ch34Manager.openDevice(deviceName); if (!opened) { return false; } // 设置参数 final configured = await Ch34Manager.setSerialParameter( deviceName, _serialNumber, SerialParameter( baud: baudRate, dataBits: DataBits.bits8, stopBits: StopBits.one, parity: Parity.none, ), ); if (!configured) { await Ch34Manager.disconnect(deviceName); return false; } _deviceName = deviceName; _isConnected = true; return true; } catch (e) { print('打开串口失败: $e'); return false; } } /// 关闭串口 Future close() async { if (_deviceName != null) { Ch34Manager.removeDataCallback(_deviceName!); await Ch34Manager.disconnect(_deviceName!); _deviceName = null; _isConnected = false; } } /// 发送数据 Future send(List data) async { if (!_isConnected || _deviceName == null) { throw StateError('串口未连接'); } return await Ch34Manager.writeData( _deviceName!, _serialNumber, Uint8List.fromList(data), ); } /// 异步发送数据 Future sendAsync(List data) async { if (!_isConnected || _deviceName == null) { throw StateError('串口未连接'); } await Ch34Manager.asyncWriteData( _deviceName!, _serialNumber, Uint8List.fromList(data), ); } /// 注册数据接收回调 Future onData(void Function(Uint8List data) callback) async { if (_deviceName == null) { throw StateError('串口未连接'); } await Ch34Manager.registerDataCallback( _deviceName!, _serialNumber, callback, ); } /// 获取连接状态 bool get isConnected => _isConnected; } ``` ### 6.3 GPIO 控制示例 ```dart class GpioController { /// 配置并设置 GPIO static Future configureGpio( String deviceName, int gpioIndex, ) async { // 检查是否支持 GPIO final supported = await Ch34Manager.isSupportGpio(deviceName); if (!supported) { print('设备不支持 GPIO 功能'); return; } // 查询 GPIO 数量 final count = await Ch34Manager.queryGpioCount(deviceName); if (gpioIndex >= count) { print('GPIO 编号超出范围'); return; } // 使能 GPIO(输出方向) final enabled = await Ch34Manager.enableGpio( deviceName, gpioIndex, true, GpioDirection.outDir, ); if (!enabled) { print('使能 GPIO 失败'); return; } // 设置高电平 await Ch34Manager.setGpioVal( deviceName, gpioIndex, GpioValue.high, ); // 读取电平值验证 final value = await Ch34Manager.getGpioVal(deviceName, gpioIndex); print('GPIO $gpioIndex 电平: $value'); } /// 查询所有 GPIO 状态 static Future printAllGpioStatus(String deviceName) async { final statuses = await Ch34Manager.queryAllGpioStatus(deviceName); for (final status in statuses) { print('GPIO ${status.index}: ' '方向=${status.direction}, ' '电平=${status.value}, ' '使能=${status.enabled}'); } } } ``` ### 6.4 Modem 状态监听 ```dart class ModemMonitor { static Future startMonitoring(String deviceName) async { await Ch34Manager.registerModemStatusCallback( deviceName, (ModemStatus status) { print('Modem 状态变化:'); print(' CTS: ${status.cts}'); print(' DSR: ${status.dsr}'); print(' RI: ${status.ri}'); print(' DCD: ${status.dcd}'); }, ); } static void stopMonitoring(String deviceName) { Ch34Manager.removeModemStatusCallback(deviceName); } } ``` ### 6.5 错误检测 ```dart class ErrorDetector { static Future checkErrors(String deviceName, int serialNumber) async { final framingErrors = await Ch34Manager.querySerialErrorCount( deviceName, serialNumber, SerialErrorType.framingError, ); final parityErrors = await Ch34Manager.querySerialErrorCount( deviceName, serialNumber, SerialErrorType.parityError, ); print('帧错误: $framingErrors, 校验错误: $parityErrors'); } } ``` --- ## 七、常见问题 ### 7.1 找不到设备 **问题**: `enumDevice()` 返回空列表 **解决方法**: 1. 确认设备支持 USB Host/OTG 功能 2. 检查 USB 线缆连接是否正常 3. 确认 Android 设备已开启 OTG 功能(部分手机需要手动开启) 4. 检查芯片是否在支持列表中 ### 7.2 权限被拒绝 **问题**: `requestPermission()` 返回 `false` **解决方法**: 1. 确保在 `AndroidManifest.xml` 中添加了 USB 权限声明 2. 用户需要在系统弹窗中点击"允许" 3. 可以尝试重新请求权限 ### 7.3 数据收发异常 **问题**: 发送数据成功但收不到回复 **解决方法**: 1. 确认串口参数(波特率、数据位、停止位、校验位)与对端设备一致 2. 使用 `registerDataCallback` 代替 `readData`,推荐回调方式接收数据 3. 检查线缆连接和电平匹配(3.3V vs 5V) 4. 使用 `querySerialErrorCount` 检查是否有串口错误 ### 7.4 多串口设备使用 **问题**: CH344/CH9104 等多串口设备如何使用 **解决方法**: ```dart // 多串口设备每个串口都需要单独配置 for (int i = 0; i < device.serialCount; i++) { await Ch34Manager.setSerialParameter( device.deviceName, i, // 不同的串口号 const SerialParameter(baud: 9600), ); Ch34Manager.registerDataCallback( device.deviceName, i, (data) { print('串口 $i 收到数据: $data'); }, ); } ``` ### 7.5 设备热插拔处理 **问题**: 设备拔出后如何检测 **解决方法**: ```dart // 注册 USB 状态监听 Ch34Manager.setUsbStateListener((deviceName, connected) { if (!connected) { print('设备 $deviceName 已拔出'); // 清理资源 Ch34Manager.removeDataCallback(deviceName); } else { print('设备 $deviceName 已插入'); // 重新打开设备 } }); ``` ### 7.6 自定义 VID/PID **问题**: 如何支持非标准 VID/PID 的设备 **解决方法**: ```dart // 添加自定义硬件 await Ch34Manager.addNewHardware( 0x1234, // 自定义 VID 0x5678, // 自定义 PID chipType: 'CH340', // 可选:指定芯片类型 ); ``` --- ## 附录:通信通道说明 插件内部使用以下通道与 Android 原生端通信: | 通道类型 | 名称 | 用途 | |---------|------|------| | MethodChannel | `ch34` | 双向方法调用 | | EventChannel | `ch34/data` | 串口数据推送 | | EventChannel | `ch34/modem` | Modem 状态变化 | | EventChannel | `ch34/usb_state` | USB 插拔状态 | --- *文档版本: 1.0.0* *插件版本: 1.0.0*