1. 驱动适配思路与背景
最近在RK3568平台上做OpenHarmony 4.0项目时遇到了一个实际问题:板载的串口资源根本不够用!我们的设备需要连接多个外设,原生串口只有3-4个,完全无法满足需求。经过一番调研,最终选择了CH9344这款USB转串口芯片,它能够将USB接口扩展为4个独立的串口,正好解决了我们的痛点。
拿到CH9344的Linux驱动后,我开始分析OpenHarmony的HDF串口驱动框架。仔细阅读//drivers/hdf_core/adapter/khdf/linux/platform/uart/uart_adapter.c源码后发现一个关键点:HDF串口驱动本质上就是对Linux标准串口设备文件的封装。它通过open("/dev/ttySx")这样的操作来访问物理串口,然后提供统一的API给上层应用。
这就让我产生了第一个想法:既然CH9344在Linux内核中注册为标准的tty设备(会产生/dev/ttyCH9344USB0之类的设备节点),那么理论上HDF驱动应该能直接识别和使用这些设备。但实际测试发现,事情并没有这么简单——直接编译驱动后,应用层根本无法正确打开CH9344创建的串口设备。
2. CH9344驱动移植详解
首先需要获取CH9344的官方Linux驱动包。解压后重点关注driver目录下的.c和.h文件。将这些文件复制到内核源码的linux-5.10/drivers/usb/serial/目录下,这是USB转串口驱动的标准存放位置。
接下来要修改内核配置系统。编辑Kconfig文件,添加以下内容:
config USB_SERIAL_CH9344
tristate "USB Winchiphead ch9344 four Port Serial Driver"
help
Say Y here if you want to use a Winchiphead CH9344 four port USB to serial adapter.
然后在同目录的Makefile中添加编译规则:
obj-$(CONFIG_USB_SERIAL_CH9344) += ch9344.o
完成这些修改后,需要配置内核。执行make menuconfig,在Device Drivers → USB support → USB Serial Converter support中找到"USB Winchiphead ch9344 four Port Serial Driver"选项,将其编译为模块或直接内置到内核中。建议选择<Y>直接编译进内核,避免模块加载的复杂性。
编译内核时我遇到了一个坑:虽然配置了CONFIG_USB_SERIAL_CH9344=y,但编译后驱动并没有生效。后来发现是内核配置没有正确保存。解决方法是直接编辑.config文件,确保有以下行:
CONFIG_USB_SERIAL_CH9344=y
编译成功后,可以在/dev目录下看到新出现的ttyCH9344USB0到ttyCH9344USB3设备节点,这说明驱动已经成功加载。
3. HCS配置文件的关键配置
HDF驱动需要通过HCS配置文件来识别和管理硬件设备。在OpenHarmony中,串口设备的配置通常位于//vendor/hihope/rk3568/hdf_config/khdf/platform/目录下。
我们需要在现有的串口配置基础上添加CH9344的设备信息。以下是一个配置示例:
device_uart_ch9344_0 :: uart_device {
num = 10;
driver_name = "ttyCH9344USB";
match_attr = "rockchip_rk3568_uart_10";
}
device_uart_ch9344_1 :: uart_device {
num = 11;
driver_name = "ttyCH9344USB";
match_attr = "rockchip_rk3568_uart_11";
}
这里有几个关键参数需要特别注意:
num参数很重要,它会被HDF驱动用来构造完整的设备路径。比如num=10,driver_name="ttyCH9344USB",最终会尝试打开/dev/ttyCH9344USB10driver_name必须与内核中注册的设备名前缀完全一致,大小写敏感match_attr需要与device_info.hcs中的配置对应,这是HDF框架识别设备的关键标识
配置完成后,需要将HCS文件编译为HCB二进制格式。有时候修改HCS后不生效,可能是因为系统使用了旧的HCB文件。解决方法是删除旧的HCB文件,强制重新编译生成新的HCB。
4. 问题排查与解决方案
在实际适配过程中,我遇到了几个典型问题,这里分享排查思路和解决方法。
应用层port参数问题:在应用调用UartOpen(port)时,port参数应该填什么值?通过分析HDF源码发现,port参数实际上对应HCS配置中的num值。如果你配置的num=10,那么port就应该传10。
阻塞模式失效问题:测试发现设置阻塞模式后,读操作仍然立即返回。查阅HDF串口驱动源码发现,阻塞模式的实现存在缺陷。在uart_adapter.c中,需要修改UartRead函数的实现,正确设置文件描述符的阻塞标志位:
// 修改前:缺少阻塞设置
int flags = fcntl(fd, F_GETFL);
// 修改后:根据模式设置阻塞标志
if (mode == BLOCKING_MODE) {
flags &= ~O_NONBLOCK;
} else {
flags |= O_NONBLOCK;
}
fcntl(fd, F_SETFL, flags);
串口接收异常问题:能够发送数据但无法接收。通过添加调试打印发现,在设置串口参数时,某些必要的标志位没有被正确设置。需要在驱动中确保CRTSCTS、CREAD等标志位被正确配置:
struct termios options;
tcgetattr(fd, &options);
options.c_cflag |= (CLOCAL | CREAD); // 添加本地连接和接收使能
tcsetattr(fd, TCSANOW, &options);
内核死循环风险:在分析HDF串口驱动源码时,发现一个潜在的死循环问题。在阻塞读取模式下,当接收数据不完整时,代码可能会陷入无限循环。需要修改循环条件,添加超时机制:
// 修改前:可能死循环
while (size >= tmp) {
// 读取操作
}
// 修改后:添加超时保护
int timeout = 1000; // 超时时间
while (size >= tmp && timeout-- > 0) {
// 读取操作
usleep(1000); // 适当休眠
}
这些修改都需要重新编译内核或驱动模块,建议在修改前备份原始文件,方便出现问题时的回退。
5. 效果验证与测试
完成所有配置和修改后,需要进行全面的功能测试。首先编写一个简单的测试程序:
#include <stdio.h>
#include "uart_if.h"
void test_ch9344_uart() {
DevHandle handle = UartOpen(10); // 对应HCS中的num值
if (handle == NULL) {
printf("Open UART failed!\n");
return;
}
// 配置串口参数
UartSetBaud(handle, 115200);
// 其他配置...
// 测试发送
uint8_t data[] = "Hello CH9344!";
UartWrite(handle, data, sizeof(data));
// 测试接收
uint8_t buffer[128];
int received = UartRead(handle, buffer, sizeof(buffer));
printf("Received %d bytes\n", received);
UartClose(handle);
}
测试时需要注意物理连接的正确性。CH9344的TX应该接目标设备的RX,RX接目标设备的TX,地线要共地。如果使用RS485模式,还需要正确连接方向控制引脚。
通过dmesg | grep ch9344可以查看内核日志,确认驱动加载状态和设备识别情况。如果一切正常,应该能看到类似这样的输出:
ch9344 1-1.2:1.0: ch9344 converter detected
usb 1-1.2: ch9344 converter now attached to ttyCH9344USB0
6. 性能优化建议
在实际使用中,我发现CH9344的性能还可以进一步优化。首先建议调整内核的USB相关参数,提高传输效率:
# 增加USB核心的传输缓冲区大小
echo 1000 > /sys/module/usbcore/parameters/usbfs_memory_mb
# 调整USB中断处理频率
echo 1 > /sys/bus/usb/devices/usb1/power/autosuspend_delay_ms
对于高速数据传输场景,可以考虑修改HDF驱动的缓冲区大小。在uart_adapter.c中调整读写缓冲区的大小:
#define READ_BUFFER_SIZE 4096 // 默认是1024
#define WRITE_BUFFER_SIZE 4096
另外,如果遇到数据丢失问题,可以启用流控功能。在HCS配置中添加硬件流控支持:
device_uart_ch9344_0 :: uart_device {
num = 10;
driver_name = "ttyCH9344USB";
match_attr = "rockchip_rk3568_uart_10";
flow_control = 2; // 1:软件流控, 2:硬件流控
}
7. 实际项目中的注意事项
在真实项目环境中,有几点特别需要注意。首先是电源管理问题,CH9344作为USB设备,容易受到电源噪声干扰。建议在硬件设计时添加适当的滤波电容,并在软件中配置合理的电源管理策略:
// 禁用USB自动挂起
echo -1 > /sys/bus/usb/devices/1-1.2/power/autosuspend_delay_ms
其次是热插拔支持。在实际部署中,USB设备可能会被频繁插拔,需要确保驱动能够正确处理这些事件。在驱动中需要完善disconnect和probe函数的实现,添加必要的错误处理和资源释放逻辑。
最后是日志记录,建议在驱动中添加详细的调试信息,方便后续的问题排查:
// 在关键函数中添加日志输出
HDF_LOGI("CH9344 probe started, device=%s", dev_name(dev));
这些经验都是从实际项目中总结出来的,希望能够帮助大家少走弯路。如果在实施过程中遇到其他问题,建议仔细阅读内核文档和CH9344的数据手册,很多时候问题的答案就在这些文档中。
318



被折叠的 条评论
为什么被折叠?



