diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/README.md" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..783f7c52e3215f3a81fac7dcc646d146b73b604c --- /dev/null +++ "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/README.md" @@ -0,0 +1,487 @@ +# 任务介绍 + +在Hi3861案例1:日志、线程、定时器中,我们安排了四个小任务,分别介绍了如何搭建Hi3861实验环境、如何打印日志、如何启动线程、如何启动定时器,本文将进一步深入学习Hi3861及其外部设备,通过本文的学习,你需要完成如下三个任务: + +任务一 控制小灯泡的闪烁 + + + +任务二 控制红绿灯的闪烁 + + + +任务三 通过按键控制红绿灯 + + + +# Hi3861开发环境准备 + +完成本篇Codelab,我们首先需要完成开发环境搭建、源码编译,可参照如下步骤进行。 + +1. [搭建开发环境](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-setting.md)。 +2. [源码获取](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md):建议开发者选择LTS 3.0版本源码进行编译,本篇Codelab是基于此版本开发的。 + +**您需要使用如下设备完成本Codelab:** + +Hi3861V100开发板主板、底板以及智能红绿灯板。 + +# 相关概念 + +GPIO(英语:General-purpose input/output),通用型之输入输出的简称。H3861芯片内部包括了GPIO模块,用于实现芯片引脚上的数字输入、输出功能。输入、输出的数字表示对应的状态,状态只能是0或1两种,通常使用低电平表示0,高电平表示1。 + +# 任务一 控制小灯泡的闪烁 + +下文将通过修改源码的方式展示如何将Hi3861V100开发板主板上的小灯泡点亮,并控制其闪烁。 + +**1. 确定目录结构**。 + +开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + +例如:在app目录下新增业务light,其中main.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + +``` +. +└── applications + └── sample + └── wifi-iot + └── app + │── light + │ │── main.c + │ └── BUILD.gn + └── BUILD.gn +``` + +**2. 编写业务代码**。 + +在./applications/sample/wifi-iot/app/light目录下新建main.c文件,在main.c中新建业务入口函数Main,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务。(SYS\_RUN定义在ohos\_init.h文件中) + +``` +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "iot_gpio.h" + +#define LED_INTERVAL_TIME_US(1 * 1000000) +#define LED_TASK_STACK_SIZE 512 +#define LED_TEST_GPIO_9 9 + +// 小灯泡每隔1秒闪烁一次 +static void SparkTask(void) +{ + IoTGpioInit(LED_TEST_GPIO_9); + hi_io_set_func(LED_TEST_GPIO_9, 0); + IoTGpioSetDir(LED_TEST_GPIO_9, IOT_GPIO_DIR_OUT); + + while (1) { + IoTGpioSetOutputVal(LED_TEST_GPIO_9, 1); + usleep(LED_INTERVAL_TIME_US); + IoTGpioSetOutputVal(LED_TEST_GPIO_9, 0); + usleep(LED_INTERVAL_TIME_US); + } +} + +static void Main(void) +{ + // 指定线程的属性 + osThreadAttr_t attr; + attr.name = "SparkTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = LED_TASK_STACK_SIZE; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)SparkTask, NULL, &attr) == NULL) { + printf("Failed to create SparkTask!\r\n"); + } + +} + +SYS_RUN(Main); +``` + +> **说明:** +>Hi3861V100开发板主板上的小灯泡是与GPIO 9号引脚进行的连接,故控制GPIO 9的高低电平就可以控制小灯泡的闪烁。 + +**3.编写用于将业务构建成静态库的BUILD.gn文件**。 + +在./applications/sample/wifi-iot/app/light目录下新建BUILD.gn文件,并完成如下配置。 + +``` +static_library("main") { + sources = [ + "main.c" + ] + include_dirs = [ + "//utils/native/lite/include" + ] +} +``` + +**4. 编写模块BUILD.gn文件,指定需参与构建的特性模块**。 + +配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + +``` +import("//build/lite/config/component/lite_component.gni") + +lite_component("app") { + features = [ + "light:main", + ] +} +``` + +**5. 代码编译**。 + +依次点击图标1和2中的两个按钮,等待代码编译,提示\[SUCCESS\]则表示项目编译成功。 + + + +**6. [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md)**。 + +**7. 运行结果**。 + +示例代码编译、烧录、运行、调测后,重启开发板后,Hi3861V100开发板主板上的小灯泡点亮将被点亮,并呈现每个1秒闪烁一次的效果,效果如下所示: + + + +# 任务二 控制红绿灯的闪烁 + +下文将通过修改源码的方式展示如何将红绿灯开发板点亮,并控制每隔1秒钟切换一个信号灯。 + +**1. 确定目录结构**。 + +开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + +例如:在app目录下新增业务traffic\_light,其中main.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + +``` +. +└── applications + └── sample + └── wifi-iot + └── app + │── traffic_light + │ │── main.c + │ └── BUILD.gn + └── BUILD.gn +``` + +**2. 编写业务代码**。 + +在./applications/sample/wifi-iot/app/traffic\_light目录下新建main.c文件,在main.c中新建业务入口函数Main,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务。(SYS\_RUN定义在ohos\_init.h文件中)。 + +``` +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "iot_gpio.h" + +#define TASK_STACK_SIZE (1024 * 4) +#define TASK_SLEEP_TIME (1 * 1000 * 1000) + +#define LED_TEST_GPIO_10 10 +#define LED_TEST_GPIO_11 11 +#define LED_TEST_GPIO_12 12 + +// 初始化红绿灯 +static void InitTrafficLight(void) +{ + IoTGpioInit(LED_TEST_GPIO_10); + hi_io_set_func(LED_TEST_GPIO_10, 0); + IoTGpioSetDir(LED_TEST_GPIO_10, IOT_GPIO_DIR_OUT); + + IoTGpioInit(LED_TEST_GPIO_11); + hi_io_set_func(LED_TEST_GPIO_11, 0); + IoTGpioSetDir(LED_TEST_GPIO_11, IOT_GPIO_DIR_OUT); + + IoTGpioInit(LED_TEST_GPIO_12); + hi_io_set_func(LED_TEST_GPIO_12, 0); + IoTGpioSetDir(LED_TEST_GPIO_12, IOT_GPIO_DIR_OUT); +} + +static void TrafficLightTask(void) +{ + InitTrafficLight(); + + while (1) { + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 1); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 0); + usleep(TASK_SLEEP_TIME); + + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 1); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 0); + usleep(TASK_SLEEP_TIME); + + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 1); + usleep(TASK_SLEEP_TIME); + } +} + +static void Main(void) +{ + // 指定线程的属性 + osThreadAttr_t attr; + attr.name = "TrafficLightTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = TASK_STACK_SIZE; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)TrafficLightTask, NULL, &attr) == NULL) { + printf("Failed to create TrafficLightTask!\r\n"); + } +} + +SYS_RUN(Main); +``` + +> **说明:** +>红绿灯开发板上红、黄、绿三个小灯泡分别与GPIO引脚10、11、12相连接。 + +**3.编写用于将业务构建成静态库的BUILD.gn文件**。 + +在./applications/sample/wifi-iot/app/light目录下新建BUILD.gn文件,并完成如下配置。 + +``` +static_library("main") { + sources = [ + "main.c" + ] + include_dirs = [ + "//utils/native/lite/include" + ] +} +``` + +**4. 编写模块BUILD.gn文件,指定需参与构建的特性模块**。 + +配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + +``` +import("//build/lite/config/component/lite_component.gni") + +lite_component("app") { + features = [ + "traffic_light:main", + ] +} +``` + +**5. 代码编译**。 + +依次点击图标1和2中的两个按钮,等待代码编译,提示\[SUCCESS\]则表示项目编译成功。 + + + +**6. [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md)**。 + +**7. 运行结果**。 + +示例代码编译、烧录、运行、调测后,重启开发板后,Hi3861V100开发板主板上的红绿灯开发板将会被点亮,并每隔1秒钟切换一个信号灯,效果如下所示: + + + +# 任务三 通过按键控制红绿灯 + +下文将通过修改源码的方式展示如何通过按钮的点击事件,实现控制红绿灯开发板上小灯的交替变化。 + +**1. 确定目录结构**。 + +开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + +例如:在app目录下新增业务light,其中main.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + +``` +. +└── applications + └── sample + └── wifi-iot + └── app + │── traffic_light_button + │ │── main.c + │ └── BUILD.gn + └── BUILD.gn +``` + +**2. 编写业务代码**。 + +在./applications/sample/wifi-iot/app/traffic\_light\_button目录下新建main.c文件,在main.c中新建业务入口函数Main,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务,(SYS\_RUN定义在ohos\_init.h文件中)。 + +``` +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "iot_gpio.h" + +#define TASK_STACK_SIZE (1024 * 4) + +#define LED_TEST_GPIO_8 8 +#define LED_TEST_GPIO_10 10 +#define LED_TEST_GPIO_11 11 +#define LED_TEST_GPIO_12 12 +#define NUM_3 3 +#define RED_LED_BRIGHT 1 +#define GREEN_LED_BRIGHT 2 +#define YELLOW_LED_BRIGHT 3 + +static int g_currentBright = 0; + +// 初始化红绿灯 +static void InitTrafficLight(void) +{ + IoTGpioInit(LED_TEST_GPIO_10); + hi_io_set_func(LED_TEST_GPIO_10, 0); + IoTGpioSetDir(LED_TEST_GPIO_10, IOT_GPIO_DIR_OUT); + + IoTGpioInit(LED_TEST_GPIO_11); + hi_io_set_func(LED_TEST_GPIO_11, 0); + IoTGpioSetDir(LED_TEST_GPIO_11, IOT_GPIO_DIR_OUT); + + IoTGpioInit(LED_TEST_GPIO_12); + hi_io_set_func(LED_TEST_GPIO_12, 0); + IoTGpioSetDir(LED_TEST_GPIO_12, IOT_GPIO_DIR_OUT); +} + +// 按键每按下一次,currentBright加1 +static void OnButtonPressed(char * arg) +{ + (void)arg; + g_currentBright++; +} + +// 初始化按钮 +static void InitButton(void) +{ + IoTGpioInit(LED_TEST_GPIO_8); + hi_io_set_func(LED_TEST_GPIO_8, 0); + IoTGpioSetDir(LED_TEST_GPIO_8, IOT_GPIO_DIR_IN); + hi_io_set_pull(LED_TEST_GPIO_8, 1); + IoTGpioRegisterIsrFunc(LED_TEST_GPIO_8, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_FALL_LEVEL_LOW, + OnButtonPressed, NULL); +} + +static void TrafficLightTask(void) +{ + InitTrafficLight(); + + InitButton(); + + while (1) { + switch (g_currentBright % NUM_3) { + case RED_LED_BRIGHT: + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 1); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 0); + break; + case GREEN_LED_BRIGHT: + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 1); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 0); + break; + case YELLOW_LED_BRIGHT: + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 1); + break; + default: + IoTGpioSetOutputVal(LED_TEST_GPIO_10, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_11, 0); + IoTGpioSetOutputVal(LED_TEST_GPIO_12, 1); + break; + } + } +} + +static void Main(void) +{ + // 指定线程的属性 + osThreadAttr_t attr; + attr.name = "TrafficLightTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = TASK_STACK_SIZE; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)TrafficLightTask, NULL, &attr) == NULL) { + printf("Failed to create TrafficLightTask!\r\n"); + } + +} + +SYS_RUN(Main); +``` + +> **说明:** +>红绿灯开发板上红、黄、绿三个小灯泡分别接到了GPIO引脚10、11、12,按键接到的GPIO引脚为8。 + +**3.编写用于将业务构建成静态库的BUILD.gn文件**。 + +在./applications/sample/wifi-iot/app/traffic\_light\_button目录下新建BUILD.gn文件,并完成如下配置。 + +``` +static_library("main") { + sources = [ + "main.c" + ] + include_dirs = [ + "//utils/native/lite/include" + ] +} +``` + +**4. 编写模块BUILD.gn文件,指定需参与构建的特性模块。** + +配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + +``` +import("//build/lite/config/component/lite_component.gni") + +lite_component("app") { + features = [ + "traffic_light_button:main", + ] +} +``` + +**5. 代码编译**。 + +依次点击图标1和2中的两个按钮,等待代码编译,提示\[SUCCESS\]则表示项目编译成功。 + + + +**6. [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md)**。 + +**7. 运行结果**。 + +示例代码编译、烧录、运行、调测后,重启开发板后,通过按钮的点击事件,即可实现控制红绿灯开发板上小灯的交替变化,效果如下所示: + + + +# 恭喜你 + +目前您已经成功完成了本Codelab,并且学到了: + +- GPIO高低电平的设置 +- 通过GPIO高低电平,控制小灯泡、控制红绿灯的闪烁 +- 按钮事件的检测 + diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001189490828.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001189490828.gif" new file mode 100644 index 0000000000000000000000000000000000000000..f8b9567f28141b4d95e0fb9a1473f30ff992f7e5 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001189490828.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001190490784.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001190490784.gif" new file mode 100644 index 0000000000000000000000000000000000000000..f8b9567f28141b4d95e0fb9a1473f30ff992f7e5 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001190490784.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001190491172.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001190491172.gif" new file mode 100644 index 0000000000000000000000000000000000000000..d6ebadb7e56a9cbda99b87a8dd91e0e382dc6e74 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001190491172.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001196059582.png" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001196059582.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68d1312c3f1d7ee3b7a33d592fcf208d3ac219f Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001196059582.png" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001196379400.png" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001196379400.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68d1312c3f1d7ee3b7a33d592fcf208d3ac219f Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001196379400.png" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001233826721.png" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001233826721.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68d1312c3f1d7ee3b7a33d592fcf208d3ac219f Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001233826721.png" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235170729.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235170729.gif" new file mode 100644 index 0000000000000000000000000000000000000000..28baf8fe217814020e3f704b4a303f062119f85b Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235170729.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235651103.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235651103.gif" new file mode 100644 index 0000000000000000000000000000000000000000..d6ebadb7e56a9cbda99b87a8dd91e0e382dc6e74 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235651103.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235890713.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235890713.gif" new file mode 100644 index 0000000000000000000000000000000000000000..28baf8fe217814020e3f704b4a303f062119f85b Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/figures/zh-cn_image_0000001235890713.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-caution.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-caution.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-caution.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-danger.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-danger.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-danger.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-note.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-note.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6314297e45c1de184204098efd4814d6dc8b1cda Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-note.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-notice.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-notice.gif" new file mode 100644 index 0000000000000000000000000000000000000000..86024f61b691400bea99e5b1f506d9d9aef36e27 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-notice.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-tip.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-tip.gif" new file mode 100644 index 0000000000000000000000000000000000000000..93aa72053b510e456b149f36a0972703ea9999b7 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-tip.gif" differ diff --git "a/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-warning.gif" "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-warning.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\345\210\251\347\224\250GPIO\347\202\271\344\272\256\345\260\217\347\201\257\346\263\241\343\200\201\347\272\242\347\273\277\347\201\257/public_sys-resources/icon-warning.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/README.md" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..b74739b854dfbb023087551e7f485f542259d4e9 --- /dev/null +++ "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/README.md" @@ -0,0 +1,375 @@ +# 任务介绍 + +PWM是脉冲宽度调制(Pulse Width Modulation)的缩写,是一种对模拟信号电平进行数字编码并将其转换为脉冲的技术。常用于马达控制、背光亮度调节等。 + +本篇Codelab将通过以下两个简单的样例,让开发者熟悉OpenHarmony PWM相关API的使用: + +使用PWM调节呼吸灯亮暗,效果如下: + + + + +# Hi3861开发环境准备 + +完成本篇Codelab,我们首先需要完成开发环境搭建、源码编译,可参照如下步骤进行。 + +1. [搭建开发环境](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-setting.md)。 +2. [源码获取](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md):建议开发者选择LTS 3.0版本源码进行编译,本篇Codelab是基于此版本开发的。 + +**您需要使用如下设备完成本Codelab:** + +Hi3861V100开发板主板、底板以及智能红绿灯板。 + +# 相关概念 + +**PWM API** + + +方法 + +说明 + + + +IoTPwmInit (unsigned int port) + +初始化PWM设备。 + + +IoTPwmDeinit (unsigned int port) + +去初始化PWM设备。 + + +IoTPwmStart (unsigned int port, unsigned short duty, unsigned int freq) + +根据给定的输出频率和占空比,从指定端口启动PWM信号输出。 + + +IoTPwmStop (unsigned int port) + +停止指定端口的PWM信号输出。 + + + + + +**Hi3861V100开发板说明** + +实现IOT外设控制,首先需要通过查阅原理图明确接线关系。经过查阅,智能红绿灯板与主控芯片(Pegasus)引脚的对应关系如下: + + +模块 + +控制管脚 + +PWM + + + +蜂鸣器 + +GPIO9 + +PWM0 + + +红灯 + +GPIO10 + +PWM1 + + + + + +> **说明:** +>开发板原理图,请开发者联系Hi3861购买渠道客服获取。 + +# 任务一 使用PWM调节呼吸灯灯亮暗 + +本节介绍如何使用PWM API,实现对GPIO的控制,达到呼吸灯亮度逐渐变化的效果(逐渐变暗和逐渐变亮)。 + +1. **确定目录结构**。 + + 开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + + 在app下新增业务pwmled,其中pwm\_led.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + + ``` + . + └── applications + └── sample + └── wifi-iot + └── app + │── pwmled + │ │── pwm_led.c + │ └── BUILD.gn + └── BUILD.gn + ``` + +2. **使PWM功能生效**。 + + 打开./device/hisilicon/hispark\_pegasus/sdk\_liteos/build/config/usr\_config.mk文件,找到CONFIG\_PWM\_SUPPORT is not set,取消注释,并将其修改为CONFIG\_PWM\_SUPPORT=y。 + +  + +3. **编写业务代码。** + + 新建./applications/sample/wifi-iot/app/pwmled下的pwm\_led.c文件,在pwm\_led.c中新建业务入口函数PWMLedExample,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口APP\_FEATURE\_INIT\(\)启动业务。 + + ``` + #include + #include + #include + + #include "ohos_init.h" + #include "cmsis_os2.h" + #include "iot_gpio.h" + #include "iot_pwm.h" + #include "hi_io.h" + #include "hi_pwm.h" + + #define TASK_STACK_SIZE 512 + // PWM输出占空比 + #define PVM_OUT_DUTY 50 + // PWM输出频率 + #define PVM_OUT_FREQ 10000 + #define TASK_SLEEP_TIME (0.03 * 1000 * 1000) + + static void PWMLedTask(void *arg) + { + (void)arg; + + while (1) { + // 逐步增加PVM_OUT_DUTY 值使LED灯逐渐变亮 + for (int i = 0; i < PVM_OUT_DUTY; i++) { + // 启动PWM信号输出 + IoTPwmStart(HI_PWM_PORT_PWM1, i, PVM_OUT_FREQ); + usleep(TASK_SLEEP_TIME); + // 停止PWM信号输出 + IoTPwmStop(HI_PWM_PORT_PWM1); + } + // 逐步减小PVM_OUT_DUTY 值使LED灯逐渐变暗 + for (int i = PVM_OUT_DUTY; i > 0; i--) { + // 启动PWM信号输出 + IoTPwmStart(HI_PWM_PORT_PWM1, i, PVM_OUT_FREQ); + usleep(TASK_SLEEP_TIME); + // 停止PWM信号输出 + IoTPwmStop(HI_PWM_PORT_PWM1); + } + } + } + + static void PWMLedExample(void) + { + osThreadAttr_t attr; + + // 初始化10号管脚(红色led灯) + IoTGpioInit(HI_IO_NAME_GPIO_10); + + // 将10号管脚设置为PWM功能 + hi_io_set_func(HI_IO_NAME_GPIO_10, HI_IO_FUNC_GPIO_10_PWM1_OUT); + + // 初始化PWM设备 + IoTPwmInit(HI_PWM_PORT_PWM1); + + attr.name = "PWMLedTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = TASK_STACK_SIZE; + attr.priority = osPriorityNormal; + + if (osThreadNew(PWMLedTask, NULL, &attr) == NULL) { + printf("[PWMLedExample] Failed to create PWMLedTask!\n"); + } + } + + APP_FEATURE_INIT(PWMLedExample); + ``` + +4. **编写用于将业务构建成静态库的BUILD.gn文件。** + + 新建./applications/sample/wifi-iot/app/pwmled下的BUILD.gn文件,并完成如下配置。 + + ``` + static_library("pwm_led") { + sources = [ + "pwm_led.c" + ] + + include_dirs = [ + "//utils/native/lite/include", + "//kernel/liteos_m/kal/cmsis", + "//base/iot_hardware/peripheral/interfaces/kits", + ] + } + ``` + + - static\_library中指定业务模块的编译结果,开发者根据实际情况完成填写。 + - sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含"//"则表示绝对路径(此处为代码根路径),若不包含"//"则表示相对路径。 + - include\_dirs中指定source所需要依赖的.h文件路径。 + +5. **编写模块BUILD.gn文件,指定需参与构建的特性模块。** + + 配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + + ``` + import("//build/lite/config/component/lite_component.gni") + + lite_component("app") { + features = [ + "pwmled:pwm_led", + ] + } + ``` + +6. **代码编译和烧录**。 + + 代码编译和烧录可以参考: + + - [源码编译](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-building.md) + - [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md) + + 完成烧录后,按下RST键复位模组,可发现呼吸灯亮度在周期性的逐渐变亮和变暗。 + + +# 任务二 使用PWM控制蜂鸣器 + +本节介绍如何使用PWM API,实现对GPIO的控制,使蜂鸣器持续鸣叫。 + +1. **确定目录结构**。 + + 开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + + 在app下新增业务pwmbeer,其中pwm\_beer.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + + ``` + . + └── applications + └── sample + └── wifi-iot + └── app + │── pwmbeer + │ │── pwm_beer.c + │ └── BUILD.gn + └── BUILD.gn + ``` + +2. **编写业务代码**。 + + 新建./applications/sample/wifi-iot/app/pwmbeer下的pwm\_beer.c文件,在pwm\_beer.c中新建业务入口函数PWMBeerExample,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口APP\_FEATURE\_INIT\(\)启动业务。 + + ``` + #include + #include "ohos_init.h" + #include "cmsis_os2.h" + #include "hi_gpio.h" + #include "hi_io.h" + #include "hi_pwm.h" + + #define TASK_STACK_SIZE 512 + // PWM输出占空比 + #define PVM_OUT_DUTY 50 + // PWM输出频率 + #define PVM_OUT_FREQ 10000 + #define TASK_SLEEP_TIME (0.03 * 1000 * 1000) + + static void PWMBeerTask(void * arg) + { + (void)arg; + + while (1) { + // 启动PWM信号输出 + IoTPwmStart(HI_PWM_PORT_PWM0, PVM_OUT_DUTY, PVM_OUT_FREQ); + usleep(TASK_SLEEP_TIME); + // 停止PWM信号输出 + IoTPwmStop(HI_PWM_PORT_PWM0); + } + } + + static void PWMBeerExample(void) + { + osThreadAttr_t attr; + + // 初始化9号管脚(蜂鸣器) + IoTGpioInit(HI_IO_NAME_GPIO_9); + + // 将9号管脚设置为PWM功能,用于控制蜂鸣器管脚 + hi_io_set_func(HI_IO_NAME_GPIO_9, HI_IO_FUNC_GPIO_9_PWM0_OUT); + + // 初始化PWM设备 + IoTPwmInit(HI_PWM_PORT_PWM0); + + attr.name = "PWMBeerTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = TASK_STACK_SIZE; + attr.priority = osPriorityNormal; + + if (osThreadNew(PWMBeerTask, NULL, &attr) == NULL) { + printf("[PWMBeerTask] Failed to create PWMBeerTask!\n"); + } + } + + APP_FEATURE_INIT(PWMBeerExample); + ``` + +3. **编写用于将业务构建成静态库的BUILD.gn文件**。 + + 新建./applications/sample/wifi-iot/app/pwmbeer下的BUILD.gn文件,并完成如下配置。 + + ``` + static_library("pwm_beer") { + sources = [ + "pwm_beer.c" + ] + + include_dirs = [ + "//utils/native/lite/include", + "//kernel/liteos_m/kal/cmsis", + "//base/iot_hardware/peripheral/interfaces/kits", + ] + } + ``` + + - static\_library中指定业务模块的编译结果,开发者根据实际情况完成填写。 + - sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含"//"则表示绝对路径(此处为代码根路径),若不包含"//"则表示相对路径。 + - include\_dirs中指定source所需要依赖的.h文件路径。 + +4. **编写模块BUILD.gn文件,指定需参与构建的特性模块。** + + 配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + + ``` + import("//build/lite/config/component/lite_component.gni") + + lite_component("app") { + features = [ + "pwmbeer:pwm_beer", + ] + } + ``` + +5. **代码编译和烧录**。 + + 代码编译和烧录可以参考: + + - [源码编译](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-building.md) + - [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md) + + 完成烧录后,按下RST键复位模组,可发现蜂鸣器持续鸣叫。 + + +# 恭喜你 + +目前您已经成功完成了本Codelab,并且学到了: + +- 如何使用PWM控制LED灯。 +- 如何使用PWM控制蜂鸣器。 + diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/figures/zh-cn_attachment_0000001192336252.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/figures/zh-cn_attachment_0000001192336252.gif" new file mode 100644 index 0000000000000000000000000000000000000000..80bac4259a9e8929c8c63ed7740430d271de5d51 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/figures/zh-cn_attachment_0000001192336252.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/figures/zh-cn_image_0000001233802341.png" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/figures/zh-cn_image_0000001233802341.png" new file mode 100644 index 0000000000000000000000000000000000000000..a30751f2ca6f4501369544f1969a6a112b9d4465 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/figures/zh-cn_image_0000001233802341.png" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-caution.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-caution.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-caution.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-danger.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-danger.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-danger.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-note.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-note.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6314297e45c1de184204098efd4814d6dc8b1cda Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-note.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-notice.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-notice.gif" new file mode 100644 index 0000000000000000000000000000000000000000..86024f61b691400bea99e5b1f506d9d9aef36e27 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-notice.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-tip.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-tip.gif" new file mode 100644 index 0000000000000000000000000000000000000000..93aa72053b510e456b149f36a0972703ea9999b7 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-tip.gif" differ diff --git "a/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-warning.gif" "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-warning.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\345\210\251\347\224\250PWM\346\226\271\346\263\242\346\216\247\345\210\266\345\221\274\345\220\270\347\201\257\345\222\214\350\234\202\351\270\243\345\231\250/public_sys-resources/icon-warning.gif" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/README.md" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..f9ca7692ef6198d9210ffcc82554446d9158f621 --- /dev/null +++ "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/README.md" @@ -0,0 +1,413 @@ +# 任务介绍 + +OpenHarmony轻量和小型系统适用于内存较小的IOT设备。通过本文,开发者可以快速熟悉OpenHarmony轻量和小型系统的环境搭建、编译、烧录、调测以及打印“Hello World”日志、启动一个线程、启动一个定时器等。本文选取的开发板为Hi3861 WLAN模组,Hi3861开发板是一片大约2cm\*5cm大小的开发板,是一款高度集成的2.4GHz WLAN SoC芯片,集成IEEE 802.11b/g/n基带和RF(Radio Frequency)电路。支持OpenHarmony,并配套提供开放、易用的开发和调试运行环境。通过本文的学习,您需要完成如下四个任务: + +- 任务一 Hi3861环境搭建 +- 任务二 如何打印日志 +- 任务三 如何启动线程 +- 任务四 如何启动定时器 + +# Hi3861开发环境准备 + +完成本篇Codelab,我们首先需要完成开发环境搭建、源码编译,可参照如下步骤进行。 + +1. [搭建开发环境](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-setting.md)。 +2. [源码获取](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md):建议开发者选择LTS 3.0版本源码进行编译,本篇Codelab是基于此版本开发的。 + +**您需要使用如下设备完成本Codelab:** + +Hi3861V100开发板主板 + +> **说明:** +>请严格按照上文步骤1-3中的指导文档进行环境搭建,如遇问题可以前往[开发者论坛](https://developer.huawei.com/consumer/cn/forum/block/device)进行求助(搜索关键字Hi3861)。 + +# 任务一 如何打印日志 + +下文将通过修改源码的方式展示如何编写简单程序,输出“Hello world”。 + +**1. 确定目录结构**。 + +开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + +例如:在app目录下新增业务helloworld,其中main.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + +``` +. +└── applications + └── sample + └── wifi-iot + └── app + │── helloworld + │ │── main.c + │ └── BUILD.gn + └── BUILD.gn +``` + +**2. 编写业务代码**。 + +在./applications/sample/wifi-iot/app/helloworld目录下新建main.c文件,在main.c中新建业务入口函数HelloWorld,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务。(SYS\_RUN定义在ohos\_init.h文件中) + +``` +#include +#include "ohos_init.h" +#include "ohos_types.h" + +void HelloWorld(void) +{ + printf("[DEMO] Hello world.\n"); +} +SYS_RUN(HelloWorld); +``` + +**3.编写用于将业务构建成静态库的BUILD.gn文件**。 + +在./applications/sample/wifi-iot/app/helloworld目录下新建BUILD.gn文件,并完成如下配置。 + +``` +static_library("main") { + sources = [ + "main.c" + ] + include_dirs = [ + "//utils/native/lite/include" + ] +} +``` + +- static\_library中指定业务模块的编译结果,为静态库文件libmyapp.a,开发者根据实际情况完成填写。 +- sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含"//"则表示绝对路径(此处为代码根路径),若不包含"//"则表示相对路径。 +- include\_dirs中指定source所需要依赖的.h文件路径。 + +**4. 编写模块BUILD.gn文件,指定需参与构建的特性模块**。 + +配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,以helloworld举例,features字段配置如下。 + +``` +import("//build/lite/config/component/lite_component.gni") + +lite_component("app") { + features = [ + "helloworld:main", + ] +} +``` + +- helloworld是相对路径,指向./applications/sample/wifi-iot/app/helloworld/BUILD.gn。 +- main是目标,指向./applications/sample/wifi-iot/app/helloworld/BUILD.gn中的static\_library\("main"\)。 + +**5. 代码编译**。 + +依次点击图标1和2中的两个按钮,等待代码编译,提示\[SUCCESS\]则表示项目编译成功。 + + + +**6. [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md)**。 + +**7. 运行结果**。 + +示例代码编译、烧录、运行、调测成功后,重启开发板后将自动在界面输出如下结果: + +``` +[DEMO] Hello world. +``` + +# 任务二 如何启动线程 + +下文将通过修改源码的方式展示如何编写简单程序,教会开发者如何开启一个线程,这里要实现开启两个线程,线程1是每隔1秒打印一次数据,线程2是每隔2秒打印一次数据。 + +**1. 确定目录结构**。 + +开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + +例如:在app目录下新增业务thread,其中main.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + +``` +. +└── applications + └── sample + └── wifi-iot + └── app + │── thread + │ │── main.c + │ └── BUILD.gn + └── BUILD.gn +``` + +**2. 编写业务代码**。 + +在./applications/sample/wifi-iot/app/thread目录下新建main.c文件,在main.c中新建业务入口函数ThreadTest,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务。(SYS\_RUN定义在ohos\_init.h文件中) + +``` +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" + +#define TASK_STACK_SIZE (1024 * 4) +#define TASK_SLEEP_TIME1 (1 * 1000 * 1000) +#define TASK_SLEEP_TIME2 (2 * 1000 * 1000) + +// 线程1是每隔1秒打印一次数据 +static void Task1(void) +{ + int count = 0; + while (1) { + printf("Task1----%d\r\n", count++); + usleep(TASK_SLEEP_TIME1); + } +} + +// 线程2是每隔2秒打印一次数据 +static void Task2(void) +{ + int count = 0; + while (1) { + printf("Task2----%d\r\n", count++); + usleep(TASK_SLEEP_TIME2); + } +} + +static void ThreadTest(void) +{ + // 指定线程的属性 + osThreadAttr_t attr; + attr.name = "Thread_1"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = TASK_STACK_SIZE; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)Task1, NULL, &attr) == NULL) { + printf("Failed to create Task1!\r\n"); + } + + attr.name = "Thread_2"; + + if (osThreadNew((osThreadFunc_t)Task2, NULL, &attr) == NULL) { + printf("Failed to create Task2!\n\n"); + } + +} + +SYS_RUN(ThreadTest); +``` + +**3.编写用于将业务构建成静态库的BUILD.gn文件**。 + +在./applications/sample/wifi-iot/app/thread目录下新建BUILD.gn文件,并完成如下配置。 + +``` +static_library("main") { + sources = [ + "main.c" + ] + include_dirs = [ + "//utils/native/lite/include" + ] +} +``` + +**4. 编写模块BUILD.gn文件,指定需参与构建的特性模块**。 + +配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + +``` +import("//build/lite/config/component/lite_component.gni") + +lite_component("app") { + features = [ + "thread:main", + ] +} +``` + +**5. 代码编译**。 + +依次点击图标1和2中的两个按钮,等待代码编译,提示\[SUCCESS\]则表示项目编译成功。 + + + +**6. [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md)**。 + +**7. 运行结果**。 + +示例代码编译、烧录、运行、调测后,重启开发板后将自动在界面输出如下结果,其中线程1是每隔1秒打印一次数据,线程2是每隔2秒打印一次数据: + +``` +Task1----0 +Task2----0 +Task1----1 +Task2----1 +Task1----2 +Task1----3 +Task2----2 +Task1----4 +... +Task1----19 +Task2----10 +Task1----20 +Task1----21 +Task2----11 +Task1----22 +``` + +# 任务三 如何启动定时器 + +下文将通过修改源码的方式展示如何编写简单程序,教会开发者如何开启一个定时器,其中定时器1是每隔1秒打印一次数据,定时器2是第10秒的时候打印一次数据并结束。 + +**1. 确定目录结构**。 + +开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。 + +例如:在app目录下新增业务timer,其中main.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下: + +``` +. +└── applications + └── sample + └── wifi-iot + └── app + │── timer + │ │── main.c + │ └── BUILD.gn + └── BUILD.gn +``` + +**2. 编写业务代码**。 + +在./applications/sample/wifi-iot/app/timer目录下新建main.c文件,在main.c中新建业务入口函数TimerTest,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口SYS\_RUN\(\)启动业务。(SYS\_RUN定义在ohos\_init.h文件中) + +``` +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" + +// 重复执行的定时器 +static void TimerRepeatCallback(void *arg) +{ + (void)arg; + printf("[TimerRepeatCallback] timer repeat callback!\r\n"); +} + +// 只执行一次的定时器 +static void TimerOnceCallback(void *arg) +{ + (void)arg; + printf("[TimerOnceCallback] timer once callback!\r\n"); +} + +static void TimerTest(void) +{ + osTimerId_t id1, id2; + uint32_t timerDelay; + osStatus_t status; + + // 启动第一个定时器:每隔一秒打印一次 + id1 = osTimerNew(TimerRepeatCallback, osTimerPeriodic, NULL, NULL); + if (id1 != NULL) { + // Hi3861 1U=10ms,100U=1S + timerDelay = 100U; + + status = osTimerStart(id1, timerDelay); + if (status != osOK) { + // Timer could not be started + printf("timer repeat start failed\r\n"); + } + } + + // 启动第二个定时器:第10秒的时候打印 + id2 = osTimerNew(TimerOnceCallback, osTimerOnce, NULL, NULL); + if (id2 != NULL) { + // Hi3861 1U=10ms,1000U=10S + timerDelay = 1000U; + + status = osTimerStart(id2, timerDelay); + if (status != osOK) { + // Timer could not be started + printf("timer once start failed\r\n"); + } + } +} + +SYS_RUN(TimerTest); +``` + +**3.编写用于将业务构建成静态库的BUILD.gn文件**。 + +在./applications/sample/wifi-iot/app/timer目录下新建BUILD.gn文件,并完成如下配置。 + +``` +static_library("main") { + sources = [ + "main.c" + ] + include_dirs = [ + "//utils/native/lite/include" + ] +} +``` + +**4. 编写模块BUILD.gn文件,指定需参与构建的特性模块。** + +配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,features字段配置如下。 + +``` +import("//build/lite/config/component/lite_component.gni") + +lite_component("app") { + features = [ + "timer:main", + ] +} +``` + +**5. 代码编译**。 + +依次点击图标1和2中的两个按钮,等待代码编译,提示\[SUCCESS\]则表示项目编译成功。 + + + +**6. [烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-burn.md)**。 + +**7. 运行结果**。 + +示例代码编译、烧录、运行、调测后,重启开发板后将自动在界面输出如下结果,其中定时器1是每隔1秒打印一次数据,定时器2是第10秒的时候打印一次数据并结束: + +``` +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerOnceCallback] timer once callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +[TimerRepeatCallback] timer repeat callback! +... +``` + +# 恭喜你 + +目前您已经成功完成了本Codelab,并且学到了: + +- Hi3861环境搭建 +- 如何打印日志 +- 如何启动线程 +- 如何启动定时器 + + diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001233898165.png" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001233898165.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68d1312c3f1d7ee3b7a33d592fcf208d3ac219f Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001233898165.png" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001241179609.png" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001241179609.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68d1312c3f1d7ee3b7a33d592fcf208d3ac219f Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001241179609.png" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001241299649.png" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001241299649.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68d1312c3f1d7ee3b7a33d592fcf208d3ac219f Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/figures/zh-cn_image_0000001241299649.png" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-caution.gif" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-caution.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-caution.gif" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-danger.gif" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-danger.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-danger.gif" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-note.gif" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-note.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6314297e45c1de184204098efd4814d6dc8b1cda Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-note.gif" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-notice.gif" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-notice.gif" new file mode 100644 index 0000000000000000000000000000000000000000..86024f61b691400bea99e5b1f506d9d9aef36e27 Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-notice.gif" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-tip.gif" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-tip.gif" new file mode 100644 index 0000000000000000000000000000000000000000..93aa72053b510e456b149f36a0972703ea9999b7 Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-tip.gif" differ diff --git "a/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-warning.gif" "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-warning.gif" new file mode 100644 index 0000000000000000000000000000000000000000..6e90d7cfc2193e39e10bb58c38d01a23f045d571 Binary files /dev/null and "b/Device/\346\227\245\345\277\227\343\200\201\347\272\277\347\250\213\343\200\201\345\256\232\346\227\266\345\231\250/public_sys-resources/icon-warning.gif" differ diff --git a/Distributed/DistributeDatabaseDrawEts/README.md b/Distributed/DistributeDatabaseDrawEts/README.md index fbd15b4a062e46e1e253e168a2a6f08e1cdd4cd1..0e6754a72888dee775826ff0d86e68c6678721b6 100644 --- a/Distributed/DistributeDatabaseDrawEts/README.md +++ b/Distributed/DistributeDatabaseDrawEts/README.md @@ -6,9 +6,13 @@  + + # 2.相关概念 -[Path组件](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-drawing-components-path.md) +[Canvas组件](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-components-canvas-canvas.md) + +[CanvasRenderingContext2D对象](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-canvasrenderingcontext2d.md) [分布式数据库](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-distributed-data.md) @@ -18,65 +22,62 @@ 完成本篇Codelab我们首先要完成开发环境的搭建,本示例以**Hi3516DV300**开发板为例,参照以下步骤进行: -1. [获取OpenHarmony系统版本](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md#%E8%8E%B7%E5%8F%96%E6%96%B9%E5%BC%8F3%E4%BB%8E%E9%95%9C%E5%83%8F%E7%AB%99%E7%82%B9%E8%8E%B7%E5%8F%96):标准系统解决方案(二进制) - - 以3.0版本为例: - -  +1. [获取OpenHarmony系统版本](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md#%E8%8E%B7%E5%8F%96%E6%96%B9%E5%BC%8F3%E4%BB%8E%E9%95%9C%E5%83%8F%E7%AB%99%E7%82%B9%E8%8E%B7%E5%8F%96):标准系统解决方案(二进制)。 -2. 搭建烧录环境 + 以3.0版本为例: - 1. [完成DevEco Device Tool的安装](https://device.harmonyos.com/cn/docs/documentation/guide/install_windows-0000001050164976) +  - 2. [完成Hi3516开发板的烧录](https://device.harmonyos.com/cn/docs/documentation/guide/hi3516_upload-0000001052148681) - -3. 搭建开发环境 +2. 搭建烧录环境。 + 1. [完成DevEco Device Tool的安装](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-standard-env-setup.md) + 2. [完成Hi3516开发板的烧录](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3516-burn.md) +3. 1. 开始前请参考[工具准备](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/start-overview.md#%E5%B7%A5%E5%85%B7%E5%87%86%E5%A4%87),完成DevEco Studio的安装和开发环境配置。 2. 开发环境配置完成后,请参考[使用工程向导](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/start-with-ets.md#%E5%88%9B%E5%BB%BAets%E5%B7%A5%E7%A8%8B)创建工程(模板选择“Empty Ability”),选择JS或者eTS语言开发。 3. 工程创建完成后,选择使用[真机进行调测](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/start-with-ets.md#%E4%BD%BF%E7%94%A8%E7%9C%9F%E6%9C%BA%E8%BF%90%E8%A1%8C%E5%BA%94%E7%94%A8)。 # 4.分布式组网 -本章节以系统自带的音乐播放器为例,介绍如何完成两台设备的分布式组网。 +本章节以系统自带的音乐播放器为例(具体以实际的应用为准),介绍如何完成两台设备的分布式组网。 -1. 硬件准备:准备两台烧录相同的版本系统的**Hi3516DV300**开发板A,B。 +1. 硬件准备:准备两台烧录相同的版本系统的**Hi3516DV300**开发板A、B。 -2. 两个开发板A,B配置在同一个WiFi网络之下。 +2. 开发板A、B连接同一个WiFi网络。 - 打开设置--\>WLAN--\>点击右侧WiFi开关--\>点击目标WiFi并输入密码。 + 打开设置--\>WLAN--\>点击右侧WiFi开关--\>点击目标WiFi并输入密码。 -  +  -3. 将设备A,B设置为互相信任的设备。 +3. 将设备A,B设置为互相信任的设备。 - 找到系统应用“音乐”。 -  +  - - 设备A打开音乐,点击左下角流转按钮,弹出列表框,在列表中会展示远端设备的id。 + - 设备A打开音乐,点击左下角流转按钮,弹出列表框,在列表中会展示远端设备的id。 -  +  - - 选择远端设备B的id,另一台开发板(设备B)会弹出验证的选项框。 + - 选择远端设备B的id,另一台开发板(设备B)会弹出验证的选项框。 -  +  - - 设备B点击允许,设备B将会弹出随机PIN码,将设备B的PIN码输入到设备A的PIN码填入框中。 + - 设备B点击允许,设备B将会弹出随机PIN码,将设备B的PIN码输入到设备A的PIN码填入框中。 -  +  - 配网完毕。 + 配网完毕。 # 5.代码结构解读 -本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在[参考](参考.md)中提供下载方式,整个工程的代码结构如下: +本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在[参考](zh-cn_topic_0000001193093116.md)中提供下载方式,整个工程的代码结构如下:  -- common:存放公共资源 +- common:存放公共资源 - media:存放图片 + media:存放图片 - model:存放数据模型类 @@ -84,243 +85,262 @@ - RemoteDeviceModel.ts:远程设备类 -- pages:存放页面 +- pages:存放页面 - index.ets:主页面 + index.ets:主页面 - config.json:配置文件 # 6.编写数据类对象 -1. 编写分布式数据类对象 - - 我们需要创建RemoteDeviceModel类来完成远程设备管理的初始化,RemoteDeviceModel .ts代码如下: - - ``` - import deviceManager from '@ohos.distributedHardware.deviceManager'; - var SUBSCRIBE_ID = 100; - export default class RemoteDeviceModel { - // 设备列表 - deviceList: any[] = [] - // 回调 - callback: any - // 设备管理Manager - #deviceManager: any - // 构造方法 - constructor() { - } - //注册设备回调方法 - registerDeviceListCallback(callback) { - if (typeof (this.#deviceManager) === 'undefined') { - let self = this; - deviceManager.createDeviceManager('com.ohos.distributedRemoteStartFA', (error, value) => { - if (error) { - console.error('createDeviceManager failed.'); - return; - } - self.#deviceManager = value; - self.registerDeviceListCallback_(callback); - }); - } else { - this.registerDeviceListCallback_(callback); - } - } - //注册设备回调方法 - registerDeviceListCallback_(callback) { - this.callback = callback; - if (this.#deviceManager == undefined) { - this.callback(); - return; - } - - console.info('CookBook[RemoteDeviceModel] getTrustedDeviceListSync begin'); - var list = this.#deviceManager.getTrustedDeviceListSync(); - if (typeof (list) != 'undefined' && typeof (list.length) != 'undefined') { - this.deviceList = list; - } - this.callback(); - let self = this; - this.#deviceManager.on('deviceStateChange', (data) => { - switch (data.action) { - case 0: - self.deviceList[self.deviceList.length] = data.device; - self.callback(); - if (self.authCallback != null) { - self.authCallback(); - self.authCallback = null; - } - break; - case 2: - if (self.deviceList.length > 0) { - for (var i = 0; i < self.deviceList.length; i++) { - if (self.deviceList[i].deviceId === data.device.deviceId) { - self.deviceList[i] = data.device; - break; - } - } - } - self.callback(); - break; - case 1: - if (self.deviceList.length > 0) { - var list = []; - for (var i = 0; i < self.deviceList.length; i++) { - if (self.deviceList[i].deviceId != data.device.deviceId) { - list[i] = data.device; - } - } - self.deviceList = list; - } - self.callback(); - break; - default: - break; - } - }); - this.#deviceManager.on('deviceFound', (data) => { - console.info('CookBook[RemoteDeviceModel] deviceFound data=' + JSON.stringify(data)); - console.info('CookBook[RemoteDeviceModel] deviceFound self.deviceList=' + self.deviceList); - console.info('CookBook[RemoteDeviceModel] deviceFound self.deviceList.length=' + self.deviceList.length); - for (var i = 0; i < self.discoverList.length; i++) { - if (self.discoverList[i].deviceId === data.device.deviceId) { - console.info('CookBook[RemoteDeviceModel] device founded, ignored'); - return; - } - } - self.discoverList[self.discoverList.length] = data.device; - self.callback(); - }); - this.#deviceManager.on('discoverFail', (data) => { - console.info('CookBook[RemoteDeviceModel] discoverFail data=' + JSON.stringify(data)); - }); - this.#deviceManager.on('serviceDie', () => { - console.error('CookBook[RemoteDeviceModel] serviceDie'); - }); - - SUBSCRIBE_ID = Math.floor(65536 * Math.random()); - var info = { - subscribeId: SUBSCRIBE_ID, - mode: 0xAA, - medium: 2, - freq: 2, - isSameAccount: false, - isWakeRemote: true, - capability: 0 - }; - console.info('CookBook[RemoteDeviceModel] startDeviceDiscovery ' + SUBSCRIBE_ID); - this.#deviceManager.startDeviceDiscovery(info); - } - //身份验证 - authDevice(deviceId, callback) { - console.info('CookBook[RemoteDeviceModel] authDevice ' + deviceId); - for (var i = 0; i < this.discoverList.length; i++) { - if (this.discoverList[i].deviceId === deviceId) { - console.info('CookBook[RemoteDeviceModel] device founded, ignored'); - let extraInfo = { - "targetPkgName": 'com.ohos.distributedRemoteStartFA', - "appName": 'demo', - "appDescription": 'demo application', - "business": '0' - }; - let authParam = { - "authType": 1, - "appIcon": '', - "appThumbnail": '', - "extraInfo": extraInfo - }; - console.info('CookBook[RemoteDeviceModel] authenticateDevice ' + JSON.stringify(this.discoverList[i])); - let self = this; - this.#deviceManager.authenticateDevice(this.discoverList[i], authParam, (err, data) => { - if (err) { - console.info('CookBook[RemoteDeviceModel] authenticateDevice failed, err=' + JSON.stringify(err)); - self.authCallback = null; - } else { - console.info('CookBook[RemoteDeviceModel] authenticateDevice succeed, data=' + JSON.stringify(data)); - self.authCallback = callback; - } - }); - } - } - } - //取消注册设备回调方法 - unregisterDeviceListCallback() { - console.info('CookBook[RemoteDeviceModel] stopDeviceDiscovery ' + SUBSCRIBE_ID); - this.#deviceManager.stopDeviceDiscovery(SUBSCRIBE_ID); - this.#deviceManager.off('deviceStateChange'); - this.#deviceManager.off('deviceFound'); - this.#deviceManager.off('discoverFail'); - this.#deviceManager.off('serviceDie'); - this.deviceList = []; - } - } - ``` - -2. 编写远程设备类对象 - - 我们需要创建KvStoreModel类来完成[分布式数据管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-distributed-data.md)的初始化工作。首先调用distributedData.createKVManager接口创建一个KVManager对象实例,用于管理数据库对象。然后调用KVManager.getKVStore接口创建并获取KVStore数据库。最后对外提供put、setDataChangeListener方法用于数据写入和订阅数据更新通知。KvStoreModel.ts代码如下: - - ``` - import distributedData from '@ohos.data.distributeddata'; - const STORE_ID = 'DrawBoard_kvstore'; - - export default class KvStoreModel { - kvManager: any; - kvStore: any; - constructor() { - } - // 创建createKvStore对象实例 - createKvStore(callback: any) { - if (typeof (this.kvStore) === 'undefined') { - var config = { - bundleName: 'com.huawei.cookbook', - userInfo: { - userId: '0', - userType: 0 - } - }; - let self = this; - console.info('DrawBoard[KvStoreModel] createKVManager begin'); - distributedData.createKVManager(config).then((manager) => { - console.info('DrawBoard[KvStoreModel] createKVManager success, kvManager=' + JSON.stringify(manager)); - self.kvManager = manager; - var options = { - createIfMissing: true, - encrypt: false, - backup: false, - autoSync: true, - kvStoreType: 1, - schema: '', - securityLevel: 3, - }; - console.info('DrawBoard[KvStoreModel] kvManager.getKVStore begin'); - self.kvManager.getKVStore(STORE_ID, options).then((store: any) => { - console.info('DrawBoard[KvStoreModel] getKVStore success, kvStore=' + store); - self.kvStore = store; - callback(); - }); - console.info('DrawBoard[KvStoreModel] kvManager.getKVStore end'); - }); - console.info('DrawBoard[KvStoreModel] createKVManager end'); - } else { - callback(); - } - } - // 添加数据 - put(key: any, value: any) { - if (typeof (this.kvStore) === 'undefined') { - return; - } - console.info('DrawBoard[KvStoreModel] kvStore.put ' + key + '=' + value); - this.kvStore.put(key, value).then((data: any) => { - this.kvStore.get(key).then((data:any) => { - console.info('DrawBoard[KvStoreModel] kvStore.get ' + key + '=' + JSON.stringify(data)); - }); - console.info('DrawBoard[KvStoreModel] kvStore.put ' + key + ' finished, data=' + JSON.stringify(data)); - }).catch((err: JSON) => { - console.error('DrawBoard[KvStoreModel] kvStore.put ' + key + ' failed, ' + JSON.stringify(err)); - }); - } - // 获取数据 +1. 编写分布式数据类对象 + + 我们需要创建RemoteDeviceModel类来完成远程设备管理的初始化,RemoteDeviceModel .ts代码如下: + + ``` + import deviceManager from '@ohos.distributedHardware.deviceManager'; + var SUBSCRIBE_ID = 100; + export default class RemoteDeviceModel { + // 设备列表 + deviceList: any[] = [] + // 回调 + callback: any + // 设备管理Manager + #deviceManager: any + // 构造方法 + constructor() { + } + //注册设备回调方法 + registerDeviceListCallback(callback) { + if (typeof (this.#deviceManager) === 'undefined') { + let self = this; + deviceManager.createDeviceManager('com.ohos.distributedRemoteStartFA', (error, value) => { + if (error) { + console.error('createDeviceManager failed.'); + return; + } + self.#deviceManager = value; + self.registerDeviceListCallback_(callback); + }); + } else { + this.registerDeviceListCallback_(callback); + } + } + //注册设备回调方法 + registerDeviceListCallback_(callback) { + this.callback = callback; + if (this.#deviceManager == undefined) { + this.callback(); + return; + } + + console.info('CookBook[RemoteDeviceModel] getTrustedDeviceListSync begin'); + var list = this.#deviceManager.getTrustedDeviceListSync(); + if (typeof (list) != 'undefined' && typeof (list.length) != 'undefined') { + this.deviceList = list; + } + this.callback(); + let self = this; + this.#deviceManager.on('deviceStateChange', (data) => { + switch (data.action) { + case 0: + self.deviceList[self.deviceList.length] = data.device; + self.callback(); + if (self.authCallback != null) { + self.authCallback(); + self.authCallback = null; + } + break; + case 2: + if (self.deviceList.length > 0) { + for (var i = 0; i < self.deviceList.length; i++) { + if (self.deviceList[i].deviceId === data.device.deviceId) { + self.deviceList[i] = data.device; + break; + } + } + } + self.callback(); + break; + case 1: + if (self.deviceList.length > 0) { + var list = []; + for (var i = 0; i < self.deviceList.length; i++) { + if (self.deviceList[i].deviceId != data.device.deviceId) { + list[i] = data.device; + } + } + self.deviceList = list; + } + self.callback(); + break; + default: + break; + } + }); + this.#deviceManager.on('deviceFound', (data) => { + console.info('CookBook[RemoteDeviceModel] deviceFound data=' + JSON.stringify(data)); + console.info('CookBook[RemoteDeviceModel] deviceFound self.deviceList=' + self.deviceList); + console.info('CookBook[RemoteDeviceModel] deviceFound self.deviceList.length=' + self.deviceList.length); + for (var i = 0; i < self.discoverList.length; i++) { + if (self.discoverList[i].deviceId === data.device.deviceId) { + console.info('CookBook[RemoteDeviceModel] device founded, ignored'); + return; + } + } + self.discoverList[self.discoverList.length] = data.device; + self.callback(); + }); + this.#deviceManager.on('discoverFail', (data) => { + console.info('CookBook[RemoteDeviceModel] discoverFail data=' + JSON.stringify(data)); + }); + this.#deviceManager.on('serviceDie', () => { + console.error('CookBook[RemoteDeviceModel] serviceDie'); + }); + + SUBSCRIBE_ID = Math.floor(65536 * Math.random()); + var info = { + subscribeId: SUBSCRIBE_ID, + mode: 0xAA, + medium: 2, + freq: 2, + isSameAccount: false, + isWakeRemote: true, + capability: 0 + }; + console.info('CookBook[RemoteDeviceModel] startDeviceDiscovery ' + SUBSCRIBE_ID); + this.#deviceManager.startDeviceDiscovery(info); + } + //身份验证 + authDevice(deviceId, callback) { + console.info('CookBook[RemoteDeviceModel] authDevice ' + deviceId); + for (var i = 0; i < this.discoverList.length; i++) { + if (this.discoverList[i].deviceId === deviceId) { + console.info('CookBook[RemoteDeviceModel] device founded, ignored'); + let extraInfo = { + "targetPkgName": 'com.ohos.distributedRemoteStartFA', + "appName": 'demo', + "appDescription": 'demo application', + "business": '0' + }; + let authParam = { + "authType": 1, + "appIcon": '', + "appThumbnail": '', + "extraInfo": extraInfo + }; + console.info('CookBook[RemoteDeviceModel] authenticateDevice ' + JSON.stringify(this.discoverList[i])); + let self = this; + this.#deviceManager.authenticateDevice(this.discoverList[i], authParam, (err, data) => { + if (err) { + console.info('CookBook[RemoteDeviceModel] authenticateDevice failed, err=' + JSON.stringify(err)); + self.authCallback = null; + } else { + console.info('CookBook[RemoteDeviceModel] authenticateDevice succeed, data=' + JSON.stringify(data)); + self.authCallback = callback; + } + }); + } + } + } + //取消注册设备回调方法 + unregisterDeviceListCallback() { + console.info('CookBook[RemoteDeviceModel] stopDeviceDiscovery ' + SUBSCRIBE_ID); + this.#deviceManager.stopDeviceDiscovery(SUBSCRIBE_ID); + this.#deviceManager.off('deviceStateChange'); + this.#deviceManager.off('deviceFound'); + this.#deviceManager.off('discoverFail'); + this.#deviceManager.off('serviceDie'); + this.deviceList = []; + } + } + ``` + +2. 编写远程设备类对象 + + 我们需要创建KvStoreModel类来完成[分布式数据管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-distributed-data.md)的初始化工作。首先调用distributedData.createKVManager接口创建一个KVManager对象实例,用于管理数据库对象。然后调用KVManager.getKVStore接口创建并获取KVStore数据库。最后对外提供put、setDataChangeListener方法用于数据写入和订阅数据更新通知。KvStoreModel.ts代码如下: + + ``` + import distributedData from '@ohos.data.distributedData'; + + const STORE_ID = 'DrawBoard_kvstore'; + + export default class KvStoreModel { + kvManager: any; + kvStore: any; + constructor() { + } + createKvStore(callback: any) { + if (typeof (this.kvStore) === 'undefined') { + var config = { + bundleName: 'com.huawei.cookbook', + userInfo: { + userId: '0', + userType: 0 + } + }; + let self = this; + console.info('DrawBoard[KvStoreModel] createKVManager begin'); + distributedData.createKVManager(config).then((manager) => { + console.info('DrawBoard[KvStoreModel] createKVManager success, kvManager=' + JSON.stringify(manager)); + self.kvManager = manager; + let options = { + createIfMissing: true, + encrypt: false, + backup: false, + autoSync: true, + kvStoreType: 0, + schema: '', + securityLevel: 1, + }; + console.info('DrawBoard[KvStoreModel] kvManager.getKVStore begin'); + self.kvManager.getKVStore(STORE_ID, options).then((store) => { + console.info('DrawBoard[KvStoreModel] getKVStore success, kvStore=' + store); + self.kvStore = store; + try { + self.kvStore.enableSync(true).then((err) => { + console.log('enableSync success'); + }).catch((err) => { + console.log('enableSync fail ' + JSON.stringify(err)); + }); + }catch(e) { + console.log('EnableSync e ' + e); + } + callback(); + }); + console.info('DrawBoard[KvStoreModel] kvManager.getKVStore end'); + }); + console.info('DrawBoard[KvStoreModel] createKVManager end'); + } else { + console.info('DrawBoard[KvStoreModel] KVManager is exist'); + callback(); + } + } + + broadcastMessage(msg: any) { + console.info('DrawBoard[KvStoreModel] broadcastMessage ' + msg); + var num = Math.random(); + let self = this; + this.createKvStore(() => { + self.put(msg, num); + }); + } + + put(key: any, value: any) { + if (typeof (this.kvStore) === 'undefined') { + return; + } + console.info('DrawBoard[KvStoreModel] kvStore.put ' + key + '=' + value); + this.kvStore.put(key, value).then((data: any) => { + this.kvStore.get(key).then((data:any) => { + console.info('DrawBoard[KvStoreModel] kvStore.get ' + key + '=' + JSON.stringify(data)); + }); + console.info('DrawBoard[KvStoreModel] kvStore.put ' + key + ' finished, data=' + JSON.stringify(data)); + }).catch((err: JSON) => { + console.error('DrawBoard[KvStoreModel] kvStore.put ' + key + ' failed, ' + JSON.stringify(err)); + }); + } + get(key: any,callback: any) { this.createKvStore(() => { this.kvStore.get(key, function (err: any ,data: any) { @@ -329,212 +349,254 @@ }); }) } - // 监听数据变化 - setDataChangeListener(callback: any) { - let self = this; - this.createKvStore(() => { - self.kvStore.on('dataChange', 1, (data: any) => { - if (data.updateEntries.length > 0) { - callback(data); - } - }); - }); - } - } - ``` + + setOnMessageReceivedListener(callback: any) { + console.info('DrawBoard[KvStoreModel] setOnMessageReceivedListener '); + let self = this; + this.createKvStore(() => { + console.info('DrawBoard[KvStoreModel] kvStore.on(dataChange) begin'); + self.kvStore.on('dataChange', 2, (data: any) => { + console.info('DrawBoard[KvStoreModel] dataChange, ' + JSON.stringify(data)); + console.info('DrawBoard[KvStoreModel] dataChange, insert ' + data.insertEntries.length + ' udpate ' + + data.updateEntries.length); + if (data.insertEntries.length < 1 && data.updateEntries.length < 1) { + return; + } + + callback(data); + }); + console.info('DrawBoard[KvStoreModel] kvStore.on(dataChange) end'); + }); + } + setDataChangeListener(callback) { + console.info('DrawBoard[KvStoreModel] setDataChangeListener come in'); + let self = this; + this.createKvStore(() => { + console.info('DrawBoard[KvStoreModel] setDataChangeListener createKvStore'); + self.kvStore.on('dataChange',2, (data: any) => { + console.info('DrawBoard[KvStoreModel] setDataChangeListener kvStore.on'); + if (data.updateEntries.length > 0) { + console.info('DrawBoard[KvStoreModel] setDataChangeListener callback'); + callback(data); + } + }); + }); + } + } + ``` # 7.页面设计 -分布式手写板页面主要由全屏Path绘制区、顶部操作栏组成。为了实现弹框选择设备的效果,在最外层添加了自定义弹框组件。Path组件设置为全屏显示,根据手指触摸的屏幕坐标直接通过Path绘制轨迹;顶部操作栏加入撤回图标、设备选择图标;自定义弹框加入标题、设备列表。页面样式请在[参考](参考.md)中查看,页面布局在index.ets中实现。 +分布式手写板页面主要由全屏Path绘制区、顶部操作栏组成。为了实现弹框选择设备的效果,在最外层添加了自定义弹框组件。Path组件设置为全屏显示,根据手指触摸的屏幕坐标直接通过Path绘制轨迹;顶部操作栏加入撤回图标、设备选择图标;自定义弹框加入标题、设备列表。页面样式请在[参考](zh-cn_topic_0000001193093116.md)中查看,页面布局在index.ets中实现。 在index.ets中按照如下步骤编写: -1. 页面整体布局 - - ``` - @Entry - @Component - struct Index { - build() { - Column({ space: 1 }) { - // 用于标题栏布局 - Flex() { - }.backgroundColor(Color.Grey).width('100%').height('10%') - // 用于Path绘制区布局 - Flex() { - }.width('100%').height('90%') - }.height('100%').width('100%') - } - ``` - -2. 标题栏布局 - - ``` - @Entry - @Component - struct Index { - build() { - Column({ space: 1 }) { - Flex() { - Image($r('app.media.goback')).width(70).height(70).position({ x: 30, y: 0 }) - Image($r('app.media.ic_hop')).width(70).height(70) - .align(Alignment.TopEnd) - .flexGrow(1) - .position({ x: 375, y: 0 }) - }.backgroundColor(Color.Grey).width('100%').height('10%') - ... - }.height('100%').width('100%') - } - ``` - -3. Path绘制区布局 - - ``` - @Entry - @Component - struct Index { - build() { - Column({ space: 1 }) { - Flex() { - ... - }.backgroundColor(Color.Grey).width('100%').height('10%') - Flex() { - Path().commands(this.pathCommands).strokeWidth(4).fill('none').stroke(Color.Black) - .width('100%') - .height('100%') - }.width('100%').height('90%') - }.height('100%').width('100%') - } - ``` - -4. 自定义弹框设计并引入到主页面中 - - 1. 自定义弹框设计 - - ``` - @CustomDialog - struct CustomDialogExample { - controller: CustomDialogController - cancel: () => void - confirm: (deviceId, deviceName) => void - startAbility: (deviceId, deviceName, positionList) => void - deviceList:() => void - positionList:() => void - build() { - Column() { - Text('设备列表').width('70%').fontSize(20).margin({ top: 10, bottom: 10 }) - Flex({ justifyContent: FlexAlign.SpaceAround }) { - List({ space: 20, initialIndex: 0 }) { - ForEach(this.deviceList, (item) => { - ListItem() { - Text('' + item.name) - .width('100%').height(100).fontSize(16) - .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF) - .onClick((event: ClickEvent) =>{ - this.controller.close(); - this.startAbility(item.id, item.name, this.positionList) - }) - }.editable(true) - }, item => item.id) - } - .listDirection(Axis.Vertical) // 排列方向 - .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线 - .edgeEffect(EdgeEffect.None) // 滑动到边缘无效果 - .chainAnimation(false) // 联动特效关闭 - .onScrollIndex((firstIndex: number, lastIndex: number) => { - console.info('first' + firstIndex) - console.info('last' + lastIndex) - }) - }.margin({ bottom: 10 }) - } - } +1. 页面整体布局 + + ``` + @Entry + @Component + struct Index { + build() { + Column({ space: 1 }) { + // 用于标题栏布局 + Row() { + }.backgroundColor(Color.Grey).width('100%').height('10%') + // 用于Path绘制区布局 + Row() { + }.width('100%').height('90%') + }.height('100%').width('100%') } - ``` + ``` + +2. 标题栏布局 + + ``` + @Entry + @Component + struct Index { + build() { + Column({ space: 1 }) { + Row() { + Image($r('app.media.goback')).width(100).height(100).margin({left:10}) + Blank() + Image($r('app.media.ic_hop')).width(100).height(100).margin({right:10}) + }.backgroundColor(Color.Grey).width('100%').height('10%') + ... + }.height('100%').width('100%') + } + } + ``` + +3. Canvas绘制区布局 + + ``` + @Entry + @Component + struct Index { + build() { + Column({ space: 1 }) { + Row() { + ... + }.backgroundColor(Color.Grey).width('100%').height('10%') + Row() { + Canvas(this.context) + .width('100%') + .height('100%') + .backgroundColor('#FFFFFF') + }.width('100%').height('90%') + }.height('100%').width('100%') + } + ``` + +4. 自定义弹框设计并引入到主页面中 + 1. 自定义弹框设计 + + ``` + @CustomDialog + struct CustomDialogExample { + controller: CustomDialogController + cancel: () => void + confirm: (deviceId, deviceName) => void + startAbility: (deviceId, deviceName, positionList) => void + deviceList:() => void + positionList:() => void + private selectedIndex: number = 0 + build() { + Column() { + Text('选择设备') + .fontSize(20) + .width('100%') + .textAlign(TextAlign.Center) + .fontColor(Color.Black) + .fontWeight(FontWeight.Bold) + List() { + ForEach(this.deviceList, (item, index) => { + ListItem() { + Row() { + Text(item.name) + .fontSize(20) + .width('90%') + .fontColor(Color.Black) + if (this.deviceList.indexOf(item) == this.selectedIndex) { + Image($r('app.media.checked')) + .width('8%') + .objectFit(ImageFit.Contain) + } else { + Image($r('app.media.uncheck')) + .width('8%') + .objectFit(ImageFit.Contain) + } + } + .height(55) + .onClick(() =>{ + this.selectedIndex = index + this.controller.close(); + this.startAbility(item.id, item.name, this.positionList) + }) + } + }, item => item.id) + } + + Button() { + Text('取消') + .fontColor('#0D9FFB') + .width('90%') + .textAlign(TextAlign.Center) + .fontSize(20) + } + .type(ButtonType.Capsule) + .backgroundColor(Color.White) + .onClick(() => { + this.controller.close() + }) + } + .backgroundColor(Color.White) + .border({ color: Color.White, radius: 20 }) + .padding(10) + } + } + ``` - 2. 引入到主页面 + 2. 引入到主页面 - ``` - @Entry - @Component - struct Index { - ... - dialogController: CustomDialogController = new CustomDialogController({ - builder: CustomDialogExample({ cancel: this.onCancel, confirm: this.onAccept, deviceList: this.deviceList,positionList: this.positionList,startAbility: this.startAbilityContinuation }), - cancel: this.existApp, - autoCancel: true, - deviceList: this.deviceList, - positionList: this.positionList - }) - onCancel() { - console.info('Callback when the first button is clicked') - } - onAccept() { - console.info('Click when confirm') - } - existApp() { - console.info('Click the callback in the blank area') - } - build() { + ``` + @Entry + @Component + struct Index { ... - } - } - ``` - -5. 为页面设置原始数据 - - 1. 在index.ets文件的顶部设置常量数据 - - ``` - // 默认设备 - var DEVICE_LIST_LOCALHOST = { name: '本机', id: 'localhost' }; - // 用于存放点位信息的key值常量 - const CHANGE_POSITION = 'change_position'; - // path组件Command参数初始化值 - const DEfAULT_PATH_COMMAND = ''; - ``` - - 2. 在Index组件(Component)中设置基本参数 - - ``` - @Entry - @Component - struct Index { - // 触控起始位置X轴坐标 - @State startX: number = 0 - // 触控起始位置Y轴坐标 - @State startY: number = 0 - // 触控移动后的位置X轴坐标 - @State moveX: number = 0 - // 触控移动后的位置Y轴坐标 - @State moveY: number = 0 - // 触控结束后的位置X轴坐标 - @State endX: number = 0 - // 触控结束后的位置Y轴坐标 - @State endY: number = 0 - // Path 组件Command属性值,默认为'' - @State pathCommands: string = DEfAULT_PATH_COMMAND - // 设备列表 - @State deviceList: any[] = [] - // BUNDLE_NAME - private BUNDLE_NAME: string = "com.huawei.cookbook"; - // 分布式数据库类对象 - private kvStoreModel: KvStoreModel = new KvStoreModel() - // 远程设备类对象 - private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel() - // 点位集合 - @State positionList: any[] = [] - // 初始化数据 - @State initialData: any[] = [] - // 是否同步 - private isNeedSync: boolean = false - // 间隔ID - private intervalID: number = 0 - ... - build() { - ... - } - } - ``` + dialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample({ cancel: this.onCancel, confirm: this.onAccept, deviceList: this.deviceList,positionList: this.positionList,startAbility: this.startAbilityContinuation }), + cancel: this.existApp, + autoCancel: true, + deviceList: this.deviceList, + positionList: this.positionList + }) + onCancel() { + console.info('Callback when the first button is clicked') + } + onAccept() { + console.info('Click when confirm') + } + existApp() { + console.info('Click the callback in the blank area') + } + build() { + ... + } + } + ``` + +5. 为页面设置原始数据 + 1. 在index.ets文件的顶部设置常量数据 + + ``` + // 默认设备 + var DEVICE_LIST_LOCALHOST = { name: '本机', id: 'localhost' }; + // 用于存放点位信息的key值常量 + const CHANGE_POSITION = 'change_position'; + ``` + + 2. 在Index组件(Component)中设置基本参数 + + ``` + @Entry + @Component + struct Index { + // 触控起始位置X轴坐标 + @State startX: number = 0 + // 触控起始位置Y轴坐标 + @State startY: number = 0 + // 触控移动后的位置X轴坐标 + @State moveX: number = 0 + // 触控移动后的位置Y轴坐标 + @State moveY: number = 0 + // 触控结束后的位置X轴坐标 + @State endX: number = 0 + // 触控结束后的位置Y轴坐标 + @State endY: number = 0 + // 设备列表 + @State deviceList: any[] = [] + // BUNDLE_NAME + private BUNDLE_NAME: string = "com.huawei.cookbook"; + // 分布式数据库类对象 + private kvStoreModel: KvStoreModel = new KvStoreModel() + // 远程设备类对象 + private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel() + // 点位集合 + @State positionList: any[] = [] + // 初始化数据 + @State initialData: any[] = [] + // 画布组件 + private settings: RenderingContextSettings = new RenderingContextSettings(true) + // CanvasRenderingContext2D对象 + private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) + ... + build() { + ... + } + } + ``` # 8.设备拉起 @@ -542,135 +604,168 @@  -1. 分享图标添加点击事件 - - ``` - Image($r('app.media.ic_hop')).width(70).height(70) - .align(Alignment.TopEnd) - .flexGrow(1) - .position({ x: 375, y: 0 }) - .onClick((event: ClickEvent) =>{ - this.onContinueAbilityClick() - }) - ``` - -2. 分布式拉起方法实现 - - ``` - onContinueAbilityClick() { - console.info('DrawBoard[IndexPage] onContinueAbilityClick'); - let self = this; - this.remoteDeviceModel.registerDeviceListCallback(() => { - console.info('DrawBoard[IndexPage] registerDeviceListCallback, callback entered'); - var list = []; - list[0] = DEVICE_LIST_LOCALHOST - var deviceList = self.remoteDeviceModel.deviceList; - console.info('DrawBoard[IndexPage] on remote device updated, count=' + deviceList.length); - for (var i = 0; i < deviceList.length; i++) { - console.info('DrawBoard[IndexPage] device ' + i + '/' + deviceList.length + ' deviceId=' - + deviceList[i].deviceId + ' deviceName=' + deviceList[i].deviceName + ' deviceType=' - + deviceList[i].deviceType); - list[i + 1] = { - name: deviceList[i].deviceName, - id: deviceList[i].deviceId, - }; - } - self.deviceList = list; - self.dialogController.open() - }); - } - ``` +1. config.json中添加分布式权限 + + ``` + { + ... + "module": { + ... + "reqPermissions": [{ + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }] + } + } + ``` + +2. 初始化方法中添加动态权限申请代码 + + ``` + async aboutToAppear() { + this.grantPermission() + ... + } + grantPermission() { + console.log('MusicPlayer[IndexPage] grantPermission') + let context = featureAbility.getContext() + context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (result) { + console.log(`MusicPlayer[IndexPage] grantPermission,requestPermissionsFromUser,result.requestCode=${result.requestCode}`) + }) + } + + ``` + +3. 分享图标添加点击事件 + + ``` + Image($r('app.media.ic_hop')).width(100).height(100).margin({right:10}) + .onClick(() =>{ + this.onContinueAbilityClick() + }) + ``` + +4. 分布式拉起方法实现 + + ``` + onContinueAbilityClick() { + console.info('DrawBoard[IndexPage] onContinueAbilityClick'); + let self = this; + this.remoteDeviceModel.registerDeviceListCallback(() => { + console.info('DrawBoard[IndexPage] registerDeviceListCallback, callback entered'); + var list = []; + list[0] = DEVICE_LIST_LOCALHOST + var deviceList = self.remoteDeviceModel.deviceList; + console.info('DrawBoard[IndexPage] on remote device updated, count=' + deviceList.length); + for (var i = 0; i < deviceList.length; i++) { + console.info('DrawBoard[IndexPage] device ' + i + '/' + deviceList.length + ' deviceId=' + + deviceList[i].deviceId + ' deviceName=' + deviceList[i].deviceName + ' deviceType=' + + deviceList[i].deviceType); + list[i + 1] = { + name: deviceList[i].deviceName, + id: deviceList[i].deviceId, + }; + } + self.deviceList = list; + self.dialogController.open() + }); + } + ``` # 9.笔记绘制 Path组件所在的Flex容器组件添加点击事件,并实现记录触控点位信息,绘制轨迹的功能。 -1. 添加点击事件 - - ``` - Flex() { - Path().commands(this.pathCommands).strokeWidth(4).fill('none').stroke(Color.Black) - .width('100%') - .height('100%') - }.onTouch((event: TouchEvent) => { - this.onTouchEvent(event) - }).width('100%').height('90%') - ``` - -2. 现记录触控点位信息的方法 - - ``` - onTouchEvent(event: TouchEvent) { - let position = {}; - switch(event.type){ - // 触控按下 - case TouchType.Down: - this.startX = event.touches[0].x - this.startY = event.touches[0].y - this.pathCommands += ' M' + this.startX + ' ' + this.startY - position.isFirstPosition = true; - position.positionX = this.startX; - position.positionY = this.startY; - this.pushData(position); - break; - // 触控移动 - case TouchType.Move: - this.moveX = event.touches[0].x - this.moveY = event.touches[0].y - this.pathCommands += ' L' + this.moveX + ' ' + this.moveY - position.isFirstPosition = false; - position.positionX = this.moveX; - position.positionY = this.moveY; - this.pushData(position); - break; - // 触控抬起 - case TouchType.Up: - this.endX = event.touches[0].x - this.endY = event.touches[0].y - position.isFirstPosition = false; - position.positionX = this.moveX; - position.positionY = this.moveY; - this.pushData(position); - break; - default: - break - } - } - ``` +1. 添加点击事件 + + ``` + Row() { + Canvas(this.context) + .width('100%') + .height('100%') + .backgroundColor('#FFFFFF') + }.onTouch((event: TouchEvent) => { + this.onTouchEvent(event) + }).width('100%').height('90%') + ``` + +2. 现记录触控点位信息的方法 + + ``` + onTouchEvent(event: TouchEvent) { + let position = {}; + switch(event.type){ + case TouchType.Down: + this.startX = event.touches[0].x + this.startY = event.touches[0].y + position.isFirstPosition = true; + position.positionX = this.startX; + position.positionY = this.startY; + position.isEndPosition = false + this.context.beginPath() + this.context.lineWidth = 4 + this.context.lineJoin = 'miter' + this.context.moveTo(this.startX, this.startY) + this.pushData(position); + break; + case TouchType.Move: + this.moveX = event.touches[0].x + this.moveY = event.touches[0].y + position.isFirstPosition = false; + position.positionX = this.moveX; + position.positionY = this.moveY; + position.isEndPosition = false + this.context.lineTo(this.moveX, this.moveY) + this.pushData(position); + break; + case TouchType.Up: + this.endX = event.touches[0].x + this.endY = event.touches[0].y + position.isFirstPosition = false; + position.positionX = this.endX; + position.positionY = this.endY; + position.isEndPosition = true + this.context.stroke() + this.pushData(position); + break; + default: + break; + } + } + ``` # 10.笔记撤回 笔迹绘制时已经记录了所有笔迹上的坐标点,点击“撤回”按钮后,对记录的坐标点进行倒序删除,当删除最后一笔绘制的起始点坐标后停止删除,然后清空画布对剩余的坐标点进行重新绘制,至此撤回操作完成。此功能在index.ets中实现,代码如下: -1. 添加撤回事件 - - ``` - Image($r('app.media.goback')).width(70).height(70).position({ x: 30, y: 0 }) - .onClick((event: ClickEvent) =>{ - this.goBack() - }) - ``` - -2. 编写撤回事件 - - ``` - // 撤回上一笔绘制 - goBack() { - if (this.positionList.length > 0) { - for (let i = this.positionList.length - 1; i > -1; i--) { - if (this.positionList[i].isFirstPosition) { - this.positionList.pop(); - this.redraw(); - break; - } else { - this.positionList.pop(); - } - } - // 保存点位信息 - this.kvStoreModel.put(CHANGE_POSITION, JSON.stringify(this.positionList)); - } - } - ``` +1. 添加撤回事件 + + ``` + Image($r('app.media.goback')).width(100).height(100).margin({left:10}) + .onClick(() =>{ + this.goBack() + }) + ``` + +2. 编写撤回事件 + + ``` + // 撤回上一笔绘制 + goBack() { + if (this.positionList.length > 0) { + for (let i = this.positionList.length - 1; i > -1; i--) { + if (this.positionList[i].isFirstPosition) { + this.positionList.pop(); + this.redraw(); + break; + } else { + this.positionList.pop(); + } + } + // 保存点位信息 + this.kvStoreModel.put(CHANGE_POSITION, JSON.stringify(this.positionList)); + } + } + ``` # 11.轨迹同步 @@ -678,162 +773,171 @@ Path组件所在的Flex容器组件添加点击事件,并实现记录触控点  -1. 设备拉起时,通过positionList将笔迹数据发送给目标设备,目标设备在aboutToAppear时将接收到的笔迹数据用Path绘制出来,从而实现笔迹的同步。此功能在index.ets中实现,关键代码如下: +1. 设备拉起时,通过positionList将笔迹数据发送给目标设备,目标设备在aboutToAppear时将接收到的笔迹数据用Canvas绘制出来,从而实现笔迹的同步。此功能在index.ets中实现,关键代码如下: - 发送端 + 发送端 - ``` - startAbilityContinuation(deviceId: string, deviceName: string,positionList: any[] ) { - // 参数 - var params = { - // 点位集合 - positionList: JSON.stringify(positionList) - } - // 拉起的设备、ability信息等 - var wantValue = { - bundleName: 'com.huawei.cookbook', - abilityName: 'com.huawei.distributedatabasedrawetsopenh.MainAbility', - deviceId: deviceId, - parameters: params - }; - // 分布式拉起 - featureAbility.startAbility({ - want: wantValue - }).then((data) => { - console.info('DrawBoard[IndexPage] featureAbility.startAbility finished, ' + JSON.stringify(data)); - }); - } - ``` - - 接收端 - - ``` - // 函数在创建自定义组件的新实例后,在执行其build函数之前执行 - async aboutToAppear() { - console.info('DrawBoard[IndexPage] aboutToAppear begin'); + ``` + startAbilityContinuation(deviceId: string, deviceName: string,positionList: any[] ) { + // 参数 + var params = { + // 点位集合 + positionList: JSON.stringify(positionList) + } + // 拉起的设备、ability信息等 + var wantValue = { + bundleName: 'com.huawei.cookbook', + abilityName: 'com.huawei.distributedatabasedrawetsopenh.MainAbility', + deviceId: deviceId, + parameters: params + }; + // 分布式拉起 + featureAbility.startAbility({ + want: wantValue + }).then((data) => { + console.info('DrawBoard[IndexPage] featureAbility.startAbility finished, ' + JSON.stringify(data)); + }); + } + ``` + + 接收端 + + ``` + //函数在创建自定义组件的新实例后,在执行其build函数之前执行 + async aboutToAppear() { + // 申请分布式权限 + this.grantPermission() + console.log('DrawBoard[IndexPage] aboutToAppear begin'); this.initialData = [] let self = this - // 获取接收的数据 + //获取初被拉起时候传来的点位信息 await featureAbility.getWant() .then((Want) => { self.positionList = JSON.parse(Want.parameters.positionList) + console.log('Operation successful. self.positionList: ' + JSON.stringify(self.positionList.length)); }).catch((error) => { - console.error('Operation failed. Cause: ' + JSON.stringify(error)); + console.error('Operation failed. Cause: ' + JSON.stringify(error)); + }) + console.log('DrawBoard[IndexPage] aboutToAppear positionList length=' + self.positionList.length); + if (self.positionList.length > 0) { + self.positionList.forEach((num) => { + self.initialData.push(num); + }); + console.log('DrawBoard[IndexPage] aboutToAppear initialData='+JSON.stringify(self.initialData)) + // 根军传来的点位信息初始化画布中的手写笔画路径 + self.initDraw(); + } + console.log('DrawBoard[IndexPage] setDataChangeListener out setDataChangeListener') + // 监听分布式数据库中数据变化,并根据数据变化重新绘制路径 + self.kvStoreModel.setDataChangeListener((data) => { + console.log('DrawBoard[IndexPage] setDataChangeListener come in') + self.positionList = []; + data.updateEntries.forEach((num) => { + const list = JSON.parse(num.value.value); + console.log('DrawBoard[IndexPage] setDataChangeListener list=' + JSON.stringify(list)) + if(list.length === 0) { + console.log('DrawBoard[IndexPage] setDataChangeListener list.length === 0') + } else{ + list.forEach((num) => { + self.positionList.push(num); + }) + console.log('DrawBoard[IndexPage] setDataChangeListener positionList=' + JSON.stringify(self.positionList)) + } + self.redraw(); + }); + }); + } + ... + // 初始化画板轨迹 + initDraw() { + this.initialData.forEach((point)=>{ + if(point.isFirstPosition) { + this.context.beginPath() + this.context.lineWidth = 4 + this.context.lineJoin = 'miter' + this.context.moveTo(point.positionX, point.positionY) + } else{ + this.context.lineTo(point.positionX, point.positionY) + if(point.isEndPosition) { + this.context.stroke() + console.log('DrawBoard[IndexPage] initDraw context.stroke') + } + } }) - //如果传来的点位集合不为空 - if (this.positionList.length > 0) { - this.positionList.forEach((num) => { - this.initialData.push(num); - }); - // 初始化绘制方法 - this.initDraw(); - } - // 监听分布式数据库中点位信息变化 - this.kvStoreModel.setDataChangeListener((data) => { - self.positionList = []; - data.updateEntries.forEach((num) => { - const list = JSON.parse(num.value.value); - console.info('DrawBoard[IndexPage] setDataChangeListener list=' + JSON.stringify(list)) - if(list.length === 0) { - self.pathCommands = DEfAULT_PATH_COMMAND - } else{ - // 如果不为空,则将数据库中的点位信息添加到页面中 - list.forEach((num) => { - self.positionList.push(num); - }) - } - setTimeout(function() { - // 重绘路径 - self.redraw(); - }, 10); - }); - }); - } - ... - // 初始化画板轨迹 - initDraw() { - const self = this; - self.pathCommands = DEfAULT_PATH_COMMAND - this.intervalID = setInterval(function() { - if (self.initialData[0].isFirstPosition) { - self.pathCommands += ' M' + self.initialData[0].positionX + ' ' + self.initialData[0].positionY - } else { - self.pathCommands += ' L' + self.initialData[0].positionX + ' ' + self.initialData[0].positionY - } - self.initialData.shift(); - if (self.initialData.length < 1) { - clearInterval(self.intervalID); - self.intervalID = 0; - } - }, 10); - } - ``` - -2. 笔迹绘制时,为了避免分布式数据库写入过于频繁,需要使用定时器setInterval,笔迹数据每100ms写入一次。其他设备通过订阅分布式数据更新通知来获取最新的笔迹数据,然后重新绘制笔迹,从而实现笔迹同步。此功能在index.js中实现,关键代码如下: - - 发送端 - - ``` - // 将绘制笔迹写入分布式数据库 - pushData(position) { - this.isNeedSync = true; - this.positionList.push(position); - const self = this; - - // 使用定时器每100ms写入一次 - if (this.intervalID === 0) { - this.intervalID = setInterval(function() { - if (self.isNeedSync) { - self.kvStoreModel.put(CHANGE_POSITION, JSON.stringify(self.positionList)); - self.isNeedSync = false; - } - }, DATA_PUT_DELAY); - } - }, - ``` - - 接收端 - - ``` - onInit() { - ... - // 订阅分布式数据更新通知 - this.kvStoreModel.setDataChangeListener((data) => { - data.updateEntries.forEach((num) => { - this.positionList = []; - const list = JSON.parse(num.value.value); - list.forEach((num) => { - this.positionList.push(num); - }) - const self = this; - setTimeout(function() { - self.redraw(); - }, DRAW_DELAY); - }); - }); - }, - // 重新绘制笔迹 - redraw() { - // 清空画布 - this.ctx.clearRect(0, 0, CLEAR_WIDTH, CLEAR_HEIGHT); - this.positionList.forEach((num) => { - if (num.isStartPosition) { - this.ctx.beginPath(); - this.ctx.moveTo(num.positionX, num.positionY); - } else { - this.ctx.lineTo(num.positionX, num.positionY); - this.ctx.stroke(); - } - }); - }, - ``` + } + ``` + +2. 笔迹绘制时,为了避免分布式数据库写入过于频繁,我们设置为每画完一笔,将点位数据提交到数据库。其他设备通过订阅分布式数据更新通知来获取最新的笔迹数据,然后重新绘制笔迹,从而实现笔迹同步。此功能在index.js中实现,关键代码如下: + + 发送端 + + ``` + // 将绘制笔迹写入分布式数据库 + pushData(position: any) { + this.positionList.push(position); + console.log('DrawBoard[IndexPage] pushData positionList 1 =' + JSON.stringify(this.positionList.length)); + // 如果为最后手指离开屏幕的点,则写入数据库 + if(position.isEndPosition){ + this.kvStoreModel.put(CHANGE_POSITION, JSON.stringify(this.positionList)); + console.log('DrawBoard[IndexPage] pushData positionList 2 =' + JSON.stringify(this.positionList.length)); + } + } + ``` + + 接收端: + + ``` + onInit() { + ... + // 订阅分布式数据更新通知 + this.kvStoreModel.setDataChangeListener((data) => { + console.log('DrawBoard[IndexPage] setDataChangeListener come in') + self.positionList = []; + data.updateEntries.forEach((num) => { + const list = JSON.parse(num.value.value); + console.log('DrawBoard[IndexPage] setDataChangeListener list=' + JSON.stringify(list)) + if(list.length === 0) { + console.log('DrawBoard[IndexPage] setDataChangeListener list.length === 0') + } else{ + list.forEach((num) => { + self.positionList.push(num); + }) + console.log('DrawBoard[IndexPage] setDataChangeListener positionList=' + JSON.stringify(self.positionList)) + } + self.redraw(); + }); + }); + }, + // 重新绘制笔迹 + redraw() { + console.log('DrawBoard[IndexPage] redraw positionList= ' + JSON.stringify(this.positionList)) + this.context.clearRect(0,0, this.context.width,this.context.height) + if (this.positionList.length > 0 ) { + this.positionList.forEach((num) => { + console.log('DrawBoard[IndexPage] redraw num=') + console.log('DrawBoard[IndexPage] redraw out isFirstPosition=' + num.isFirstPosition) + if (num.isFirstPosition) { + console.log('DrawBoard[IndexPage] redraw isFirstPosition=' + num.isFirstPosition) + this.context.beginPath() + this.context.lineWidth = 4 + this.context.lineJoin = 'miter' + this.context.moveTo(num.positionX, num.positionY) + console.log('DrawBoard[IndexPage] redraw context.moveTo' + num.positionX+','+ num.positionY) + } else { + this.context.lineTo(num.positionX, num.positionY) + console.log('DrawBoard[IndexPage] redraw context.lineTo' + num.positionX+','+ num.positionY) + if(num.isEndPosition) { + this.context.stroke() + console.log('DrawBoard[IndexPage] redraw context.stroke') + } + } + }); + } + } + ``` 3. 笔迹撤回时,直接将撤回后的笔迹数据写入分布式数据库,其他设备也是通过订阅分布式数据更新通知来获取最新的笔迹数据,最终实现笔迹同步,这里不再做讲解。 # 12.恭喜你 -通过本教程的学习,您已经学会使用基于TS扩展的声明式开发范式中的分布式数据管理和分布式拉起以及利用Path组件绘制图形。 - -# 13.参考 - -[gitee地址](https://gitee.com/openharmony/codelabs/tree/master/Distributed/DistributeDatabaseDrawEts) \ No newline at end of file +通过本教程的学习,您已经学会使用基于TS扩展的声明式开发范式中的分布式数据管理和分布式拉起以及利用Canvas组件绘制图形。 diff --git a/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/model/KvStoreModel.ts b/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/model/KvStoreModel.ts index ce8d65984b14b02a14546a9c37c240eb565fd725..4c407fc0dc651b2d0e89e9329dc3ad74a560fbdb 100644 --- a/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/model/KvStoreModel.ts +++ b/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/model/KvStoreModel.ts @@ -51,6 +51,15 @@ export default class KvStoreModel { self.kvManager.getKVStore(STORE_ID, options).then((store) => { console.info('DrawBoard[KvStoreModel] getKVStore success, kvStore=' + store); self.kvStore = store; + try { + self.kvStore.enableSync(true).then((err) => { + console.log('enableSync success'); + }).catch((err) => { + console.log('enableSync fail ' + JSON.stringify(err)); + }); + }catch(e) { + console.log('EnableSync e ' + e); + } callback(); }); console.info('DrawBoard[KvStoreModel] kvManager.getKVStore end'); @@ -100,7 +109,7 @@ export default class KvStoreModel { let self = this; this.createKvStore(() => { console.info('DrawBoard[KvStoreModel] kvStore.on(dataChange) begin'); - self.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data: any) => { + self.kvStore.on('dataChange', 2, (data: any) => { console.info('DrawBoard[KvStoreModel] dataChange, ' + JSON.stringify(data)); console.info('DrawBoard[KvStoreModel] dataChange, insert ' + data.insertEntries.length + ' udpate ' + data.updateEntries.length); diff --git a/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/pages/index.ets b/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/pages/index.ets index 6464bb2dd9bdccdd394482abc457561fbb8abd8f..88d663e81749f2634f3adb0f7f3bd5bfe377d2e5 100644 --- a/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/pages/index.ets +++ b/Distributed/DistributeDatabaseDrawEts/entry/src/main/ets/MainAbility/pages/index.ets @@ -4,7 +4,6 @@ import KvStoreModel from '../model/KvStoreModel'; import RemoteDeviceModel from '../model/RemoteDeviceModel'; var DEVICE_LIST_LOCALHOST = { name: '本机', id: 'localhost' }; const CHANGE_POSITION = 'change_position'; -const DEfAULT_PATH_COMMAND = ''; @CustomDialog struct CustomDialogExample { @@ -73,21 +72,20 @@ struct CustomDialogExample { @Entry @Component struct Index { - @State startX: number = 0 - @State startY: number = 0 - @State moveX: number = 0 - @State moveY: number = 0 - @State endX: number = 0 - @State endY: number = 0 - @State pathCommands: string = DEfAULT_PATH_COMMAND + private startX: number = 0 + private startY: number = 0 + private moveX: number = 0 + private moveY: number = 0 + private endX: number = 0 + private endY: number = 0 @State deviceList: any[] = [] private BUNDLE_NAME: string = "com.huawei.cookbook"; private kvStoreModel: KvStoreModel = new KvStoreModel() private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel() - @State positionList: any[] = [] - @State initialData: any[] = [] - private isNeedSync: boolean = false - private intervalID: number = 0 + private positionList: any[] = [] + private initialData: any[] = [] + private settings: RenderingContextSettings = new RenderingContextSettings(true) + private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) dialogController: CustomDialogController = new CustomDialogController({ builder: CustomDialogExample({ cancel: this.onCancel, confirm: this.onAccept, deviceList: this.deviceList,positionList: this.positionList,startAbility: this.startAbilityContinuation }), @@ -119,33 +117,35 @@ struct Index { this.onContinueAbilityClick() }) }.backgroundColor(Color.Grey).width('100%').height('10%') - Flex() { - Path().commands(this.pathCommands).strokeWidth(4).fill('none').stroke(Color.Black) + Row() { + Canvas(this.context) .width('100%') .height('100%') + .backgroundColor('#FFFFFF') }.onTouch((event: TouchEvent) => { this.onTouchEvent(event) }).width('100%').height('90%') }.height('100%').width('100%') } // 函数在创建自定义组件的新实例后,在执行其build函数之前执行 - aboutToAppear() { + async aboutToAppear() { this.grantPermission() console.log('DrawBoard[IndexPage] aboutToAppear begin'); this.initialData = [] let self = this - featureAbility.getWant() + await featureAbility.getWant() .then((Want) => { self.positionList = JSON.parse(Want.parameters.positionList) console.log('Operation successful. self.positionList: ' + JSON.stringify(self.positionList.length)); }).catch((error) => { - console.error('Operation failed. Cause: ' + JSON.stringify(error)); - }) - console.log('DrawBoard[IndexPage] aboutToAppear positionList length=' + this.positionList.length); + console.error('Operation failed. Cause: ' + JSON.stringify(error)); + }) + console.log('DrawBoard[IndexPage] aboutToAppear positionList length=' + self.positionList.length); if (self.positionList.length > 0) { self.positionList.forEach((num) => { self.initialData.push(num); }); + console.log('DrawBoard[IndexPage] aboutToAppear initialData='+JSON.stringify(self.initialData)) self.initDraw(); } console.log('DrawBoard[IndexPage] setDataChangeListener out setDataChangeListener') @@ -157,16 +157,13 @@ struct Index { console.log('DrawBoard[IndexPage] setDataChangeListener list=' + JSON.stringify(list)) if(list.length === 0) { console.log('DrawBoard[IndexPage] setDataChangeListener list.length === 0') - self.pathCommands = DEfAULT_PATH_COMMAND } else{ list.forEach((num) => { self.positionList.push(num); }) console.log('DrawBoard[IndexPage] setDataChangeListener positionList=' + JSON.stringify(self.positionList)) } - setTimeout(function() { - self.redraw(); - }, 10); + self.redraw(); }); }); } @@ -179,40 +176,43 @@ struct Index { } // 初始化画板轨迹 initDraw() { - let self = this; - self.pathCommands = '' - self.intervalID = setInterval(function() { - if (self.initialData[0].isFirstPosition) { - self.pathCommands += ' M' + self.initialData[0].positionX + ' ' + self.initialData[0].positionY - console.log('DrawBoard[IndexPage] initDraw pathCommands=' + self.pathCommands) - - } else { - self.pathCommands += ' L' + self.initialData[0].positionX + ' ' + self.initialData[0].positionY - console.log('DrawBoard[IndexPage] initDraw pathCommands=' + self.pathCommands) - } - self.initialData.shift(); - - if (self.initialData.length < 1) { - clearInterval(self.intervalID); - self.intervalID = 0; + this.initialData.forEach((point)=>{ + if(point.isFirstPosition) { + this.context.beginPath() + this.context.lineWidth = 4 + this.context.lineJoin = 'miter' + this.context.moveTo(point.positionX, point.positionY) + } else{ + this.context.lineTo(point.positionX, point.positionY) + if(point.isEndPosition) { + this.context.stroke() + console.log('DrawBoard[IndexPage] initDraw context.stroke') + } } - }, 10); + }) } // 轨迹重绘制 redraw() { console.log('DrawBoard[IndexPage] redraw positionList= ' + JSON.stringify(this.positionList)) - this.pathCommands = DEfAULT_PATH_COMMAND + this.context.clearRect(0,0, this.context.width,this.context.height) if (this.positionList.length > 0 ) { this.positionList.forEach((num) => { console.log('DrawBoard[IndexPage] redraw num=') console.log('DrawBoard[IndexPage] redraw out isFirstPosition=' + num.isFirstPosition) if (num.isFirstPosition) { console.log('DrawBoard[IndexPage] redraw isFirstPosition=' + num.isFirstPosition) - this.pathCommands += ' M' + num.positionX + ' ' + num.positionY - console.log('DrawBoard[IndexPage] redraw pathCommands=' + this.pathCommands) + this.context.beginPath() + this.context.lineWidth = 4 + this.context.lineJoin = 'miter' + this.context.moveTo(num.positionX, num.positionY) + console.log('DrawBoard[IndexPage] redraw context.moveTo' + num.positionX+','+ num.positionY) } else { - this.pathCommands += ' L' + num.positionX + ' ' + num.positionY - console.log('DrawBoard[IndexPage] redraw pathCommands=' + this.pathCommands) + this.context.lineTo(num.positionX, num.positionY) + console.log('DrawBoard[IndexPage] redraw context.lineTo' + num.positionX+','+ num.positionY) + if(num.isEndPosition) { + this.context.stroke() + console.log('DrawBoard[IndexPage] redraw context.stroke') + } } }); } @@ -224,7 +224,6 @@ struct Index { for (let i = this.positionList.length - 1; i > -1; i--) { if (this.positionList[i].isFirstPosition) { this.positionList.pop(); - this.redraw(); break; } else { this.positionList.pop(); @@ -287,46 +286,46 @@ struct Index { case TouchType.Down: this.startX = event.touches[0].x this.startY = event.touches[0].y - this.pathCommands += ' M' + this.startX + ' ' + this.startY position.isFirstPosition = true; position.positionX = this.startX; position.positionY = this.startY; + position.isEndPosition = false + this.context.beginPath() + this.context.lineWidth = 4 + this.context.lineJoin = 'miter' + this.context.moveTo(this.startX, this.startY) this.pushData(position); break; case TouchType.Move: this.moveX = event.touches[0].x this.moveY = event.touches[0].y - this.pathCommands += ' L' + this.moveX + ' ' + this.moveY position.isFirstPosition = false; position.positionX = this.moveX; position.positionY = this.moveY; + position.isEndPosition = false + this.context.lineTo(this.moveX, this.moveY) this.pushData(position); break; case TouchType.Up: this.endX = event.touches[0].x this.endY = event.touches[0].y position.isFirstPosition = false; - position.positionX = this.moveX; - position.positionY = this.moveY; + position.positionX = this.endX; + position.positionY = this.endY; + position.isEndPosition = true + this.context.stroke() this.pushData(position); break; default: - break + break; } } pushData(position: any) { - this.isNeedSync = true; this.positionList.push(position); console.log('DrawBoard[IndexPage] pushData positionList 1 =' + JSON.stringify(this.positionList.length)); - let self = this; - if (this.intervalID === 0) { - this.intervalID = setInterval(function () { - if (self.isNeedSync) { - self.kvStoreModel.put(CHANGE_POSITION, JSON.stringify(self.positionList)); - console.log('DrawBoard[IndexPage] pushData positionList 2 =' + JSON.stringify(this.positionList.length)); - self.isNeedSync = false; - } - }, 100); + if(position.isEndPosition){ + this.kvStoreModel.put(CHANGE_POSITION, JSON.stringify(this.positionList)); + console.log('DrawBoard[IndexPage] pushData positionList 2 =' + JSON.stringify(this.positionList.length)); } } } \ No newline at end of file
方法
说明
IoTPwmInit (unsigned int port)
初始化PWM设备。
IoTPwmDeinit (unsigned int port)
去初始化PWM设备。
IoTPwmStart (unsigned int port, unsigned short duty, unsigned int freq)
根据给定的输出频率和占空比,从指定端口启动PWM信号输出。
IoTPwmStop (unsigned int port)
停止指定端口的PWM信号输出。
模块
控制管脚
PWM
蜂鸣器
GPIO9
PWM0
红灯
GPIO10
PWM1