目录

前言一、开发工具及环境搭建二、知识了解三、案例编写3.1 代码结构3.2 led_gpio.c 代码编写3.3 添加模块配置规则3.4 添加部件配置规则3.5 修改子系统配置文件3.6 修改产品配置文件3.7 配置HCS

四、代码编译、烧录五、测试运行六、结果

前言

  笔者作为一名Web开发者,最近接手了一个新项目,其中涉及到了硬件开发方面的需求。考虑到我对硬件方面领域一窍不通,这对我来说无疑是一个巨大的挑战。尽管时间紧迫,任务繁重,但我抱着学习的态度,经历了两周的不懈努力,终于实现了一个LED的案例。在这个过程中我踩坑无数遇到了无数次的困难 ,但也因此获得了宝贵的经验和教训。希望通过这篇博客能分享我的学习经历和成果,帮助更多与我有类似经历的开发者,互相勉励。

笔者开发环境情况如下:

DevEco Device Tool 4.0 Release VMware Workstation Pro 17 OpenHarmony-v3.2-Release 润和 瑞芯微(非标准) RK3568 标准系统 HDC 1.2.0a

目标:通过HDF API调用实现搭载OpenHarmony-v3.2-Release的瑞芯微 RK3568 的LED点亮和熄灭。

一、开发工具及环境搭建

官方文档已经给出详细搭建教程,将不再过多赘述。HUAWEI DevEco DeviceTool使用指南如果过程中遇到什么问题,建议参考此课程辅助能解决很多问题。HarmonyOS Connect开发工具系列课

二、知识了解

开发前的基础知识,详情请移步官网

GPIOHDFHDC

三、案例编写

3.1 代码结构

  打开项目工程,在代码根目录创建samples子系统文件夹,在子系统目录下创建led部件文件夹, led目录下创建构建文件BUILD.gn及部件配置文件bundle.json及src文件夹,src文件夹下创建led_gpio.c源文件,完整目录如下:

samples/led │── BUILD.gn │── src │ └── led_gpio.c │── bundle.json build └── subsystem_config.json vendor/hihope └── rk3568  └── config.json

3.2 led_gpio.c 代码编写

#include

#include

#include

#include

#include

#include

#include "hdf_log.h" // 标准日志接口头文件

#include "gpio_if.h" // GPIO标准接口头文件

// 定义版本号

#define SOFTWARE_VERSION "V1.0"

// 打印信息,用于打印普通信息

#define PRINT_INFO(fmt, args...) printf("%s, %s, %d, info: "fmt, __FILE__, __func__, __LINE__, ##args)

// 打印信息,用于打印错误信息

#define PRINT_ERROR(fmt, args...) printf("%s, %s, %d, error: "fmt, __FILE__, __func__, __LINE__, ##args)

// GPIO引脚序号

static uint16_t m_gpio_id = 0;

// GPIO引脚是否设置为输入,GPIO_DIR_OUT为输出,GPIO_DIR_IN为输入

static uint16_t m_gpio_dir = GPIO_DIR_IN;

// GPIO引脚的高低电平,GPIO_VAL_LOW为低电平,GPIO_VAL_HIGH为高电平

static uint16_t m_gpio_value = GPIO_VAL_LOW;

///

/***************************************************************

* 函数名称: main_help_gpio

* 说 明: 帮助文档

* 参 数: 无

* 返 回 值: 无

***************************************************************/

void main_help_gpio(char *cmd)

{

printf("%s: platform device gpio\n", cmd);

printf("Version: %s\n", SOFTWARE_VERSION);

printf("%s [options]...\n", cmd);

printf(" -g, --gpio gpio id\n");

printf(" -v, --value the value of gpio, 0 is low, 1 is high\n");

printf(" -o, --out gpio dir set to out\n");

printf(" -i, --in gpio dir set to in\n");

printf(" -h, --help help info\n");

printf("\n");

}

/***************************************************************

* 函数名称: parse_opt_gpio

* 说 明: 解析参数

* 参 数:

* @argc: 参数数量

* @argv: 参数变量数组

* 返 回 值: 无

***************************************************************/

void parse_opt_gpio(int argc, char *argv[])

{

while (1) {

struct option long_opts[] = {

{ "gpio", required_argument, NULL, 'g' },

{ "value", required_argument, NULL, 'v' },

{ "out", no_argument, NULL, 'o' },

{ "in", no_argument, NULL, 'i' },

{ "help", no_argument, NULL, 'h' },

};

int option_index = 0;

int c;

c = getopt_long(argc, argv, "g:v:oih", long_opts, &option_index);

if (c == -1) break;

switch (c) {

case 'g':

m_gpio_id = (uint16_t)atoi(optarg);

break;

case 'v':

m_gpio_value = (uint16_t)atoi(optarg);

break;

case 'o':

m_gpio_dir = GPIO_DIR_OUT;

break;

case 'i':

m_gpio_dir = GPIO_DIR_IN;

break;

case 'h':

default:

main_help_gpio(argv[0]);

exit(0);

break;

}

}

}

