上周有个朋友问我一个问题:同样是把手里的开发板外设跑起来,为什么有的教程让写HDF驱动,有的教程直接把一个 .ko 内核模块编进去,还有的教程只是改改内核配置就把CH340、CP2102这类芯片识别出来了?他一开始以为这三种说法是新旧版本差异,翻了很多OpenHarmony资料还是懵。

这个困惑很典型。OpenHarmony的驱动开发不像单纯的Linux驱动那样只有一条主线,它天然存在三条路径:官方主推的HDF驱动框架、传统Linux内核模块、以及内核配置层面的外设适配。这三条路径不是替代关系,而是对应不同场景。折腾过几个项目之后,我越来觉得这个“三条路径”的框架值得单独写一篇梳理清楚,因为大多数人在开头选错路径,后面就一直在跟框架、配置、编译环境较劲,反而把真正的硬件工作给耽误了。

下面的内容适合三类人:刚接触OpenHarmony、想知道驱动该从哪下手的初学者;已经在Linux里写过驱动、想对比HDF差异的移植开发者;以及只想把手头板子的串口、网口、传感器快速用起来、不打算深挖框架的“拿来主义”选手。

1. 先区分三条路径:不是新旧之争,而是场景之分

1.1 为什么OpenHarmony驱动开发不能只学一种姿势

OpenHarmony的驱动子系统在设计上跟传统Linux有一个巨大的区别:它同时在支撑LiteOS和Linux两类内核,还要给上层应用提供统一、可裁剪的硬件访问能力。如果沿用Linux那套“一切皆文件、驱动就是file_operations”的模式,上层服务就得直接面对内核版本差异、设备节点混乱、权限管理各自为政的问题。

所以OpenHarmony在Linux内核驱动之上又建了一层HDF(HarmonyOS Driver Foundation)框架。HDF做的事情,相当于给驱动安了一个统一“入口系统”:驱动模块怎么被加载、怎么暴露服务、上层应用怎么找到这个设备、权限怎么控制,全部由HDF接管。这就是第一条路径,也是官方最推荐的方式。

但问题在于,HDF框架本身有学习成本,而且OpenHarmony的Linux内核仍然保留了完整的Linux驱动能力。于是就有了第二条路径:直接写一个传统内核模块, insmod 加载,用 /dev 节点或者procfs、sysfs跟用户态通信。这种方式在快速验证硬件寄存器、移植现成Linux驱动、做原型Demo时非常高效。

至于第三条路径,是最容易被“搞驱动的人”忽略的:OpenHarmony内核本质上还是基于Linux内核维护的,内核源码的Kconfig里躺着大量已经写好的现成驱动。很多外设根本不需要你写一行驱动代码,只需要在 defconfig 里把对应的配置项打开,重新编译内核,硬件就被识别了。USB转串口芯片、USB网卡、存储控制器、I2C控制器,全都属于这一类。

这三条路径的关系可以用一个很朴素的类比:HDF是给设备建了一个正规的“物业服务处”,设备要上报、要跟业主沟通都得经过物业;裸内核模块是“自己开门做生意”,直接面对内核和设备,灵活但管理粗糙;内核配置则是“精装房直接拎包入住”,前提是房子已经盖好了,你只需要把门锁换掉。

1.2 三条路径的选型对照表

路径 开发方式 典型场景 学习成本 与OpenHarmony系统服务的关联
HDF驱动框架 用HDF提供的DriverEntry模型,编写Bind/Init/Release,配HCS设备描述符 新产品量产的正式驱动、需要接入系统电源管理/服务发布/权限控制的设备、传感器/显示/音频/输入类设备 强。上层HAL和服务可以直接通过HDF接口访问设备
传统Linux内核模块 写module_init/module_exit,通过file_operations或miscdevice提供节点 快速验证硬件寄存器、移植现成Linux驱动、临时调试工具、不想接HDF的内部模块 弱。设备节点对上层是独立的,系统服务感知不到,需要自己处理权限
内核配置适配 修改defconfig,打开Kconfig里现成的驱动配置项 USB转串口、USB网卡、存储、部分显示/声卡、调试器适配 中。设备节点正常生成,但仍然是传统Linux设备模型,不走HDF

实际项目里,这三条路径经常是混用的。我见过很多量产方案的驱动架构是:核心传感器走HDF,带日志和在线升级能力;一块临时用的扩展板先挂个裸模块跑起来验证;主板上的CH340串口和USB网卡直接通过内核配置打开,完全没写驱动。

