2.3.1. hid按键

  • 本案例为基于 HID 的键盘设备,可以用来媒体播放,上下曲暂停音量的控制,支持安卓和 IOS 的双系统,并且支持 BLE 和 EDR 两种工作模式。

  • 支持的板级: br25、br23、br30、bd19、br34 支持的芯片: AC636N、AC635N、AC632N、AC638N

2.3.1.1. hid按键case使用

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

  • 板级选择默认板级即可

  • 编译下载代码、接线、复位设备,再使用手机搜索设备进行连接。

  • 开发者可以操作板子上面的按键去控制手机:媒体播放,上下曲暂停音量的控制(需要打开手机的播歌软件)。 至此恭喜你已经完成case的下载与测试

    Note

    若出现无法进行:媒体播放,上下曲暂停音量的控制。开发者应该确保手机蓝牙是否正常连接、查看ad key接线是否接上,如还是无法成功,请先查阅 faq,可能你的答案已经存在。

2.3.1.2. hid按键代码流程

完成了代码下载,case使用,下一步即开发代码,hid的代码结构如下,同时参考启动流程了解: :SDK启动流程

Note

以下只是为演示代码流程,开发者不用去修改代码。以下示例是用默认CONFIG_APP_KEYBOARD例子进行说明,其他例子流程参考如下流程理解。

  • APP 注册运行:打开app_main.c,里面的app_main()函数即做app的注册和运行

    #if (CONFIG_APP_MOUSE)
        it.name = "mouse";
        it.action = ACTION_MOUSE_MAIN;
    #elif(CONFIG_APP_KEYFOB)
        it.name = "keyfob";
        it.action = ACTION_KEYFOB;
    #elif(CONFIG_APP_KEYBOARD)
        it.name = "hid_key";
    
        start_app(&it);
    }
    
  • 首先会在 app_main.c 函数中添加 hid_key 应用分支,然后进行应用注册

    REGISTER_APPLICATION(app_hid) = {
        .name   = "hid_key",
        .action = ACTION_HID_MAIN,
        .ops    = &app_hid_ops,
        .state  = APP_STA_DESTROY,
    };
    
  • 按照上述代码进行APP注册,执行配置好的app。之后进入APP_state_machine(app_keyboard.c文件里),根据状态机的不同状态执行不同的分支,第一次执行时进入APP_STA_CREATE分支后再进入APP_STA_START分支,执行对应的app_start()。开始执行app_start()在该函数内进行时钟初始化,进行蓝牙模式选择,按键消息使能等一些初始化操作,其中按键使能使得系统在有外部按键事件发生时及时响应,进行事件处理。

    REGISTER_LP_TARGET(app_hidkey_lp_target) = {
        .name = "app_hidkey_deal",
        .is_idle = hidkey_app_idle_query,
    };
    static const struct application_operation app_hidkey_ops = {
        .state_machine  = hidkey_state_machine,
        .event_handler  = hidkey_event_handler,
    };
    
  • hie_key应用注册以后,进行以上的app_hid_ops进行处理。分为两个模块hidkey_state_machine和hidkey_event_handler。执行流程大致如下,对应函数位于app_keyboard.c文件:hidkey_state_machine()—>app_start()—>sys_key_event_enable()。主要根据应用的状态进行时钟初始化,蓝牙名设置,读取配置信息,消息按键使能等配置。hidkey_event_handler(struct application *app, struct sys_event *event)—>hidkey_key_event_handler(sys_event *event)—>hidkey_key_deal_test(key_type,key_value)—->hidkey_key_deal_test(key_msg)。事件处理流程大致如上所示。hidkey_event_handler()根据传入的第二个参数事件类型,选择对应的处理分支,此处选择执行按键事件,然后调用按键事件处理函数根据事件的按键值和按键类型进行对应的事件处理。

2.3.1.3. hid代码APP事件处理机制

  • 参考开发框架理解 :APP 开发框架

  • 事件的产生与定义:外部事件的数据采集在系统软件定时器的中断服务函数中完成,采集的数据将被打包为相应的 事件周期性地上报至全局事件列表。void sys_event_notify(struct sys_event *e);此函数为事件通知函数,系统有事件发生时调用。

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

    • 蓝牙连接事件处理:在APP运行以后,首先进行的蓝牙连接事件处理,进行蓝牙初始化,HID描述符解读,蓝牙模式选择等,。调用hidkey_event_handler(),hidkey_bt_connction_status_event_handler()函数实现蓝牙连接等事件(可理解为 :APP 开发框架 图中的handler_1)。

    • 按键事件处理:通过hidkey_event_handler()函数中调用hidkey_key_event_handler()函数实现对按键事件的处理(可理解为 :APP 开发框架 图中的handler_2)。

2.3.1.4. 主要代码说明

hid代码数据发送模块:

  • 自定义的键盘描述符(上发给手机的包格式就是按照hidkey_report_map里面的格式进行上发给手机)如下所示(app_keyboard.c):

    static const u8 hidkey_report_map[] = {
        0x05, 0x0C,        // Usage Page (Consumer)
        0x09, 0x01,        // Usage (Consumer Control)
        0xA1, 0x01,        // Collection (Application)
        0x85, 0x01,        //   Report ID (1)
        0x09, 0xE9,        //   Usage (Volume Increment)
        0x09, 0xEA,        //   Usage (Volume Decrement)
        0x09, 0xCD,        //   Usage (Play/Pause)
        0x09, 0xE2,        //   Usage (Mute)
        0x09, 0xB6,        //   Usage (Scan Previous Track)
        0x09, 0xB5,        //   Usage (Scan Next Track)
        0x09, 0xB3,        //   Usage (Fast Forward)
        0x09, 0xB4,        //   Usage (Rewind)
        0x15, 0x00,        //   Logical Minimum (0)
        0x25, 0x01,        //   Logical Maximum (1)
        0x75, 0x01,        //   Report Size (1)
        0x95, 0x10,        //   Report Count (16)
        0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        0xC0,              // End Collection
        // 35 bytes
    };
    
  • 根据不同的按键值和按键类型,进行数据发送。部分发送函数入下(app_keyboard.c):

        if (key_msg) {
            printf("key_msg = %02x\n", key_msg);
            if (bt_hid_mode == HID_MODE_EDR) {
                edr_hid_key_deal_test(key_msg);
                bt_sniff_ready_clean();
            } else {
    #if TCFG_USER_BLE_ENABLE
                ble_hid_key_deal_test(key_msg);
    #endif
            }
            return;
    

Note

开发者可以参考( :HID )了解hid描述符含义。