自制智能镜之——应用入口及触摸按键实现篇

Posted 三明治开发社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自制智能镜之——应用入口及触摸按键实现篇相关的知识,希望对你有一定的参考价值。


​ ​ ​ ​ 在上一篇文章《自制智能镜之——嵌入式开发环境搭建篇》中介绍了如何在涂鸦平台上创建智能镜和嵌入式开发环境的搭建流程。在搭建完一个实现了配网绑定和 dp 功能点上报下发的基本代码框架后,我们就需要来完成具体的功能实现代码了。在这里给出一个本 demo 的例程,大家可以参照文章一起浏览,也可以在本 demo 例程的基础上添加别的新颖的功能。

应用层入口

​ ​ ​ ​ 打开demo例程,其中的apps文件夹内就是demo的应用代码。应用代码结构如下:

├── src	
|    ├── mirror_driver
|    |    ├── tuya_mirror_pwm.c             //PWM驱动相关文件
|    |    ├── tuya_mirror_key.c             //触摸按键相关代码文件
|    |    └── tuya_mirror_screen.c            //显示屏相关代码文件
|    ├── mirror_soc                   //tuya SDK soc层接口相关文件
|    ├── tuya_device.c             //应用层入口文件
|    ├── tuya_app.c            //主要应用层
|    ├── svc_weather_service.c            //天气服务组件(暂不对外开放)
|    └── tuya_mirror_control.c             //设备功能逻辑
| 
├── include				//头文件目录
|    ├── mirror_driver
|    |    ├── tuya_mirror_pwm.h      
|    |    ├── tuya_mirror_key.h   
|    |    └── tuya_mirror_screen.h         
|    ├── mirror_soc
|    ├── tuya_device.h
|    ├── tuya_app.h
|    ├── svc_weather_service.h
|    └── tuya_mirror_control.h
|
└── output              //编译产物

打开tuya_device.c文件,找到device_init函数:

OPERATE_RET device_init(VOID_T) 
{
  OPERATE_RET op_ret = OPRT_OK;

  TY_IOT_CBS_S wf_cbs = {
      status_changed_cb,\\
      gw_ug_inform_cb,\\
      gw_reset_cb,\\
      dev_obj_dp_cb,\\
      dev_raw_dp_cb,\\
      dev_dp_query_cb,\\
      NULL,
  };

  op_ret = tuya_iot_wf_soc_dev_init_param(WIFI_WORK_MODE_SEL, WF_START_SMART_ONLY, &wf_cbs, NULL, PRODECT_ID, DEV_SW_VERSION);
  if(OPRT_OK != op_ret) {
      PR_ERR("tuya_iot_wf_soc_dev_init_param error,err_num:%d",op_ret);
      return op_ret;
  }

  op_ret = tuya_iot_reg_get_wf_nw_stat_cb(wf_nw_status_cb);
  if(OPRT_OK != op_ret) {
      PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb is error,err_num:%d",op_ret);
      return op_ret;
  }

  op_ret = app_mirror_init(APP_MIRROR_NORMAL);
  if(OPRT_OK != op_ret) {
      PR_ERR("app init err!");
      return op_ret;
  }
  
  return op_ret;
}

​ ​ ​ ​ 在BK7231N平台的SDK环境中,该函数为重要的应用代码入口,设备上电后平台适配层运行完一系列初始化代码后就会调用该函数来进行应用层的初始化操作。

该函数做了三件事:

  • 调用tuya_iot_wf_soc_dev_init_param()接口进行SDK初始化,配置了工作模式、配网模式,同时注册了各种回调函数并存入了PID(代码中宏定义为PRODECT_KEY)。
   TY_IOT_CBS_S wf_cbs = {
       status_changed_cb,\\
       gw_ug_inform_cb,\\
       gw_reset_cb,\\
       dev_obj_dp_cb,\\
       dev_raw_dp_cb,\\
       dev_dp_query_cb,\\
       NULL,
   };

   op_ret = tuya_iot_wf_soc_dev_init_param(WIFI_WORK_MODE_SEL, WF_START_SMART_FIRST, &wf_cbs, NULL, PRODECT_ID, DEV_SW_VERSION);
   if(OPRT_OK != op_ret) {
       PR_ERR("tuya_iot_wf_soc_dev_init_param error,err_num:%d",op_ret);
       return op_ret;
   }
  • 调用tuya_iot_reg_get_wf_nw_stat_cb()接口注册设备网络状态回调函数。
   op_ret = tuya_iot_reg_get_wf_nw_stat_cb(wf_nw_status_cb);
   if(OPRT_OK != op_ret) {
       PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb is error,err_num:%d",op_ret);
       return op_ret;
   }
  • 调用应用层初始化函数
   op_ret = app_mirror_init(APP_MIRROR_NORMAL);
   if(OPRT_OK != op_ret) {
       PR_ERR("app init err!");
       return op_ret;
   }

