2.3.2. 自拍杆

  • 本案例主要用于蓝牙自拍器实现,进行以下配置后,打开手机蓝牙连接设备可进行对应的拍照 操作。由于自拍器的使用会用到 LED 所以本案例也要对 LED 进行对应的设置,自拍器设备上电以后 没有连接蓝牙之前,LED 以一定的频率闪烁,直到连接或者是进入 sleep 模式时熄灭。蓝牙连接以后 LED 熄灭,同时按键按下的时候 LED 会变化,可以通过 LED 的状态来判断自拍器的工作状态。

  • 支持板级:br25、bd19 支持芯片:AC6368A、AC6328A

2.3.2.1. 自拍杆case使用

  • 使用快捷键Alt + g 打开app_config.h,配置CONFIG_APP_KEYFOB使能

//app case 选择,只选1,要配置对应的board_config.h
#define CONFIG_APP_KEYFOB        1//自拍器,  board_ac6328a,board_6328
  • 使用快捷键Alt + g 打开board_config.h,板级选择CONFIG_BOARD_AC6328A_KEYFOB

//#define CONFIG_BOARD_AC632N_DEMO       // CONFIG_APP_KEYBOARD,CONFIG_APP_PAGE_TURNER
// #define CONFIG_BOARD_AC6321A_MOUSE  // CONFIG_APP_MOUSE
 #define CONFIG_BOARD_AC6328A_KEYFOB     // CONFIG_APP_KEYFOB
  • 按照 :HID DEMO说明 编译下载代码、接线(接线参考下列)、复位设备,再使用手机搜索设备进行连接。

    KEY: key接线需要打开board_ac6328a_keyfob_cfg.h查看IO key接线,将IO key1 连接USB0DP(IO_PORT_DP)、IO key2 连接USB0DM(IO_PORT_DM)。

    //*********************************************************************************//
    //                                 iokey 配置                                      //
    //*********************************************************************************//
    #define TCFG_IOKEY_ENABLE                   ENABLE_THIS_MOUDLE //是否使能IO按键
    #define TCFG_IOKEY_PREV_CONNECT_WAY         ONE_PORT_TO_LOW  //按键一端接低电平一端接IO
    #define TCFG_IOKEY_PREV_ONE_PORT            IO_PORT_DP
    #define TCFG_IOKEY_NEXT_CONNECT_WAY         ONE_PORT_TO_LOW  //按键一端接低电平一端接IO
    #define TCFG_IOKEY_NEXT_ONE_PORT            IO_PORT_DM
    

    LED: led接线打开board_ac6328a_keyfob_cfg.h查看led接线

    //*********************************************************************************//
    //                                  LED 配置                                       //
    //*********************************************************************************//
    #define TCFG_PWMLED_ENABLE                  ENABLE_THIS_MOUDLE          //是否支持PMW LED推灯模块
    #define TCFG_PWMLED_IO_PUSH                 DISABLE_THIS_MOUDLE
    #define TCFG_PWMLED_IOMODE                  LED_ONE_IO_MODE             //LED模式,单IO还是两个IO推灯
    #define TCFG_PWMLED_PIN                     IO_PORTA_09                 //LED使用的IO口
    
  • 开发者可以操作板子IO key1、IO key2实现蓝牙自拍器的功能。

2.3.2.2. 主要代码说明

APP注册运行:

  • 代码进行 APP 注册,执行配置好的 app。之后进入 APP_state_machine,根据状态机的 不同状态执行不同的分支,第一次执行时进入 APP_STA_CREATE 分支,执行对应的 app_start()。开始执行 app_start()在该函数内进行时钟初始化,进行蓝牙模式选择,按键消息使能等一些初始化操作, 其中按键使能使得系统在有外部按键事件发生时及时响应,进行事件处理(app_keyfob.c)。

    REGISTER_LP_TARGET(app_hid_lp_target) = {
    .name = "app_hid_deal",
    .is_idle = app_hid_idle_query,
    };
    
    static const struct application_operation app_hid_ops = {
    .state_machine = state_machine,
    .event_handler = event_handler,
    };
    /*
    * 注册 AT Module 模式
    */
    REGISTER_APPLICATION(app_hid) = {
    .name = "keyfob",
    .action = ACTION_KEYFOB,
    .ops = &app_hid_ops,
    .state = APP_STA_DESTROY,
    };
    

