#冲刺创作新星#【FFH】Bearpi-Micro深入解析通过JS应用控制LED灯 原创 精华

Z·y
发布于 2022-11-1 00:12
浏览
7收藏

@toc

一、前言

最近跑了一遍Bearpi-Micro编写点亮LED灯程序的Demo,深入了解了如何在开发板上运行一个控制LED灯的程序,达到能关闭灯、开启灯以及翻转灯的状态,南向如何编写JS API接口提供驱动服务给北向应用使用。突发奇想,发现了官方给出的点灯应用中的一个不足,并进行优化
参考文章:编写通过JS应用控制LED灯

二、(南向)深入解析通过JS应用控制LED灯

1.前提

请确保已经完成编写一个点亮LED灯程序实验,因为本实验将依赖编写一个点亮LED灯程序实验中开发的驱动,以下教程篇幅较长,请耐心仔细阅读。

2.JS API接口开发

注:以下代码为主要代码的剖析,详细完整的代码可查看参考文章编写通过JS应用控制LED灯

(1)添加控制LED灯的JS API接口

修改foundation\ace\ace_engine_lite\frameworks\src\core\modules\app_module.h,加入toggleLed JS API,

static JSIValue ToggleLed(const JSIValue thisVal, const JSIValue* args, uint8_t argsNum);

("##start##“和”##end##"仅用来标识位置,添加完配置后删除这两行)。

void InitAppModule(JSIValue exports)
{
    JSI::SetModuleAPI(exports, "getInfo", AppModule::GetInfo);
    JSI::SetModuleAPI(exports, "terminate", AppModule::Terminate);
##start##
    JSI::SetModuleAPI(exports, "ledcontrol", AppModule::ToggleLed);
##end##
#ifdef FEATURE_SCREEN_ON_VISIBLE
    JSI::SetModuleAPI(exports, "screenOnVisible", AppModule::ScreenOnVisible);
#endif
}

解析:在头文件中封装好JS API接口函数。
提供给北向的接口名为:==ledcontrol== ,南向业务代码函数为==ToggleLed== 。

(2)编写控制LED灯c++ 业务代码

在foundation\ace\ace_engine_lite\frameworks\src\core\modules\app_module.cpp中加入控制LED灯c++ 业务代码
注:以下代码仅为部分重要代码

                 主函数ToggleLed将会调用的GpioWriteRead函数👇
static int GpioWriteRead(struct HdfIoService *serv, int32_t eventData, int32_t *val)    
//传入驱动服务(已在前面的宏定义),命令eventData,待返回的数据指针val
{
    int ret = HDF_FAILURE;
    struct HdfSBuf *data = HdfSBufObtainDefaultSize();  //获取256字节的容量
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); //获取256字节的容量

    if (data == NULL || reply == NULL) {
        HILOG_ERROR(HILOG_MODULE_ACE, "fail to obtain sbuf data\n");
        return ret;
    }

    if (!HdfSbufWriteUint8(data, (uint8_t)eventData)) { //将8位无符号整数eventData写入data
        HILOG_ERROR(HILOG_MODULE_ACE, "fail to write sbuf\n");
        HdfSBufRecycle(data);
        HdfSBufRecycle(reply);
        return ret;
    }

    ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data,reply); //调度驱动服务函数,传入命令LED_WRITE_READ,传入到驱动的数据指针data,驱动待返回的数据指针reply
    if (ret != HDF_SUCCESS) {
        HILOG_ERROR(HILOG_MODULE_ACE, "fail to send service call\n");
        HdfSBufRecycle(data);
        HdfSBufRecycle(reply);
        return ret;
    }
    if (!HdfSbufReadInt32(reply, val)) { //读取32位整型数reply的值赋给val;如果reply的值为NULL则获取驱动服务的回复失败
        HILOG_ERROR(HILOG_MODULE_ACE, "fail to get service call reply\n");
        ret = HDF_ERR_INVALID_OBJECT;
        HdfSBufRecycle(data);
        HdfSBufRecycle(reply);
        return ret;
    }
    HILOG_ERROR(HILOG_MODULE_ACE, "Get reply is: %d\n", val); //终端打印消息

    HdfSBufRecycle(data);  //释放内存data
    HdfSBufRecycle(reply); //释放内存reply
    return ret;
}