应用层初始化函数 app_mirror_init 就实现在我们的 tuya_app.c 中,进入该函数中时就可以创建应用层的线程,从而开始运行应用功能代码。
​ ​ ​ ​
​ ​ ​ ​

应用初始化

​ ​ ​ ​ 在调用到 tuya_app.capp_mirror_init 函数时,首先读取 flash 中存放的设备数据,然后调用mirror_device_init 进行功能逻辑层的初始化操作,最后开启各个功能逻辑线程:

OPERATE_RET app_mirror_init(IN APP_MIRROR_MODE mode)
{
   OPERATE_RET op_ret = OPRT_OK;

   if(APP_MIRROR_NORMAL == mode) {
       
       UCHAR_T read_buff[SAVE_DATA_LEN] = {0};
       uiSocFlashRead(APP_DATA_SAVE,APP_DATA_SAVE_OFFSET,SAVE_DATA_LEN,read_buff);
       mirror_data_load(read_buff);

       UCHAR_T i = 0;
       for(i = 0;i < SAVE_DATA_LEN;i++){
           PR_NOTICE("------- readbuff = %d -----",read_buff[i]);
       }

       mirror_device_init();

       tuya_hal_thread_create(NULL, "thread_data_get", 512*8, TRD_PRIO_4, sensor_data_get_thread, NULL);

       tuya_hal_thread_create(NULL, "thread_data_deal", 512*4, TRD_PRIO_4, sensor_data_deal_thread, NULL);

       tuya_hal_thread_create(NULL, "key_scan_thread", 512*4, TRD_PRIO_3, key_scan_thread, NULL);

       tuya_hal_thread_create(NULL, "diplay_send_thread", 512*4, TRD_PRIO_3, diplay_send_thread, NULL);

   }else {
       //not factory test mode
   }

   return op_ret;
}

然后就需要实现各个线程handle, handle 内调用的函数皆在 mirror.control.c 中:

#define TASKDELAY_SEC         1000

STATIC VOID sensor_data_get_thread(PVOID_T pArg)
{   
   while(1) {

       mirror_data_get_handle();

       tuya_hal_system_sleep(TASKDELAY_SEC);
   }
}

STATIC VOID diplay_send_thread(PVOID_T pArg)
{     
   while(1) {

       tuya_hal_system_sleep(TASKDELAY_SEC/2);

       mirror_display_poll();
   }
}

STATIC VOID key_scan_thread(PVOID_T pArg)
{   
   while(1) {

       mirror_key_poll();
      
       tuya_hal_system_sleep(25);       
   }
}

STATIC VOID sensor_data_deal_thread(PVOID_T pArg)
{   
   while(1) {
       
       tuya_hal_system_sleep(TASKDELAY_SEC/2);

       if(mirror_ctrl_data.Wifi_state == connecting)
       {
           mirror_wifi_light_handle();
       }else {

           mirror_ctrl_handle();
       }
   }
}

​ ​ ​ ​ ​ ​ ​ ​
​ ​ ​ ​

触摸按键

​ ​ ​ ​ 在tuya_mirror_key.c文件中,封装了app_key_init()app_key_scan()两个函数。app_key_init()用于初始化按键IO,app_key_scan()用于扫描按键按下情况获取键值:

void app_key_scan(unsigned char *trg,unsigned char *cont)
{
    unsigned char read_data;
    
    if(KEY_RELEAS_LEVEL) {
        read_data = 0x0F;
    }else {
        read_data = 0x00; 
    }

    read_data = (tuya_gpio_read(KEY_SWITCH_PIN)<<3)|(tuya_gpio_read(KEY_SET_PIN)<<2)|(tuya_gpio_read(KEY_UP_PIN)<<1)|(tuya_gpio_read(KEY_DOWN_PIN));
    *trg = (read_data & (read_data ^ (*cont)));
    *cont = read_data;
}