/***************************************************************

* 函数名称: main

* 说 明: 主函数,用于GPIO控制

* 参 数:

* @argc: 参数数量

* @argv: 参数变量数组

* 返 回 值: 0为成功,反之为错误

***************************************************************/

int main(int argc, char* argv[])

{

int32_t ret;

// 解析参数

parse_opt_gpio(argc, argv);

printf("gpio id: %d\n", m_gpio_id);

printf("gpio dir: %s\n", ((m_gpio_dir == GPIO_DIR_OUT) ? ("out") : ("in")));

printf("gpio value: %d\n", m_gpio_value);

if (m_gpio_dir == GPIO_DIR_OUT) {

// GPIO设置为输出

ret = GpioSetDir(m_gpio_id, GPIO_DIR_OUT);

if (ret != 0) {

PRINT_ERROR("GpioSetDir failed and ret = %d\n", ret);

return -1;

}

// GPIO输出电平

ret = GpioWrite(m_gpio_id, m_gpio_value);

if (ret != 0) {

PRINT_ERROR("GpioWrite failed and ret = %d\n", ret);

return -1;

}

} else {

// GPIO设置为输出

ret = GpioSetDir(m_gpio_id, GPIO_DIR_IN);

if (ret != 0) {

PRINT_ERROR("GpioSetDir failed and ret = %d\n", ret);

return -1;

}

// 读取GPIO引脚的电平

ret = GpioRead(m_gpio_id, &m_gpio_value);

if (ret != 0) {

PRINT_ERROR("GpioRead failed and ret = %d\n", ret);

return -1;

}

printf("GPIO Read Successful and GPIO = %d, value = %d\n", m_gpio_id, m_gpio_value);

}

return 0;

}

本案例中的主要程序文件,通过HDF_PLATFORM_GPIO_MANAGER驱动实现对GPIO的控制。 代码借鉴于:https://gitee.com/Lockzhiner-Electronics/lockzhiner-rk3568-openharmony#/Lockzhiner-Electronics/lockzhiner-rk3568-openharmony/blob/master/samples/b03_platform_device_gpio

3.3 添加模块配置规则

samples/led/BUILD.gn内容如下:

import("//build/ohos.gni") # 导入编译模板

import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")

ohos_executable("led_gpio") { # 可执行模块

sources = [ "src/led_gpio.c" ] # 模块源码

include_dirs = [ # 模块依赖头文件目录

"$hdf_framework_path/include",

"$hdf_framework_path/include/core",

"$hdf_framework_path/include/osal",

"$hdf_framework_path/include/platform",

"$hdf_framework_path/include/utils",

"$hdf_uhdf_path/osal/include",

"$hdf_uhdf_path/ipc/include",

"//base/hiviewdfx/hilog/interfaces/native/kits/include",

"//third_party/bounds_checking_function/include",

]

deps = [ # 部件内部依赖

"$hdf_uhdf_path/platform:libhdf_platform",

"$hdf_uhdf_path/utils:libhdf_utils",

"//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog",

]

cflags = [

"-Wall",

"-Wextra",

"-Werror",

"-Wno-format",

"-Wno-format-extra-args",

]

cflags_c = []

cflags_cc = []

ldflags = []

configs = []

part_name = "led" # 所属部件名称,必选

install_enable = true # 是否默认安装

}

编译子系统通过模块、部件和产品三层配置来实现编译和打包。模块就是编译子系统的一个目标,包括(动态库、静态库、配置文件、预编译模块等)。模块要定义属于哪个部件,一个模块只能归属于一个部件。OpenHarmony使用定制化的Gn模板来配置模块规则,Gn语法相关的基础知识请参考官网手册 本案例中,项目在进行build命令编译过程中执行的是存在于根目录的build.sh文件,会遍历扫描.gn文件编译对应程序。ohos_executable为配置项目的可执行指令,sources 为led_gpio.c的所在位置。

3.4 添加部件配置规则

samples/led/bundle.json内容如下:

{

"name": "@ohos/led", # HPM部件英文名称,格式"@组织/部件名称"

"description": "leds example.", # 部件功能一句话描述

"version": "3.1", # 版本号,版本号与OpenHarmony版本号一致

"license": "Apache License 2.0", # 部件License

"publishAs": "code-segment", # HPM包的发布方式,当前默认都为code-segment

"segment": {

"destPath": "samples/led"

}, # 发布类型为code-segment时为必填项,定义发布类型code-segment的代码还原路径(源码路径)

"dirs": {}, # HPM包的目录结构,字段必填内容可以留空

"scripts": {}, # HPM包定义需要执行的脚本,字段必填,值非必填

"component": { # 部件属性

"name": "led", # 部件名称

"subsystem": "samples", # 部件所属子系统

"syscap": [], # 部件为应用提供的系统能力

"features": [], # 部件对外的可配置特性列表,一般与build中的sub_component对应,可供产品配置

"adapted_system_type": [ "mini", "small", "standard" ], # 轻量(mini)小型(small)和标准(standard),可以是多个

"rom": "10KB", # 部件ROM值

"ram": "10KB", # 部件RAM估值

"deps": {

"components": [], # 部件依赖的其他部件

"third_party": [] # 部件依赖的三方开源软件

},

"build": { # 编译相关配置

"sub_component": [

"//samples/led:led_gpio" # 部件编译入口

], # 部件编译入口,模块在此处配置

"inner_kits": [], # 部件间接口

"test": [] # 部件测试用例编译入口

}

}

}

