【小熊派BearPi-HM Micro】五:利用HDF驱动框架实现LED闪烁
<div class='showpostmsg'> 本帖最后由 数码小叶 于 2022-4-19 08:26 编辑<p><span style="font-size:16px;"> HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。</span></p>
<p><span style="font-size:16px;"> 基于HDF框架进行驱动的开发主要分为两个部分,驱动实现和驱动配置,驱动实现包含驱动业务代码和驱动入口注册,这两者开发文档里都提供了模板,比如驱动业务代码,一个简单的模板如下:</span></p>
<pre>
<code>#include"hdf_device_desc.h"// HDF框架对驱动开放相关能力接口的头文件
#include"hdf_log.h"// HDF 框架提供的日志接口头文件
#define HDF_LOG_TAG sample_driver // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签
//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
int32_t HdfSampleDriverBind(structHdfDeviceObject*deviceObject)
{
HDF_LOGD("Sample driver bind success");
return0;
}
// 驱动自身业务初始的接口
int32_t HdfSampleDriverInit(structHdfDeviceObject*deviceObject)
{
HDF_LOGD("Sample driver Init success");
return0;
}
// 驱动资源释放的接口
void HdfSampleDriverRelease(structHdfDeviceObject*deviceObject)
{
HDF_LOGD("Sample driver release success");
return;
}</code></pre>
<p> </p>
<p><span style="font-size:16px;">实际编程时,就可以直接在此模板上扩展,比如BearPi-HM Micro提供的demo</span></p>
<pre>
<code>//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL)
{
HDF_LOGE("Led driver bind failed!");
return HDF_ERR_INVALID_OBJECT;
}
static struct IDeviceIoService ledDriver = {
.Dispatch = LedDriverDispatch,
};
deviceObject->service = (struct IDeviceIoService *)(&ledDriver);
HDF_LOGD("Led driver bind success");
return HDF_SUCCESS;
}</code></pre>
<p><span style="font-size:16px;"> </span></p>
<p><span style="font-size:16px;"> 驱动注册,就是实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。</span></p>
<pre>
<code>struct HdfDriverEntry g_ledDriverEntry = {
.moduleVersion = 1,
.Bind = HdfLedDriverBind, //GPIO不需要实现Bind,本例是一个空实现,厂商可根据自身需要添加相关操作
.Init = HdfLedDriverInit, //见Init参考
.Release = HdfLedDriverRelease, //见Release参考
.moduleName = "HDF_LED",,//【必要且需要与HCS文件中里面的moduleName匹配】
};
//调用HDF_INIT将驱动入口注册到HDF框架中
HDF_INIT(g_ledDriverEntry);</code></pre>
<p><span style="font-size:16px;"> 而对于驱动配置,HDF框架使用的是.HCS文件,.HCS文件全称是HDF Configuration Source,是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN(HDF Configuration Generator)是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:在弱性能环境中,转换为配置树源码或配置树宏定义,驱动可直接调用C代码或宏式APIs获取配置。在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。</span></p>
<p></p>
<p><span style="font-size:16px;"> </span></p>
<p><span style="font-size:16px;"> 和驱动实现一样,驱动配置同样包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,HDF框架定义的驱动设备描述位于:~\bearpi\project\bearpi-hm_micro_small\device\st\bearpi_hm_micro\liteos_a\hdf_config\device_info\device_info.hcs。驱动的私有配置信息,则是用户自己添加的配置信息,比如这里的~\bearpi\project\bearpi-hm_micro_small\device\st\bearpi_hm_micro\liteos_a\hdf_config\led\led_config.hcs</span></p>
<p> </p>
<pre>
<code>device_led :: device { // led设备节点
device0 :: deviceNode { // led驱动的DeviceNode节点
policy = 2; // policy字段是驱动服务发布的策略,等于0,不需要发布服务
priority = 10; // 驱动启动优先级(0-200),值越大优先级越低,
preload = 1; // 驱动按需加载字段
permission = 0777; // 驱动创建设备节点权限
moduleName = "HDF_LED"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
serviceName = "hdf_led"; // 驱动对外发布服务的名称,必须唯一
deviceMatchAttr = "st_stm32mp157_led"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
}
}</code></pre>
<p> </p>
<pre>
<code>root {
LedDriverConfig {
led_gpio_num = 13;
match_attr = "st_stm32mp157_led"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
}
}</code></pre>
<p> </p>
<p> </p>
<p><span style="font-size:16px;">最后,是需要将需要将该配置文件添加到板级配置入口文件hdf.hcs</span></p>
<p><span style="font-size:16px;">device\st\bearpi_hm_micro\liteos_a\hdf_config\hdf.hcs</span></p>
<pre>
<code>#include "device_info/device_info.hcs"
#include "led/led_config.hcs"</code></pre>
<p><span style="font-size:16px;">关于GPIO口的操作,GPIO驱动已经提供了相关的接口</span></p>
<p></p>
<p> </p>
<p><span style="font-size:16px;">然后就是led闪烁的业务代码编写了,就是驱动程序指令接受的的代码,用户态向内核态发送指令</span></p>
<pre>
<code>int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{
uint8_t contrl;
HDF_LOGE("Led driver dispatch");
if (client == NULL || client->device == NULL)
{
HDF_LOGE("Led driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
switch (cmdCode)
{
/* 接收到用户态发来的LED_WRITE_READ命令 */
case LED_WRITE_READ:
/* 读取data里的数据,赋值给contrl */
HdfSbufReadUint8(data,&contrl);
switch (contrl)
{
/* 开灯 */
case LED_ON:
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
break;
/* 关灯 */
case LED_OFF:
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
break;
/* 状态翻转 */
case LED_TOGGLE:
if(status == 0)
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
}
else
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
}
break;
default:
break;
}
/* 把LED的状态值写入reply, 可被带至用户程序 */
if (!HdfSbufWriteInt32(reply, status))
{
HDF_LOGE("replay is fail");
return HDF_FAILURE;
}
break;
default:
break;
}
return HDF_SUCCESS;
}</code></pre>
<p> </p>
<p><span style="font-size:16px;"> 通过代码可以感受到OpenHarmony的设备开发中的驱动调用与单片机的驱动开发不太相同,单片机的驱动调用往往是采用函数调用的方式,但是OpenHarmony没有这样的操作,驱动调用采用的是驱动程序向用户态暴露server后,程序才能通过Dispatch的方式发送指令到驱动程序,并可以将用户态的数据携带到驱动程序,也可以从驱动程序读出数据。像上面的代码就是通过Dispatch向驱动程序发送指令从而实现LED灯的亮灭,当然,内核态也可以向用户态发送数据。</span></p>
<p><span style="font-size:16px;"> 和上一篇,helloword一样,还需要编译文件,对于业务代码,和上一篇一模一样的操作,配置BUILD.gn、applications.json、config.json。对于驱动文件,不但需要自己写的BUILD.gn,还需要修改~\bearpi\project\bearpi-hm_micro_small\device\st\drivers\BUILD.gn</span></p>
<pre>
<code>import("//drivers/adapter/khdf/liteos/hdf.gni")
hdf_driver("hdf_led") {
sources = [
"led.c",
]
}</code></pre>
<p></p>
<p> </p>
<p> </p>
<p><span style="font-size:16px;">最后编译,烧录到板子了,和之前一样,打开串口助手 ,通过指令操作led的亮灭状态变化</span></p>
<p></p>
<p> </p>
<p> </p>
<p></p>
<p> </p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p>确实,OpenHarmony的设备开发中的驱动调用和单片机的驱动开发相差还是比较大的。</p>
<p>可以可以,学习了。</p>
写的不错! 感觉OpenHarmony更像是内核微服务,你请求服务器再去执行,不像别的直接底层调用
页:
[1]