​ ​ ​ ​ 该函数会检测四个按键的按下情况,然后将键值赋值给传参,其中trg是在整个按键按下动作中只出现一次的键值,而cont是只在按键松手后才会复位的键值。通过这两种不同特效的键值,就可以实现长短按和组合键的功能。

​ ​ ​ ​ key_scan_thread 是上文提到的几个应用线程 handle 的其中一个,在该handle 里调用的是触摸按键扫描轮询函数,实现在tuya_mirror_control.c中,此时线程的休眠时间即是按键扫描间隔时间。

VOID mirror_key_poll(VOID)
{
   MIRROR_CTRL_DATA_T *p;

   p = &mirror_ctrl_data;

   app_key_scan(&key_trg,&key_cont);
   
   switch (key_cont)
   {
   case KEY_CODE_RELEASE:

       if(key_buf != 0) {

           mirror_key_event(key_buf);
       }

       key_buf = 0;
       key_old = KEY_CODE_RELEASE;

       break;
   case KEY_CODE_SWITCH:

       vTaskDelay(10);
       app_key_scan(&key_trg,&key_cont);

       if(key_cont == KEY_CODE_SWITCH) {

           key_buf = KEY_CODE_SWITCH;
       }

       key_old = KEY_CODE_SWITCH;

       break;
   case KEY_CODE_SET_LIGHT_COLOR:

       if(key_old == KEY_CODE_SET_LIGHT_COLOR) {

           key_delay_cont++;
       }else{

           key_delay_cont = 0;
       }

       if(key_delay_cont >= 2) {

           key_buf = KEY_CODE_SET_LIGHT_COLOR;
       }

       if(key_delay_cont >= 200) {

           key_buf = 0;
           key_delay_cont = 0;
           tuya_iot_wf_gw_unactive();

           // start connect;
       }

       key_old = KEY_CODE_SET_LIGHT_COLOR;

       break;
   case KEY_CODE_UP:

       if(p->Mirror_switch == FALSE) {

           key_buf = 0;

           return ;
       }

       if(p->Light_switch == FALSE) {

           key_buf = 0;

           return ;
       }

       if(key_old == KEY_CODE_UP) {

           key_delay_cont++;
       }else{

           key_delay_cont = 0;
       }

       if(key_delay_cont >= 2) {

           key_buf = KEY_CODE_UP;
       }

       if(key_delay_cont >= 40) {

           key_buf = 0;

           if(p->Light_value <= 995) {

               p->Light_value += 10;

               mirror_pwm_set(p->Light_mode,p->Light_value);
           }
       }
       
       key_old = KEY_CODE_UP;
       break;
   case KEY_CODE_DOWN:

       if(p->Mirror_switch == FALSE) {

           key_buf = 0;

           return ;
       }

       if(p->Light_switch == FALSE) {

           key_buf = 0;

           return ;
       }

       if(key_old == KEY_CODE_DOWN) {

           key_delay_cont++;
       }else{

           key_delay_cont = 0;
       }

       if(key_delay_cont >= 2) {

           key_buf = KEY_CODE_DOWN;
       }

       if(key_delay_cont >= 40) {

           key_buf = 0;

           if(p->Light_value>=205) {

               p->Light_value -= 10;

               mirror_pwm_set(p->Light_mode,p->Light_value);
           } 
       }
       
       key_old = KEY_CODE_DOWN;        
       break;          
   default:
       break;
   }

}

按键扫描获取对应键值,再根据键值执行相应的操作事件,即可完成触摸按键部分的功能。

​ ​ ​ ​
​ ​ ​ ​

下节内容:《自制智能镜之——屏幕显示时间日期篇》

以上是关于自制智能镜之——应用入口及触摸按键实现篇的主要内容,如果未能解决你的问题,请参考以下文章

自制智能镜之——硬件选型篇

自制智能镜之——功能逻辑分析篇

自制智能镜之——产品创建及开发环境搭建篇(限时活动进行中)

自制智能镜之——化妆灯和人体感应篇

自制智能镜之——硬件&结构设计篇

涂鸦智能烧水壶软件实现之TS02N触摸按键驱动