电子产业一站式赋能平台

PCB联盟网

搜索
查看: 745|回复: 0
收起左侧

[文章] 【HarmonyOS HiSpark AI Camera试用连载】基于HDF驱动框架的gpio驱动

[复制链接]

2607

主题

2607

帖子

7472

积分

高级会员

Rank: 5Rank: 5

积分
7472
发表于 2020-12-30 09:56:04 | 显示全部楼层 |阅读模式
[文章] 【HarmonyOS HiSpark AI Camera试用连载】基于HDF驱动框架的gpio驱动,   
本帖最后由 jf_64182030 于 2020-12-29 23:35 编辑



参考文档:



  • GPIO链接:https://gitee.com/openharmony/docs/blob/master/driver/GPIO.md
      


  • GPIO概述
  • GPIO使用指导
  • GPIO使用实例
      




  • Touchscreen开发实例 https://gitee.com/openharmony/docs/blob/master/driver/Touchscreen%E5%BC%80%E5%8F%91%E5%AE%9E%E4%BE%8B.md
      
参考该源码实现类似linux的文件操作接口:file_operations_vfs。





  • 第六次情迷-鸿蒙OS的GPIO开发(点灯) https://home.elecfans.com/jishu_2023276_1_1.html
      
在论坛里搜了一下,发现这位发烧友也是采用了file_operations_vfs的方式实现gpio控制,操作LED灯的亮灭。





前言

现在有两种方式实现GPIO的控制:



方式一:通过register_driver的方式,注册file_operations_vfs的一些驱动操作接口,比如open、close、read、write、ioctl等等。这个方法几乎和Linux的驱动接口一模一样,很方便有Linux驱动经验的人员开发。



方式二:通过hdf服务的方式,应用通过服务名称获取服务句柄,然后通过API接口直接获取或者订阅该服务对应的驱动事件。用户空间将数据放在一段共享内存(HdfSBuf),然后将数据和cmd编号发送给内核空间的驱动;驱动也可以触发一个事件,通过一段共享内存(HdfSBuf)将内核空间的数据传递给用户空间,该事件会通知用户空间回调函数进行相应处理。



方式一的方法已经有官方和发烧友的参考例程了,这里就不单独来写了,本文将基于方式二进行说明。看起来方式一和方式二似乎是可以共存的,这个测试可能要放到后面的追加文章了。而且还有驱动如何从hcs配置文件里获取板级配置也还待确定一下。





1. 硬件

(1) LED_light/GPIO2_3

灯板上的D4 LED。



