driver-peripherals-light-des.md 17.3 KB
Newer Older
Z
zhouyanxu 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
# LIGHT

- [概述](##概述)
  - [功能简介](###功能简介)
  - [运作机制](###运作机制)
  
- [开发指导](##开发指导)
  - [接口说明](#section188213414114)
  - [开发步骤](#section7893102915819)
  - [开发实例](#section257750691)
  - [调测验证](#section106021256121219)
  

## 概述

### 功能简介

​        灯(light)驱动模型为上层灯硬件服务层提供稳定的灯控制能力接口,包括获取灯类型,灯常亮,灯闪烁效果配置,灯停止的能力。基于HDF(**H**ardware **D**river **F**oundation)驱动框架开发的灯(light)驱动模型,实现跨操作系统迁移,器件差异配置等功能。实现Light驱动“一次开发,多系统部署”的目标。Light驱动模型如[图1](#fig10451455446)所示:

**图 1**  Light驱动模型图

![Light驱动模型图](figures\Light驱动模型图.png)

### 运作机制

通过介绍Light驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如[图2](#Sensor驱动模型图)所示:

**图 2** 灯驱动模型图

![Light驱动运行图](figures\Light驱动运行图.png)

灯驱动模型以标准系统Hi3516DV300为例,介绍整个驱动加载及运行流程:

1. 从device info HCS 的Light Host里读取Light设备管理配置信息。
2. 从light_config HCS读取Light数据配置信息。
3. 解析Light设备管理配置信息,并关联对应设备驱动。
4. 客户端下发Light Stub控制到服务端。
5. 服务端调用Light Stub控制。
6. 启动灯抽象驱动接口。

## 开发指导

### 接口说明

 灯驱动模型支持系统中所有灯的信息和动态配置闪烁模式和闪烁时间配置能力。灯硬件服务调用GetLightInfo获取灯设备的基本信息;调用TurnOnLight接口启动配置的闪烁效果。灯驱动模型对HDI开放的API接口能力,参考[表1](#table203963834718)

[表1](#table203963834718)   灯驱动模型对外API接口能力介绍

| 接口名                                                       | 功能描述                                                     |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| int32_t (*GetLightInfo)(struct LightInfo **lightInfo, uint32_t *count) | 获取系统中所有灯的信息,lightInfo表示灯设备的基本信息,count表示获取灯的个数。 |
| int32_t (*TurnOnLight)(uint32_t type, struct LightEffect *effect) | 根据指定的灯类型打开灯列表中可用的灯,type表示灯类型,effect表示要设置的效果信息。 |
| int32_t (*TurnOffLight)(uint32_t type)                       | 根据指定的灯类型关闭灯列表中可用的灯。                       |

### 开发步骤
1.  基于HDF驱动框架,按照驱动Driver Entry程序,完成灯抽象驱动开发,主要有Bind,Init,Release,Dispatch函数接口实现,配置资源和HCS解析。完成加速度传感器驱动的设备信息配置。
3.  调用配置解析接口,完成器件属性信息解析,器件寄存器解析,并注册到Light设备管理中。
3.  完成灯获取类型、闪烁和停止接口开发,会根据闪烁模式创建和销毁定时器。

### 开发实例

1. 灯驱动的初始化和去初始化

   - 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 灯驱动模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md)介绍。其中Driver Entry入口函数定义如下:

     ```c
     /*注册灯入口数据结构体对象*/
     struct HdfDriverEntry g_lightDriverEntry = {
         .moduleVersion = 1, /*灯模块版本号*/
         .moduleName = "HDF_LIGHT", /*灯模块名,要与device_info.hcs文件里灯moduleName字段值一样*/
         .Bind = BindLightDriver, /*灯绑定函数*/
         .Init = InitLightDriver, /*灯初始化函数*/
         .Release = ReleaseLightDriver, /*灯资源释放函数*/
     };
     /* 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 */
     HDF_INIT(g_lightDriverEntry);
     ```

   - 基于HDF驱动框架,按照驱动Driver Entry程序,完成灯抽象驱动开发,主要有Bind,Init,Release,Dispatch函数接口实现。

     ```c
     /* 灯驱动对外发布的能力 */
     static int32_t DispatchLight(struct HdfDeviceIoClient *client,
         int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
     {
         .....
         if (cmd == LIGHT_IO_CMD_GET_INFO_LIST) {
             CHECK_LIGHT_NULL_PTR_RETURN_VALUE(reply, HDF_ERR_INVALID_PARAM);
             return GetAllLightInfo(data, reply);
         }
     
         CHECK_LIGHT_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM);
         (void)OsalMutexLock(&drvData->mutex);
         if (!HdfSbufReadInt32(data, &lightType)) {
             HDF_LOGE("%s: sbuf read lightType failed", __func__);
             (void)OsalMutexUnlock(&drvData->mutex);
             return HDF_ERR_INVALID_PARAM;
         }
         .....
         ret = DispatchCmdHandle(lightType, data, reply);
         (void)OsalMutexUnlock(&drvData->mutex);
         return ret;
     }
     
     /* 灯驱动对外提供的服务绑定到HDF框架 */
     int32_t BindLightDriver(struct HdfDeviceObject *device)
     {
         struct LightDriverData *drvData = NULL;
     
         CHECK_LIGHT_NULL_PTR_RETURN_VALUE(device, HDF_FAILURE);
         // 私有接口分配资源
         drvData = (struct LightDriverData *)OsalMemCalloc(sizeof(*drvData));
         CHECK_LIGHT_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_MALLOC_FAIL);
         // 需要发布的接口函数
         drvData->ioService.Dispatch = DispatchLight;
         drvData->device = device;
         device->service = &drvData->ioService;
         g_lightDrvData = drvData;
         return HDF_SUCCESS;
     }
     
     /* 灯驱动初始化入口函数*/
     int32_t InitLightDriver(struct HdfDeviceObject *device)
     { 
     	.....
         // 工作队列初始化
         if (HdfWorkQueueInit(&drvData->workQueue, LIGHT_WORK_QUEUE_NAME) != HDF_SUCCESS) {
             HDF_LOGE("%s: init workQueue fail!", __func__);
             return HDF_FAILURE;
         }
         // 工作项初始化
         if (HdfWorkInit(&drvData->work, LightWorkEntry, (void*)drvData) != HDF_SUCCESS) {
             HDF_LOGE("%s: init workQueue fail!", __func__);
             return HDF_FAILURE;
         }
         // 解析HCS配置文件
         if (GetLightConfigData(device->property) != HDF_SUCCESS) {
             HDF_LOGE("%s: get light config fail!", __func__);
             return HDF_FAILURE;
         }
     
         return HDF_SUCCESS;
     }
     
     /* 释放灯驱动初始化时分配的资源 */
     void ReleaseLightDriver(struct HdfDeviceObject *device)
     { 
         .....
         // 释放已分配资源
         for (i = LIGHT_TYPE_NONE; i < LIGHT_TYPE_BUTT; ++i) {
     
             if (drvData->info[i] != NULL) {
                 OsalMemFree(drvData->info[i]);
                 drvData->info[i] = NULL;
             }
         }
         // 销毁工作队列资源
         HdfWorkDestroy(&drvData->work);
         HdfWorkQueueDestroy(&drvData->workQueue);
         (void)OsalMutexDestroy(&drvData->mutex);
         (void)OsalMemFree(drvData);
         g_lightDrvData = NULL;
     }
     
     ```

   - 灯设备管理模块负责系统中灯器件接口发布,在系统启动过程中,HDF框架机制通过灯 Host里设备HCS配置信息,加载设备管理驱动。

     ```hcs
     /* 灯设备HCS配置 */
     device_light :: device {
         device0 :: deviceNode {
             policy = 2; /* policy字段是驱动服务发布的策略 */
             priority = 100; /* 灯驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 */
             preload = 0; /* 驱动按需加载字段,0表示加载,2表示不加载 */
             permission = 0664;  /* 驱动创建设备节点权限 */
             moduleName = "HDF_LIGHT"; /* 灯驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 */
             serviceName = "hdf_light"; /* 灯驱动对外发布服务的名称,必须唯一 */
             deviceMatchAttr = "hdf_light_driver"; /*  驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 */
         }
     }
     ```

2. 分配资源,解析灯hcs配置。

   - 解析hcs配置文件。

     ```c
     /* 分配资源,解析灯HCS配置 */
     static int32_t ParseLightInfo(const struct DeviceResourceNode *node)
     {
         .....
         // 从hcs获取支持灯的类型个数
         drvData->lightNum = parser->GetElemNum(light, "lightType");
         ....
         for (i = 0; i < drvData->lightNum; ++i) {
         // 获取类型
         ret = parser->GetUint32ArrayElem(light, "lightType", i, &temp, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "lightType");
         }
     
         for (i = 0; i < drvData->lightNum; ++i) {
         .....
         // 类型作为下标开辟空间
         drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo));
         .....
         // 将Light设备信息进行填充
         ret = parser->GetUint32(light, "busRNum", &drvData->info[temp]->busRNum, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "busRNum");
         ret = parser->GetUint32(light, "busGNum", &drvData->info[temp]->busGNum, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "busGNum");
         ret = parser->GetUint32(light, "busBNum", &drvData->info[temp]->busBNum, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "busBNum");
         .....
         return HDF_SUCCESS;
     
     }
     ```

   - 灯效果模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md)介绍。

     ```hcs
     灯数据配置模板(light_config.hcs)
     root {
         lightConfig {
             boardConfig {
                 match_attr = "hdf_light_driver";
                 lightAttr {
                     light01 {
                         lightType = [1, 2];          // 灯类型组,一个物理灯对应多个逻辑灯
                         busRNum = 31;                // 红色对映的GPIO管脚
                         busGNum = 30;                // 绿色对映的GPIO管脚
                         busBNum = 29;                // 蓝色对映的GPIO管脚
                         lightBrightness = 0X80000000;// RGB: R:16-31bit、G:8-15bit、                                                    // B:0-7bit
                         onTime = 50;                 // 最小闪烁时间(ms)
                         offTime = 50;
                     }
                 }
             }
         }
     }
     ```

3. 完成获取灯类型,闪烁和停止接口开发,会根据闪烁模式创建和销毁定时器。

   ```c
   /* 灯驱动服务调用GetAllLightInfo获取灯类型,Enable接口启动闪烁模式,
      调用Disable接口停止闪烁 */
   static int32_t GetAllLightInfo(struct HdfSBuf *data, struct HdfSBuf *reply)
   {
       .....
       // 获取light类型个数
       if (!HdfSbufWriteUint32(reply, drvData->lightNum)) {
           HDF_LOGE("%s: write sbuf failed", __func__);
           return HDF_FAILURE;
       }
       for (i = 0; i < LIGHT_TYPE_BUTT; ++i) {
           if (drvData->info[i] == NULL) {
               continue;
           }
           lightInfo.lightType = i;
           lightInfo.reserved = NULL;
           //  将Light设备信息填充进reply
           if (!HdfSbufWriteBuffer(reply, &lightInfo, sizeof(lightInfo))) {
               HDF_LOGE("%s: write sbuf failed", __func__);
               return HDF_FAILURE;
           }
       }
   
       return HDF_SUCCESS;
   }
   
   /* 按照指定的类型和用户传入的参数使能灯 */
   static int32_t Enable(uint32_t lightType, struct HdfSBuf *data, struct HdfSBuf *reply)
   {
       .....
       // 根据用户传的亮度值设置灯的颜色  RGB: R:16-31bit、G:8-15bit、B:0-7bit
       if ((drvData->info[lightType]->lightBrightness & LIGHT_MAKE_R_BIT) != 0) {
           drvData->info[lightType]->busNum = drvData->info[lightType]->busRNum;
       } else if ((drvData->info[lightType]->lightBrightness & LIGHT_MAKE_G_BIT) != 0) {
           drvData->info[lightType]->busNum = drvData->info[lightType]->busGNum;
       } else if ((drvData->info[lightType]->lightBrightness & LIGHT_MAKE_B_BIT) != 0) {
           drvData->info[lightType]->busNum = drvData->info[lightType]->busBNum;
       }
       // 常亮模式
       if (buf->flashEffect.flashMode == LIGHT_FLASH_NONE) {
   
           if (GpioWrite(drvData->info[lightType]->busNum, GPIO_VAL_HIGH) != HDF_SUCCESS) {
               return HDF_FAILURE;
           }
       }
       // 闪烁模式
       if (buf->flashEffect.flashMode == LIGHT_FLASH_TIMED) {
           drvData->info[lightType]->lightState = LIGHT_STATE_START;
           // 用户设置的闪烁时间小于系统支持的最短时间,采用系统配置的时间(HCS配置)
           drvData->info[lightType]->onTime = buf->flashEffect.onTime < drvData->info[lightType]->onTime ?
           drvData->info[lightType]->onTime : buf->flashEffect.onTime;
           drvData->info[lightType]->offTime = buf->flashEffect.offTime < drvData->info[lightType]->offTime ?
           drvData->info[lightType]->offTime : buf->flashEffect.offTime;
           // 创建定时器
           if (OsalTimerCreate(&drvData->timer, drvData->info[lightType]->onTime,
               LightTimerEntry, (uintptr_t)lightType) != HDF_SUCCESS) {
           HDF_LOGE("%s: create light timer fail!", __func__);
           return HDF_FAILURE;
           }
           // 启动周期定时器
           if (OsalTimerStartLoop(&drvData->timer) != HDF_SUCCESS) {
           HDF_LOGE("%s: start light timer fail!", __func__);
           return HDF_FAILURE;
           }
       }
       return HDF_SUCCESS;
   }
   
   /* 按照指定的类型关闭灯 */
   static int32_t Disable(uint32_t lightType, struct HdfSBuf *data, struct HdfSBuf *reply)
   {
       // 删除定时器
       if (drvData->timer.realTimer != NULL) {
   
           if (OsalTimerDelete(&drvData->timer) != HDF_SUCCESS) {
               HDF_LOGE("%s: delete haptic timer fail!", __func__);
           }
       }
       // 对应的GPIO下电
       if (GpioWrite(drvData->info[lightType]->busRNum, GPIO_VAL_LOW) != HDF_SUCCESS){
           HDF_LOGE("%s: gpio write failed", __func__);
           return HDF_FAILURE;
       }
   
       return HDF_SUCCESS;
   }
   ```

### 调测验证

驱动开发完成后,在灯单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。

```c++
/* 用例执行前,初始化灯接口实例 */
void HdfLightTest::SetUpTestCase()
{
    g_lightDev = NewLightInterfaceInstance();
    if (g_lightDev == nullptr) {
        printf("test light get Module instance failed\n\r");
    }
    int32_t ret = g_lightDev->GetLightInfo(&g_lightInfo, &g_count);
    if (ret == -1) {
        printf("get light informations failed\n\r");
    }
}

/* 用例执行后,释放用例资源 */
void HdfLightTest::TearDownTestCase()
{
    if(g_lightDev != nullptr){
        FreeLightInterfaceInstance();
        g_lightDev = nullptr;
    }
}

/* 测试灯获取类型 */
HWTEST_F(HdfLightTest, GetLightList001, TestSize.Level1)
{
    struct LightInfo *info = nullptr;

    if (g_lightInfo == nullptr) {
        EXPECT_NE(nullptr, g_lightInfo);
        return;
    }

    printf("get light list num[%d]\n\r", g_count);
    info = g_lightInfo;

    for (int i = 0; i < g_count; ++i) {
        printf("get lightId[%d]\n\r", info->lightType);
        EXPECT_GE(info->lightType, g_minLightType);
        EXPECT_LE(info->lightType, g_maxLightType);
        info++;
    }
}

/* 测试灯常亮模式 */
HWTEST_F(HdfLightTest, EnableLight001, TestSize.Level1)
{
    int32_t i;
    int32_t ret;
    struct LightEffect effect;
    effect->lightBrightness = 0x80000000;
    effect->flashEffect.flashMode = LIGHT_FLASH_NONE;
    effect->flashEffect.onTime = 0;
    effect->flashEffect.offTime = 0;

    for (i = 0; i < g_count; ++i) {

        ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightType, effect);
        EXPECT_EQ(0, ret);

        OsalSleep(LIGHT_WAIT_TIME);

        ret = g_lightDev->TurnOffLight(type);
        EXPECT_EQ(0, ret);
    }
}

/* 测试灯闪烁模式 */
HWTEST_F(HdfLightTest, EnableLight002, TestSize.Level1)
{
    int32_t i;
    int32_t ret;
    struct LightEffect effect;
    effect->lightBrightness = 0x80000000;
    effect->flashEffect.flashMode = LIGHT_FLASH_TIMED;
    effect->flashEffect.onTime = g_onTime;
    effect->flashEffect.offTime = g_offTime;

    for (i = 0; i < g_count; ++i) {

        ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightType, effect);
        EXPECT_EQ(0, ret);

        OsalSleep(LIGHT_WAIT_TIME);

        ret = g_lightDev->TurnOffLight(type);
        EXPECT_EQ(0, ret);
    }
}
```