1.3 贯穿本文的实验参考

后面章节里用到的命令和路径,我主要以OpenHarmony 3.2/4.x版本的Linux 5.10内核为参照,这套操作在RK3568这类主流开发板上比较通用。如果你用的是其他版本或者其他芯片平台,位置可能略有差异,但逻辑完全一样。关键就是要会对自己手里的源码树“找到对应位置”,别死记路径。

准备一个最小实验环境,我建议不要一上来就买开发板。先在你的电脑上装个Ubuntu,配合QEMU跑一个x86的OpenHarmony镜像,或者直接在x86电脑上刷OpenHarmony的社区桌面版本,这样反复编译内核、验证模块的成本最低。等把整个编译、打包、启动流程摸熟,再切到真机板子上操作,问题会少很多。社区里经常有人问“为什么我的开发板编译一次要半小时、刷机还要拆Flash”,多半就是没先在虚拟机里把流程跑通。

2. HDF驱动框架路径:OpenHarmony官方主推的驱动开发方式

2.1 HDF的基本运行模型:从设备描述符到DriverEntry

HDF驱动的核心思想是“描述与实现分离”。你写了一个驱动,但它什么时候加载、挂在哪个host下面、对外怎么命名,不是由C代码里的 module_init 决定的,而是由HCS设备描述符决定的。

HCS是HDF的配置格式,后缀是 .hcs ,编译时会转成二进制,最终打包进系统镜像。系统启动时,HDF框架会解析这些描述符,把设备树里的节点和驱动模块里的 moduleName 配对,配对成功就调驱动的 Bind Init 。所谓“设备挂载”,本质上是这个配对过程。

我自己刚开始接触HDF时,最大的误区是先写C代码,写完才发现不知道怎么把驱动“挂上去”。后来才意识到,对HDF来说, .hcs 是驱动骨架的一部分,甚至比C代码更关键。一个驱动能不能被加载、设备节点怎么暴露,基本靠HCS描述符来控制。

2.2 一个最小HDF字符驱动骨架

先看C代码部分。这里用一个最小的LED驱动举例,它不做具体硬件操作,只把驱动生命周期打印出来,让你确认框架确实加载了它:

#include "hdf_base.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"

#define HDF_LOG_TAG my_led_driver

static int32_t MyLedBind(struct HdfDeviceObject *device)
{
    HDF_LOGI("MyLedBind enter");
    return HDF_SUCCESS;
}

static int32_t MyLedInit(struct HdfDeviceObject *device)
{
    HDF_LOGI("MyLedInit enter");
    return HDF_SUCCESS;
}

static void MyLedRelease(struct HdfDeviceObject *device)
{
    HDF_LOGI("MyLedRelease enter");
}

struct HdfDriverEntry g_myLedDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "my_led_driver",
    .Bind = MyLedBind,
    .Init = MyLedInit,
    .Release = MyLedRelease,
};

HDF_INIT(g_myLedDriverEntry);

这段代码的套路是固定的。 Bind 负责把驱动跟设备服务绑定, Init 做硬件初始化, Release 做释放。绝大多数HDF驱动都是这个模板加具体业务逻辑。

注意 moduleName 是字符串 "my_led_driver" ,后面 .hcs 描述符里必须有一个节点的 moduleName 跟它完全一致,大小写都不能差。这里的 HDF_INIT 宏相当于传统驱动的 module_init ,但它注册的是HDF驱动入口,不是内核模块入口。

2.3 HCS设备描述符:驱动是怎么被挂到系统里的

接着写设备描述符,新建一个 my_led_config.hcs

root {
    my_led_host :: host {
        device_my_led :: device {
            device0 :: deviceNode {
                policy = 2;
                priority = 100;
                permission = 0664;
                moduleName = "my_led_driver";
                deviceMatchAttr = "my_led_config";
            }
        }
    }
}

这个格式看着唬人,拆开其实就三部分。 host 是一个独立的驱动宿主环境,你可以把它理解为一个驱动仓,里面放一堆同类型的设备驱动。 device 表示这个host下挂了一类设备。 deviceNode 才是具体的设备节点,声明了驱动的名字、匹配属性和对外权限。

policy 字段值得单独说。它决定驱动的服务模式: 0 表示不对外发布服务, 1 表示内核态可用, 2 表示同时支持内核态和用户态访问。很多新手写HDF驱动后发现用户态程序打不开设备,大概率就是 policy 设成了0或者1。

