I am currently evaluating the nrf52840 for a possible future application which is based around BLE and should also provide a HID Generic interface to perform communication with a computer. Nothing special, just some random data every few seconds.
The BLE part is working so far but I am struggling with the USB HID part. I browsed the available examples and found some under the peripheral branch and some in the ble_peripheral branch (nRF5 16.0.0 SDK). The issue lies with the setup of the usb descriptors for HID. I found that many examples (e.g. ble_app_hids_mouse_pca10056_s140) rely on e.g. the following code:
/**@brief Function for initializing HID Service. */ static void hids_init(void) { ret_code_t err_code; ble_hids_init_t hids_init_obj; ble_hids_inp_rep_init_t * p_input_report; uint8_t hid_info_flags; static ble_hids_inp_rep_init_t inp_rep_array[INPUT_REPORT_COUNT]; static uint8_t rep_map_data[] = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) // Report ID 1: Mouse buttons + scroll/pan 0x85, 0x01, // Report Id 1 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x95, 0x05, // Report Count (3) 0x75, 0x01, // Report Size (1) 0x05, 0x09, // Usage Page (Buttons) 0x19, 0x01, // Usage Minimum (01) 0x29, 0x05, // Usage Maximum (05) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x81, 0x02, // Input (Data, Variable, Absolute) 0x95, 0x01, // Report Count (1) 0x75, 0x03, // Report Size (3) 0x81, 0x01, // Input (Constant) for padding 0x75, 0x08, // Report Size (8) 0x95, 0x01, // Report Count (1) 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x38, // Usage (Wheel) 0x15, 0x81, // Logical Minimum (-127) 0x25, 0x7F, // Logical Maximum (127) 0x81, 0x06, // Input (Data, Variable, Relative) 0x05, 0x0C, // Usage Page (Consumer) 0x0A, 0x38, 0x02, // Usage (AC Pan) 0x95, 0x01, // Report Count (1) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0xC0, // End Collection (Physical) // Report ID 2: Mouse motion 0x85, 0x02, // Report Id 2 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x75, 0x0C, // Report Size (12) 0x95, 0x02, // Report Count (2) 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x16, 0x01, 0xF8, // Logical maximum (2047) 0x26, 0xFF, 0x07, // Logical minimum (-2047) 0x81, 0x06, // Input (Data, Variable, Relative) 0xC0, // End Collection (Physical) 0xC0, // End Collection (Application) // Report ID 3: Advanced buttons 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, 0x01, // Report Count (1) 0x09, 0xCD, // Usage (Play/Pause) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xB5, // Usage (Scan Next Track) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xB6, // Usage (Scan Previous Track) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xEA, // Usage (Volume Down) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xE9, // Usage (Volume Up) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x0A, 0x25, 0x02, // Usage (AC Forward) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x0A, 0x24, 0x02, // Usage (AC Back) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0xC0 // End Collection }; memset(inp_rep_array, 0, sizeof(inp_rep_array)); // Initialize HID Service. p_input_report = &inp_rep_array[INPUT_REP_BUTTONS_INDEX]; p_input_report->max_len = INPUT_REP_BUTTONS_LEN; p_input_report->rep_ref.report_id = INPUT_REP_REF_BUTTONS_ID; p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT; p_input_report->sec.cccd_wr = SEC_JUST_WORKS; p_input_report->sec.wr = SEC_JUST_WORKS; p_input_report->sec.rd = SEC_JUST_WORKS; p_input_report = &inp_rep_array[INPUT_REP_MOVEMENT_INDEX]; p_input_report->max_len = INPUT_REP_MOVEMENT_LEN; p_input_report->rep_ref.report_id = INPUT_REP_REF_MOVEMENT_ID; p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT; p_input_report->sec.cccd_wr = SEC_JUST_WORKS; p_input_report->sec.wr = SEC_JUST_WORKS; p_input_report->sec.rd = SEC_JUST_WORKS; p_input_report = &inp_rep_array[INPUT_REP_MPLAYER_INDEX]; p_input_report->max_len = INPUT_REP_MEDIA_PLAYER_LEN; p_input_report->rep_ref.report_id = INPUT_REP_REF_MPLAYER_ID; p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT; p_input_report->sec.cccd_wr = SEC_JUST_WORKS; p_input_report->sec.wr = SEC_JUST_WORKS; p_input_report->sec.rd = SEC_JUST_WORKS; hid_info_flags = HID_INFO_FLAG_REMOTE_WAKE_MSK | HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK; memset(&hids_init_obj, 0, sizeof(hids_init_obj)); hids_init_obj.evt_handler = on_hids_evt; hids_init_obj.error_handler = service_error_handler; hids_init_obj.is_kb = false; hids_init_obj.is_mouse = true; hids_init_obj.inp_rep_count = INPUT_REPORT_COUNT; hids_init_obj.p_inp_rep_array = inp_rep_array; hids_init_obj.outp_rep_count = 0; hids_init_obj.p_outp_rep_array = NULL; hids_init_obj.feature_rep_count = 0; hids_init_obj.p_feature_rep_array = NULL; hids_init_obj.rep_map.data_len = sizeof(rep_map_data); hids_init_obj.rep_map.p_data = rep_map_data; hids_init_obj.hid_information.bcd_hid = BASE_USB_HID_SPEC_VERSION; hids_init_obj.hid_information.b_country_code = 0; hids_init_obj.hid_information.flags = hid_info_flags; hids_init_obj.included_services_count = 0; hids_init_obj.p_included_services_array = NULL; hids_init_obj.rep_map.rd_sec = SEC_JUST_WORKS; hids_init_obj.hid_information.rd_sec = SEC_JUST_WORKS; hids_init_obj.boot_mouse_inp_rep_sec.cccd_wr = SEC_JUST_WORKS; hids_init_obj.boot_mouse_inp_rep_sec.wr = SEC_JUST_WORKS; hids_init_obj.boot_mouse_inp_rep_sec.rd = SEC_JUST_WORKS; hids_init_obj.protocol_mode_rd_sec = SEC_JUST_WORKS; hids_init_obj.protocol_mode_wr_sec = SEC_JUST_WORKS; hids_init_obj.ctrl_point_wr_sec = SEC_JUST_WORKS; err_code = ble_hids_init(&m_hids, &hids_init_obj); APP_ERROR_CHECK(err_code); }
Other examples rely on crazy macros (usbd_hid_generic_pca10056):
APP_USBD_HID_GENERIC_GLOBAL_DEF(m_app_hid_generic, HID_GENERIC_INTERFACE, hid_user_ev_handler, ENDPOINT_LIST(), reps, REPORT_IN_QUEUE_SIZE, REPORT_OUT_MAXSIZE, REPORT_FEATURE_MAXSIZE, APP_USBD_HID_SUBCLASS_BOOT, APP_USBD_HID_PROTO_MOUSE);
Which is the preferred approach for a generic device with an in and an out port?
I thought of taking the descriptor for a mouse which can be found in app_usb_hid_mouse_desc.h and rewriting that for a generic hid device but I'd like to know which is the preferred way. Is the macro just another level of abstraction?