bundle.json文件包含两个部分,第一部分描述该部件所属子系统的信息,第二部分component则定义该部件构建相关配置。添加的时候需要指明该部件包含的模块sub_component,假如有提供给其它部件的接口,需要在inner_kits中说明,假如有测试用例,需要在test中说明,inner_kits与test没有也可以不添加。 本案例中sub_component配置为//samples/led:led_gpio,//代表根目录,samples/led代表路径,led_gpio代表BUILD.gn文件里ohos_executable的名称。

3.5 修改子系统配置文件

在build/subsystem_config.json中添加新建的子系统的配置,新增子系统的配置如下所示:

"sample": {

"path": "samples", # 路径

"name": "samples" # 子系统名

}

子系统的配置规则主要是在build/subsystem_config.json中指定子系统的路径和子系统名称。 本案例path配置samples文件夹所在路径, name与bundle.json里subsystem名称匹配即可。

3.6 修改产品配置文件

在vendor/hihope/rk3568/config.json中添加对应的led部件,直接添加到原有部件后即可

{

"subsystem": "samples",

"components": [

{

"component": "led",

"features": []

}

]

}

本案例中,subsystem配置为bundle.json里subsystem名称,component配置为led部件名称。

3.7 配置HCS

打开vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs,添加GPIO驱动设备描述,具体内容如下:

root {

device_info {

platform :: host {

device_gpio :: device {

device0 :: deviceNode { // GPIO控制器信息描述

policy = 2; // 对外发布服务,必须为2,用于定义GPIO管理器的服务

priority = 50;

permission = 0644;

moduleName = "HDF_PLATFORM_GPIO_MANAGER"; // 这与drivers/hdf_core/framework/support/platform/src/gpio/gpio_service.c的g_gpioServiceEntry.moduleName对应,它主要负责GPIO引脚的管理

serviceName = "HDF_PLATFORM_GPIO_MANAGER";

}

device1 :: deviceNode {

policy = 0; // 等于0,不需要发布服务

priority = 55; // 驱动驱动优先级

permission = 0644; // 驱动创建设备节点权限

moduleName = "linux_gpio_adapter"; // 用于指定驱动名称,必须是linux_adc_adapter,与drivers/hdf_core/adapter/khdf/linux/platform/gpio/gpio_adapter.c对应

deviceMatchAttr = ""; // 用于配置控制器私有数据,不定义

}

}

}

}

}

vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs在此文件中引入device_info.hcs文件地址,默认已经引入无需修改。 本案例中,通过moduleName配置HDF_PLATFORM_GPIO_MANAGER驱动加载到HDF驱动平台从而实现对GPIO的控制。

四、代码编译、烧录

编译之前,删除以下文件:vendor/hihope/rk3568/hdf_config/khdf/hdf_test/hdf_hcs.hcb、out/kernel/vendor/hihope/rk3568/hdf_config/khdf/hdf_test/hdf_hcs_hex.o、out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image、out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image.lz4、out/rk3568/packages/phone/images/boot_linux.img、out/kernel/src_tmp笔者在编译这步吃过很大的亏,很多时候只是因为编译后没有重新生成并覆盖旧生成的文件,导致卡了很久进度,如果发现编译后新文件未生效还是旧文件,大概率是编译问题建议直接删除out重新编译编译完成直接进行烧录即可

五、测试运行

烧录完成之后重启开发板,通过hdc工具调试,进入shell命令模式,执行以下命令:

# led_gpio -g 495 -v 1 -o

gpio id: 495

gpio dir: out

gpio value: 1

GPIO Read Successful and GPIO = 13, value = 1

#

#

# led_gpio -g 495 -v 0 -o

gpio id: 495

gpio dir: out

gpio value: 0

#

命令解析:

led_gpio 可执行模块 -g 495   led的gpio号,我这里的led的gpio号为495,请根据实际情况填写 -v 0    设置低电平 -v 1:设置高电平 -o     设置GPIO为输出 -i:设置GPIO为输入

六、结果

操作过程中如看到led点亮和熄灭,则案例实验成功如遇报错,需通过串口日志进一步排查问题所在

排查思路:

led设备是否挂载在i2c下,设备树dts、dtsi对应i2c是否配置并开启修改设备树是否成功,编译之后查看out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip下的dts、dtsi文件是否成功生效,如果未生效表明修改方式不对,需要自己打patch并替换原patchHCS是否配置成功,通过hdc工具调试连接开发板,进入shell命令模式,cd /sys/devices/virtual/hdf查看文件列表是否存在HDF_PLATFORM_GPIO_MANAGERgpio号是否正确

相关阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。