鸿蒙 HDF 驱动开发初探:如何为 Hi3861 开发板编写一个自定义的 LED 驱动?
你可能会问:“直接操作寄存器或者调 GPIO 库不香吗?为什么要套这么厚一层 HDF 壳子?HDF 的核心价值:解耦:驱动代码不依赖具体的 OS 内核。统一管理:系统统一加载、管理驱动的生命周期(加载、初始化、卸载)。安全性:在标准系统(Standard)中,HDF 驱动运行在用户态,挂了也不会导致内核崩溃。fill:#333;important;important;fill:none;color
标签: #HarmonyOS #HDF #驱动开发 #Hi3861 #嵌入式 #南向开发
🧬 前言:为什么一定要用 HDF?
你可能会问:“直接操作寄存器或者调 GPIO 库不香吗?为什么要套这么厚一层 HDF 壳子?”
HDF 的核心价值:
- 解耦:驱动代码不依赖具体的 OS 内核。
- 统一管理:系统统一加载、管理驱动的生命周期(加载、初始化、卸载)。
- 安全性:在标准系统(Standard)中,HDF 驱动运行在用户态,挂了也不会导致内核崩溃。
HDF 驱动架构图 (Mermaid):
📝 一、 配置文件:描述你的硬件 (HCS)
鸿蒙引入了 HCS (HDF Configuration Source),类似于 Linux 的设备树 (Device Tree)。我们需要告诉系统:有一个名为 sample_led 的设备。
找到源码中的 device_info.hcs (通常在 vendor/hisilicon/hispark_pegasus/hdf_config/ihct/ 下),添加节点:
root {
device_info {
match_attr = "hdf_manager";
template host {
hostName = "host0";
priority = 100;
// 定义一个名为 sample_host 的宿主
device_sample :: device {
device0 :: deviceNode {
policy = 2; // 2: 对外发布服务,应用层可见
priority = 100;
preload = 0; // 0: 按需加载 1: 随系统启动
permission = 0664;
moduleName = "HDF_SAMPLE_LED"; // ⚠️ 关键:要与C代码匹配
serviceName = "hdf_led_service"; // 应用层绑定的名字
deviceMatchAttr = "sample_led_config";
}
}
}
}
}
💻 二、 驱动实现:核心 C 代码
在 drivers/hdf_core/adapter/khdf/liteos_m/ (或其他驱动目录) 下新建 hdf_led_driver.c。
驱动开发的核心就是填充 HdfDriverEntry 结构体。
1. 定义控制指令
#include "hdf_device_desc.h"
#include "hdf_log.h"
#include "gpio_if.h"
// LED 连接的 GPIO 管脚 (Hi3861通常是 GPIO 9)
#define LED_GPIO_PIN 9
// 命令码
enum {
LED_WRITE_ON = 1,
LED_WRITE_OFF,
};
2. 业务逻辑 (Dispatch)
这是应用层调用驱动的入口。
// 接收用户态发来的指令
static int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply)
{
if (cmdId == LED_WRITE_ON) {
// 点亮 LED (低电平还是高电平取决于电路,假设低电平亮)
GpioWrite(LED_GPIO_PIN, 0);
HDF_LOGI("LED Driver: ON");
} else if (cmdId == LED_WRITE_OFF) {
// 熄灭 LED
GpioWrite(LED_GPIO_PIN, 1);
HDF_LOGI("LED Driver: OFF");
} else {
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
3. 生命周期绑定 (Bind & Init)
// 1. 驱动对外提供的服务对象
static int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
{
static struct IDeviceIoService service = {
.Dispatch = LedDriverDispatch, // 绑定分发函数
};
deviceObject->service = &service;
return HDF_SUCCESS;
}
// 2. 驱动初始化 (设置 GPIO 方向)
static int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject)
{
GpioSetDir(LED_GPIO_PIN, GPIO_DIR_OUT);
HDF_LOGI("Hdf Led Driver Init Success!");
return HDF_SUCCESS;
}
// 3. 驱动卸载
static void HdfLedDriverRelease(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("Hdf Led Driver Released");
}
4. 注册驱动
这一步将 C 代码与 HCS 配置文件关联起来。
// 定义驱动入口
struct HdfDriverEntry g_ledDriverEntry = {
.moduleVersion = 1,
.moduleName = "HDF_SAMPLE_LED", // ⚠️ 必须与 HCS 中的 moduleName 一致
.Bind = HdfLedDriverBind,
.Init = HdfLedDriverInit,
.Release = HdfLedDriverRelease,
};
// 宏注册
HDF_INIT(g_ledDriverEntry);
🔨 三、 编译构建:GN 脚本
鸿蒙使用 GN + Ninja 进行构建。你需要修改 BUILD.gn 文件,将你的 .c 文件加入编译列表。
hdf_driver("hdf_sample_led") {
sources = [
"hdf_led_driver.c",
]
include_dirs = [
"//drivers/hdf_core/framework/include",
"//drivers/hdf_core/framework/include/core",
"//drivers/hdf_core/adapter/khdf/liteos_m",
]
}
🕹️ 四、 应用层调用:点灯时刻
最后,我们写一个简单的 APP 来验证驱动。
#include "hdf_io_service_if.h"
void TestLedDriver() {
// 1. 获取驱动服务 (对应 HCS 中的 serviceName)
struct HdfIoService *serv = HdfIoServiceBind("hdf_led_service");
if (serv == NULL) {
printf("Fail to bind service!\n");
return;
}
// 2. 发送指令
static struct HdfSBuf *data = NULL;
static struct HdfSBuf *reply = NULL;
// 亮灯
printf("Send command: ON\n");
serv->dispatcher->Dispatch(&serv->object, 1, data, reply);
osDelay(100); // 延时
// 灭灯
printf("Send command: OFF\n");
serv->dispatcher->Dispatch(&serv->object, 2, data, reply);
// 3. 回收资源
HdfIoServiceRecycle(serv);
}
🎯 总结
通过这个案例,我们走通了鸿蒙驱动开发的完整链路:
- HCS 配置:像填写简历一样描述硬件。
- HdfDriverEntry:像填写表格一样实现驱动生命周期。
- HdfIoServiceBind:像调用 API 一样在应用层控制硬件。
这比直接写裸机代码要复杂,但它带来的标准化和可移植性,是迈向高级嵌入式开发的必经之路。
Next Step:
尝试给驱动增加一个“读取状态”的功能。在 LedDriverDispatch 中增加一个 cmdId,利用 GpioRead 读取当前 LED 的电平,并通过 reply 参数返回给应用层。这能让你理解 HDF 是如何做双向数据传输的。
更多推荐

所有评论(0)