配好之后,需要把这个 .hcs 文件加入到驱动构建的hdf hcs列表中。不同版本的构建组织方式不太一样,但常见做法是在 device/board/xxx/liteos_a 或对应的 hdf 配置目录里添加 hcs 条目,然后执行整包编译。先别管编译细节,重要的是理解这个流程:C代码负责实现,HCS负责描述,编译打包把两者配对,启动阶段完成加载。

2.4 为什么要优先考虑HDF:系统层面的收益

可能有人觉得,HDF这套流程比传统Linux驱动繁琐多了。但HDF带来的收益也是实实在在的。第一,用户态驱动的支持。HDF允许驱动逻辑的一部分跑在用户态,这样可以把复杂的算法逻辑放到用户态调,省得动不动就崩内核。第二,服务发布与订阅。驱动的能力可以通过HDF的服务接口发布给上层,应用不需要知道设备节点具体是 /dev/hdf/xxx 还是 /dev/xxx ,只要拿到服务句柄就行。第三,系统级节电和状态管理。HDF驱动可以更方便地接入OpenHarmony的电源管理、热插拔和权限控制体系。

真实项目里,如果这个设备是主板上的核心外设,比如陀螺仪、触控屏、指纹模组、音频Codec,我一定建议走HDF。因为后面系统框架层的传感器服务、输入服务、音频服务默认就是从HDF拿数据,你如果只写一个裸模块,想对接正式框架就得自己搭一套胶水层,工作量反而更大。

3. 裸内核模块路径:绕过HDF直接操作内核的务实选择

3.1 什么时候该放弃HDF去写.ko

既然HDF是官方推荐,为什么还要聊裸模块?因为实际开发中,HDF不是万能的。以下几种情况,裸模块反而是更高效的选择。

第一种是快速验证。芯片寄存器地址都不知道对不对的时候,要先用一段最简单的代码读写寄存器,确认上电时序和引脚定义。这种验证代码没必要写成HDF驱动,一个 .ko 加载进去、 dmesg 看输出、完事卸载,十分钟就验证完了。

第二种是移植现成Linux驱动。很多传感器、显示屏、网卡芯片,Linux内核里已经有厂商驱动或者开源驱动,代码形态是标准的内核模块。要把它们搬到OpenHarmony上,最快的方式是先当成裸模块编译跑通,确认硬件功能正常,再慢慢封装成HDF驱动接进系统。直接一上来就改造成HDF,很可能把驱动本身的问题和框架适配问题混在一起,排查很痛苦。

第三种是内核内部的调试工具。比如你想抓GPIO中断频率、要看电源域的寄存器状态、要给某个设备临时加一个调试接口。这些工具没必要暴露给上层服务,裸模块加个debugfs入口就够用了。

3.2 模块Makefile和依赖管理

OpenHarmony内核里编译裸模块的方式跟标准Linux几乎一样。写一个最简单的模块:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init demo_init(void)
{
    printk(KERN_INFO "demo module loaded\n");
    return 0;
}