APP 事件处理机制:

  • 事件的产生与定义

    • 外部事件的数据采集在系统软件定时器的中断服务函数中完成,采集的数据将被打包为相应的“事件”周期性地上报至全局事件列表。

    • void sys_event_notify(struct sys_event *e); 此函数为事件通知函数,系统有事件发生时调用。

  • 事件的处理

    • 本案例中主要的事件处理包括连接事件处理、按键事件处理和 LED 事件处理,事件处理函数的 共同入口都是 event_handler().之后调用不同的函数实现不同类型事件的响应处理。

    • 蓝牙连接事件处理:在 APP 运行以后,首先进行的蓝牙连接事件处理,再进行蓝牙初始化,HID 描述符解读,蓝牙模式选择等,下列函数的第二个参数根据事件的不同,传入不同的事件类型,执行不同分支,如下代码所示(app_keyfob.c):

      static int keyfob_event_handler(struct application *app, struct sys_event *event)
      {
      
      #if (TCFG_HID_AUTO_SHUTDOWN_TIME)
          //重置无操作定时计数
          if (event->type != SYS_DEVICE_EVENT || DEVICE_EVENT_FROM_POWER != event->arg) { //过滤电源消息
              sys_timer_modify(g_auto_shutdown_timer, TCFG_HID_AUTO_SHUTDOWN_TIME * 1000);
          }
      #endif
      
      #if TCFG_USER_EDR_ENABLE
          bt_comm_edr_sniff_clean();
      #endif
          /* log_info("event: %s", event->arg); */
          switch (event->type) {
          case SYS_KEY_EVENT:
              /* log_info("Sys Key : %s", event->arg); */
              app_keyfob_event_handler(event);
              return 0;
      
      • 以下代码为蓝牙连接事件处理函数,进行蓝牙初始化以及模式选择(app_keyfob.c)。

      static int keyfob_bt_connction_status_event_handler(struct bt_event *bt)
      {
      
          log_info("--------keyfob_bt_connction_status_event_handler %d", bt->event);
      
          switch (bt->event) {
          case BT_STATUS_INIT_OK:
              /*
               * 蓝牙初始化完成
               */
              log_info("BT_STATUS_INIT_OK\n");
      
      • 调用 keyfob_event_handler(),keyfob_bt_connction_status_event_handler()函数实现蓝牙连接等事件。

    • 按键事件处理和 LED 事件处理:通过调用 app_keyfob_event_handler()函数进入按键事件处理流程,根据按键的类型和按键值进 入 app_key_deal_test()函数进行事件处理(app_keyfob.c)。

      static void app_keyfob_event_handler(struct sys_event *event)
      {
          /* u16 cpi = 0; */
          u8 event_type = 0;
          u8 key_value = 0;
      
          if (event->arg == (void *)DEVICE_EVENT_FROM_KEY) {
              event_type = event->u.key.event;
              key_value = event->u.key.value;
              printf("app_key_evnet: %d,%d\n", event_type, key_value);
              app_keyfob_deal_test(event_type, key_value);
          }
      }
      
      static void app_keyfob_deal_test(u8 key_type, u8 key_value)
      {
          u16 key_msg = 0;
      
      #if TCFG_USER_EDR_ENABLE
          if (bt_hid_mode == HID_MODE_EDR && !edr_hid_is_connected()) {
              if (bt_connect_phone_back_start()) { //回连
                  return;
              }
          }
      #endif
      
          switch (key_type) {
      
      • 下图为 LED 工作状态部分实现函数(app_keyfob.c)。

      static void led_on_off(u8 state, u8 res)
      {
          /* if(led_state != state || (state == LED_KEY_HOLD)){ */
          if (1) { //相同状态也要更新时间
              u8 prev_state = led_state;
              log_info("led_state: %d>>>%d", led_state, state);
              led_state = state;
              led_io_flash = 0;
      
  • 数据发送:

    • KEYFOB 属于 HID 设备范畴,数据的定义与发送要根据 HID 设备描述符的内容进行确定,由下 图的描述符可知,该描述符是一个用户自定义描述符,可以组合实现各种需要的功能,一共有两个 Input 实体描述符。其中每个功能按键对应一个 bit,一共 11bit,剩余一个 13bit 的常数输入实体,所以 自定义描述符的数据包长度位 3byte.如果用户需要在自拍器的基础上增加不同按键类型的事件,可 以在下面的描述符中先添加该功能,然后在按键处理函数分支进行对应的按键值和按键类型的设置, 来实现对应的功能。

    • 用户自定义的描述符组成本案例的 KEYFOB 描述符,实现对应的按键功能(app_keyfob.c)。

      static const u8 keyfob_report_map[] = {
          //通用按键
          0x05, 0x0C,        // Usage Page (Consumer)
          0x09, 0x01,        // Usage (Consumer Control)
          0xA1, 0x01,        // Collection (Application)
          0x85, 0x03,        //   Report ID (3)
          0x15, 0x00,        //   Logical Minimum (0)
          0x25, 0x01,        //   Logical Maximum (1)
          0x75, 0x01,        //   Report Size (1)
          0x95, 0x0B,        //   Report Count (11)
          0x0A, 0x23, 0x02,  //   Usage (AC Home)
          0x0A, 0x21, 0x02,  //   Usage (AC Search)
          0x0A, 0xB1, 0x01,  //   Usage (AL Screen Saver)
          0x09, 0xB8,        //   Usage (Eject)
          0x09, 0xB6,        //   Usage (Scan Previous Track)
          0x09, 0xCD,        //   Usage (Play/Pause)
          0x09, 0xB5,        //   Usage (Scan Next Track)
          0x09, 0xE2,        //   Usage (Mute)
          0x09, 0xEA,        //   Usage (Volume Decrement)
          0x09, 0xE9,        //   Usage (Volume Increment)
          0x09, 0x30,        //   Usage (Power)
          0x0A, 0xAE, 0x01,  //   Usage (AL Keyboard Layout)
          0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
          0x95, 0x01,        //   Report Count (1)
          0x75, 0x0D,        //   Report Size (13)
          0x81, 0x03,        //   Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
          0xC0,              // End Collection
      
          // 119 bytes
      };
      
    • 下列即发送模块,发送的数据是按照上面的 HID 设备描述符 格式进行发送的(app_keyfob.c)。

      static void key_value_send(u8 key_value, u8 mode)
      {
          void (*hid_data_send_pt)(u8 report_id, u8 * data, u16 len) = NULL;
      
          if (bt_hid_mode == HID_MODE_EDR) {
      #if TCFG_USER_EDR_ENABLE
              hid_data_send_pt = edr_hid_data_send;
      #endif
          } else {
      #if TCFG_USER_BLE_ENABLE
              hid_data_send_pt = ble_hid_data_send;
      #endif
          }
      
          if (!hid_data_send_pt) {
              return;
          }
      
          if (key_value == KEY_BIG_ID) {