瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工藝,搭載一顆四核Cortex-A55處理器和Mali G52 2EE 圖形處理器。RK3568 支持4K 解碼和 1080P 編碼,支持SATA/PCIE/USB3.0 外圍接口。RK3568內(nèi)置獨立NPU,可用于輕量級人工智能應(yīng)用。RK3568 支持安卓 11 和 linux 系統(tǒng),主要面向物聯(lián)網(wǎng)網(wǎng)關(guān)、NVR 存儲、工控平板、工業(yè)檢測、工控盒、卡拉 OK、云終端、車載中控等行業(yè)。

?
【本文摘自】【北京迅為】iTOP-RK3568OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)
【相關(guān)視頻】OpenHarmony學(xué)習(xí)開發(fā)系列教程(第1期 北向基礎(chǔ)篇一)
OpenHarmony學(xué)習(xí)開發(fā)系列教程(第2期 南向基礎(chǔ)篇一)
第3章 實操-HDF驅(qū)動配置LED
從本章節(jié)開始,我們來實操一下,配置HDF驅(qū)動控制LED。
3.1 查看原理圖
首先打開底板原理圖,如下圖所示:

由上圖可以看出,LED燈是由GPIO0_B7控制的。當(dāng)GPIO0_B7為高電平時,三極管Q16導(dǎo)通,LED9點亮。當(dāng)GPIO0_B7為低電平時,三極管Q16截止,LED9不亮。由1.2小節(jié)可以計算出GPIO的引腳編號是15。
3.2 修改HCS硬件配置
驅(qū)動的設(shè)備描述修改/vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs文件,添加如下代碼,如下所示:
device_topeet_led :: device {
device0::deviceNode {
policy = 2;
priority = 100;
preload = 0;
permission = 0666;
moduleName = "topeet_led_driver";
serviceName = "topeet_led_service";
deviceMatchAttr = "topeet_led_config";
}
}

接下來解釋一下上面的節(jié)點配置
device_topeet_led設(shè)備節(jié)點歸類于platform這個host
device_topeet_led :: device表示led類設(shè)備
device0::deviceNode表示led類設(shè)備下的某個具體設(shè)備節(jié)點的配置
policy = 2;表示驅(qū)動服務(wù)發(fā)布策略,內(nèi)核態(tài)用戶態(tài)都可調(diào)用
priority = 100;表示驅(qū)動啟動優(yōu)先級
preload = 0;表示驅(qū)動按需加載字段,啟動加載
permission = 0666;表示驅(qū)動創(chuàng)建設(shè)備節(jié)點
moduleName = "topeet_led_driver";表示驅(qū)動名稱是topeet_led_driver,必須和驅(qū)動入口結(jié)構(gòu)中的moduleName值一致。
serviceName = "topeet_led_service";表示驅(qū)動對外發(fā)布服務(wù)的名稱,必須唯一
deviceMatchAttr = "topeet_led_config";表示驅(qū)動私有數(shù)據(jù)匹配關(guān)鍵詞,必須和驅(qū)動私有數(shù)據(jù)配置節(jié)點的match_attr匹配
3.3 創(chuàng)建私有配置文件
接下來新建vendor/hihope/rk3568/hdf_config/khdf/topeet/topeet_config.hcs文件,topeet_config.hcs為驅(qū)動私有配置文件,用來填寫一些驅(qū)動的默認(rèn)配置信息。HDF 框架在加載驅(qū)動時,會獲取相應(yīng)的配置信息并將其保存在 HdfDeviceObject 的 property 中。這些配置信息通過 Bind 和 Init 方法傳遞給驅(qū)動。
topeet_config.hcs具體內(nèi)容如下所示:
root {
platform{
topeet_led_config {
//該字段的值必須和device_info.hcs中的deviceMatchAttr一致
match_attr = "topeet_led_config";
led_version = 1;//版本號
led_number = 15;//GPIO引腳號
}
}
}
驅(qū)動私有配置文件寫完之后,我們需要將該配置文件添加到板級配置入口文件vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs中,如下圖所示:

3.4 新增topeet子系統(tǒng)
在Openharmony源碼根目錄下新建topeet文件夾及其文件夾下的文件。目錄如下所示:

接下來依次解釋一下每個文件的作用。
bundle.json:
demos:組件目錄
hdf_led:子組件目錄
app:led應(yīng)用層目錄
├── BUILD.gn:應(yīng)用APP的GN文件
└── led_test.c:應(yīng)用層LED測試程序
driver:內(nèi)核HDF驅(qū)動程序目錄
├── led_driver.c:內(nèi)核LED HDF驅(qū)動程序
└── Makefile:內(nèi)核LED HDF驅(qū)動編譯腳本
3.4.1 編寫bundle.json文件
bundle.json文件內(nèi)容如下所示:
{
"name":"@ohos/demos",
"description":"topeet demos",
"version":"4.1",
"license":"Apache-2.0",
"publishAs":"code-segment",
"segment":{
"destPath":"topeet/demos"
},
"dirs":{},
"scripts":{},
"component":{
"name":"demos",
"subsystem":"topeet",
"features":[],
"syscap":[],
"adapted_system_type":["standard"],
"rom":"675KB",
"ram":"7400KB",
"deps":{
"components":[
"c_utils",
"hilog",
"hdf_core",
"napi"
],
"third_party":[]
},
"build":{
"sub_component":[
"http://topeet/demos/hdf_led/app:led_test"
]
}
}
}
下面是對各個字段的解釋:
name: "@ohos/demos" - 這是組件或項目的名稱,這里表示它屬于OHOS(OpenHarmony OS)生態(tài)系統(tǒng)下的一個名為"demos"的組件。
description: "topeet demos" -它描述了組件的簡短說明
version: "4.1" - 組件的版本號。
license: "Apache-2.0" - 組件使用的許可證類型,這里是Apache 2.0許可證。
publishAs: "code-segment" - 表示這個組件或項目是以代碼段的形式發(fā)布的。
segment:
destPath: "topeet/demos" - 代碼段的目標(biāo)路徑,即這個組件或項目在系統(tǒng)中的存放位置。
dirs: {} - 一個空對象,可能用于定義與組件相關(guān)的目錄結(jié)構(gòu),但在這個配置中未使用。
scripts: {} - 一個空對象,可能用于定義與組件相關(guān)的腳本,如構(gòu)建腳本、測試腳本等,但在這個配置中未使用。
component:
name: "demos" - 組件的名稱。
subsystem: "topeet" - 組件所屬的子系統(tǒng)名稱。
features: [] - 組件的功能列表,這里為空,表示沒有列出特定功能。
syscap: [] - 系統(tǒng)能力列表,這里為空,表示沒有列出特定的系統(tǒng)能力。
adapted_system_type: ["standard"] - 適配的系統(tǒng)類型,這里表示適用于標(biāo)準(zhǔn)系統(tǒng)。
rom: "675KB" - 組件所需的ROM大小。
ram: "7400KB" - 組件所需的RAM大小。
deps:
components: ["c_utils", "hilog", "hdf_core", "napi"] - 組件依賴的其他組件列表。
third_party: [] - 第三方依賴列表,這里為空。
build:
sub_component: ["http://topeet/demos/hdf_led/app:led_test"] - 構(gòu)建時包含的子組件路徑,這里指定了一個具體的構(gòu)建目標(biāo)。
這個JSON配置文件提供了關(guān)于如何構(gòu)建、部署和管理這個名為"demos"的組件的詳細(xì)信息。它定義了組件的基本屬性、依賴關(guān)系、構(gòu)建信息以及目標(biāo)系統(tǒng)類型等。
3.4.2 編寫內(nèi)核LED HDF驅(qū)動程序
接下來編譯LED驅(qū)動,該驅(qū)動用于在基于華為設(shè)備框架(HDF)的系統(tǒng)中控制LED燈的開關(guān),完整代碼如下所示:
#include "device_resource_if.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"
#include "gpio_if.h"
#define HDF_LOG_TAG led_driver
#define LED_WRITE 1
#define LED_VERSION 1
#define LED_ON 1
#define LED_OFF 0
struct Led_config{
uint32_t led_version;
uint32_t led_number;
};
struct Led_config g_LedCfg = {0};
/**
* @brief 控制LED的GPIO引腳
*
* 根據(jù)傳入的GPIO引腳號和模式,控制LED的開關(guān)狀態(tài)。
*
* @param gpio GPIO引腳號
* @param mode 控制模式,LED_ON表示打開LED,LED_OFF表示關(guān)閉LED
*
* @return 成功返回HDF_SUCCESS,失敗返回HDF_FAILURE
*/
static int32_t LedGpioCtl(uint16_t gpio, uint32_t mode){
// 設(shè)置GPIO電平為高電平
uint16_t level = GPIO_VAL_HIGH;
// 設(shè)置GPIO為輸出方向
if(HDF_SUCCESS != GpioSetDir(gpio, GPIO_DIR_OUT)){
// 設(shè)置GPIO方向失敗
HDF_LOGE("GpioSetDir fail");
return HDF_FAILURE;
}
// 根據(jù)mode設(shè)置GPIO電平
if(mode == LED_ON){
level = GPIO_VAL_HIGH;
}else if(mode==LED_OFF){
level = GPIO_VAL_LOW;
}
// 日志記錄GPIO操作
HDF_LOGE("%s:Write gpio %d:%d",__func__,gpio,mode);
// 向GPIO寫入電平
if(HDF_SUCCESS != GpioWrite(gpio, level)){
// 寫入GPIO電平失敗
HDF_LOGE("GpioWrite fail",__func__);
}
return HDF_SUCCESS;
}
/**
* @brief 驅(qū)動LED設(shè)備
*
* 根據(jù)傳入的命令I(lǐng)D和數(shù)據(jù),控制LED設(shè)備的狀態(tài)。
*
* @param client HDF設(shè)備客戶端指針
* @param cmdId 命令I(lǐng)D,用于指示執(zhí)行的操作類型
* @param dataBuf 輸入數(shù)據(jù)緩沖區(qū)指針,包含需要傳遞給設(shè)備的數(shù)據(jù)
* @param replyBuf 輸出數(shù)據(jù)緩沖區(qū)指針,用于存儲設(shè)備返回的數(shù)據(jù)
*
* @return 返回操作結(jié)果,成功返回HDF_SUCCESS,失敗返回相應(yīng)的錯誤碼
*/
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int32_t cmdId, struct HdfSBuf *dataBuf, struct HdfSBuf *replyBuf){
int32_t result = HDF_FAILURE;
int32_t LedMode = 0;
// 檢查客戶端和設(shè)備是否為空
if(client == NULL || client->device == NULL){
HDF_LOGE("driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
// 檢查LED配置版本是否支持
if(g_LedCfg.led_version != LED_VERSION){
HDF_LOGE("led version is not support");
return HDF_FAILURE;
}
switch(cmdId){
case LED_WRITE:
// 從數(shù)據(jù)緩沖區(qū)讀取LED模式
result = HdfSbufReadInt32(dataBuf,&LedMode);
if(result ){
// 根據(jù)LED模式控制GPIO
LedGpioCtl(g_LedCfg.led_number, (LedMode == LED_ON) ? LED_ON: LED_OFF);
}
break;
default:
// 不支持的命令I(lǐng)D
HDF_LOGE("cmdId is not support");
break;
}
return result;
}
/**
* @brief 綁定LED設(shè)備驅(qū)動
*
* 將LED設(shè)備驅(qū)動綁定到HDF設(shè)備對象上。
*
* @param deviceObject HDF設(shè)備對象指針
*
* @return 返回HDF狀態(tài)碼,成功返回HDF_SUCCESS,失敗返回相應(yīng)的錯誤碼
*/
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject){
// 檢查deviceObject是否為空
if(deviceObject == NULL){
// 如果為空,則記錄錯誤日志并返回錯誤碼
HDF_LOGE("HdfLedDriverBind: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 定義一個靜態(tài)的IDeviceIoService結(jié)構(gòu)體變量ledDriverServ
static struct IDeviceIoService ledDriverServ = {
.Dispatch = LedDriverDispatch,
};
// 將ledDriverServ的地址賦值給deviceObject的service成員
deviceObject->service =(struct IDeviceIoService *)(&ledDriverServ);
// 記錄綁定成功的日志,包括設(shè)備名稱
HDF_LOGI("g_LedDriverEntry: %s success NodeName[%s]", __func__, deviceObject->property->name);
// 返回成功碼
return HDF_SUCCESS;
}
/**
* @brief 初始化HDF LED驅(qū)動
*
* 該函數(shù)用于初始化HDF LED驅(qū)動,從HCS配置文件中讀取硬件相關(guān)配置屬性。
*
* @param deviceObject 設(shè)備對象指針
*
* @return 初始化結(jié)果
* - HDF_SUCCESS: 初始化成功
* - HDF_ERR_INVALID_OBJECT: 設(shè)備對象無效
* - HDF_FAILURE: 初始化失敗
*/
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject){
// 檢查deviceObject是否為空
if(deviceObject == NULL){
HDF_LOGE("g_LedDriverEntry: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 獲取DeviceResourceIface實例
struct DeviceResourceIface *cfgops= NULL;
cfgops = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
// 檢查cfgops及其方法GetUint32是否有效
if (cfgops == NULL || cfgops->GetUint32 == NULL) {
HDF_LOGE("%s:DeviceResourceGetIfaceInstance failed", __func__);
return HDF_FAILURE;
}
// 讀取hcs配置中的硬件相關(guān)配置屬性:led_version
// 讀取led_version
if(cfgops->GetUint32(deviceObject->property,"led_version",&g_LedCfg.led_version,0)!= HDF_SUCCESS){
HDF_LOGE("%s: read led_version failed", __func__);
return HDF_FAILURE;
}
// 讀取引腳號:led_number
// 讀取led_number
if(cfgops->GetUint32(deviceObject->property,"led_number",&g_LedCfg.led_number,0)!= HDF_SUCCESS){
HDF_LOGE("%s:Gread led_number failed", __func__);
return HDF_FAILURE;
}
// 打印初始化成功日志
HDF_LOGI("g_LedDriverEntry: %s success", __func__);
return HDF_SUCCESS;
}
/**
* @brief 釋放HDF LED驅(qū)動資源
*
* 該函數(shù)用于釋放HDF LED驅(qū)動的資源。如果傳入的HdfDeviceObject指針為空,則打印錯誤日志并直接返回。
* 如果HdfDeviceObject指針不為空,則打印成功日志并返回。
*
* @param HdfDeviceObject HDF設(shè)備對象指針
*/
void HdfLedDriverRelease(struct HdfDeviceObject *HdfDeviceObject){
// 如果傳入的HdfDeviceObject為空
if(HdfDeviceObject == NULL){
// 打印錯誤日志
HDF_LOGE("HdfLedDriverRelease: %s failed",__func__);
// 返回
return;
}
// 打印成功日志
HDF_LOGI("HdfLedDriverRelease: %s success", __func__);
// 返回
return;
}
//定義了一個結(jié)構(gòu)體HdfDriverEntry的實例g_LedDriverEntry,并初始化了它的成員變量
struct HdfDriverEntry g_LedDriverEntry = {
.moduleVersion = 1,
.moduleName = "topeet_led_driver",
.Bind = HdfLedDriverBind,
.Init = HdfLedDriverInit,
.Release = HdfLedDriverRelease,
};
//使用HDF_INIT宏來注冊或初始化這個結(jié)構(gòu)體實例g_LedDriverEntry
HDF_INIT(g_LedDriverEntry);
3.4.3 接口函數(shù)
在一小節(jié)的代碼中實現(xiàn)了一個簡單的LED驅(qū)動,下面是對代碼的詳細(xì)解釋:
包含的頭文件如下所示:
#include "device_resource_if.h":提供設(shè)備資源接口,用于從配置文件中讀取設(shè)備信息
#include "hdf_device_desc.h":包含HDF設(shè)備描述相關(guān)的定義
#include "hdf_log.h":提供日志記錄功能
#include "gpio_if.h:提供GPIO接口,用于控制LED燈的開關(guān)
宏定義如下所示:
#define HDF_LOG_TAG led_driver :定義日志標(biāo)簽,用于區(qū)分不同模塊的日志
#define LED_WRITE 1:定義LED控制命令的ID
#define LED_VERSION 1: 定義LED驅(qū)動的版本號
#define LED_ON 1 :定義LED燈打開的狀態(tài)
#define LED_OFF 0:定義LED燈關(guān)閉的狀態(tài)
數(shù)據(jù)結(jié)構(gòu)如下所示:
struct Led_config{ //led_config結(jié)構(gòu)體用于存儲LED配置信息,包括LED驅(qū)動版本號和LED GPIO編號
uint32_t led_version;
uint32_t led_number;
};
struct Led_config g_LedCfg = {0}; //全局變量,用于存儲LED配置
g_LedDriverEntry結(jié)構(gòu)體是驅(qū)動入口結(jié)構(gòu)體,如下所示,包含了驅(qū)動的版本號、模塊名、綁定、初始化和釋放函數(shù)。
struct HdfDriverEntry g_LedDriverEntry = {
.moduleVersion = 1,
.moduleName = "topeet_led_driver",
.Bind = HdfLedDriverBind,
.Init = HdfLedDriverInit,
.Release = HdfLedDriverRelease,
};
HDF_INIT(g_LedDriverEntry);
HdfLedDriverInit函數(shù)是驅(qū)動初始化函數(shù)。
參數(shù):deviceObject(設(shè)備對象)。
流程:獲取設(shè)備資源接口,讀取設(shè)備配置中的led_version和led_number(GPIO號),并保存到全局配置變量中。
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject){
// 檢查deviceObject是否為空
if(deviceObject == NULL){
HDF_LOGE("g_LedDriverEntry: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 獲取DeviceResourceIface實例
struct DeviceResourceIface *cfgops= NULL;
cfgops = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
// 檢查cfgops及其方法GetUint32是否有效
if (cfgops == NULL || cfgops->GetUint32 == NULL) {
HDF_LOGE("%s:DeviceResourceGetIfaceInstance failed", __func__);
return HDF_FAILURE;
}
// 讀取hcs配置中的硬件相關(guān)配置屬性:led_version
// 讀取led_version
if(cfgops->GetUint32(deviceObject->property,"led_version",&g_LedCfg.led_version,0)!= HDF_SUCCESS){
HDF_LOGE("%s: read led_version failed", __func__);
return HDF_FAILURE;
}
// 讀取引腳號:led_number
// 讀取led_number
if(cfgops->GetUint32(deviceObject->property,"led_number",&g_LedCfg.led_number,0)!= HDF_SUCCESS){
HDF_LOGE("%s:Gread led_number failed", __func__);
return HDF_FAILURE;
}
// 打印初始化成功日志
HDF_LOGI("g_LedDriverEntry: %s success", __func__);
return HDF_SUCCESS;
}
HdfLedDriverRelease:驅(qū)動釋放函數(shù)。
參數(shù):HdfDeviceObject(設(shè)備對象)。
流程:記錄日志,表示驅(qū)動釋放成功。
void HdfLedDriverRelease(struct HdfDeviceObject *HdfDeviceObject){
// 如果傳入的HdfDeviceObject為空
if(HdfDeviceObject == NULL){
// 打印錯誤日志
HDF_LOGE("HdfLedDriverRelease: %s failed",__func__);
// 返回
return;
}
// 打印成功日志
HDF_LOGI("HdfLedDriverRelease: %s success", __func__);
// 返回
return;
}
HdfLedDriverBind:綁定解析函數(shù)
參數(shù):deviceObject(設(shè)備對象)。
流程:將LED驅(qū)動的服務(wù)對象賦值給設(shè)備對象的服務(wù)成員。
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject){
// 檢查deviceObject是否為空
if(deviceObject == NULL){
// 如果為空,則記錄錯誤日志并返回錯誤碼
HDF_LOGE("HdfLedDriverBind: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 定義一個靜態(tài)的IDeviceIoService結(jié)構(gòu)體變量ledDriverServ
static struct IDeviceIoService ledDriverServ = {
.Dispatch = LedDriverDispatch,
};
// 將ledDriverServ的地址賦值給deviceObject的service成員
deviceObject->service =(struct IDeviceIoService *)(&ledDriverServ);
// 記錄綁定成功的日志,包括設(shè)備名稱
HDF_LOGI("g_LedDriverEntry: %s success NodeName[%s]", __func__, deviceObject->property->name);
// 返回成功碼
return HDF_SUCCESS;
}
LedDriverDispatch:解析函數(shù),解析應(yīng)用層下發(fā)的命令,執(zhí)行命令對應(yīng)的操作,控制led燈的亮滅。
參數(shù):client(客戶端信息),cmdId(命令I(lǐng)D),dataBuf(輸入數(shù)據(jù)緩沖區(qū)),replyBuf(回復(fù)數(shù)據(jù)緩沖區(qū))。
流程:檢查設(shè)備對象的有效性,驗證LED版本,根據(jù)命令I(lǐng)D讀取數(shù)據(jù)并調(diào)用LedGpioCtl控制LED。
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int32_t cmdId, struct HdfSBuf *dataBuf, struct HdfSBuf *replyBuf){
int32_t result = HDF_FAILURE;
int32_t LedMode = 0;
// 檢查客戶端和設(shè)備是否為空
if(client == NULL || client->device == NULL){
HDF_LOGE("driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
// 檢查LED配置版本是否支持
if(g_LedCfg.led_version != LED_VERSION){
HDF_LOGE("led version is not support");
return HDF_FAILURE;
}
switch(cmdId){
case LED_WRITE:
// 從數(shù)據(jù)緩沖區(qū)讀取LED模式
result = HdfSbufReadInt32(dataBuf,&LedMode);
if(result ){
// 根據(jù)LED模式控制GPIO
LedGpioCtl(g_LedCfg.led_number, (LedMode == LED_ON) ? LED_ON: LED_OFF);
}
break;
default:
// 不支持的命令I(lǐng)D
HDF_LOGE("cmdId is not support");
break;
}
return result;
}
LedGpioCtl:控制指定GPIO(LED)的高低電平,從而控制LED燈的開關(guān)。
參數(shù):gpio(GPIO號),mode(LED模式,開或關(guān))。
流程:設(shè)置GPIO為輸出方向,根據(jù)mode設(shè)置GPIO的電平,最后記錄日志。
static int32_t LedGpioCtl(uint16_t gpio, uint32_t mode){
// 設(shè)置GPIO電平為高電平
uint16_t level = GPIO_VAL_HIGH;
// 設(shè)置GPIO為輸出方向
if(HDF_SUCCESS != GpioSetDir(gpio, GPIO_DIR_OUT)){
// 設(shè)置GPIO方向失敗
HDF_LOGE("GpioSetDir fail");
return HDF_FAILURE;
}
// 根據(jù)mode設(shè)置GPIO電平
if(mode == LED_ON){
level = GPIO_VAL_HIGH;
}else if(mode==LED_OFF){
level = GPIO_VAL_LOW;
}
// 日志記錄GPIO操作
HDF_LOGE("%s:Write gpio %d:%d",__func__,gpio,mode);
// 向GPIO寫入電平
if(HDF_SUCCESS != GpioWrite(gpio, level)){
// 寫入GPIO電平失敗
HDF_LOGE("GpioWrite fail",__func__);
}
return HDF_SUCCESS;
}
3.4.4 添加內(nèi)核編譯
編譯內(nèi)核時將該HDF驅(qū)動編譯到鏡像中,接下來編寫驅(qū)動編譯腳本Makefile,代碼如下所示:
include drivers/hdf/khdf/platform/platform.mk
obj-y += led_driver.o
加入編譯體系,填加模塊目錄到drivers/hdf_core/adapter/khdf/linux/Makefile 文件
obj-$(CONFIG_DRIVERS_HDF) += ../../../../../topeet/demos/hdf_led/driver/
3.4.5 編寫應(yīng)用APP
在應(yīng)用代碼中我們實現(xiàn)如下功能:
當(dāng)應(yīng)用程序啟動后會獲取命令行參數(shù)。如果命令行沒有參數(shù),LED燈將循環(huán)閃爍;如果命令行帶有參數(shù),則根據(jù)傳輸?shù)膮?a href="http://www.www27dydycom.cn/v/tag/949/" target="_blank">數(shù)控制LED燈的開啟或關(guān)閉。通過HdfIoServiceBind 綁定LED燈的HDF服務(wù),獲取HDF空間緩存區(qū),并向該緩沖區(qū)寫入控制數(shù)據(jù)。然后,通過 LED_WRITE 命令將數(shù)據(jù)發(fā)送到 HDF 驅(qū)動,從而控制 LED 燈的亮滅。在程序結(jié)束時,會回收 HDF 空間緩沖區(qū)和 HDF 服務(wù)。
接下來編寫應(yīng)用測試文件led_test.c,完整代碼如下所示。
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "hdf_base.h"
#include "hdf_io_service.h"
#include "hilog/log.h"
#undef LOG_TAG
#undef LOG_DOMAIN
#define LOG_TAG "led_test"
#define LOG_DOMAIN 0xD0020240
#define ARGS_NUM 2
#define LED_SERVICE_NAME "topeet_led_service"
#define LED_WRITE 1
/**
* @brief 主函數(shù),用于控制LED燈的開關(guān)狀態(tài)
*
* 根據(jù)傳入的參數(shù)控制LED燈的開關(guān)狀態(tài),如果沒有傳入?yún)?shù),則進(jìn)入主循環(huán),不斷切換LED燈的開關(guān)狀態(tài)。
*
* @param argc 命令行參數(shù)的數(shù)量
* @param argv 命令行參數(shù)的數(shù)組
*
* @return 返回HDF_SUCCESS表示成功,否則返回錯誤碼
*/
int main(int argc, char *argv[]){
int ret = HDF_SUCCESS;
int32_t mode = -1;
// 判斷命令行參數(shù)數(shù)量
if (argc == ARGS_NUM) {
// 將命令行參數(shù)轉(zhuǎn)換為整數(shù)并賦值給 mode
mode = atoi(argv[1]);
// 打印 mode 的狀態(tài)
printf("mode[%s][0x%x]\n",(mode==1)?"On":"Off",mode);
} else {
// 命令行參數(shù)數(shù)量不正確,打印提示信息
printf("led main loop. \n");
}
// 綁定 LED 服務(wù)
struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE_NAME);
if(serv == NULL){
// 綁定服務(wù)失敗,打印錯誤信息并返回 -1
HILOG_ERROR(LOG_APP, "get service %s failed!", LED_SERVICE_NAME);
return -1;
}
// 打印綁定服務(wù)成功的日志
HILOG_ERROR(LOG_APP, "get service %s succeed", LED_SERVICE_NAME);
// 獲取默認(rèn)大小的 SBuf 對象
struct HdfSBuf *data = HdfSbufObtainDefaultSize();
if(data == NULL){
// 獲取 SBuf 對象失敗,打印錯誤信息并返回 -1
HILOG_ERROR(LOG_APP,"obtain data failed\n");
return -1;
}
// 打印獲取 SBuf 對象成功的日志
HILOG_ERROR(LOG_APP,"obtain data succeed\n");
// 如果 mode 為 -1,則進(jìn)入循環(huán)
if(mode == -1){
while(1){
// 清空 SBuf 對象
HdfSbufFlush(data);
// 向 SBuf 對象寫入整數(shù) 1
if(!HdfSbufWriteInt32(data, 1)){
// 寫入數(shù)據(jù)失敗,打印錯誤信息并返回 -1
HILOG_ERROR(LOG_APP,"write data failed");
return -1;
}
// 調(diào)用 Dispatch 方法,發(fā)送 LED 寫入命令
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE, data, NULL);
usleep(500 * 1000);
// 清空 SBuf 對象
HdfSbufFlush(data);
// 向 SBuf 對象寫入整數(shù) 0
if(!HdfSbufWriteInt32(data, 0)){
// 寫入數(shù)據(jù)失敗,打印錯誤信息并返回 -1
HILOG_ERROR(LOG_APP,"write data failed");
return -1;
}
// 調(diào)用 Dispatch 方法,發(fā)送 LED 寫入命令
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE, data, NULL);
usleep(500 * 1000);
}
} else {
// 如果 mode 不為 -1,則向 SBuf 對象寫入 mode 值
if(!HdfSbufWriteInt32(data, mode)){
// 寫入數(shù)據(jù)失敗,打印錯誤信息并返回 -1
HILOG_ERROR(LOG_APP,"write data failed");
return -1;
}
// 調(diào)用 Dispatch 方法,發(fā)送 LED 寫入命令
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE, data, NULL);
// 打印 Dispatch 成功的日志
HILOG_ERROR(LOG_APP,"Dispatch succeed");
}
// 回收 SBuf 對象
HdfSbufRecycle(data);
// 回收服務(wù)對象
HdfIoServiceRecycle(serv);
// 打印主程序退出的日志
HILOG_INFO(LOG_APP,"[%s] main exit.",LOG_TAG);
return ret;
}
接下來編寫應(yīng)用APP的GN文件BUILD.gn,代碼內(nèi)容如下所示:
HDF_FRAMEWORKS = "http://drivers/hdf_core/framework"
HDF_ADAPTER = "http://drivers/hdf_core/adapter"
import("http://build/ohos.gni")
import("$HDF_ADAPTER/uhdf2/uhdf.gni")
print("demos: compile led_test")
ohos_executable("led_test"){
sources = ["led_test.c"]
include_dirs = [
"$HDF_FRAMEWORKS/include",
"$HDF_FRAMEWORKS/include/core",
"$HDF_FRAMEWORKS/include/osal",
"$HDF_FRAMEWORKS/include/platform",
"$HDF_FRAMEWORKS/include/utils",
"$HDF_ADAPTER/uhdf2/ipc/include",
"$HDF_ADAPTER/uhdf2/osal/include",
"http://base/hiviewdfx/hilog/interfaces/native/innerkits/include",
"http://third_party/bounds_checking_function/include",
]
external_deps = [
"c_utils:utils",
"hdf_core:libhdf_platform",
"hdf_core:libhdf_utils",
"hilog:libhilog",
]
cflags = [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-format",
"-Wno-format-extra-args",
]
part_name = "demos"
install_enable = true
}
上面的代碼用于構(gòu)建一個“l(fā)ed_test”的可執(zhí)行文件的構(gòu)建腳本,它使用了GN(Generate Ninja)構(gòu)建系統(tǒng),這是一種元構(gòu)建系統(tǒng),用于生成Ninja構(gòu)建文件。
1-2行定義了兩個變量HDF_FRAMEWORKS和HDF_ADAPTER,它們分別指向HDF(Hardware Driver Foundation,硬件驅(qū)動框架)核心框架和適配器的路徑。這些路徑是相對于項目根目錄的。
4-5行 使用import語句導(dǎo)入兩個GNI(GN Include)文件。GNI文件是GN構(gòu)建系統(tǒng)用來包含變量定義、函數(shù)和模板的文件。這里導(dǎo)入的文件可能包含了一些預(yù)定義的變量、函數(shù)或構(gòu)建規(guī)則,用于支持構(gòu)建過程。//build/ohos.gni可能包含了OpenHarmony特有的構(gòu)建配置,而$HDF_ADAPTER/uhdf2/uhdf.gni可能包含了與uHDF(Unified Hardware Driver Framework,統(tǒng)一硬件驅(qū)動框架)相關(guān)的配置。
7行 打印一條消息到控制臺,表明正在編譯led_test示例。
9-40行 定義一個名為led_test的ohos_executable目標(biāo),這是一個構(gòu)建規(guī)則,用于生成一個可執(zhí)行文件。下面是該目標(biāo)的具體配置:
sources:指定源文件列表,這里只有一個文件led_test.c。
include_dirs:指定頭文件搜索路徑列表。這些路徑用于在編譯時查找包含的文件(#include指令引用的文件)。這些路徑包括了HDF框架、適配器的多個子目錄,以及一些第三方庫和內(nèi)部工具庫的頭文件路徑。
external_deps:指定外部依賴項列表。這些依賴項是在構(gòu)建過程中需要鏈接的庫。這里列出了幾個庫,如c_utils:utils、hdf_core:libhdf_platform等,這些庫提供了構(gòu)建led_test所需的功能。
cflags:指定傳遞給C編譯器的標(biāo)志列表。這里包括了一些常見的編譯選項,如-Wall(打開所有警告)、-Wextra(打開額外警告)、-Werror(將所有警告視為錯誤)、以及兩個用于關(guān)閉特定警告的選項。
part_name:指定構(gòu)建產(chǎn)物所屬的部件名稱,這里是demos。
install_enable:設(shè)置為true,表示構(gòu)建產(chǎn)物應(yīng)該被安裝。這可能意味著在構(gòu)建成功后,led_test可執(zhí)行文件會被復(fù)制到某個特定的目錄,以便于執(zhí)行或分發(fā)。
3.5 在產(chǎn)品中新增子系統(tǒng)
在build/subsystem_config.json文件中增加名為topeet的子系統(tǒng),在3.4節(jié)已經(jīng)新建了topeet文件夾存放子系統(tǒng)代碼。添加topeet子系統(tǒng)進(jìn)行一個登記,說明路徑和子系統(tǒng)名稱,如下所示:
“topeet”: {
“path”: “topeet”,
“name”: ”topeet”
}

在vendor/hihope/rk3568/config.json文件中增加topeet子系統(tǒng)的引入,如下所示:
{
"subsystem": "topeet",
"components": [
{
"component": "demos",
"features": [
]
}
]
}

修改完成之后,保存修改。
3.6 編譯源碼
重新編譯Openharmony4.1源碼,如下所示:
./build.sh --product-name rk3568 --ccache
或者單獨編譯部件
./build.sh --product-name rk3568 --build-target demos --ccache
編譯之后,在源碼out/rk3568/topeet目錄下生成編譯產(chǎn)物,如下圖所示:

3.7 LED測試
將編譯好的鏡像全部進(jìn)行燒寫,鏡像在源碼根目錄out/rk3568/packages/phone/images/目錄下。

燒寫完成之后,在調(diào)試串口查看打印日志,如下圖所示:

然后打開hdc工具,運行測試程序,輸入“l(fā)ed_test 1”,LED燈點亮,如下圖所示:


輸入“l(fā)ed_test 0”,LED燈熄滅,如下圖所示:


-
開發(fā)板
+關(guān)注
關(guān)注
25文章
5389瀏覽量
100878 -
OpenHarmony
+關(guān)注
關(guān)注
26文章
3804瀏覽量
17849 -
RK3568
+關(guān)注
關(guān)注
5文章
562瀏覽量
5920
發(fā)布評論請先 登錄
相關(guān)推薦
北京迅為RK3568開發(fā)板OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)內(nèi)核HDF驅(qū)動框架架構(gòu)

鴻蒙OpenHarmony南向/北向快速開發(fā)教程-迅為RK3568開發(fā)板
迅為RK3568開發(fā)板篇OpenHarmony配置HDF驅(qū)動控制LED-新增 topeet子系統(tǒng)
迅為RK3568開發(fā)板篇OpenHarmony實操HDF驅(qū)動控制LED-編寫內(nèi)核 LED HDF 驅(qū)動程序
迅為RK3568開發(fā)板篇OpenHarmony實操HDF驅(qū)動控制LED-添加內(nèi)核編譯
迅為RK3568開發(fā)板鴻蒙OpenHarmony系統(tǒng)固件燒寫步驟
【北京迅為】itop-RK3568開發(fā)板驅(qū)動開發(fā)指南
北京迅為RK3568開發(fā)板嵌入式學(xué)習(xí)之Linux驅(qū)動全新更新-CAN+

【北京迅為】iTOP-RK3568OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)GPIO基礎(chǔ)知識

【北京迅為】iTOP-RK3568開發(fā)板OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)-第4章 UART基礎(chǔ)知識

【北京迅為】iTOP-RK3568開發(fā)板鴻蒙OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)實操-HDF驅(qū)動配置UART

評論