static void __exit demo_exit(void)
{
    printk(KERN_INFO "demo module unloaded\n");
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("your-name");

Makefile长这样:

obj-m := demo.o
KERNELDIR := /path/to/openharmony/kernel/linux
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- clean

这里有几个细节要提醒。 KERNELDIR 指向的必须是OpenHarmony实际编译用的内核源码树,而且这个源码树不能是全新的,得是已经执行过内核配置、生成了 Module.symvers 和头文件链接的树。你拿一台普通电脑上编译好的Ubuntu内核源码树去编OpenHarmony的模块,大概率会报版本不一致。交叉编译工具链前缀按平台改,ARM64用 aarch64-linux-gnu- ,ARM32用 arm-linux-gnueabihf-

编译完会得到 demo.ko 。传到开发板上执行:

insmod demo.ko
dmesg | tail
lsmod
rmmod demo

如果模块依赖其他符号,比如依赖内核里某个导出函数,编译完会出现 demo.ko insmod 时说“unknown symbol”。这时候你要检查被依赖的模块是否已经加载,或者它的符号是否导出了。

3.3 开机自动加载与节点权限问题

裸模块不像HDF那样有HCS描述符自动管理,开机自启需要自己做。最快的方案是把 insmod 命令写进系统启动脚本里,或者把 .ko 放到内核模块加载目录,用 modprobe 配合 modules.alias 加载。OpenHarmony不同版本的自启脚本位置不太一样,常见的是 init 进程的cfg文件或者 /system/etc/init/*.cfg ,格式类似:

{
    "services": [{
        "name": "demo_module",
        "path": ["/vendor/bin/insmod", "/vendor/modules/demo.ko"],
        "once": true
    }]
}

还有一个很容易被忽略的问题:裸模块生成的设备节点的权限。默认情况下,设备节点可能是 root:root 、权限 0600 ,上层应用根本打不开。这时候要么改 udev 规则(如果内核支持),要么在模块里初始化设备时用 class_create device_create 创建好节点并设置好权限,要么在系统服务里用 chmod chown 修正。HDF框架里一个 permission = 0664 字段就能搞定的事,裸模块需要自己处理,这也是很多人说“裸模块不好做产品”的原因之一。

3.4 裸模块与HDF互操作的边界

还有一个小众但很实际的需求:能不能让HDF驱动去调用一个裸模块的导出接口?答案是可以的。裸模块可以用 EXPORT_SYMBOL 导出函数,HDF驱动的C代码直接声明外部函数来调用,链接层面和普通内核符号一样。反过来,裸模块访问HDF服务就比较麻烦,因为HDF的服务接口不是简单内核符号,要走HDF提供的API。

所以我的经验是:核心外设走HDF,辅助调试和一次性验证走裸模块。两个世界可以共存,但别把业务逻辑拆得稀碎,否则代码维护起来很痛苦。

4. 内核配置路径:让现成驱动直接可用的60%解法

4.1 你不需要写代码的外设驱动:串口芯片、调试器与网卡

很多刚接触OpenHarmony的人,一听说“驱动”两个字就觉得要写C代码。实际上,OpenHarmony内核沿用了Linux内核的配置体系,Kconfig里已经包含了成千上万个现成驱动。你要做的只是把对应的配置开关打开。拿大家都熟悉的几个芯片举例。

比如CH340、CH341这类USB转串口芯片,对应配置项是:

CONFIG_USB_SERIAL_CH341=y

CP2102对应:

CONFIG_USB_SERIAL_CP210X=y

FT232R、FT231X、FT232系列对应:

CONFIG_USB_SERIAL_FTDI_SIO=y

只要内核的USB host支持正常,把这些配置项打开,插上USB转串口线,系统就会自动生成 /dev/ttyUSB0 这种设备节点,根本不需要写驱动。类似的情况还有USB网卡、USB蓝牙、部分USB声卡、NVMe存储控制器等等,全都可以在Kconfig里找到对应开关。

J-Link、ST-Link这类调试器在OpenHarmony设备上又不太一样。它们不是“用户系统要驱动的外设”,而是“用户系统要作为USB设备被电脑端工具识别的”。如果你在开发板上跑OpenHarmony,然后通过J-Link接电脑调试,那不是给OpenHarmony装驱动,而是给电脑端的J-Link软件装驱动。当然,如果你的OpenHarmony设备要作为USB Host去访问一个J-Link探针或者ST-Link探针,那一般也不需要额外驱动,Linux标准的USB HID或USB bulk传输支持就够了。很多人在这个问题上绕圈子,其实是没分清“Host端”和“Device端”的角色。

4.2 defconfig下手实操:内核配置在哪改

OpenHarmony内核源码树的配置文件和Linux主线有个小差异。Linux主线一般用 arch/arm64/configs/xxx_defconfig 作为默认配置,OpenHarmony也有类似文件,但往往还会通过构建脚本动态修改。比较稳妥的做法是先看你现在用的内核源码树里的构建文档,找到目标平台对应的 defconfig

以Linux 5.10内核为例,常见位置是:

kernel/linux/linux-5.10/arch/arm64/configs/

里面可能有 ok3568_defconfig rk3568_defconfig 或者厂商自定义的文件。修改方式有两种:

一种是直接编辑 defconfig 文件,把对应的 CONFIG_XXX=n 改成 CONFIG_XXX=y ,或者在文件末尾追加一行,然后重新编译内核。这种方式适合一次改一堆配置项,方便做配置基线管理。

另一种是在已经配置好的源码树里运行:

make ARCH=arm64 menuconfig

然后进入 Device Drivers -> USB support -> USB Serial Converter support ,在里面勾选对应的串口芯片驱动。保存后配置会写到 .config 里,你可以用 make savedefconfig 生成精简的配置,或者直接复制 .config 覆盖原来 defconfig 来固化改动。

还有一类驱动是以外部模块方式存在的,配置项是 =m ,编译后生成 .ko ,需要手动加载。建议在OpenHarmony设备上优先使用 =y 把常用驱动编进内核,能省掉不少insmod和权限的麻烦。

4.3 验证外设是否工作的完整链路

配置改完、内核编译完、系统启动后,怎么判断这个驱动真的生效了?不要只看一个命令。我一般按下面的顺序走一遍。

先看设备是否被USB总线枚举到:

lsusb

CH340的厂商ID通常是 1a86 ,CP2102是 10c4 ,FT232是 0403 。如果这条命令里能看到对应ID,说明硬件连接和USB控制器都是好的,问题大概率在内核驱动配置。

再看驱动是否绑定成功:

dmesg | grep -i tty
dmesg | grep -i usb

如果看到 ch341-uart converter now attached to ttyUSB0 这类的输出,说明串口驱动已经绑定成功。如果没有,先查 /sys/bus/usb/devices/ 下面的目录结构,看看当前设备处于什么状态。

最后看设备节点:

ls -l /dev/ttyUSB*

能列出 ttyUSB0 就说明用户态可以访问了。再用 echo cat 或者 stty 做一次最基础的读写测试。

这三步走完,90%的“外设没反应”问题都能定位出来:看不到ID是硬件问题,看到ID没绑定是驱动配置问题,看到节点没数据是应用层或模式问题。这个套路无论在OpenHarmony、普通Linux还是Android里都通用。

4.4 内核配置与模块编译的联动关系

配置驱动的过程中有一个关键机制要理解:内核配置项不只决定驱动是否编译,还决定了它怎么编译。 =y 编进内核镜像, =m 编成独立模块, =n 不编译。而一个驱动具体是哪种类型,由对应的 Makefile 里这段逻辑控制:

obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o

CONFIG_USB_SERIAL_CH341=y 时, obj-y 会把 ch341.o 链接进内核。当 CONFIG_USB_SERIAL_CH341=m 时, obj-m 会把它编成 ch341.ko 。搞清楚这条链路,你才能理解为什么改了defconfig之后需要重新编译整个内核镜像,也才能理解为什么有时候只编译模块不够,必须整包重编。

5. 三条路径共享的踩坑清单与调试手段

5.1 驱动编进内核但设备节点不出现

这是最高频的问题,三条路径都逃不过。

如果是HDF路径,先查 /dev/hdf/ 目录下有没有对应的设备节点,再用 hdc shell 进去看内核日志: hilog dmesg 。常见原因是HCS里 policy 配置不对,或者 moduleName 跟C代码里的对不上。HDF日志里通常会打印“Load device failed”类似的字样,根据日志里的错误码去翻 drivers/hdf_core/framework 里的错误码定义。

如果是裸模块或者内核配置路径,先确认驱动确实加载了:

lsmod
dmesg | grep -i driver_name

再检查设备创建是否被注册。老牌的排查思路是看 /sys/class/ /sys/bus/ 下有没有你的设备的子目录。如果驱动已经加载但没有创建设备,多半是驱动自己的 probe 没有执行,或者设备树里根本没有匹配的节点。很多人听到“设备树”觉得头疼,但排查到最后,往往问题就出在设备树里某个compatible字符串和驱动里对不上。

5.2 串口工具找不到ttyUSB0:不只是驱动的问题

很多人在OpenHarmony里用CH340,驱动配置已经打开了, lsusb 也能看到芯片,但 /dev/ttyUSB0 就是不出现。这种情况先别怀疑驱动,先看看系统里有没有被 modem 或者 getty 之类的进程占用,再检查USB电源管理有没有把设备挂起。

另一个常见原因是rootfs里没有 /dev 节点自动创建的规则。OpenHarmony的devtmpfs如果没配对,设备节点不会自己冒出来。临时解决办法是手动执行:

mknod /dev/ttyUSB0 c 188 0
chmod 666 /dev/ttyUSB0

但这个办法只是应急,调试完还得把自动创建设备节点的机制修复,否则重启又没了。

5.3 依赖符号找不到与内核版本不匹配

编译裸模块报“Unknown symbol”是家常便饭。这类问题的本质是:你编译模块时用的内核源码树,和你运行模块的目标内核镜像不是同一个。符号表对不上,自然找不到。

怎么确认?先在目标设备上查找内核符号让你使用导出符号,OpenHarmony内核一般留了 /proc/kallsyms ,可以把它拷出来,然后在你编译模块的源码树里对比。如果直接对比太麻烦,最简单的办法是从头完整编一次你正在用的内核,然后把模块源码放到内核树外编译,保证 Module.symvers 一致。踩过几次坑之后我养成一个习惯:每拿到一套新SDK,第一件事就是把内核源码完整编译一遍,生成好 Module.symvers arch/arm64/include/generated/ 下的头文件,之后的模块编译就顺畅了。

5.4 调试手段要提前配置好

不管走哪条路径,内核尽量都把这几项调试能力打开,别等到出问题了才后悔:

CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_KALLSYMS=y
CONFIG_KPROBES=y

有了这些,你可以用 debugfs 查看内核内部状态,用 tracefs trace_printk 做轻量级日志,用 kprobe 在不停机的状况下挂到任意内核函数上。特别是 kprobe ,排查“为什么probe函数没执行”这类问题极其好用,比一遍遍重新编译内核高效多了。

5.5 HDF与裸模块混用时的命名冲突

最后提一个容易被忽略的坑:HDF驱动的 moduleName 和内核模块名是两个独立命名空间,但它们可能会在日志、sysfs里撞名。比如你有一个HDF驱动的 moduleName foo ,又加载了一个 foo.ko ,日志会产生误导,排查时经常分不清到底加载的是哪个。

我的习惯是给HDF驱动加统一前缀,比如 hdf_foo ,裸模块保持 foo ,这样从日志和 lsmod 输出里一眼就能区分。这个习惯帮我避免好多次看错日志白费时间。

6. 从零到一手把手:一个完整的驱动验证流程示例

前面把三条路径都展开了,最后用一个综合示例把它们串起来。假设你现在拿到了一块RK3568开发板,OpenHarmony系统已经能正常启动,目标是把一个USB转串口的CH340识别出来,并且跑一个简单的串口读写程序。

第一步,先不要急着写驱动。在电脑上准备OpenHarmony内核源码树,执行完整编译,确保内核源码的生成文件完整。这一步大约会花几十分钟,但能节省后面所有排错时间。

第二步,在内核源码树里确认或修改 defconfig

cd kernel/linux/linux-5.10
grep -n "CONFIG_USB_SERIAL_CH341" arch/arm64/configs/xxx_defconfig

如果没有就追加一行:

CONFIG_USB_SERIAL_CH341=y
CONFIG_USB_SERIAL_CP210X=y
CONFIG_USB_SERIAL_FTDI_SIO=y

第三步,用编译命令构建新内核和OpenHarmony系统镜像。OpenHarmony官方推荐用 hb 工具,具体构建命令按你SDK的文档来,一般是:

hb build -f -p rk3568

构建完成后,把新镜像烧录到开发板或者启动到QEMU里。

第四步,系统启动后插入CH340模块,执行验证:

lsusb
dmesg | grep -i ch341
ls -l /dev/ttyUSB0

如果 ttyUSB0 出现了,恭喜你,三次路径里的“内核配置路径”已经走通了。如果没出现,回去看第5章的排查清单。

第五步,如果你想体验HDF路径,就把第2章那个最小LED驱动也编进去。这里的建议是:在同一个源码树里,先保证裸模块和内核配置路径都验证通过,再往HDF上靠。一步一步来,不要把“跑起来一个设备”的目标和“学会HDF框架”的目标混在一起。

最后说句实在话

折腾OpenHarmony驱动开发这段时间,我最大的一个体会是:别被“框架”两个字吓住。HDF确实复杂,但它解决的是一个很实在的问题——让驱动和服务之间有统一、可控的协作方式。而很多硬件问题本质上是内核配置和硬件连接问题,跟用什么框架一点关系都没有。先把三条路径的分界搞清楚,再根据手里的任务选对路径,你就能少走很多弯路。

如果你刚开始接触,建议顺序是这样:第一周只做内核配置路径的事情,把手头板子上的串口、网卡、存储全都跑通;第二周试着写一个裸模块,把一个IO口拉高拉低,用示波器或者万用表确认引脚状态;第三周再把同样的逻辑改写成HDF驱动。等这三周过去,你对OpenHarmony驱动开发的理解,会比你盲目刷两个月教程要扎实得多。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