数码小叶 发表于 2022-4-19 08:28

【小熊派BearPi-HM Micro】五:利用HDF驱动框架实现LED闪烁

<div class='showpostmsg'> 本帖最后由 数码小叶 于 2022-4-19 08:26 编辑

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp; HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。</span></p>

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp;基于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>&nbsp;</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-&gt;service = (struct IDeviceIoService *)(&amp;ledDriver);
    HDF_LOGD("Led driver bind success");
    return HDF_SUCCESS;
}</code></pre>

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp; </span></p>

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp; 驱动注册,就是实例化驱动入口,驱动入口必须为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;">&nbsp; &nbsp; &nbsp; &nbsp; 而对于驱动配置,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;">&nbsp; &nbsp; &nbsp; &nbsp; </span></p>

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp; 和驱动实现一样,驱动配置同样包含两部分,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>&nbsp;</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>&nbsp;</p>

<pre>
<code>root {
    LedDriverConfig {
      led_gpio_num = 13;
      match_attr = "st_stm32mp157_led";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
    }
}</code></pre>

<p>&nbsp;</p>

<p>&nbsp;</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>&nbsp;</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-&gt;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,&amp;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>&nbsp;</p>

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp; 通过代码可以感受到OpenHarmony的设备开发中的驱动调用与单片机的驱动开发不太相同,单片机的驱动调用往往是采用函数调用的方式,但是OpenHarmony没有这样的操作,驱动调用采用的是驱动程序向用户态暴露server后,程序才能通过Dispatch的方式发送指令到驱动程序,并可以将用户态的数据携带到驱动程序,也可以从驱动程序读出数据。像上面的代码就是通过Dispatch向驱动程序发送指令从而实现LED灯的亮灭,当然,内核态也可以向用户态发送数据。</span></p>

<p><span style="font-size:16px;">&nbsp; &nbsp; &nbsp; &nbsp; 和上一篇,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>&nbsp;</p>

<p>&nbsp;</p>

<p><span style="font-size:16px;">最后编译,烧录到板子了,和之前一样,打开串口助手 ,通过指令操作led的亮灭状态变化</span></p>

<p></p>

<p>&nbsp;</p>

<p>&nbsp; &nbsp;</p>

<p></p>

<p>&nbsp;</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>

Jacktang 发表于 2022-4-20 07:19

<p>确实,OpenHarmony的设备开发中的驱动调用和单片机的驱动开发相差还是比较大的。</p>

freebsder 发表于 2022-4-20 18:43

<p>可以可以,学习了。</p>

elynson 发表于 2022-4-20 21:44

写的不错!

吾妻思萌 发表于 2022-6-8 12:22

感觉OpenHarmony更像是内核微服务,你请求服务器再去执行,不像别的直接底层调用
页: [1]
查看完整版本: 【小熊派BearPi-HM Micro】五:利用HDF驱动框架实现LED闪烁