解析:已在代码中注释。
重要的:Dispatch函数,调度驱动服务呼叫,需要传入4个参数:*service-驱动服务对象的指针,cmdId-函数的命令字,*data-想要传入驱动的数据指针,*reply-驱动待回复的数据指针。
::: hljs-center

接口ToggleLed主函数👇

:::

JSIValue AppModule::ToggleLed(const JSIValue thisVal,const JSIValue *args,uint8_t argsNum) //南向接口具体内容,控制LED灯c++ 业务代码
{
    HILOG_ERROR(HILOG_MODULE_ACE, "led button pressed.");

    struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE); //绑定HDF驱动中的LED服务
    if (serv == NULL) {
        HILOG_ERROR(HILOG_MODULE_ACE, "fail to get service2 %s\n", LED_SERVICE);
        return JSI::CreateUndefined();
    }

    if ((args == nullptr) || (argsNum == 0) || (JSI::ValueIsUndefined(args[0]))) {
        return JSI::CreateUndefined();
    }

    JSIValue success = JSI::GetNamedProperty(args[0], CB_SUCCESS); //获取北向接口中的属性
    JSIValue fail = JSI::GetNamedProperty(args[0], CB_FAIL);
    JSIValue complete = JSI::GetNamedProperty(args[0], CB_COMPLETE);
    int32_t num = (int32_t)JSI::GetNumberProperty(args[0], "code"); //获取北向下发的命令code

    int32_t replyData = 0;

    if (GpioWriteRead(serv, num,&replyData)) { //使用上文定义的GpioWriteRead函数,传入驱动服务名,命令num,待返回的数据指针
        HILOG_ERROR(HILOG_MODULE_ACE, "fail to send event\n");
        JSI::CallFunction(fail, thisVal, nullptr, 0);
        JSI::CallFunction(complete, thisVal, nullptr, 0);
        JSI::ReleaseValueList(success, fail, complete);
        return JSI::CreateUndefined();
    }

    JSIValue result = JSI::CreateObject();
    JSI::SetNumberProperty(result, "led_status", replyData); //将replyData的值赋给result

    JSIValue argv[ARGC_ONE] = {result};
    JSI::CallFunction(success, thisVal, argv, ARGC_ONE);
    JSI::CallFunction(complete, thisVal, nullptr, 0);
    JSI::ReleaseValueList(success, fail, complete, result);

    HdfIoServiceRecycle(serv); //销毁led的驱动程序服务对象以释放不再需要的资源。

    return JSI::CreateUndefined();
}

解析:获取北向接口中的属性success、fail、complete、code

JSIValue success = JSI::GetNamedProperty(args[0], CB_SUCCESS); //获取北向接口中的属性
    JSIValue fail = JSI::GetNamedProperty(args[0], CB_FAIL);
    JSIValue complete = JSI::GetNamedProperty(args[0], CB_COMPLETE);
    int32_t num = (int32_t)JSI::GetNumberProperty(args[0], "code"); //获取北向下发的命令code

#冲刺创作新星#【FFH】Bearpi-Micro深入解析通过JS应用控制LED灯-鸿蒙开发者社区
几个功能相似的赋值函数

JSI::SetNumberProperty(result, "led_status", replyData); //将replyData的值赋给result
HdfSbufWriteUint8(data, (uint8_t)eventData) //将8位无符号整数eventData写入data
HdfSbufReadInt32(reply, val)//读取32位整型数reply的值赋给val

主要流程:北向应用调用ledcontrol接口,南向ToggleLed获取ledcontrol接口中的属性,并将属性值通过驱动服务中的Dispatch函数传递进驱动任务中,驱动给出返回值。

三、样例效果

点击应用的开关灯,嗯!挺流畅的。但是,如果我只打开北向应用,通过南向命令行终端打开LED灯,北向应用中灯的状态并不会发生改变。
::: hljs-center

南北向没有达到完美的联动效果。。。

:::
#冲刺创作新星#【FFH】Bearpi-Micro深入解析通过JS应用控制LED灯-鸿蒙开发者社区

四、优化官方的应用样例

