24 KiB
CH34 Flutter 插件使用手册
版本: 1.0.0
支持的芯片: CH340/CH341/CH342/CH343/CH344/CH347/CH9101/CH9102/CH9103/CH9104/CH9143
目录
一、简介
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 中添加:
dependencies:
ch34: ^1.0.0
2.2 导入插件
import 'package:ch34/ch34.dart';
2.3 初始化配置
在应用启动时进行初始化配置:
// 开启调试模式(可选,默认关闭)
await Ch34Manager.setDebug(true);
// 设置全局读取超时时间(可选,默认 0 表示异步传输)
await Ch34Manager.setReadTimeout(1000); // 1000ms
三、快速开始
3.1 基本使用流程
// 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 权限处理
// 请求 USB 设备权限
final granted = await Ch34Manager.requestPermission(device.deviceName);
if (!granted) {
print('USB 权限被拒绝');
return;
}
四、API 参考
4.1 基础方法
getPlatformVersion
获取平台版本。
static Future<String?> getPlatformVersion()
返回值: 平台版本字符串
4.2 设备枚举与识别
enumDevice
枚举当前所有可用的 USB 设备。
static Future<List<UsbDeviceInfo>> enumDevice()
返回值: 可用 USB 设备列表
异常: Ch34Exception - 枚举失败时抛出
getChipType
获取指定设备的芯片型号。
static Future<String?> getChipType(String deviceName)
参数:
deviceName- 设备名称
返回值: 芯片型号字符串,null 表示无法识别
4.3 设备打开与权限
openDevice
打开 USB 设备。
static Future<bool> openDevice(String deviceName)
参数:
deviceName- 设备名称
返回值: true 成功,false 失败
requestPermission
申请 USB 设备的权限。
static Future<bool> requestPermission(String deviceName)
参数:
deviceName- 设备名称
返回值: true 已授权,false 被拒绝
4.4 USB 状态监听
setUsbStateListener
注册 USB 设备插拔状态监听。
static void setUsbStateListener(
void Function(String deviceName, bool connected) onStateChanged,
)
参数:
onStateChanged- 状态变化回调函数
removeUsbStateListener
移除 USB 状态监听。
static void removeUsbStateListener()
4.5 串口信息
getSerialCount
获取设备的串口数目。
static Future<int> getSerialCount(String deviceName)
参数:
deviceName- 设备名称
返回值: 串口数目,-1 表示读取芯片型号失败
getSerialBaud
获取串口波特率(仅 CH9114 系列有效)。
static Future<int> getSerialBaud(String deviceName, int serialNumber)
参数:
deviceName- 设备名称serialNumber- 串口号
返回值: 大于 0 表示波特率,小于 0 表示出错
getChipMasterFrequency
获取芯片主频(仅 CH9114 系列有效)。
static Future<ChipMasterFrequency> getChipMasterFrequency(String deviceName)
参数:
deviceName- 设备名称
返回值: 芯片主频信息对象
enableSerial
打开或关闭串口(仅 CH9114 系列有效)。
static Future<bool> enableSerial(
String deviceName,
int serialNumber,
bool enable,
)
参数:
deviceName- 设备名称serialNumber- 串口号enable-true打开,false关闭
返回值: true 设置成功,false 设置失败
4.6 串口参数设置
setSerialParameter
设置串口参数。
static Future<bool> setSerialParameter(
String deviceName,
int serialNumber,
SerialParameter parameter,
)
参数:
deviceName- 设备名称serialNumber- 串口号(从 0 开始)parameter- 串口参数配置
返回值: true 设置成功,false 设置失败
4.7 数据读写
writeData
发送串口数据(同步发送)。
static Future<int> writeData(
String deviceName,
int serialNumber,
Uint8List data, {
int timeout = 0,
})
参数:
deviceName- 设备名称serialNumber- 串口号data- 要发送的数据timeout- 超时时间(毫秒),0 表示不超时
返回值: 实际发送的字节数
asyncWriteData
发送串口数据(异步发送)。
static Future<void> asyncWriteData(
String deviceName,
int serialNumber,
Uint8List data,
)
参数:
deviceName- 设备名称serialNumber- 串口号data- 要发送的数据
说明: 将数据加入缓存持续发送,不返回状态和结果
readData
阻塞读取串口数据。
static Future<Uint8List> readData(
String deviceName,
int serialNumber,
)
参数:
deviceName- 设备名称serialNumber- 串口号
返回值: 读取到的数据
readDataWithTimeout
主动读取串口数据(带超时参数)。
static Future<Uint8List> readDataWithTimeout(
String deviceName,
int serialNumber,
int vTime,
int vMin,
)
参数:
deviceName- 设备名称serialNumber- 串口号vTime- 等待时间(毫秒)vMin- 读取的最小字节数
返回值: 读取到的数据
读取行为说明:
vTime>0, vMin>0: 阻塞直到读取到第一个字符后开始计时,时间到或已读够 vMin 个字符则返回vTime>0, vMin=0: 读到数据立即返回,否则最多等待 vTimevTime=0, vMin>0: 一直阻塞直到读到 vMin 个字符后返回
registerDataCallback
注册串口数据回调。
static Future<void> registerDataCallback(
String deviceName,
int serialNumber,
void Function(Uint8List data) onData,
)
参数:
deviceName- 设备名称serialNumber- 串口号onData- 数据接收回调函数
说明: 注册后数据自动推送,不需要主动调用 readData。推荐使用此方式接收数据。
removeDataCallback
取消注册串口数据回调。
static void removeDataCallback(String deviceName)
参数:
deviceName- 设备名称
4.8 连接状态
isConnected
判断 USB 设备是否已经连接。
static Future<bool> isConnected(String deviceName)
参数:
deviceName- 设备名称
返回值: true 已连接,false 未连接
getConnectedDevices
获取当前已经打开的设备列表。
static Future<List<String>> getConnectedDevices()
返回值: 已打开的设备名称列表
4.9 断开与关闭
disconnect
断开 USB 设备的连接。
static Future<void> disconnect(String deviceName)
参数:
deviceName- 设备名称
close
释放资源,关闭所有串口设备。
static Future<void> close()
4.10 GPIO 功能
isSupportGpio
查询设备是否支持 GPIO 功能。
static Future<bool> isSupportGpio(String deviceName)
参数:
deviceName- 设备名称
返回值: true 支持,false 不支持
queryGpioCount
查询该 USB 设备的 GPIO 数目。
static Future<int> queryGpioCount(String deviceName)
参数:
deviceName- 设备名称
返回值: GPIO 数目
queryGpioStatus
查询指定 GPIO 的状态。
static Future<GpioStatus> queryGpioStatus(
String deviceName,
int gpioIndex,
)
参数:
deviceName- 设备名称gpioIndex- GPIO 编号(从 0 开始)
返回值: GPIO 状态
queryAllGpioStatus
查询所有 GPIO 状态。
static Future<List<GpioStatus>> queryAllGpioStatus(String deviceName)
参数:
deviceName- 设备名称
返回值: 全部 GPIO 状态列表
enableGpio
使能指定 GPIO。
static Future<bool> enableGpio(
String deviceName,
int gpioIndex,
bool enable,
GpioDirection direction,
)
参数:
deviceName- 设备名称gpioIndex- GPIO 编号enable-true使能,false关闭direction- GPIO 方向
返回值: true 使能成功,false 使能失败
setGpioVal
设置指定 GPIO 的电平值。
static Future<bool> setGpioVal(
String deviceName,
int gpioIndex,
GpioValue value,
)
参数:
deviceName- 设备名称gpioIndex- GPIO 编号value- GPIO 电平值
返回值: true 设置成功,false 设置失败
getGpioVal
获取指定 GPIO 的电平值。
static Future<GpioValue> getGpioVal(
String deviceName,
int gpioIndex,
)
参数:
deviceName- 设备名称gpioIndex- GPIO 编号
返回值: GPIO 电平值
4.11 信号控制
setDtr
设置 DTR 信号。
static Future<bool> setDtr(
String deviceName,
int serialNumber,
bool valid,
)
参数:
deviceName- 设备名称serialNumber- 串口号valid- 是否有效(低电平有效)
返回值: true 设置成功,false 设置失败
setRts
设置 RTS 信号。
static Future<bool> setRts(
String deviceName,
int serialNumber,
bool valid,
)
参数:
deviceName- 设备名称serialNumber- 串口号valid- 是否有效(低电平有效)
返回值: true 设置成功,false 设置失败
setBreakSignal
设置 Break 信号。
static Future<bool> setBreakSignal(
String deviceName,
int serialNumber,
bool valid,
)
参数:
deviceName- 设备名称serialNumber- 串口号valid- 是否有效(低电平有效)
返回值: true 设置成功,false 设置失败
4.12 Modem 状态回调
registerModemStatusCallback
注册 Modem 控制信号状态回调。
static Future<void> registerModemStatusCallback(
String deviceName,
void Function(ModemStatus status) onModemStatus,
)
参数:
deviceName- 设备名称onModemStatus- Modem 状态变化回调
removeModemStatusCallback
移除 Modem 状态回调。
static void removeModemStatusCallback(String deviceName)
参数:
deviceName- 设备名称
4.13 错误查询
querySerialErrorCount
查询串口错误状态。
static Future<int> querySerialErrorCount(
String deviceName,
int serialNumber,
SerialErrorType errorType,
)
参数:
deviceName- 设备名称serialNumber- 串口号errorType- 错误类型
返回值: 该种错误出现的次数
4.14 全局配置
setReadTimeout
设置读取超时时间。
static Future<void> setReadTimeout(int timeout)
参数:
timeout- 超时时间(毫秒)
说明: 全局有效,应在 APP 初始化时调用
addNewHardware
添加自定义硬件 VID/PID。
static Future<void> addNewHardware(
int vid,
int pid, {
String? chipType,
})
参数:
vid- 硬件 VIDpid- 硬件 PIDchipType- 芯片类型(可选)
setDebug
设置调试模式。
static Future<void> setDebug(bool enabled)
参数:
enabled-true开启,false关闭
isDebugMode
返回当前是否处于调试模式。
static Future<bool> isDebugMode()
返回值: true 处于调试模式,false 不处于
五、类型定义
5.1 枚举类型
DataBits - 数据位
enum DataBits {
bits5(5), // 5 位数据位
bits6(6), // 6 位数据位
bits7(7), // 7 位数据位
bits8(8); // 8 位数据位(默认)
}
StopBits - 停止位
enum StopBits {
one(1), // 1 位停止位(默认)
two(2), // 2 位停止位
}
Parity - 校验位
enum Parity {
none(0), // 无校验(默认)
odd(1), // 奇校验
even(2), // 偶校验
mark(3), // 标记校验
space(4), // 空格校验
}
GpioDirection - GPIO 方向
enum GpioDirection {
inDir, // 输入方向
outDir, // 输出方向
}
GpioValue - GPIO 电平值
enum GpioValue {
low, // 低电平
high, // 高电平
}
SerialErrorType - 串口错误类型
enum SerialErrorType {
framingError, // 帧错误
parityError, // 校验错误
overrunError, // 溢出错误
breakInterrupt, // 中断错误
}
5.2 数据类
SerialParameter - 串口参数
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 设备信息
class UsbDeviceInfo {
final String deviceName; // 设备名称
final int productId; // 产品 ID (PID)
final int vendorId; // 厂商 ID (VID)
final int serialCount; // 串口数量
final String? chipType; // 芯片型号(如果识别)
}
GpioStatus - GPIO 状态
class GpioStatus {
final int index; // GPIO 编号
final GpioDirection direction; // 方向
final GpioValue value; // 电平值
final bool enabled; // 是否已使能
}
ChipMasterFrequency - 芯片主频
class ChipMasterFrequency {
final int frequency; // 芯片主频(Hz)
final bool switchEnable; // 是否允许切换主频
final int coStatus; // 晶振状态
}
ModemStatus - Modem 状态
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 设备扫描与管理
import 'package:ch34/ch34.dart';
import 'dart:typed_data';
class SerialDeviceManager {
static List<UsbDeviceInfo> _devices = [];
/// 扫描并连接设备
static Future<void> 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 串口通信封装
class SerialPortService {
String? _deviceName;
int _serialNumber = 0;
bool _isConnected = false;
/// 打开串口
Future<bool> 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<void> close() async {
if (_deviceName != null) {
Ch34Manager.removeDataCallback(_deviceName!);
await Ch34Manager.disconnect(_deviceName!);
_deviceName = null;
_isConnected = false;
}
}
/// 发送数据
Future<int> send(List<int> data) async {
if (!_isConnected || _deviceName == null) {
throw StateError('串口未连接');
}
return await Ch34Manager.writeData(
_deviceName!,
_serialNumber,
Uint8List.fromList(data),
);
}
/// 异步发送数据
Future<void> sendAsync(List<int> data) async {
if (!_isConnected || _deviceName == null) {
throw StateError('串口未连接');
}
await Ch34Manager.asyncWriteData(
_deviceName!,
_serialNumber,
Uint8List.fromList(data),
);
}
/// 注册数据接收回调
Future<void> 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 控制示例
class GpioController {
/// 配置并设置 GPIO
static Future<void> 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<void> 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 状态监听
class ModemMonitor {
static Future<void> 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 错误检测
class ErrorDetector {
static Future<void> 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() 返回空列表
解决方法:
- 确认设备支持 USB Host/OTG 功能
- 检查 USB 线缆连接是否正常
- 确认 Android 设备已开启 OTG 功能(部分手机需要手动开启)
- 检查芯片是否在支持列表中
7.2 权限被拒绝
问题: requestPermission() 返回 false
解决方法:
- 确保在
AndroidManifest.xml中添加了 USB 权限声明 - 用户需要在系统弹窗中点击"允许"
- 可以尝试重新请求权限
7.3 数据收发异常
问题: 发送数据成功但收不到回复
解决方法:
- 确认串口参数(波特率、数据位、停止位、校验位)与对端设备一致
- 使用
registerDataCallback代替readData,推荐回调方式接收数据 - 检查线缆连接和电平匹配(3.3V vs 5V)
- 使用
querySerialErrorCount检查是否有串口错误
7.4 多串口设备使用
问题: CH344/CH9104 等多串口设备如何使用
解决方法:
// 多串口设备每个串口都需要单独配置
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 设备热插拔处理
问题: 设备拔出后如何检测
解决方法:
// 注册 USB 状态监听
Ch34Manager.setUsbStateListener((deviceName, connected) {
if (!connected) {
print('设备 $deviceName 已拔出');
// 清理资源
Ch34Manager.removeDataCallback(deviceName);
} else {
print('设备 $deviceName 已插入');
// 重新打开设备
}
});
7.6 自定义 VID/PID
问题: 如何支持非标准 VID/PID 的设备
解决方法:
// 添加自定义硬件
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