feat(ch934x): 添加GPIO方向控制和串口参数配置功能
- 添加Ch934xMode和GpioDirection枚举类定义 - 实现GPIO方向控制相关方法(setGpioDir/queryGpioDirFromCache) - 添加GPIO使能控制(enableGpio)和数量/组数查询(getGpiocount/getGpiogroup) - 实现串口参数配置(setSerialParameter)包括波特率/数据位/停止位/校验位/流控 - 添加Break信号控制(setBreak)功能 - 实现当前串口模式查询(getCurrentMode) - 添加设备连接状态查询(isConnected)和已打开设备列表获取(getConnectedDevices) - 更新文档说明GPIO方向控制和串口参数配置用法 - 在示例应用中添加相关功能按钮和操作逻辑
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# ch934x_serial
|
||||
|
||||
Flutter 插件:封装南京沁恒微电子 **CH934X 系列** USB 转多串口芯片的
|
||||
Android SDK,提供设备查找、串口读写、GPIO 与 Modem 控制等能力。
|
||||
Android SDK,提供设备枚举、串口读写、GPIO/Modem 控制、Break 信号、
|
||||
串口参数配置、设备状态查询等完整能力。
|
||||
|
||||
## 文档
|
||||
|
||||
@@ -26,5 +27,5 @@ final devices = await plugin.getDeviceList();
|
||||
|
||||
## 平台支持
|
||||
|
||||
- ✅ Android(基于 `CH934XLib.jar` 反射桥接)
|
||||
- ✅ Android(基于 `CH934XLib.jar`)
|
||||
- ❌ iOS / Web / Desktop(暂未实现)
|
||||
|
||||
@@ -229,6 +229,36 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
||||
exceptionCallbackEnabled = true;
|
||||
result.success(null);
|
||||
break;
|
||||
case "setBreak":
|
||||
result.success(handleSetBreak(call));
|
||||
break;
|
||||
case "getCurrentMode":
|
||||
result.success(handleGetCurrentMode(call));
|
||||
break;
|
||||
case "getGPIOCount":
|
||||
result.success(handleGetGPIOCount(call));
|
||||
break;
|
||||
case "getGPIOGroup":
|
||||
result.success(handleGetGPIOGroup(call));
|
||||
break;
|
||||
case "enableGPIO":
|
||||
result.success(handleEnableGPIO(call));
|
||||
break;
|
||||
case "setGPIODir":
|
||||
result.success(handleSetGPIODir(call));
|
||||
break;
|
||||
case "queryGPIODirFromCache":
|
||||
result.success(handleQueryGPIODirFromCache(call));
|
||||
break;
|
||||
case "isConnected":
|
||||
result.success(handleIsConnected(call));
|
||||
break;
|
||||
case "getConnectedDevices":
|
||||
result.success(handleGetConnectedDevices());
|
||||
break;
|
||||
case "setSerialParameter":
|
||||
result.success(handleSetSerialParameter(call));
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
@@ -637,6 +667,187 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
||||
return modemStatusCache;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 新增接口(对应文档 setBreak / querySerialErrorCount / getCurrentMode / …)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** 文档 setBreak。 */
|
||||
private boolean handleSetBreak(@NonNull MethodCall call) {
|
||||
if (activeDevice == null || activeSerialNumber < 0) {
|
||||
return false;
|
||||
}
|
||||
Boolean valid = call.argument("valid");
|
||||
if (valid == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return CH934XManager.getInstance().setBreak(
|
||||
activeDevice, activeSerialNumber, valid);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 getCurrentMode。 */
|
||||
private int handleGetCurrentMode(@NonNull MethodCall call) {
|
||||
if (activeDevice == null || activeSerialNumber < 0) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
Mode mode = CH934XManager.getInstance().getCurrentMode(
|
||||
activeDevice, activeSerialNumber);
|
||||
if (mode == null) return -1;
|
||||
String name = mode.name();
|
||||
if ("NORMAL".equals(name)) return 0;
|
||||
if ("HARDFLOW".equals(name)) return 1;
|
||||
if ("GPIO".equals(name)) return 2;
|
||||
return -1;
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 getGPIOCount。 */
|
||||
private int handleGetGPIOCount(@NonNull MethodCall call) {
|
||||
UsbDevice device = resolveDevice(call);
|
||||
if (device == null) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return CH934XManager.getInstance().getGPIOCount(device);
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 getGPIOGroup。 */
|
||||
private int handleGetGPIOGroup(@NonNull MethodCall call) {
|
||||
UsbDevice device = resolveDevice(call);
|
||||
if (device == null) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return CH934XManager.getInstance().getGPIOGroup(device);
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 enableGPIO。需要 ChipType 参数,由 Dart 侧传入 int 映射值。 */
|
||||
private boolean handleEnableGPIO(@NonNull MethodCall call) {
|
||||
UsbDevice device = resolveDevice(call);
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
Integer chipTypeVal = call.argument("chipType");
|
||||
Integer gpioGroup = call.argument("gpioGroup");
|
||||
Integer enable = call.argument("enable");
|
||||
if (chipTypeVal == null || gpioGroup == null || enable == null) {
|
||||
return false;
|
||||
}
|
||||
ChipType chipType = parseChipType(chipTypeVal);
|
||||
if (chipType == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return CH934XManager.getInstance().enableGPIO(
|
||||
device, chipType, gpioGroup, enable);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 setGPIODir。 */
|
||||
private boolean handleSetGPIODir(@NonNull MethodCall call) {
|
||||
if (activeDevice == null) {
|
||||
return false;
|
||||
}
|
||||
Integer gpioGroup = call.argument("gpioGroup");
|
||||
Integer gpioNumber = call.argument("gpioNumber");
|
||||
Integer dir = call.argument("dir");
|
||||
if (gpioGroup == null || gpioNumber == null || dir == null) {
|
||||
return false;
|
||||
}
|
||||
GPIO_DIR gpioDir = dir == 0 ? GPIO_DIR.IN : GPIO_DIR.OUT;
|
||||
try {
|
||||
return CH934XManager.getInstance().setGPIODir(
|
||||
activeDevice, gpioGroup, gpioNumber, gpioDir);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 queryGPIODirFromCache。返回 0=IN,1=OUT,负值=失败。 */
|
||||
private int handleQueryGPIODirFromCache(@NonNull MethodCall call) {
|
||||
if (activeDevice == null) {
|
||||
return -1;
|
||||
}
|
||||
Integer gpioGroup = call.argument("gpioGroup");
|
||||
Integer gpioNumber = call.argument("gpioNumber");
|
||||
if (gpioGroup == null || gpioNumber == null) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
GPIO_DIR dir = CH934XManager.getInstance().queryGPIODirFromCache(
|
||||
activeDevice, gpioGroup, gpioNumber);
|
||||
if (dir == null) return -1;
|
||||
return dir == GPIO_DIR.OUT ? 1 : 0;
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 isConnected。 */
|
||||
private boolean handleIsConnected(@NonNull MethodCall call) {
|
||||
UsbDevice device = resolveDevice(call);
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return CH934XManager.getInstance().isConnected(device);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档 getConnectedDevices。返回已打开设备的 deviceId 列表。 */
|
||||
private List<Integer> handleGetConnectedDevices() {
|
||||
List<Integer> ids = new ArrayList<>();
|
||||
try {
|
||||
ArrayList<UsbDevice> devices = CH934XManager.getInstance().getConnectedDevices();
|
||||
if (devices != null) {
|
||||
for (UsbDevice d : devices) {
|
||||
ids.add(d.getDeviceId());
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
/** 文档 setSerialParameter。配置当前活跃串口的参数。 */
|
||||
private boolean handleSetSerialParameter(@NonNull MethodCall call) {
|
||||
if (activeDevice == null || activeSerialNumber < 0) {
|
||||
return false;
|
||||
}
|
||||
Integer baud = call.argument("baud");
|
||||
Integer dataBit = call.argument("dataBit");
|
||||
Integer stopBit = call.argument("stopBit");
|
||||
Integer parityBit = call.argument("parityBit");
|
||||
Boolean flow = call.argument("flow");
|
||||
if (baud == null || dataBit == null || stopBit == null
|
||||
|| parityBit == null || flow == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return CH934XManager.getInstance().setSerialParameter(
|
||||
activeDevice, activeSerialNumber,
|
||||
baud, dataBit, stopBit, parityBit, flow);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内部辅助
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -680,6 +891,25 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
||||
return 5;
|
||||
}
|
||||
|
||||
/** 将 Dart 侧传入的 int 还原为 SDK ChipType。null 表示未知类型。 */
|
||||
@Nullable
|
||||
private ChipType parseChipType(int value) {
|
||||
String name;
|
||||
switch (value) {
|
||||
case 0: name = "CH9344"; break;
|
||||
case 1: name = "CH9344L"; break;
|
||||
case 2: name = "CH9350"; break;
|
||||
case 3: name = "CH9348Q"; break;
|
||||
case 4: name = "CH9342"; break;
|
||||
default: return null;
|
||||
}
|
||||
try {
|
||||
return ChipType.valueOf(name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> buildSerialPortList(int count) {
|
||||
List<Map<String, Object>> ports = new ArrayList<>();
|
||||
for (int i = 0; i < Math.max(count, 0); i++) {
|
||||
|
||||
+120
-7
@@ -16,9 +16,13 @@ Android SDK 封装的 Flutter 插件,文档面向其他 Flutter 开发者,介绍
|
||||
- CH934X 设备枚举、序列号读取、芯片类型识别
|
||||
- 多串口打开 / 关闭 / **同设备内串口切换**
|
||||
- 字节级串口读写
|
||||
- GPIO 输出 / 输入
|
||||
- GPIO 输出 / 输入 / 方向控制 / 使能
|
||||
- Modem 控制 (DTR/RTS) 与状态 (CTS/DSR/RI/DCD) 读取
|
||||
- **主动申请 USB 权限**
|
||||
- **Break 信号控制**
|
||||
- **串口参数配置(波特率/数据位/停止位/校验/流控)**
|
||||
- **查询设备连接状态与已打开设备列表**
|
||||
- **查询 GPIO 数量、组信息**
|
||||
- 设备拔出、Modem 错误等异常事件回调
|
||||
- **底层依赖:** 沁恒官方 `CH934XLib.jar`(插件随包发布,位于
|
||||
`android/libs/CH934XLib.jar`),通过原生 `CH934XManager` 单例直接调用,
|
||||
@@ -189,14 +193,40 @@ await sub.cancel();
|
||||
### 3.4 GPIO 接口
|
||||
|
||||
```dart
|
||||
// 设置 GPIO 输出电平
|
||||
final ok = await plugin.setGpioOutput(gpioNumber: 0, level: 1);
|
||||
final value = await plugin.getGpioInput(0); // 负值表示失败
|
||||
|
||||
// 读取 GPIO 输入电平(负值表示失败)
|
||||
final value = await plugin.getGpioInput(0);
|
||||
```
|
||||
|
||||
- `gpioNumber` 与 `level` 与原文档保持一致(0/1)。
|
||||
- `getGpioInput` 失败时返回 -1,业务方需要自行处理。
|
||||
- GPIO 操作同样作用于**当前活跃串口**。
|
||||
|
||||
### 3.4.1 GPIO 方向控制
|
||||
|
||||
```dart
|
||||
// 使能 GPIO(需要先获取 chipType,可从 Ch934xDeviceInfo.deviceType 取得)
|
||||
await plugin.enableGpio(chipType: device.deviceType, gpioGroup: 0, enable: 1);
|
||||
|
||||
// 设置 GPIO 方向
|
||||
await plugin.setGpioDir(gpioGroup: 0, gpioNumber: 0, dir: GpioDirection.out);
|
||||
|
||||
// 从缓存查询 GPIO 方向
|
||||
final dir = await plugin.queryGpioDirFromCache(gpioGroup: 0, gpioNumber: 0);
|
||||
if (dir == GpioDirection.out) { /* 输出 */ }
|
||||
|
||||
// 查询 GPIO 数量与组数
|
||||
final count = await plugin.getGpiocount(device.deviceId);
|
||||
final groups = await plugin.getGpiogroup(device.deviceId);
|
||||
```
|
||||
|
||||
- `GpioDirection` 常量: `in_ = 0`(输入)、`out = 1`(输出)。
|
||||
- `enableGpio` 的 `enable` 参数: CH9344 传 1/0 控制整组;CH348 使用位掩码。
|
||||
- 方向查询返回 -1 表示失败。
|
||||
- `getGpiocount`/`getGpiogroup` 失败时返回 -1,需先 `openPort`。
|
||||
|
||||
### 3.5 Modem 控制接口
|
||||
|
||||
```dart
|
||||
@@ -224,6 +254,78 @@ final sub = await plugin.setExceptionCallback((event) {
|
||||
await sub.cancel();
|
||||
```
|
||||
|
||||
- 异常类型见 `Ch934xExceptionType`:
|
||||
- `deviceDetached = 1`:设备被拔出(由 SDK 的 `usbDeviceDetach` 触发)。
|
||||
- `ioError = 2`:Modem overrun / parity / frame 等错误。
|
||||
- `sdk = 3`:原生 SDK 主动抛出的其他异常(目前未触发,保留语义)。
|
||||
- `unknown = 0`:未识别(当前未触发,保留语义)。
|
||||
- `Ch934xException` 包含 `type / message / cause`,`toString()` 会把
|
||||
`type` 翻译为对应的常量名,方便日志/UI 显示。
|
||||
- 返回的 `StreamSubscription` **必须在合适时机 `cancel`**,以便
|
||||
释放底层 `StreamController` 与 `MethodCallHandler`。
|
||||
|
||||
### 3.7 其他接口
|
||||
|
||||
#### 3.7.1 Break 信号
|
||||
|
||||
```dart
|
||||
final ok = await plugin.setBreak(true); // 设置 Break(低电平有效)
|
||||
```
|
||||
|
||||
- 作用于**当前活跃串口**。
|
||||
|
||||
#### 3.7.2 获取当前串口模式
|
||||
|
||||
```dart
|
||||
final mode = await plugin.getCurrentMode();
|
||||
// mode 取值见 Ch934xMode 常量
|
||||
```
|
||||
|
||||
- `Ch934xMode` 常量: `normal = 0`(普通)、`hardflow = 1`(硬件流控)、`gpio = 2`(GPIO)。
|
||||
- **仅对 CH934X 型号有效**,CH348 返回 -1。
|
||||
|
||||
#### 3.7.3 设备连接状态
|
||||
|
||||
```dart
|
||||
final connected = await plugin.isConnected(device.deviceId);
|
||||
final ids = await plugin.getConnectedDevices();
|
||||
```
|
||||
|
||||
- `isConnected`: 查询指定设备是否已被打开。
|
||||
- `getConnectedDevices`: 返回当前已打开设备的 `deviceId` 列表。
|
||||
|
||||
### 3.8 串口参数配置
|
||||
|
||||
```dart
|
||||
// 配置波特率为 9600
|
||||
final ok = await plugin.setSerialParameter(
|
||||
baud: 9600,
|
||||
dataBit: 8,
|
||||
stopBit: 1,
|
||||
parityBit: 0,
|
||||
flow: false,
|
||||
);
|
||||
```
|
||||
|
||||
- 作用于**当前活跃串口**。`openPort` 内部默认设为 `115200/8/1/N/无流控`,
|
||||
如需修改可在此之后调用。
|
||||
- `stopBit`: 0=1 停止位,1=1.5 停止位,2=2 停止位。
|
||||
- `parityBit`: 0=无校验,1=奇校验,2=偶校验。
|
||||
|
||||
---
|
||||
|
||||
## 4. 完整示例
|
||||
|
||||
```dart
|
||||
final sub = await plugin.setExceptionCallback((event) {
|
||||
debugPrint('设备异常: ${event.type} ${event.message}');
|
||||
// 业务方应主动关闭串口、刷新设备列表或提示用户重新插拔
|
||||
});
|
||||
|
||||
// 主动取消监听
|
||||
await sub.cancel();
|
||||
```
|
||||
|
||||
- 异常类型见 `Ch934xExceptionType`:
|
||||
- `deviceDetached = 1`:设备被拔出(由 SDK 的 `usbDeviceDetach` 触发)。
|
||||
- `ioError = 2`:Modem overrun / parity / frame 等错误。
|
||||
@@ -346,9 +448,9 @@ void main() {
|
||||
才返回真实数据,枚举阶段调用只会得到空列表。
|
||||
|
||||
**Q3. `read()` 一直返回空数组。**
|
||||
- 确认对端设备正在发送数据,且波特率/校验位等参数与原 SDK 默认值
|
||||
一致(`CH934XLib` 提供独立的 `setConfig` 接口,本插件当前未做
|
||||
封装,需要时可扩展原 SDK 直接调用)。
|
||||
- 确认对端设备正在发送数据,且波特率/校验位等参数匹配。可通过
|
||||
`setSerialParameter(baud: 9600, dataBit: 8, stopBit: 1, parityBit: 0, flow: false)`
|
||||
配置串口参数。
|
||||
- 检查线序:RX/TX 是否接反,以及硬件流控是否正确。
|
||||
- 确认 `setActivePort` 已切换到正确的串口索引。
|
||||
|
||||
@@ -398,12 +500,23 @@ void main() {
|
||||
| `requestUsbPermission` | 2.2 增强 | 主动授权(插件新增) |
|
||||
| `read` | 6.1.1 | 串口读写 |
|
||||
| `write` | 6.2.1 | 串口读写 |
|
||||
| `setGpioOutput` | 7.1.1 | GPIO |
|
||||
| `getGpioInput` | 7.1.2 | GPIO |
|
||||
| `setSerialParameter` | setSerialParameter | 串口参数 |
|
||||
| `setBreak` | setBreak | 其他 |
|
||||
| `getCurrentMode` | getCurrentMode | 其他 |
|
||||
| `isConnected` | isConnected | 设备状态 |
|
||||
| `getConnectedDevices` | getConnectedDevices | 设备状态 |
|
||||
| `setGpioOutput` | setGPIOValue | GPIO |
|
||||
| `getGpioInput` | getGPIOValue | GPIO |
|
||||
| `setGpioDir` | setGPIODir | GPIO 方向 |
|
||||
| `queryGpioDirFromCache` | queryGPIODirFromCache | GPIO 方向 |
|
||||
| `enableGpio` | enableGPIO | GPIO |
|
||||
| `getGpiocount` | getGPIOCount | GPIO |
|
||||
| `getGpiogroup` | getGPIOGroup | GPIO |
|
||||
| `setModemControl` | 7.2.1 | Modem |
|
||||
| `getModemStatus` | 7.2.2 | Modem |
|
||||
| `setExceptionCallback` | 7.3.1 | 异常 |
|
||||
| `dataStream` | 6.1 增强 | 串口流(插件新增) |
|
||||
| `portDataStream` | 6.1 增强 | 多端口轮询流(插件新增) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -378,6 +378,148 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
icon: const Icon(Icons.input),
|
||||
label: const Text('读取 GPIO0'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
if (!_portOpened) return;
|
||||
final ok = await _plugin.setBreak(true);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = ok ? 'Break 已设置' : 'Break 失败');
|
||||
},
|
||||
icon: const Icon(Icons.pause),
|
||||
label: const Text('SetBreak'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
if (!_portOpened) return;
|
||||
final mode = await _plugin.getCurrentMode();
|
||||
if (!mounted) return;
|
||||
final label = switch (mode) {
|
||||
Ch934xMode.normal => '普通',
|
||||
Ch934xMode.hardflow => '硬件流控',
|
||||
Ch934xMode.gpio => 'GPIO',
|
||||
_ => '未知($mode)',
|
||||
};
|
||||
setState(() => _status = '当前模式: $label');
|
||||
},
|
||||
icon: const Icon(Icons.settings),
|
||||
label: const Text('当前模式'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final device = _selectedDevice;
|
||||
if (device == null) {
|
||||
setState(() => _status = '请先选择设备');
|
||||
return;
|
||||
}
|
||||
final cnt = await _plugin.getGpiocount(device.deviceId);
|
||||
final grp = await _plugin.getGpiogroup(device.deviceId);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = 'GPIO 数量=$cnt, 组数=$grp');
|
||||
},
|
||||
icon: const Icon(Icons.developer_board),
|
||||
label: const Text('GPIO 信息'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final device = _selectedDevice;
|
||||
if (device == null) {
|
||||
setState(() => _status = '请先选择设备');
|
||||
return;
|
||||
}
|
||||
if (!_portOpened) {
|
||||
setState(() => _status = '请先打开串口');
|
||||
return;
|
||||
}
|
||||
final ok = await _plugin.enableGpio(
|
||||
chipType: device.deviceType,
|
||||
gpioGroup: 0,
|
||||
enable: 1,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = ok ? 'GPIO 已使能' : '使能 GPIO 失败');
|
||||
},
|
||||
icon: const Icon(Icons.toggle_on),
|
||||
label: const Text('使能GPIO'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
if (!_portOpened) return;
|
||||
final ok = await _plugin.setGpioDir(
|
||||
gpioGroup: 0,
|
||||
gpioNumber: 0,
|
||||
dir: GpioDirection.out,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = ok ? 'GPIO0 方向设为输出' : '设置方向失败');
|
||||
},
|
||||
icon: const Icon(Icons.swap_vert),
|
||||
label: const Text('GPIO方向'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
if (!_portOpened) return;
|
||||
final dir = await _plugin.queryGpioDirFromCache(
|
||||
gpioGroup: 0,
|
||||
gpioNumber: 0,
|
||||
);
|
||||
if (!mounted) return;
|
||||
final label = switch (dir) {
|
||||
GpioDirection.in_ => '输入',
|
||||
GpioDirection.out => '输出',
|
||||
_ => '未知($dir)',
|
||||
};
|
||||
setState(() => _status = 'GPIO0 方向缓存: $label');
|
||||
},
|
||||
icon: const Icon(Icons.visibility),
|
||||
label: const Text('查方向缓存'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final device = _selectedDevice;
|
||||
if (device == null) {
|
||||
setState(() => _status = '请先选择设备');
|
||||
return;
|
||||
}
|
||||
final connected = await _plugin.isConnected(device.deviceId);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = connected ? '设备已连接' : '设备未连接');
|
||||
},
|
||||
icon: const Icon(Icons.link),
|
||||
label: const Text('是否已连'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final ids = await _plugin.getConnectedDevices();
|
||||
if (!mounted) return;
|
||||
setState(() => _status = '已打开设备: ${ids.isEmpty ? "无" : ids.join(", ")}');
|
||||
},
|
||||
icon: const Icon(Icons.list),
|
||||
label: const Text('已开设备'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
if (!_portOpened) return;
|
||||
final ok = await _plugin.setSerialParameter(
|
||||
baud: 9600, dataBit: 8, stopBit: 1, parityBit: 0, flow: false,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = ok ? '已设为 9600/8/1/N' : '设参失败');
|
||||
},
|
||||
icon: const Icon(Icons.tune),
|
||||
label: const Text('波特率9600'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
if (!_portOpened) return;
|
||||
final ok = await _plugin.setSerialParameter(
|
||||
baud: 115200, dataBit: 8, stopBit: 1, parityBit: 0, flow: false,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() => _status = ok ? '已设为 115200/8/1/N' : '设参失败');
|
||||
},
|
||||
icon: const Icon(Icons.tune),
|
||||
label: const Text('波特率115200'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -168,4 +168,80 @@ class Ch934xSerial {
|
||||
await _platform.setExceptionCallback(controller.add);
|
||||
return subscription;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 新增接口(文档补充的完整 SDK API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 设置 Break 信号。
|
||||
Future<bool> setBreak(bool valid) => _platform.setBreak(valid);
|
||||
|
||||
/// 获取当前串口模式。
|
||||
Future<int> getCurrentMode() => _platform.getCurrentMode();
|
||||
|
||||
/// 获取 GPIO 数量。
|
||||
Future<int> getGpiocount(int deviceId) =>
|
||||
_platform.getGpiocount(deviceId);
|
||||
|
||||
/// 获取 GPIO 组数。
|
||||
Future<int> getGpiogroup(int deviceId) =>
|
||||
_platform.getGpiogroup(deviceId);
|
||||
|
||||
/// 使能 GPIO。
|
||||
Future<bool> enableGpio({
|
||||
required int chipType,
|
||||
required int gpioGroup,
|
||||
required int enable,
|
||||
}) =>
|
||||
_platform.enableGpio(
|
||||
chipType: chipType,
|
||||
gpioGroup: gpioGroup,
|
||||
enable: enable,
|
||||
);
|
||||
|
||||
/// 设置 GPIO 方向。
|
||||
Future<bool> setGpioDir({
|
||||
required int gpioGroup,
|
||||
required int gpioNumber,
|
||||
required int dir,
|
||||
}) =>
|
||||
_platform.setGpioDir(
|
||||
gpioGroup: gpioGroup,
|
||||
gpioNumber: gpioNumber,
|
||||
dir: dir,
|
||||
);
|
||||
|
||||
/// 从缓存查询 GPIO 方向。
|
||||
Future<int> queryGpioDirFromCache({
|
||||
required int gpioGroup,
|
||||
required int gpioNumber,
|
||||
}) =>
|
||||
_platform.queryGpioDirFromCache(
|
||||
gpioGroup: gpioGroup,
|
||||
gpioNumber: gpioNumber,
|
||||
);
|
||||
|
||||
/// 查询设备是否已打开。
|
||||
Future<bool> isConnected(int deviceId) =>
|
||||
_platform.isConnected(deviceId);
|
||||
|
||||
/// 获取当前已打开的设备 ID 列表。
|
||||
Future<List<int>> getConnectedDevices() =>
|
||||
_platform.getConnectedDevices();
|
||||
|
||||
/// 配置当前活跃串口的参数。
|
||||
Future<bool> setSerialParameter({
|
||||
required int baud,
|
||||
int dataBit = 8,
|
||||
int stopBit = 1,
|
||||
int parityBit = 0,
|
||||
bool flow = false,
|
||||
}) =>
|
||||
_platform.setSerialParameter(
|
||||
baud: baud,
|
||||
dataBit: dataBit,
|
||||
stopBit: stopBit,
|
||||
parityBit: parityBit,
|
||||
flow: flow,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -215,6 +215,117 @@ class MethodChannelCh934xSerial extends Ch934xSerialPlatform {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setBreak(bool valid) {
|
||||
return methodChannel.invokeMethod<bool>(
|
||||
'setBreak',
|
||||
<String, Object>{'valid': valid},
|
||||
).then((v) => v ?? false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getCurrentMode() {
|
||||
return methodChannel.invokeMethod<int>('getCurrentMode')
|
||||
.then((v) => v ?? -1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getGpiocount(int deviceId) {
|
||||
return methodChannel.invokeMethod<int>(
|
||||
'getGPIOCount',
|
||||
<String, Object>{'deviceId': deviceId},
|
||||
).then((v) => v ?? -1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getGpiogroup(int deviceId) {
|
||||
return methodChannel.invokeMethod<int>(
|
||||
'getGPIOGroup',
|
||||
<String, Object>{'deviceId': deviceId},
|
||||
).then((v) => v ?? -1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> enableGpio({
|
||||
required int chipType,
|
||||
required int gpioGroup,
|
||||
required int enable,
|
||||
}) {
|
||||
return methodChannel.invokeMethod<bool>(
|
||||
'enableGPIO',
|
||||
<String, Object>{
|
||||
'chipType': chipType,
|
||||
'gpioGroup': gpioGroup,
|
||||
'enable': enable,
|
||||
},
|
||||
).then((v) => v ?? false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setGpioDir({
|
||||
required int gpioGroup,
|
||||
required int gpioNumber,
|
||||
required int dir,
|
||||
}) {
|
||||
return methodChannel.invokeMethod<bool>(
|
||||
'setGPIODir',
|
||||
<String, Object>{
|
||||
'gpioGroup': gpioGroup,
|
||||
'gpioNumber': gpioNumber,
|
||||
'dir': dir,
|
||||
},
|
||||
).then((v) => v ?? false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> queryGpioDirFromCache({
|
||||
required int gpioGroup,
|
||||
required int gpioNumber,
|
||||
}) {
|
||||
return methodChannel.invokeMethod<int>(
|
||||
'queryGPIODirFromCache',
|
||||
<String, Object>{
|
||||
'gpioGroup': gpioGroup,
|
||||
'gpioNumber': gpioNumber,
|
||||
},
|
||||
).then((v) => v ?? -1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isConnected(int deviceId) {
|
||||
return methodChannel.invokeMethod<bool>(
|
||||
'isConnected',
|
||||
<String, Object>{'deviceId': deviceId},
|
||||
).then((v) => v ?? false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<int>> getConnectedDevices() async {
|
||||
final raw = await methodChannel.invokeMethod<List<dynamic>>('getConnectedDevices');
|
||||
if (raw == null) return const <int>[];
|
||||
return raw.whereType<int>().toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setSerialParameter({
|
||||
required int baud,
|
||||
int dataBit = 8,
|
||||
int stopBit = 1,
|
||||
int parityBit = 0,
|
||||
bool flow = false,
|
||||
}) {
|
||||
return methodChannel.invokeMethod<bool>(
|
||||
'setSerialParameter',
|
||||
<String, Object>{
|
||||
'baud': baud,
|
||||
'dataBit': dataBit,
|
||||
'stopBit': stopBit,
|
||||
'parityBit': parityBit,
|
||||
'flow': flow,
|
||||
},
|
||||
).then((v) => v ?? false);
|
||||
}
|
||||
|
||||
/// 释放内部资源,通常仅在测试或热重载场景下使用。
|
||||
@visibleForTesting
|
||||
void dispose() {
|
||||
|
||||
@@ -112,4 +112,60 @@ abstract class Ch934xSerialPlatform extends PlatformInterface {
|
||||
Future<void> setExceptionCallback(
|
||||
void Function(Ch934xException exception) onException,
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 新增接口(文档补充的完整 SDK API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 设置 Break 信号(对应 `CH934XManager.setBreak`)。
|
||||
Future<bool> setBreak(bool valid);
|
||||
|
||||
/// 获取当前串口模式(对应 `CH934XManager.getCurrentMode`)。
|
||||
/// 返回值见 [Ch934xMode];仅 CH934X 有效,CH348 返回 -1。
|
||||
Future<int> getCurrentMode();
|
||||
|
||||
/// 获取 GPIO 数量(对应 `CH934XManager.getGPIOCount`)。
|
||||
Future<int> getGpiocount(int deviceId);
|
||||
|
||||
/// 获取 GPIO 组数(对应 `CH934XManager.getGPIOGroup`)。
|
||||
Future<int> getGpiogroup(int deviceId);
|
||||
|
||||
/// 使能 GPIO(对应 `CH934XManager.enableGPIO`)。
|
||||
/// [chipType] 由 [Ch934xDeviceType] 常量标识,[gpioGroup] 为组号,
|
||||
/// [enable] 含义见文档: CH9344 传 1/0 控制整组;CH348 使用位掩码。
|
||||
Future<bool> enableGpio({
|
||||
required int chipType,
|
||||
required int gpioGroup,
|
||||
required int enable,
|
||||
});
|
||||
|
||||
/// 设置 GPIO 方向(对应 `CH934XManager.setGPIODir`)。
|
||||
/// [dir] 取值见 [GpioDirection]。
|
||||
Future<bool> setGpioDir({
|
||||
required int gpioGroup,
|
||||
required int gpioNumber,
|
||||
required int dir,
|
||||
});
|
||||
|
||||
/// 从缓存查询 GPIO 方向(对应 `CH934XManager.queryGPIODirFromCache`)。
|
||||
/// 返回值见 [GpioDirection];-1 表示查询失败。
|
||||
Future<int> queryGpioDirFromCache({
|
||||
required int gpioGroup,
|
||||
required int gpioNumber,
|
||||
});
|
||||
|
||||
/// 查询设备是否已打开(对应 `CH934XManager.isConnected`)。
|
||||
Future<bool> isConnected(int deviceId);
|
||||
|
||||
/// 获取当前已打开的设备 ID 列表(对应 `CH934XManager.getConnectedDevices`)。
|
||||
Future<List<int>> getConnectedDevices();
|
||||
|
||||
/// 配置当前活跃串口的参数(对应 `CH934XManager.setSerialParameter`)。
|
||||
Future<bool> setSerialParameter({
|
||||
required int baud,
|
||||
int dataBit = 8,
|
||||
int stopBit = 1,
|
||||
int parityBit = 0,
|
||||
bool flow = false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,3 +26,30 @@ class Ch934xDeviceType {
|
||||
/// 未知或非 CH934X 设备。
|
||||
static const int unknown = -1;
|
||||
}
|
||||
|
||||
/// 串口当前模式,对应 SDK [Mode] 枚举。
|
||||
///
|
||||
/// 仅对 CH934X 型号有效(CH348 调用 [Ch934xSerial.getCurrentMode] 无意义)。
|
||||
class Ch934xMode {
|
||||
const Ch934xMode._();
|
||||
|
||||
/// 普通模式。
|
||||
static const int normal = 0;
|
||||
|
||||
/// 硬件流控模式。
|
||||
static const int hardflow = 1;
|
||||
|
||||
/// GPIO 模式。
|
||||
static const int gpio = 2;
|
||||
}
|
||||
|
||||
/// GPIO 方向,对应 SDK [GPIO_DIR] 枚举。
|
||||
class GpioDirection {
|
||||
const GpioDirection._();
|
||||
|
||||
/// 输入方向。
|
||||
static const int in_ = 0;
|
||||
|
||||
/// 输出方向。
|
||||
static const int out = 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user