分析:
从上文的剖析中可以看出南北向的接口似乎没有优化的地方,北向调用接口,南向就返回灯的状态值给北向。但是我们通过南向命令行终端执行应用程序的时候虽然也使用到了LED的驱动服务,但是并没有将灯的状态值反馈给北向(因为北向没有调用接口,自然就不会有返回值)。
思路:
既然知道了问题可能出在北向的应用方面,那么就着手尝试修改/增加一下,让北向应用中灯的图片一直调用接口就能一直得到返回的状态值。既然需要用到接口就得传参数。经过实验发现,不管传入命令code=0(关灯)、1(开灯)、2(转换灯状态),效果都极不理想。于是乎深入底层了解LED的驱动服务。

::: hljs-center

👇以下为主要代码段

:::

// Dispatch是用来处理用户态发下来的消息
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{//在这里cmdCode一直都是1,data表示传入的开关灯命令,reply表示将要返回灯的状态值
    uint8_t contrl;
    HDF_LOGE("Led driver dispatch");
    if (client == NULL || client->device == NULL)
    {
        HDF_LOGE("Led driver device is NULL");
        return HDF_ERR_INVALID_OBJECT;
    }

    switch (cmdCode)
    {
    /* 接收到用户态发来的LED_WRITE_READ命令 */
    case LED_WRITE_READ:
        /* 读取data里的数据,赋值给contrl */
        HdfSbufReadUint8(data,&contrl);                  
        switch (contrl)                                         //在这里判断传递进来的命令
        {
        /* 开灯 */
        case LED_ON:                                            //*data=1,开灯
            GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
            status = 1;
            break;
        /* 关灯 */
        case LED_OFF:                                           //*data=0,关灯
            GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
            status = 0;
            break;
        /* 状态翻转 */
        case LED_TOGGLE:                                         //*data=2,转换灯
            if(status == 0)
            {
                GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
                status = 1;
            }
            else
            {
                GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
                status = 0;
            }                                        
            break;
        default:                                                //*data=其他值时不做操作
            break;
        }
        /* 把LED的状态值写入reply, 可被带至用户程序 */
        if (!HdfSbufWriteInt32(reply, status))                
        {
            HDF_LOGE("replay is fail");
            return HDF_FAILURE;
        }
        break;
    default:
        break;
    }
    return HDF_SUCCESS;
}

解决方案
既然如此如果我们在北向传入其他命令值,比如3,它同样会调用LED的驱动服务,同样会返回灯的状态值,只是没有进行其他操作。
所以在北向可以多写一个函数为getstatus()👇,发送命令3,单纯只为获得灯的状态值。

getstatus(){
        //if(this.statu=='0')this.statu='1'
        //else this.statu='0'

        try{
            let that=this
            app.ledcontrol({
                code:3,
                success(res){
                    that.statu = res.led_status
                },
                fail(res,code){

                },
                complete(){

                }
            })
        }catch{
            this.openstatus="调用错误"
        }
    },

效果展示
南向点灯的同时,北向的应用也会实时更新灯的状态。完美!
#冲刺创作新星#【FFH】Bearpi-Micro深入解析通过JS应用控制LED灯-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-11-1 00:15:54修改
12
收藏 7
回复
举报
8条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

优秀的打通南北案例

回复
2022-11-1 10:06:39
冰淇淋爱我
冰淇淋爱我

很清晰的思路,学技术就要这样

回复
2022-11-2 18:00:45
Z·y
Z·y 回复了 冰淇淋爱我
很清晰的思路,学技术就要这样

感谢~~

回复
2022-11-3 08:48:12
liurick
liurick

学习下优化思路

回复
2022-11-4 10:46:35
hmyxd
hmyxd

光看教程还是不行,果然还是要有自己的东西。

回复
2022-11-7 10:24:11
Z·y
Z·y 回复了 hmyxd
光看教程还是不行,果然还是要有自己的东西。

谢谢~~

回复
2022-11-7 18:29:25
啃论文俱乐部的欧sir
啃论文俱乐部的欧sir

回复
2022-11-9 14:15:57
qq622f05848a215
qq622f05848a215

你好,请问一下

struct HdfSBuf *data = HdfSBufObtainDefaultSize(); //获取256字节的容量

向这个函数开辟的空间中写入uint8_t类型的数据只能写入64个,这个涉及到了内存对齐的原因吗?

请问一下我想写入256个数据的话要怎么操作啊?

回复
2022-12-8 16:57:42
回复
    相关推荐