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) {