這一篇我們繼續深入一點點,嘗試打通從用戶態UI到內核態HDF之間的聯系。其中涉及到的調用關系比較復雜,建議在“用鴻蒙開發AI應用(五)HDF 驅動補光燈”的基礎上閱讀本文,HDF的相關細節這里就不在贅述了。
背景知識
用戶程序框架子系統包含兩個大的模塊:Ability子系統和包管理子系統。
1. Ability子系統
1.1 Ability
Ability是系統調度應用的最小單元,是能夠完成一個獨立功能的組件,一個應用可以包含一個或多個Ability。Ability分為兩種類型:Page類型的Ability和Service類型的Ability
Page類型的Ability:帶有界面,為用戶提供人機交互的能力。
Service類型的Ability:不帶界面,為用戶提供后臺任務機制。
1.2 AbilitySlice
AbilitySlice是單個頁面及其控制邏輯的總和,是Page類型Ability特有的組件,一個Page類型的Ability可以包含多個AbilitySlice,此時,這些頁面提供的業務能力應當是高度相關的。
1.3 生命周期
生命周期是Ability被調度到啟動、激活、隱藏和退出等各個狀態的的統稱。
Ability生命周期各狀態解析:
UNINITIALIZED:未初始狀態,為臨時狀態,Ability被創建后會由UNINITIALIZED狀態進入INITIAL狀態;
INITIAL:初始化狀態,也表示停止狀態,表示當前Ability未運行,調用Start后進入INACTIVE,同時回調開發者的OnStart生命周期回調;
INACTIVE:未激活狀態,表示當前窗口已顯示但是無焦點狀態,由于Window暫未支持焦點的概念,當前狀態與ACTIVE一致。
ACTIVE:前臺激活狀態,表示當前窗口已顯示,并獲取焦點,Ability在退到后臺之前先由ACTIVE狀態進入INACTIVE狀態;
BACKGROUND: 后臺狀態,表示當前Ability退到后臺,Ability在被銷毀后由BACKGROUND狀態進入INITIAL狀態,或者重新被激活后由BACKGROUND狀態進入ACTIVE狀態。
1.4 AbilityLoader
AbilityLoader負責注冊和加載開發者Ability的模塊。開發者開發的Ability先要調用AbilityLoader的注冊接口注冊到框架中,接著Ability啟動時會被實例化。
1.5 AbilityManager
AbilityManager負責AbilityKit和Ability管理服務進行IPC的通信。
1.6 EventHandler
EventHandler是AbilityKit提供給開發者的用于在Ability中實現線程間通信的一個模塊。
1.7 Ability運行管理服務
Ability運行管理服務是用于協調各Ability運行關系、及生命周期進行調度的系統服務。
其中,服務啟動模塊負責Ability管理服務的啟動、注冊等。
服務接口管理模塊負責Ability管理服務對外能力的管理。
進程管理模塊負責Ability應用所在進程的啟動和銷毀、及其進程信息維護等功能。Ability棧管理模塊負責維護各個Ability之間跳轉的先后關系。
生命周期調度模塊是Ability管理服務根據系統當前的操作調度Ability進入相應的狀態的模塊。
連接管理模塊是Ability管理服務對Service類型Ability連接管理的模塊。
1.8 AppSpawn
AppSpawn是負責創建Ability應用所在進程的系統服務,該服務有較高的權限,為Ability應用設置相應的權限,并預加載一些通用的模塊,加速應用的啟動。
2. 包管理子系統
包管理子系統,是OpenHarmony為開發者提供的安裝包管理框架。
BundleKit:是包管理服務對外提供的接口,有安裝/卸載接口、包信息查詢接口、包狀態變化監聽接口。
包掃描器:用來解析本地預制或者安裝的安裝包,提取里面的各種信息,供管理子模塊進行管理,持久化。
包安裝子模塊:安裝,卸載,升級一個包;包安裝服務一個單獨進程的用于創建刪除安裝目錄,具有較高的權限。
包管理子模塊:管理安裝包相關的信息,存儲持久化包信息。
包安全管理子模塊:簽名檢查、權限授予、權限管理。
HDF驅動LED(可選)
之前在內核中已經注冊過一個led_driver驅動,并以led_service服務發布,這一節稍微重構一下代碼,功能上沒有變化,我們快速過一遍,熟悉HDF的可以自行跳過。
1. 業務代碼
先新建頭文件vendorhuaweihdfledincludeled_ctrl.h。
#ifndef _LED_CTRL_H #define _LED_CTRL_H #include "hdf_device_desc.h" #include "hdf_log.h" #include "device_resource_if.h" #include "osal_io.h" #include "osal_mem.h" #include "gpio_if.h" #include "osal_irq.h" #include "osal_time.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ extern int32_t CtlLED(int mode); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _LED_CTRL_H */
再新建源文件 vendorhuaweihdfledled_ctrl.c
#include "led_ctrl.h" #define HDF_LOG_TAG led_driver // 打印日志所包含的標簽,如果不定義則用默認定義的HDF_TAG標簽 int32_t CtlLED(int mode) { int32_t ret; uint16_t valRead; /* LED的GPIO管腳號 */ // uint16_t gpio = 5 * 8 + 1; // 紅外補光燈 uint16_t gpio = 2 * 8 + 3; // 綠色指示燈 // uint16_t gpio = 3 * 8 + 4; // 紅色指示燈 /* 將GPIO管腳配置為輸出 */ ret = GpioSetDir(gpio, GPIO_DIR_OUT); if (ret != 0) { HDF_LOGE("GpioSerDir: failed, ret %d ", ret); return ret; } if (mode == -1) { // 翻轉輸出口 (void)GpioRead(gpio, &valRead); ret = GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW); } else { ret = GpioWrite(gpio, mode); } if (ret != 0) { HDF_LOGE("GpioWrite: failed, ret %d ", ret); return ret; } return ret; }
先完成對綠色指示燈的控制邏輯。
2. 驅動實現 在 huawei/hdf 目錄下新建一個文件夾 led, 然后在其中新建一個源文件 led.c。
#include "hdf_device_desc.h" // HDF框架對驅動開放相關能力接口的頭文件 #include "hdf_log.h" // HDF 框架提供的日志接口頭文件 #include "led_ctrl.h" // #define HDF_LOG_TAG led_driver // 打印日志所包含的標簽,如果不定義則用默認定義的HDF_TAG標簽 #define LED_WRITE_READ 1 // 讀寫操作碼1 // Dispatch是用來處理用戶態發下來的消息 int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) { int32_t result = HDF_FAILURE; HDF_LOGE("Led driver dispatch"); if (client == NULL || client->device == NULL) { HDF_LOGE("Led driver device is NULL"); return HDF_ERR_INVALID_OBJECT; } switch (cmdCode) { case LED_WRITE_READ: const char *recv = HdfSbufReadString(data); if (recv != NULL) { HDF_LOGI("recv: %s", recv); result = CtlLED(-1); // result = CtlLED(GPIO_VAL_HIGH); if (!HdfSbufWriteInt32(reply, result)) { HDF_LOGE("replay is fail"); } return HdfDeviceSendEvent(client->device, cmdCode, data); } break; default: break; } return result; } //驅動對外提供的服務能力,將相關的服務接口綁定到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->service = (struct IDeviceIoService *)(&ledDriver); HDF_LOGD("Led driver bind success"); return HDF_SUCCESS; } // 驅動自身業務初始的接口 int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject) { if (deviceObject == NULL) { HDF_LOGE("Led driver Init failed!"); return HDF_ERR_INVALID_OBJECT; } HDF_LOGD("Led driver Init success"); return HDF_SUCCESS; } // 驅動資源釋放的接口 void HdfLedDriverRelease(struct HdfDeviceObject *deviceObject) { if (deviceObject == NULL) { HDF_LOGE("Led driver release failed!"); return; } HDF_LOGD("Led driver release success"); return; } // 定義驅動入口的對象,必須為HdfDriverEntry(在hdf_device_desc.h中定義)類型的全局變量 struct HdfDriverEntry g_ledDriverEntry = { .moduleVersion = 1, .moduleName = "led_driver", .Bind = HdfLedDriverBind, .Init = HdfLedDriverInit, .Release = HdfLedDriverRelease, }; // 調用HDF_INIT將驅動入口注冊到HDF框架中,在加載驅動時HDF框架會先調用Bind函數,再調用Init函數加載該驅動,當Init調用異常時,HDF框架會調用Release釋放驅動資源并退出。 HDF_INIT(g_ledDriverEntry);
3. 驅動編譯 在 huawei/hdf/led 目錄下新建編譯文件 Makefile。
include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk MODULE_NAME := hdf_led_driver LOCAL_SRCS += led_ctrl.c led.c LOCAL_INCLUDE := ./include LOCAL_CFLAGS += -fstack-protector-strong -Wextra -Wall -Werror include $(HDF_DRIVER)
4. 編譯結果鏈接到內核鏡像 修改 huawei/hdf/hdf_vendor.mk 文件,添加以下代碼
LITEOS_BASELIB += -lhdf_led_driver #鏈接生成的靜態庫 LIB_SUBDIRS += $(VENDOR_HDF_DRIVERS_ROOT)/led #驅動代碼Makefile的目錄
5. 驅動配置 修改 vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs配置文件,添加驅動的設備描述。
platform :: host { hostName = "platform_host"; // host名稱,host節點是用來存放某一類驅動的容器 priority = 50; // host啟動優先級(0-200),值越大優先級越低,建議默認配100,優先級相同則不保證host的加載順序 device_led :: device { // led設備節點 device0 :: deviceNode { // led驅動的DeviceNode節點 policy = 2; // policy字段是驅動服務發布的策略,在驅動服務管理章節有詳細介紹 priority = 100; // 驅動啟動優先級(0-200),值越大優先級越低,建議默認配100,優先級相同則不保證device的加載順序 preload = 0; // 驅動按需加載字段 permission = 0666; // 驅動創建設備節點權限 moduleName = "led_driver"; // 驅動名稱,該字段的值必須和驅動入口結構的moduleName值一致 serviceName = "led_service"; // 驅動對外發布服務的名稱,必須唯一 deviceMatchAttr = "led_config"; // 驅動私有數據匹配的關鍵字,必須和驅動私有數據配置表中的match_attr值相等 } }
編譯用戶程序框架子系統 1. 添加配置文件 在 build/lite/platform/hi3516dv300_liteos_a/platform.json中的subsystems字段下面添加appexecfwk和aafwk。
{ "subsystem": "aafwk", "components": [ { "component": "ability", "optional": "true", "dirs": [ "foundation/aafwk" ], "targets": [ "http://foundation/aafwk/frameworks/ability_lite:aafwk_abilitykit_lite", "http://foundation/aafwk/frameworks/ability_lite:aafwk_abilityMain_lite", "http://foundation/aafwk/frameworks/abilitymgr_lite:aafwk_abilityManager_lite", "http://foundation/aafwk/services/abilitymgr_lite:aafwk_services_lite" ], "features": [ {"enable_ohos_appexecfwk_feature_ability": "true"} ], "deps": { "components": [ "hilog_a", "bundle_mgr", "system_ability_manager", "distributed_schedule", "graphic", "utils", "ipc" ], "third_party": [ "cjson", "bounds_checking_function" ] } } ] }, { "subsystem": "appexecfwk", "components": [ { "component": "bundle_mgr", "optional": "true", "dirs": [ "foundation/appexecfwk" ], "targets": [ "http://foundation/appexecfwk/services/bundlemgr_lite:appexecfwk_services_lite", "http://foundation/appexecfwk/frameworks/bundle_lite:appexecfwk_kits_lite" ], "features": [], "deps": { "components": [ "iam", "app_verify", "hilog_a", "system_ability_manager", "global_resource_manager", "graphic", "utils" ], "third_party": [ "cjson", "zlib" ] } } ] },
2. 添加編譯文件 新建buildliteconfigsubsystemaafwkBUILD.gn文件,
import("http://build/lite/config/subsystem/lite_subsystem.gni") lite_subsystem("aafwk") { subsystem_components = [ "http://foundation/aafwk/frameworks/ability_lite:aafwk_abilitykit_lite", "http://foundation/aafwk/frameworks/abilitymgr_lite:aafwk_abilityManager_lite", "http://foundation/aafwk/services/abilitymgr_lite:aafwk_services_lite", ] }
新建/build/lite/config/subsystem/appexecfwk/BUILD.gn文件,
import("http://build/lite/config/subsystem/lite_subsystem.gni") lite_subsystem("appexecfwk") { subsystem_components = [ "http://foundation/appexecfwk/kits/appkit_lite:appexecfwk_kit_lite", "http://foundation/appexecfwk/services/bundlemgr_lite:appexecfwk_services_lite", ] }
3. 運行管理服務 用戶程序框架有兩個系統服務ability管理服務(abilityms)和(bundlems),兩系統服務運行于foundation進程中。
abilityms和bundlems注冊到sa_manager中,sa_manager運行于foundation進程中,sa_manager為abilityms和bundlems創建線程運行環境。
在foundation/distributedschedule/services/safwk_lite/BUILD.gn中添加對abilityms和bundlems
deps = [ "...", ] if (ohos_kernel_type == "liteos_a") { deps += [ "...", "http://foundation/aafwk/services/abilitymgr_lite:abilityms", "http://foundation/appexecfwk/services/bundlemgr_lite:bundlems", "...", ] }
基于AbilityKit開發的Ability 1. 主頁面實現 新建源文件applicationssamplecameramyLedAppsrcmain_ability.cpp
#include "main_ability.h" namespace OHOS { REGISTER_AA(MainAbility) void MainAbility::OnStart(const Want &want) { printf("MainAbility::OnStart "); SetMainRoute("MainAbilitySlice"); Ability::OnStart(want); }
2. 分片頁面 2.1 定義控件常量 新建源文件main_ability_slice.cpp, 屏幕大小為960x480
#include "main_ability_slice.h" #include "ability_manager.h" #include "components/ui_label.h" #include "components/ui_label_button.h" namespace OHOS { REGISTER_AS(MainAbilitySlice) constexpr static int BUTTON1_POSITION_X = 380; constexpr static int BUTTON1_POSITION_Y = 200; constexpr static int BUTTON_WIDTH = 200; constexpr static int BUTTON_HEIGHT = 80; constexpr static int ROOT_VIEW_POSITION_X = 0; constexpr static int ROOT_VIEW_POSITION_Y = 0; constexpr static int ROOT_VIEW_WIDTH = 960; constexpr static int ROOT_VIEW_HEIGHT = 480; constexpr static uint8_t ROOT_VIEW_OPACITY = 255; constexpr static uint8_t FONT_ID = 10; constexpr static int BUTTON1_POSITION_X = 380; constexpr static int BUTTON1_POSITION_Y = 200; constexpr static int BUTTON_WIDTH = 200; constexpr static int BUTTON_HEIGHT = 80; constexpr static int ROOT_VIEW_POSITION_X = 0; constexpr static int ROOT_VIEW_POSITION_Y = 0; constexpr static int ROOT_VIEW_WIDTH = 960; constexpr static int ROOT_VIEW_HEIGHT = 480; constexpr static uint8_t ROOT_VIEW_OPACITY = 255; constexpr static uint8_t FONT_ID = 10; } // namespace OHOS
2.2 創建按鈕和布局 在生命周期函數OnStart中,全屏放置一個rootView_,居中位置放置一個按鈕button1。
void MainAbilitySlice::OnStart(const Want &want) { printf("MainAbilitySlice::OnStart "); AbilitySlice::OnStart(want); auto button1 = new UILabelButton(); button1->SetPosition(BUTTON1_POSITION_X, BUTTON1_POSITION_Y); button1->SetText("翻轉 LED"); button1->Resize(BUTTON_WIDTH, BUTTON_HEIGHT); button1->SetFontId(FONT_ID); button1->SetStyle(STYLE_TEXT_COLOR, Color::Black().full); button1->SetStyle(STYLE_TEXT_OPA, ROOT_VIEW_OPACITY); button1->SetStyle(STYLE_BACKGROUND_OPA, ROOT_VIEW_OPACITY); rootView_ = RootView::GetWindowRootView(); rootView_->SetPosition(ROOT_VIEW_POSITION_X, ROOT_VIEW_POSITION_Y); rootView_->Resize(ROOT_VIEW_WIDTH, ROOT_VIEW_HEIGHT); rootView_->Add(button1); SetUIContent(rootView_); }
2.3 實現驅動消息機制 這里順便提一下,文檔中DevSvcManagerClntGetService接口僅在內核態有效,可以方便的獲取服務并直接調用。鴻蒙作為微內核的OS,想從用戶態調用內核態函數,要么用框架的消息機制,要么自己用中斷服務實現。
#define LED_WRITE_READ 1 #define LED_SERVICE "led_service" static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) { const char *string = HdfSbufReadString(data); if (string == NULL) { printf("fail to read string in event data "); return HDF_FAILURE; } printf("%s: dev event received: %u %s ", (char *)priv, id, string); return HDF_SUCCESS; } static int SendEvent(struct HdfIoService *serv, const char *eventData) { int ret = 0; struct HdfSBuf *data = HdfSBufObtainDefaultSize(); if (data == NULL) { printf("fail to obtain sbuf data "); return 1; } struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); if (reply == NULL) { printf("fail to obtain sbuf reply "); ret = HDF_DEV_ERR_NO_MEMORY; HdfSBufRecycle(data); return ret; } if (!HdfSbufWriteString(data, eventData)) { printf("fail to write sbuf "); ret = HDF_FAILURE; HdfSBufRecycle(data); HdfSBufRecycle(reply); return ret; } ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data, reply); if (ret != HDF_SUCCESS) { printf("fail to send service call "); HdfSBufRecycle(data); HdfSBufRecycle(reply); return ret; } int replyData = 0; if (!HdfSbufReadInt32(reply, &replyData)) { printf("fail to get service call reply "); ret = HDF_ERR_INVALID_OBJECT; HdfSBufRecycle(data); HdfSBufRecycle(reply); return ret; } printf("Get reply is: %d ", replyData); HdfSBufRecycle(data); HdfSBufRecycle(reply); return ret; }
2.4 加入點擊事件 每次點擊按鈕,向內核態發送一次消息。
auto onClick = [this](UIView &view, const Event &event) -> bool { printf("led button pressed "); struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE, 0); if (serv == NULL) { printf("fail to get service %s ", LED_SERVICE); return false; } static struct HdfDevEventlistener listener = { .callBack = OnDevEventReceived, .priv = (void *)"Service0"}; if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) { printf("fail to register event listener "); return false; } const char *send_cmd = "toggle LED"; if (SendEvent(serv, send_cmd)) { printf("fail to send event "); return false; } if (HdfDeviceUnregisterEventListener(serv, &listener)) { printf("fail to unregister listener "); return false; } HdfIoServiceRecycle(serv); return true; };
2.5 注銷頁面 在生命周期函數OnStop中,刪除所有節點,回收系統資源。
void DeleteViewChildren(UIView *view) { if (view == nullptr) { return; } while (view != nullptr) { UIView *tempView = view; view = view->GetNextSibling(); if (tempView->IsViewGroup()) { DeleteViewChildren(dynamic_cast(tempView)->GetChildrenHead()); } if (tempView->GetParent()) { dynamic_cast (tempView->GetParent())->Remove(tempView); } delete tempView; } } void MainAbilitySlice::OnStop() { printf("MainAbilitySlice::OnStop "); AbilitySlice::OnStop(); DeleteViewChildren(rootView_); }
3. 編譯配置 新增 applicationssamplecameramyLedAppBUILD.gn文件
import("http://build/lite/config/component/lite_component.gni") import("http://build/lite/config/subsystem/aafwk/config.gni") HDF_FRAMEWORKS = "http://drivers/hdf/frameworks" src_path = "http://applications/sample/camera/myLedApp/src" lite_library("ledability") { target_type = "shared_library" ldflags = [ "-shared", ] sources = [ "${src_path}/main_ability.cpp", "${src_path}/main_ability_slice.cpp", ] include_dirs = [ ".", "http://foundation/aafwk/frameworks/ability_lite/example/entry/src/main/cpp", "http://foundation/aafwk/interfaces/innerkits/abilitymgr_lite", "http://foundation/aafwk/interfaces/kits/ability_lite", "http://foundation/aafwk/interfaces/kits/want_lite", "http://foundation/appexecfwk/interfaces/kits/bundle_lite", "http://foundation/appexecfwk/utils/bundle_lite", "http://foundation/communication/interfaces/kits/ipc_lite", "http://foundation/graphic/lite/interfaces/kits/config", "http://foundation/graphic/lite/interfaces/kits/ui", "http://foundation/graphic/lite/interfaces/kits/utils", "http://kernel/liteos_a/kernel/common", "http://kernel/liteos_a/kernel/include", "http://drivers/hdf/lite/include/host", "$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", "http://kernel/liteos_a/platform/include", "$HDF_FRAMEWORKS/adapter/syscall/include", "$HDF_FRAMEWORKS/adapter/vnode/include", ] deps = [ "http://foundation/aafwk/frameworks/ability_lite:aafwk_abilitykit_lite", "http://drivers/hdf/lite/manager:hdf_core", "http://drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal", ] defines = [ "OHOS_APPEXECFWK_BMS_BUNDLEMANAGER", ] if (enable_ohos_appexecfwk_feature_ability == true) { deps += [ "http://foundation/graphic/lite/frameworks/ui:ui", ] defines += [ "ENABLE_WINDOW=1", "ABILITY_WINDOW_SUPPORT" ] } output_dir = "$root_out_dir/dev_tools/led" }
4. 應用配置文件 新建 applicationssamplecameramyLedAppconfig.json
{ "app": { "bundleName": "com.bluishfish.ledability", "vendor": "huawei", "version": { "code": 1, "name": "1.0" }, "apiVersion": { "compatible": 3, "target": 3 } }, "deviceConfig": { "default": { "keepAlive": false } }, "module": { "deviceType": [ "smartVision" ], "distro": { "deliveryWithInstall": true, "moduleName": "ledability", "moduleType": "entry" }, "abilities": [{ "name": "MainAbility", "icon": "assets/entry/resources/base/media/icon.png", "label": "Led Ability", "launchType": "standard", "type": "page", "visible": true } ] } }
5. 板級編譯配置 復制 build/lite/product/ipcamera_hi3516dv300.json,改名為my_hi3516dv300在子系統里加入
{ "ohos_version": "OpenHarmony 1.0", "board": "hi3516dv300", "kernel": "liteos_a", "compiler": "clang", "subsystem": [ { "name": "aafwk", "component": [ "......", { "name": "ability_led", "dir": "http://applications/sample/camera/myLedApp:ledability", "features": []} ] "......"
6. 編譯應用
python build.py my_hi3516dv300 -b debug
將系統燒錄到開發板上。
7. 打包應用 在 assetsentry esourcesasemedia目錄下放置一個icon.png作為啟動圖標。
將applicationssamplecameramyLedAppconfig.json和 Z:openharmonyoutmy_hi3516dv300dev_toolsledlibledability.so打包壓縮成zip包
改名為ledability.hap ,復制到NFS共享目錄
8. 安裝Hap
mkdir nfs mount 192.168.1.57:/nfs /nfs nfs ./nfs/dev_tools/bin/bm set -s disable ./nfs/dev_tools/bin/bm install -p ./nfs/ledability.hap
9. 運行程序
./nfs/dev_tools/bin/aa start -p com.bluishfish.ledability -n MainAbility
完美!
-
AI
+關注
關注
88文章
34588瀏覽量
276170 -
鴻蒙系統
+關注
關注
183文章
2641瀏覽量
67836
發布評論請先 登錄
觸摸屏的工作原理
觸摸屏的應用與工作原理

電阻式觸摸屏,什么是電阻式觸摸屏
基于單片機的液晶顯示觸摸屏控制設計
基于觸摸屏控制的LED彩色臺燈控制系統的設計

組態王和觸摸屏哪個好_組態王和觸摸屏區別
基于觸摸屏的LED驅動電路設計

PLC觸摸屏的作用_觸摸屏是怎樣控制PLC的
觸摸屏電容屏原理_觸摸屏有哪些應用領域
觸摸屏電容屏的原理,觸摸屏有哪些應用領域
modbus觸摸屏

評論