• OpenHarmony 串口服务访问


    项目介绍

    本文档是在eTS项目hap包中实现串口访问的使用说明,通过JS接口开放给上层应用使用。

    一、开发环境准备

    安装OpenHarmony SDK

    1. 在DevEco Studio菜单栏选择Tools->SDK Manager

    2. OpenHarmony SDK选项中选择配备API版本进行安装

    二、创建eTS项目

    创建支持Native C++的eTS项目

    三、NAPI库相关

    生成串口NAPI库

    1. 添加文件src/main/cpp/types/libserialhelper/serialhelper.d.ts
    /*
     * Copyright (C) 2021-2022 Huawei Device Co., Ltd.
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    import {AsyncCallback, Callback} from "basic";
    declare namespace serialHelper {
      /**
       * Open serial port.
       * @param dev Indicates the serial port dev.
       */
      function openSerial(dev:string, callback: AsyncCallback<void>): void;
      function openSerial(dev:string): Promise<void>;
    
      /**
       * Close serial port.
       * @param dev Indicates the serial port dev.
       */
      function closeSerial(dev:string, callback: AsyncCallback<void>): void;
      function closeSerial(dev:string): Promise<void>;
    }
    export default serialHelper;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    2. 添加文件src/main/cpp/types/libserialhelper/package.json
    {
      "name": "libserialhelper.so",
      "types": "./serialhelper.d.ts"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    3. 根据serialhelper.d.ts文件生成对应的c++源码

    方式一:手动编写src/main/cpp/serial_helper.cpp

    struct AsyncCallInfo{
        napi_env env = nullptr;
        napi_ref callbackRef = nullptr;
        napi_deferred deferred = nullptr;
        napi_async_work work = nullptr;
        void *data = nullptr;
    };
    static void AsyncCallFinish(AsyncCallInfo* asyncCallInfo, int32_t result, napi_value *asyncResult)
    {
        if (asyncCallInfo->deferred) {
            if (result == 0) {
                napi_resolve_deferred(asyncCallInfo->env, asyncCallInfo->deferred,
                        asyncResult[1]==nullptr?asyncResult[0]:asyncResult[1]);
            } else {
                napi_reject_deferred(asyncCallInfo->env, asyncCallInfo->deferred, asyncResult[0]);
            }
        } else {
            napi_value callback = nullptr;
            napi_get_reference_value(asyncCallInfo->env, asyncCallInfo->callbackRef, &callback);
            napi_call_function(asyncCallInfo->env, nullptr, callback, CALLBACK_ARGV_CNT, asyncResult, nullptr);
            napi_delete_reference(asyncCallInfo->env, asyncCallInfo->callbackRef);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    static napi_value Call_OpenSerial(napi_env env, napi_callback_info info)
    {
        size_t argc = 0;
        napi_value args[DEFAULT_ARG_COUNT] = {0};
        napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
        ...
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "x_napi_tool", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName,
                [](napi_env env, void* data) {
                    AsyncCallInfo* asyncCallInfo = (AsyncCallInfo*)data;
                    OpenSerialValue* openValue = (OpenSerialValue*)asyncCallInfo->data;
                    //openValue->out = SerialClient::GetInstance()->OpenSerial(openValue->dev);
                },
                [](napi_env env, napi_status status, void* data) {
                    AsyncCallInfo* asyncCallInfo = (AsyncCallInfo*)data;
                    OpenSerialValue* openValue = (OpenSerialValue*)asyncCallInfo->data;
                    napi_value asyncResult[CALLBACK_ARGV_CNT]={nullptr, nullptr};
                    napi_create_int32(env, openValue->out, &asyncResult[0]);
                    AsyncCallFinish(asyncCallInfo, openValue->out,asyncResult);
                    napi_delete_async_work(env, asyncCallInfo->work);
                    delete openValue;
                    delete asyncCallInfo;
                },
                (void*)asyncCallInfo, &asyncCallInfo->work);
        napi_queue_async_work(env, asyncCallInfo->work);
        return retValue;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    方式二:使用NAPI框架生成工具生成 工具链接

    1)将serialhelper.d.ts、basic.d.ts复制到同一目录中,创建out目录

    2)执行./napi_generator-linux -f serialhelper.d.ts -o out

    3)将生成的源码文件复制到src/main/cpp

    4. make文件:src/main/cpp/CMakeList.txt
    # the minimum version of CMake.
    cmake_minimum_required(VERSION 3.4.1)
    project(XComponent)
    
    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    
    include_directories(${NATIVERENDER_ROOT_PATH})
    add_library(serialhelper SHARED serial_helper.cpp)
    target_link_libraries(serialhelper PUBLIC libace_napi.z.so libc++.a)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    5. 添加项目依赖

    entry/package.json

    "devDependencies": {
    	"@types/libserialhelper.so": "file:./src/main/cpp/types/libserialhelper"
    }
    
    
    • 1
    • 2
    • 3
    • 4

    entry/package-lock.json

    "dependencies": {
       "@types/libserialhelper.so": {
          "version": "file:src/main/cpp/types/libserialhelper",
          "dev": true
       }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    6. 编译生成

    修改编译项entry/build-profile.json5:

    "buildOption": {
      "externalNativeOptions": {
        "path": "./src/main/cpp/CMakeLists.txt",
        "arguments": "-v -DOHOS_STL=c++_shared",
        "abiFilters": [
          "armeabi-v7a",
        ],
        "cppFlags": "",
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、实现串口异步回调

    添加串口IPC客户端libserialport_service_api.z.so库,并且实现具体的异步回调功能

    1. 将libserialport_service_api.z.so复制到entry/libs/armeabi-a7v目录

    2. 将库的头文件复制到entry/src/main/cpp/include目录

    3. 继承SerialCallbackBase类,实现串口数据异步回调SerialAsyncCallback

      class SerialAsyncCallback: public SerialCallbackBase {
      public:
          SerialAsyncCallback() = default;
          ~SerialAsyncCallback();
      
          // 通知回调事件
          void OnCallBackEvent() override;
          // 接收到串口数据
          void OnRecvData(const uint8_t *buffer, uint32_t length) override;
          ...
      };
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    4. 修改src/main/cpp/CMakeList.txt文件

      # the minimum version of CMake.
      cmake_minimum_required(VERSION 3.4.1)
      project(XComponent)
      
      set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
      
      include_directories(${NATIVERENDER_ROOT_PATH}
                          ${NATIVERENDER_ROOT_PATH}/include
                          )
      
      link_directories(${NATIVERENDER_ROOT_PATH}/../../../libs/${CMAKE_OHOS_ARCH_ABI})
      add_library(serialhelper SHARED serial_helper.cpp x_napi_tool.cpp serial_async_callback.cpp)
      target_link_libraries(serialhelper PUBLIC libace_napi.z.so libc++.a libhilog_ndk.z.so libuv.so libserialport_service_api.z.so)
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    5. 在napi函数中调用api函数,使用NAPI框架生成工具生成OpenSerial代码,如下:

      struct OpenSerial_value_struct {
          std::string in0;
          int32_t out;
      };
      
      void OpenSerial_execute(XNapiTool *pxt, void *data)
      {
          OpenSerial_value_struct *vio = (OpenSerial_value_struct *)data;
          vio->out = get_serial_client()->OpenSerial(vio->in0);
      }
          
      void OpenSerial_complete(XNapiTool *pxt, void *data)
      {
          OpenSerial_value_struct *vio = (OpenSerial_value_struct *)data;
          napi_value result = nullptr;
          result = NUMBER_C_2_JS(pxt, Int32, vio->out);
          {
              napi_value args[XNapiTool::ARGV_CNT] = {result, nullptr};
              pxt->FinishAsync(vio->out, args);
          }
          delete vio;
      }
      napi_value OpenSerial_middle(napi_env env, napi_callback_info info)
      {
          XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
          if (pxt->IsFailed()) {
              napi_value err = pxt->GetError();
              delete pxt;
              return err;
          }
      
          struct OpenSerial_value_struct *vio = new OpenSerial_value_struct();
          pxt->SwapJs2CUtf8(pxt->GetArgv(0), vio->in0);
          napi_value result = pxt->StartAsync(OpenSerial_execute, vio, OpenSerial_complete,
              pxt->GetArgc() == 2 ? pxt->GetArgv(1) : nullptr);
          if (pxt->IsFailed()) {
              result = pxt->GetError();
          }
          return result;
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
    6. 模块注册

      static napi_value init(napi_env env, napi_value exports)
      {
          std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, exports);
          //js函数与C++函数映射
          pxt->DefineFunction("setOptions", OHOS::SerialPort::SetOptions_middle);
          pxt->DefineFunction("openSerial", OHOS::SerialPort::OpenSerial_middle);
          pxt->DefineFunction("closeSerial", OHOS::SerialPort::CloseSerial_middle);
          pxt->DefineFunction("clearBuffer", OHOS::SerialPort::ClearBuffer_middle);
          pxt->DefineFunction("sendData", OHOS::SerialPort::SendData_middle);
          pxt->DefineFunction("recvData", OHOS::SerialPort::RecvData_middle);
          pxt->DefineFunction("transmit", OHOS::SerialPort::Transmit_middle);
          pxt->DefineFunction("on", OHOS::SerialPort::on_middle);
          pxt->DefineFunction("off", OHOS::SerialPort::off_middle);
          pxt->DefineFunction("setGPIODirection", OHOS::SerialPort::setGPIODirection_middle);
          pxt->DefineFunction("setGPIOValue", OHOS::SerialPort::setGPIOValue_middle);
          pxt->DefineFunction("getGPIOValue", OHOS::SerialPort::getGPIOValue_middle);
          return exports;
      }
      
      static napi_module g_serialHelper_Module = {
          .nm_version = 1,
          .nm_flags = 0,
          .nm_filename = nullptr,
          .nm_register_func = init,
          .nm_modname = "serialhelper",
          .nm_priv = ((void *)0),
          .reserved = {(void *)0},
      };
      
      extern "C" __attribute__((constructor)) void Register_serialHelper_Module(void)
      {
          napi_module_register(&g_serialHelper_Module);
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
    7. eTS调用接口验证

        import serialHelper from "libserialhelper.so"
        ...
        //打开串口this.tty /dev/ttyXRUSB0
        serialHelper.openSerial(this.tty).then(()=>{
          HiLog.i(TAG, "serial openSerial " + this.tty + " success")
          this.status = '开'
        }).catch((error)=> {
          HiLog.i(TAG, "openSerial " + this.tty + " failed:" + error)
        });
        ...
        //设置为异步
        serialHelper.on("/dev/ttyXRUSB0", (data) => {
          var dataString = "";
          for (var i = 0; i < data.length; i++) {
            dataString += String.fromCharCode(data[i]);
          }
          HiLog.i(TAG, "ttyXRUSB0 len:" + data.length + " data:" + dataString);
        })
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    应用启动后点击"打开/dev/ttyXRUSB0"按钮查看输出日志,出现serialport_client与serial_service_impl标志,表示访问串口服务成功

    为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

    《鸿蒙开发学习手册》:

    入门必看:https://qr21.cn/FV7h05

    1. 应用开发导读(ArkTS)
    2. ……

    HarmonyOS 概念:https://qr21.cn/FV7h05

    1. 系统定义
    2. 技术架构
    3. 技术特性
    4. 系统安全

    如何快速入门?:https://qr21.cn/FV7h05

    1. 基本概念
    2. 构建第一个ArkTS应用
    3. 构建第一个JS应用
    4. ……

    开发基础知识:https://qr21.cn/FV7h05

    1. 应用基础知识
    2. 配置文件
    3. 应用数据管理
    4. 应用安全管理
    5. 应用隐私保护
    6. 三方应用调用管控机制
    7. 资源分类与访问
    8. 学习ArkTS语言
    9. ……

    基于ArkTS 开发:https://qr21.cn/FV7h05

    1.Ability开发
    2.UI开发
    3.公共事件与通知
    4.窗口管理
    5.媒体
    6.安全
    7.网络与链接
    8.电话服务
    9.数据管理
    10.后台任务(Background Task)管理
    11.设备管理
    12.设备使用信息统计
    13.DFX
    14.国际化开发
    15.折叠屏系列
    16.……

  • 相关阅读:
    TexStudio使用教程
    04 Spring MVC 源码总结 - 启动流程
    新一轮消费浪潮席卷而来,中国科技驱动行业新发展
    【Python从入门到进阶】38、selenium关于Chrome handless的基本使用
    【仿牛客网笔记】 Spring Boot进阶,开发社区核心功能-事务管理
    探索 Scikit-learn:Python 机器学习初级篇
    FPGA工程师面试试题集锦31~40
    Day24力扣打卡
    嵌入式系统测试思路
    提高 K8S 容器运行时的可观察性最佳方法之一
  • 原文地址:https://blog.csdn.net/weixin_61845324/article/details/136254407