led-d4.png (190.01 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:18 上传



led-d4-sch.png (25.67 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:18 上传



led-d4-sch1.png (45.04 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:18 上传





(2) GPIO5_1/UART4_TXD

补光灯。



full-led-sch.png (32.87 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:18 上传



full-led-sch1.png (33.11 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:18 上传



(3) LSADC_CH0/GPIO10_3

红外检测。



ldr.png (15.53 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:18 上传



ldr-sch.png (16.61 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:19 上传



首先需要选取一个空闲的GPIO管脚,本例程基于Hi3516DV300某开发板,GPIO管脚选择GPIO10_3,换算成GPIO号为83。



Hi3516DV300控制器管理12组GPIO管脚,每组8个。

GPIO号 = GPIO组索引 (0~11) * 每组GPIO管脚数(8) + 组内偏移



举例:GPIO10_3的GPIO号 = 10 * 8 + 3 = 83





编写软件:

2. 在HDF框架的配置文件中添加该驱动的配置信息,如下所示:

$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs



  •         sample_host :: host {
      
  •             hostName = “sample_host“;
      
  •             ...
      
  •         }
      
  • +++
      
  •         gpio_host :: host {
      
  •             hostName = “gpio_host“;
      
  •             gpio_device :: device {
      
  •                 device0 :: deviceNode {
      
  •                     policy = 2;
      
  •                     priority = 100;
      
  •                     preload = 0;
      
  •                     permission = 0664;
      
  •                     moduleName = “gpio_driver“;
      
  •                     serviceName = “gpio_service“;
      
  •                 }
      
  •             }
      
  •         }
      
  • +++
      
  •         platform :: host {

复制代码



3. 编写驱动代码

3.1 基于HDF框架编写的gpio驱动代码如下:

$ mkdir -p ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample

$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample/gpio_sample.c



  • #include <fcntl.h>
      
  • #include <sys/stat.h>
      
  • #include <sys/ioctl.h>
      
  • #include “hdf_log.h“
      
  • #include “hdf_base.h“
      
  • #include “hdf_device_desc.h“
      
  • #include “gpio_if.h“
      
  • #include “osal_io.h“
      
  • #include “osal_irq.h“
      
  • #include “osal_time.h“
      

  •   
  • #define HDF_LOG_TAG gpio_driver
      

  •   
  • // 设置cmd编号,类似于Linux的ioctl命令码。
      
  • #define CMD_GPIO_LDR_TEST                   100
      
  • #define CMD_GPIO_LED_TEST                   101
      
  • #define CMD_GPIO_FILL_LIGHT                 102
      
  • // GPIO NUM
      
  • #define GPIO_NUM_LDR_GPIO10_3               83  // 10*8+3
      
  • #define GPIO_NUM_LED_GPIO2_3                19  // 2*8+3
      
  • #define GPIO_NUM_FILL_LIGHT_GPIO5_1         41  // 5*8+1
      

  •   
  • static uint32_t g_irqCnt;
      

  •   
  • // 中断服务函数
      
  • static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data)
      
  • {
      
  •     HDF_LOGE(“%s: irq triggered! on gpio:%u, data=%p“, __func__, gpio, data);
      
  •     g_irqCnt++; // 如果中断服务函数触发执行,则将全局中断计数加1
      
  •     return GpioDisableIrq(gpio);
      
  • }
      

  •   
  • // 测试用例函数
      
  • static int32_t gpio_test_ldr_irq_edge(void)
      
  • {
      
  •     int32_t ret;
      
  •     uint16_t valRead;
      
  •     uint16_t mode;
      
  •     uint16_t gpio = GPIO_NUM_LDR_GPIO10_3; // 待测试的GPIO管脚号
      
  •     uint32_t timeout;
      

  •   
  •     // 将管脚方向设置为输出
      
  •     ret = GpioSetDir(gpio, GPIO_DIR_OUT);
      
  •     if (ret != HDF_SUCCESS) {
      
  •         HDF_LOGE(“%s: set dir fail! ret:%d\n“, __func__, ret);
      
  •         return ret;
      
  •     }
      

  •   
  •     // 先禁止该管脚中断
      
  •     ret = GpioDisableIrq(gpio);
      
  •     if (ret != HDF_SUCCESS) {
      
  •         HDF_LOGE(“%s: disable irq fail! ret:%d\n“, __func__, ret);
      
  •         return ret;
      
  •     }
      

  •   
  •     // 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发
      
  •     mode = OSAL_IRQF_TRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING;
      
  •     HDF_LOGE(“%s: mode:%0x\n“, __func__, mode);
      
  •     ret = GpioSetIrq(gpio, mode, TestCaseGpioIrqHandler, NULL);
      
  •     if (ret != HDF_SUCCESS) {
      
  •         HDF_LOGE(“%s: set irq fail! ret:%d\n“, __func__, ret);
      
  •         return ret;
      
  •     }
      

  •   
  •     // 使能此管脚中断
      
  •     ret = GpioEnableIrq(gpio);
      
  •     if (ret != HDF_SUCCESS) {
      
  •         HDF_LOGE(“%s: enable irq fail! ret:%d\n“, __func__, ret);
      
  •         (void)GpioUnSetIrq(gpio);
      
  •         return ret;
      
  •     }
      

  •   
  •     g_irqCnt = 0; // 清除全局计数器
      
  •     timeout = 0;  // 等待时间清零
      
  •     // 等待此管脚中断服务函数触发,等待超时时间为1000毫秒
      
  •     while (g_irqCnt <= 0 && timeout < 1000) {
      
  •         (void)GpioRead(gpio, &valRead);
      
  •         (void)GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW);
      
  •         HDF_LOGE(“%s: wait irq timeout:%u\n“, __func__, timeout);
      
  •         OsalMDelay(200); // wait for irq trigger
      
  •         timeout += 200;
      
  •     }
      
  •     (void)GpioUnSetIrq(gpio);
      
  •     return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE;
      
  • }
      

  •   
  • static void gpio_test_led_blink(uint16_t gpio)
      
  • {
      
  •     int32_t ret;
      
  •     int i = 0;
      

  •   
  •     // 将管脚方向设置为输出
      
  •     ret = GpioSetDir(gpio, GPIO_DIR_OUT);
      
  •     if (ret != HDF_SUCCESS) {
      
  •         HDF_LOGE(“%s: set dir fail! ret:%d\n“, __func__, ret);
      
  •     }
      
  •     //HDF_LOGE(“\nGpioSetDir GPIO_DIR_OUT“);
      

  •   
  •     for (i = 0; i < 3; i++) {
      
  •         ret = GpioWrite(gpio, GPIO_VAL_HIGH);
      
  •         if (ret != 0) {
      
  •             HDF_LOGE(“GpioWrite: failed, ret %d\n“, ret);
      
  •         }
      
  •         //HDF_LOGE(“GpioWrite GPIO_VAL_HIGH“);
      

  •   
  •         //OsalMDelay(500); // 500ms, but OsalMDelay work is abnormal.
      
  •         OsalMSleep(500);
      
  •    
      
  •         ret = GpioWrite(gpio, GPIO_VAL_LOW);
      
  •         if (ret != 0) {
      
  •             HDF_LOGE(“GpioWrite: failed, ret %d\n“, ret);
      
  •         }
      
  •         //HDF_LOGE(“GpioWrite GPIO_VAL_LOW“);
      

  •   
  •         //OsalMDelay(500); // 500ms
      
  •         OsalMSleep(500);
      
  •     }
      
  • }
      

  •   
  • // Dispatch是驱动中用来处理用户态发下来的消息的函数。
      
  • static int32_t GpioSampleDriverDispatch(struct HdfDeviceIoClient *deviceIoClient, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
      
  • {
      
  •     HDF_LOGE(“%s: received cmd %d“, __func__, id);
      

  •   
  •     switch (id) {
      
  •         case CMD_GPIO_LDR_TEST: { // 通常会采用switch case的方式来写。
      
  •             const char *readData = HdfSbufReadString(data); // 内核从用户空间获取数据,类似Linux的copy_from_user。
      
  •             if (readData != NULL) {
      
  •                 HDF_LOGE(“%s: read data is: %s“, __func__, readData); // 内核打印从用户空间独到的数据。
      
  •             }
      
  •    
      
  •             // GPIO中断检测
      
  •             gpio_test_ldr_irq_edge();
      
  •    
      
  •             // 将向用户空间返回内容修改为字符串
      
  •             char *sendData = “come from kernel sapce: CMD_GPIO_LDR_TEST“;
      
  •             if (!HdfSbufWriteString(reply, sendData)) {
      
  •                 HDF_LOGE(“%s: fail to write sbuf“, __func__);
      
  •             }
      
  •    
      
  •             // 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
      
  •             return HdfDeviceSendEvent(deviceIoClient->device, id, data);
      
  •         }
      

  •   
  •         case CMD_GPIO_LED_TEST: {
      
  •             const char *readData = HdfSbufReadString(data); // 内核从用户空间获取数据,类似Linux的copy_from_user。
      
  •             if (readData != NULL) {
      
  •                 HDF_LOGE(“%s: read data is: %s“, __func__, readData); // 内核打印从用户空间独到>的数据。
      
  •             }
      

  •   
  •             // GPIO test
      
  •             gpio_test_led_blink(GPIO_NUM_LED_GPIO2_3);
      

  •   
  •             // 将向用户空间返回内容修改为字符串
      
  •             char *sendData = “come from kernel sapce: CMD_GPIO_LED_TEST“;
      
  •             if (!HdfSbufWriteString(reply, sendData)) {
      
  •                 HDF_LOGE(“%s: fail to write sbuf“, __func__);
      
  •             }
      

  •   
  •             // 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
      
  •             return HdfDeviceSendEvent(deviceIoClient->device, id, data);
      
  •         }
      

  •   
  •         case CMD_GPIO_FILL_LIGHT: {
      
  •             const char *readData = HdfSbufReadString(data); // 内核从用户空间获取数据,类似Linux的copy_from_user。
      
  •             if (readData != NULL) {
      
  •                 HDF_LOGE(“%s: read data is: %s“, __func__, readData); // 内核打印从用户空间独到>的数据。
      
  •             }
      

  •   
  •             // GPIO test
      
  •             gpio_test_led_blink(GPIO_NUM_FILL_LIGHT_GPIO5_1);
      

  •   
  •             // 将向用户空间返回内容修改为字符串
      
  •             char *sendData = “come from kernel sapce: CMD_GPIO_FILL_LIGHT“;
      
  •             if (!HdfSbufWriteString(reply, sendData)) {
      
  •                 HDF_LOGE(“%s: fail to write sbuf“, __func__);
      
  •             }
      

  •   
  •             // 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
      
  •             return HdfDeviceSendEvent(deviceIoClient->device, id, data);
      
  •         }
      

  •   
  •         default: {
      
  •             HDF_LOGE(“no such cmd id“);
      
  •             break;
      
  •         }
      
  •     }
      

  •   
  •     return HDF_FAILURE;
      
  • }
      

  •   
  • // 驱动资源释放的接口,本例未分配需要回收的资源,因此为空。
      
  • static void GpioSampleDriverRelease(struct HdfDeviceObject *deviceObject)
      
  • {
      
  •     // release resources here
      
  •     return;
      
  • }
      

  •   
  • // 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。
      
  • static int GpioSampleDriverBind(struct HdfDeviceObject *deviceObject)
      
  • {
      
  •     if (deviceObject == NULL) {
      
  •         return HDF_FAILURE;
      
  •     }
      
  •     static struct IDeviceIoService testService = {
      
  •         // Dispatch是用来处理用户态发下来的消息。
      
  •         .Dispatch = GpioSampleDriverDispatch,
      
  •     };
      
  •     deviceObject->service = &testService;
      
  •     return HDF_SUCCESS;
      
  • }
      

  •   
  • // 驱动自身业务初始的接口。
      
  • static int GpioSampleDriverInit(struct HdfDeviceObject *deviceObject)
      
  • {
      
  •     if (deviceObject == NULL) {
      
  •         HDF_LOGE(“%s::ptr is null!“, __func__);
      
  •         return HDF_FAILURE;
      
  •     }
      
  •     HDF_LOGE(“gpio driver Init success“);
      
  •     return HDF_SUCCESS;
      
  • }
      

  •   
  • // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。
      
  • struct HdfDriverEntry g_gpioSampleDriverEntry = {
      
  •     .moduleVersion = 1,
      
  •     .moduleName = “gpio_driver“, // 驱动的关键名称。
      
  •     .Bind = GpioSampleDriverBind,
      
  •     .Init = GpioSampleDriverInit,
      
  •     .Release = GpioSampleDriverRelease,
      
  • };
      

  •   
  • // 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,
      
  • // 再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
      
  • HDF_INIT(g_gpioSampleDriverEntry);
      


复制代码

3.2 创建Kconfig

$ cd ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample

$ touch Kconfig && vi Kconfig



  • # Copyright (c) 2020 Huawei Device Co., Ltd.
      
  • # Licensed under the Apache License, Version 2.0 (the “License“);
      
  • # you may not use this file except in compliance with the License.
      
  • # You may obtain a copy of the License at
      
  • #
      
  • #     http://www.apache.org/licenses/LICENSE-2.0
      
  • #
      
  • # Unless required by applicable law or agreed to in writing, software
      
  • # distributed under the License is distributed on an “AS IS“ BASIS,
      
  • # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      
  • # See the License for the specific language governing permissions and
      
  • # limitations under the License.
      

  •   
  • config DRIVERS_HDF_PLATFORM_GPIO_SAMPLE
      
  •     bool “Enable HDF platform GPIO sample driver“
      
  •     default n
      
  •     depends on DRIVERS_HDF_PLATFORM
      
  •     help
      
  •       Answer Y to enable HDF platform GPIO sample driver.

复制代码 修改上层Kconfig

$ vi ~/harmony/sdk/vendor/huawei/hdf/Kconfig



  • source “../../vendor/huawei/hdf/<a hidefocus=“true“ style=“color: rgb(51, 51, 51); padding-right: 10px; padding-left: 10px; border: 1px solid rgb(205, 205, 205); background: rgb(229, 237, 242); font-family: "Helvetica Neue", Helvetica, "PingFang SC", 微软雅黑, Tahoma, Arial, sans-serif; font-size: 14px; display: inline !important;“>片 </a>sample/platform/gpio-sample/Kconfig“

复制代码

3.3 创建Makefile

$ cd ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample

$ touch Makefile && vi Makefile



  • # Copyright (c) 2020 Huawei Device Co., Ltd.
      
  • # Licensed under the Apache License, Version 2.0 (the “License“);
      
  • # you may not use this file except in compliance with the License.
      
  • # You may obtain a copy of the License at
      
  • #
      
  • #     http://www.apache.org/licenses/LICENSE-2.0
      
  • #
      
  • # Unless required by applicable law or agreed to in writing, software
      
  • # distributed under the License is distributed on an “AS IS“ BASIS,
      
  • # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      
  • # See the License for the specific language governing permissions and
      
  • # limitations under the License.
      

  •   
  • include $(LITEOSTOPDIR)/config.mk
      
  • include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk
      

  •   
  • MODULE_NAME := gpio_sample
      

  •   
  • LOCAL_CFLAGS += $(HDF_INCLUDE)
      

  •   
  • LOCAL_SRCS += gpio_sample.c
      

  •   
  • # 警告需要关掉,不然unused的变量都会报错!
      
  • #LOCAL_CFLAGS += -fstack-protector-strong -Wextra -Wall -Werror
      
  • LOCAL_CFLAGS += -fstack-protector-strong
      

  •   
  • include $(HDF_DRIVER)
      


复制代码

3.4 将驱动模块加入编译

$ vi ~/harmony/sdk/vendor/huawei/hdf/hdf_vendor.mk



  • LITEOS_BASELIB += -lgpio_sample
      
  • LIB_SUBDIRS    += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/gpio-sample

复制代码 LITEOS_BASELIB中的hdf_sample为Makefile里的MODULE_NAME名字。



3.5 驱动编译

3.5.1 手动使能驱动源码编译:

$ vi ~/harmony/sdk/kernel/liteos_a/tools/build/config/debug/hi3516dv300_clang.config



  • LOSCFG_DRIVERS_HDF_LCD=y
      
  • LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
      
  • LOSCFG_DRIVERS_HDF_PLATFORM_HDF_SAMPLE=y
      
  • +LOSCFG_DRIVERS_HDF_PLATFORM_GPIO_SAMPLE=y

复制代码 让修改生效:

$ cd ~/harmony/sdk/kernel/liteos_a/

$ ./build.sh hi3516dv300 clang debug



  • sh param:hi3516dv300,clang,debug

复制代码

3.5.2 编译

$ cd ~/harmony/sdk/

$ python build.py ipcamera_hi3516dv300 -b debug



  • ohos ipcamera_hi3516dv300 build success!

复制代码

3.5.3 查看驱动模块gpio_sample存放位置:

~/harmony/sdk$ find -name gpio_sample



  • ./out/ipcamera_hi3516dv300/obj/kernel/liteos_a/obj/vendor/huawei/hdf/sample/platform/gpio_sample

复制代码



4. 编写用户程序和驱动交互代码

4.1 基于HDF框架编写的用户态程序和驱动交互的代码如下:

$ mkdir -p ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample/app

$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample/app/gpio_sample_app.c



  • #include <fcntl.h>
      
  • #include <unistd.h>
      
  • #include “hdf_log.h“
      
  • #include “hdf_sbuf.h“
      
  • #include “hdf_io_service_if.h“
      
  • #include <stdio.h>
      
  • #include <stdlib.h>
      
  • #include <string.h>
      
  • #include <math.h>
      

  •   
  • #define HDF_LOG_TAG “gpio_sample_test“
      
  • #define GPIO_SERVICE_NAME “gpio_service“  // 服务的关键名称。
      

  •   
  • // 设置cmd编号,类似于Linux的ioctl命令码。
      
  • #define CMD_GPIO_LDR_TEST                   100
      
  • #define CMD_GPIO_LED_TEST                   101
      
  • #define CMD_GPIO_FILL_LIGHT                 102
      
  • // GPIO NUM
      
  • #define GPIO_NUM_LDR_GPIO10_3               83  // 10*8+3
      
  • #define GPIO_NUM_LED_GPIO2_3                19  // 2*8+3
      
  • #define GPIO_NUM_FILL_LIGHT_GPIO5_1         41  // 5*8+1
      

  •   
  • int g_replyFlag = 0;
      

  •   
  • // 用户空间回调函数,驱动通过HdfDeviceSendEvent发送事件后,该函数将处理返回数据。
      
  • static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
      
  • {
      
  •     const char *string = HdfSbufReadString(data);
      
  •     if (string == NULL) {
      
  •         HDF_LOGE(“fail to read string in event data“);
      
  •         g_replyFlag = 1;
      
  •         return HDF_FAILURE;
      
  •     }
      
  •     HDF_LOGE(“user space received:“);
      
  •     HDF_LOGE(“%s: dev event received: %u %s“,  (char *)priv, id, string);
      
  •     g_replyFlag = 1;
      
  •     return HDF_SUCCESS;
      
  • }
      

  •   
  • // 用户空间给内核驱动发送数据
      
  • static int SendEvent(struct HdfIoService *serv, char *eventData, int cmd)
      
  • {
      
  •     int ret = 0;
      

  •   
  •     // 获取系统分配的data sbuf。
      
  •     struct HdfSBuf *data = HdfSBufObtainDefaultSize();
      
  •     if (data == NULL) {
      
  •         HDF_LOGE(“fail to obtain sbuf data“);
      
  •         return 1;
      
  •     }
      

  •   
  •     // 获取系统分配的reply sbuf。
      
  •     struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
      
  •     if (reply == NULL) {
      
  •         HDF_LOGE(“fail to obtain sbuf reply“);
      
  •         ret = HDF_DEV_ERR_NO_MEMORY;
      
  •         goto out;
      
  •     }
      

  •   
  •     // 将用户空间目标数据放到sbuf。
      
  •     if (!HdfSbufWriteString(data, eventData)) {
      
  •         HDF_LOGE(“fail to write sbuf“);
      
  •         ret = HDF_FAILURE;
      
  •         goto out;
      
  •     }
      

  •   
  •     // 通过驱动服务提供的Dispatch实现用户态应用和内核态驱动的信息交互。
      
  •     switch (cmd) {
      
  •         case 1: {
      
  •             ret = serv->dispatcher->Dispatch(&serv->object, CMD_GPIO_LDR_TEST, data, reply);
      
  •             if (ret != HDF_SUCCESS) {
      
  •                 HDF_LOGE(“fail to send service call“);
      
  •                 goto out;
      
  •             }
      

  •   
  •             break;
      
  •         }
      

  •   
  •         case 2: {
      
  •             ret = serv->dispatcher->Dispatch(&serv->object, CMD_GPIO_LED_TEST, data, reply);
      
  •             if (ret != HDF_SUCCESS) {
      
  •                 HDF_LOGE(“fail to send service call“);
      
  •                 goto out;
      
  •             }
      

  •   
  •             break;
      
  •         }
      

  •   
  •         case 3: {
      
  •             ret = serv->dispatcher->Dispatch(&serv->object, CMD_GPIO_FILL_LIGHT, data, reply);
      
  •             if (ret != HDF_SUCCESS) {
      
  •                 HDF_LOGE(“fail to send service call“);
      
  •                 goto out;
      
  •             }
      

  •   
  •             break;
      
  •         }
      

  •   
  •         default: {
      
  •             HDF_LOGE(“no such cmd“);
      
  •             break;
      
  •         }
      
  •     }
      

  •   
  •     // 将sbuf中获取到的字符串数据放到用户空间变量中。
      
  •     const char *replyData = HdfSbufReadString(reply);
      
  •     if (replyData == NULL) {
      
  •         HDF_LOGE(“fail to get service call reply“);
      
  •         ret = HDF_ERR_INVALID_OBJECT;
      
  •         goto out;
      
  •     }
      
  •     HDF_LOGE(“Get reply is: %s“, replyData);
      
  • out:
      
  •     HdfSBufRecycle(data); // 释放用于数据中转的sbuf
      
  •     HdfSBufRecycle(reply); // 释放用于数据中转的sbuf
      
  •     return ret;
      
  • }
      

  •   
  • int main(int argc, char **argv)
      
  • {
      
  •     char *sendData = “come from user sapce: default event info“;
      

  •   
  •     if (argc != 2) {
      
  •         HDF_LOGE(“Usage: \n ./bin/gpio_sample_app 1        CMD_GPIO_LDR_TEST\n ./bin/gpio_sample_app 2        CMD_GPIO_LED_TEST\n ./bin/gpio_sample_app 3        CMD_GPIO_FILL_LIGHT“);
      
  •         return HDF_FAILURE;
      
  •     }
      

  •   
  •     // 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。
      
  •     struct HdfIoService *serv = HdfIoServiceBind(GPIO_SERVICE_NAME, 0);
      
  •     if (serv == NULL) {
      
  •         HDF_LOGE(“fail to get service %s“, GPIO_SERVICE_NAME);
      
  •         return HDF_FAILURE;
      
  •     }
      

  •   
  •     // 注册订阅驱动服务的回调函数。
      
  •     static struct HdfDevEventlistener listener = {
      
  •         .callBack = OnDevEventReceived,
      
  •         .priv =“Service0“
      
  •     };
      

  •   
  •     // 当对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动。
      
  •     if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
      
  •         HDF_LOGE(“fail to register event listener“);
      
  •         return HDF_FAILURE;
      
  •     }
      

  •   
  •     // 用户空间给内核驱动发送数据
      
  •     HDF_LOGE(“user space send:“);
      
  •     HDF_LOGE(“cmd: %s, send data is: %s“, argv[1], sendData);
      
  •     if (SendEvent(serv, sendData, atoi(argv[1]))) {
      
  •         HDF_LOGE(“fail to send event“);
      
  •         return HDF_FAILURE;
      
  •     }
      

  •   
  •     /* wait for event receive event finishing */
      
  •     while (g_replyFlag == 0) {
      
  •         sleep(1);
      
  •     }
      

  •   
  •     // 用户态程序注册接收驱动上报事件的操作方法。
      
  •     if (HdfDeviceUnregisterEventListener(serv, &listener)) {
      
  •         HDF_LOGE(“fail to  unregister listener“);
      
  •         return HDF_FAILURE;
      
  •     }
      

  •   
  •     // 释放驱动服务。
      
  •     HdfIoServiceRecycle(serv);
      
  •     return HDF_SUCCESS;
      
  • }
      


复制代码

4.2 编写编译APP源码的BUILD.gn文件

$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/gpio-sample/app/BUILD.gn



  • # Copyright (c) 2020 Huawei Device Co., Ltd.
      
  • # Licensed under the Apache License, Version 2.0 (the “License“);
      
  • # you may not use this file except in compliance with the License.
      
  • # You may obtain a copy of the License at
      
  • #
      
  • #     http://www.apache.org/licenses/LICENSE-2.0
      
  • #
      
  • # Unless required by applicable law or agreed to in writing, software
      
  • # distributed under the License is distributed on an “AS IS“ BASIS,
      
  • # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      
  • # See the License for the specific language governing permissions and
      
  • # limitations under the License.
      

  •   
  • import(“//build/lite/config/component/lite_component.gni“)
      
  • import(“//build/lite/ndk/ndk.gni“)
      

  •   
  • HDF_FRAMEWORKS = “//drivers/hdf/frameworks“
      

  •   
  • executable(“gpio_sample_app“) {
      
  •     sources = [
      
  •         “gpio_sample_app.c“
      
  •     ]
      

  •   
  •     include_dirs = [
      
  •         “$HDF_FRAMEWORKS/ability/sbuf/include“,
      
  •         “$HDF_FRAMEWORKS/core/shared/include“,
      
  •         “$HDF_FRAMEWORKS/core/host/include“,
      
  •         “$HDF_FRAMEWORKS/core/master/include“,
      
  •         “$HDF_FRAMEWORKS/include/core“,
      
  •         “$HDF_FRAMEWORKS/include/utils“,
      
  •         “$HDF_FRAMEWORKS/utils/include“,
      
  •         “$HDF_FRAMEWORKS/include/osal“,
      
  •         “//third_party/bounds_checking_function/include“,
      
  •     ]
      

  •   
  •     deps = [
      
  •         “//drivers/hdf/lite/manager:hdf_core“,
      
  •         “//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal“,
      
  •     ]
      

  •   
  •     public_deps = [
      
  •         “//third_party/bounds_checking_function:libsec_shared“,
      
  •     ]
      
  •     defines = [
      
  •         “__USER__“,
      
  •     ]
      

  •   
  •     cflags = [
      
  •         “-Wall“,
      
  •         “-Wextra“,
      
  •     ]
      
  • }
      


复制代码

4.3 将hdf_sample_app.c加入编译

$ vi ~/harmony/sdk/build/lite/product/ipcamera_hi3516dv300.json



  •     {
      
  •       “name“: “hdf“,
      
  •       “component“: [
      
  •         { “name“: “posix“, “dir“: “//drivers/hdf/lite/posix:hdf_posix“, “features“:[] },
      
  •         { “name“: “manager“, “dir“: “//drivers/hdf/lite/manager:hdf_manager“, “features“:[] },
      
  •         { “name“: “wifi“, “dir“: “//vendor/huawei/hdf/wifi:wifi_firmware“, “features“:[] },
      
  •         { “name“: “display“, “dir“: “//vendor/huawei/hdf/display/hdi:hdi_display“, “features“:[] },
      
  •         { “name“: “input“, “dir“: “//vendor/huawei/hdf/input/hdi:hdi_input“, “features“:[] },
      
  •         { “name“: “hdf_sample“, “dir“: “//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample“, “features“:[] },
      
  •         { “name“: “hdf_sample_app“, “dir“: “//vendor/huawei/hdf/sample/platform/hdf-sample/app:hdf_sample_app“, “features“:[] },
      
  • +        { “name“: “gpio_sample_app“, “dir“: “//vendor/huawei/hdf/sample/platform/gpio-sample/app:gpio_sample_app“, “features“:[] }
      
  •       ]
      
  •     },

复制代码

4.4 应用编译

$ python build.py ipcamera_hi3516dv300 -b debug

$ vi out/ipcamera_hi3516dv300/build.log



  • [1159/1348] clang obj/vendor/huawei/hdf/sample/platform/gpio-sample/app/gpio_sample_app.o
      
  • [1162/1348] LLVM LINK ./bin/gpio_sample_app

复制代码 通过上面的编译日志可以找到gpio_sample_app应用放在了系统的/bin目录:



  • ./bin/gpio_sample_app

复制代码



5. 烧录:

5.1 将系统bin文件拷贝到Windows共享目录中:

$ rm /mnt/hgfs/proj-harmony/images/out/ -RF

$ cp -rf out/ /mnt/hgfs/proj-harmony/images/



5.2 在HiTool工具中依次填入OHOS_Image.bin、rootfs.img、userfs.img的文件位置

E:\Share_Space\proj-harmony\images\out\ipcamera_hi3516dv300





bin_file.png (38.61 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:21 上传

操作流程如下:



shunxu.png (239.2 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:21 上传

解决windows-x86-64的PL2303驱动开机后无法使用的办法:每次开机后,在设备管理中找到有问题的设备,鼠标右键先卸载驱动,安装USB-to-Serial Comm Port.exe,要注意的是需要赶在驱动失效前用HiTool将串口占用,系统就不会提示驱动有问题了。

修改一下分区长度:



part.png (97.92 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:21 上传

点击“烧写”按键后,控制台会提示重启目标板,重启后,系统就自动进入烧写了。



单板初次启动需要修改启动参数,重新上电后登陆会进入uboot中,如果分区位置没有变化则不用执行下面修改启动参数的指令。



hisilicon # setenv bootcmd “mmc read 0x0 0x80000000 0x800 0x3000; go 0x80000000“;

hisilicon # setenv bootargs “console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=7M rootsize=15M rw“;

hisilicon # saveenv

hisilicon # reset

重启后进入系统。



注释:表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,“rootaddr=6M rootsize=14M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与新增rootfs.img文件时所填大小必须相同。





6. 测试

在根目录下,在命令行输入下面命令执行写入的demo程序,显示成功结果如下所示。没执行一次gpio_sample_app将会触发1s时长的红外检测,超时将会自动退出检测。



  • OHOS # ./bin/gpio_sample_app
      
  • // 用户空间给内核驱动发送数据
      
  • [HDF:E/“gpio_sample_test“]user space send:
      
  • [HDF:E/“gpio_sample_test“]cmd: 1, send data is: come from user sapce: default event info
      
  • // 被驱动中用来处理用户态发下来的消息的函数HdfSampleDriverDispatch接收到
      
  • [ERR][HDF:E/gpio_driver]GpioSampleDriverDispatch: received cmd 100
      
  • [ERR][HDF:E/gpio_driver]GpioSampleDriverDispatch: read data is: come from user sapce: default event info
      

  •   
  • // gpio中断测试,此时需要将手放到LDR1的前面遮挡一次触发中断,否则驱动将会超时退出。
      
  • // 设置模式:双边沿触发
      
  • [ERR][HDF:E/gpio_driver]gpio_test_ldr_irq_edge: mode:3
      
  • // TestCaseGpioIrqHandler()这个回调函数被执行,说明检测到中断
      
  • [ERR][HDF:E/gpio_driver]TestCaseGpioIrqHandler: irq triggered! on gpio:83, data=(nil)
      
  • // 在1000ms超时前已经检测到gpio中断
      
  • [ERR][HDF:E/gpio_driver]gpio_test_ldr_irq_edge: wait irq timeout:0
      

  •   
  • // 用户空间收到内核空间返回的数据
      
  • [HDF:E/“gpio_sample_test“]Get reply is: come from kernel sapce: CMD_GPIO_LDR_TEST
      
  • [HDF:E/“gpio_sample_test“]user space received:
      
  • [HDF:E/“gpio_sample_test“]Service0: dev event received: 100 come from user sapce: default event info
      
  • [HDF:E/hdf_syscall_adapter]event listener task exit

复制代码

打印结果的截图:

帮助信息:



帮助信息.png (17.17 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:22 上传

gpio中断检测成功:



gpio中断检测成功.png (37.73 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:22 上传

gpio中断检测超时失败:



gpio中断检测失败.png (45.61 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:22 上传

D4-LED控制:此时D4和处理器所在板子上的两颗绿色LED会闪烁三次。



D4-LED控制.png (30.4 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:22 上传

补光灯控制:此时两颗红色补光灯会闪烁三次,之所以出现红色可能是该灯需要pwm控制,然后直接控制电平时输入电流太小导致的,这个也暂时没去验证,反正能亮灭就达到了测试目的。



补光灯控制.png (30.35 KB, 下载次数: 0)

下载附件  保存到相册  

昨天 23:22 上传





7. 附件:

修改过后的diff文件:

ubuntu@ubuntu20:~/harmony/sdk$ git diff> gpio_sample.diff
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


联系客服 关注微信 下载APP 返回顶部 返回列表