一直被各种事情耽搁,直到现在才继续有空发东西.
在说之前,先吐槽一个,下载SoftDevice之后,各种下载不良,竟然是因为休眠,各种麻烦.就算到现在我也没尝试出100%能下载的方法,痛苦啊.
而且调试时候会暂停SoftDevice运行就会GG... 只能复位.
我们引入学习的是ble_app_blinky
没什么特别的,学习就先看入口,就是main函数.
int main(void) { // Initialize. log_init(); leds_init(); timers_init(); buttons_init(); power_management_init(); ble_stack_init(); gap_params_init(); gatt_init(); services_init(); advertising_init(); conn_params_init(); // Start execution. NRF_LOG_INFO("Blinky example started."); advertising_start(); // Enter main loop. for (;;) { idle_state_handle(); } }
前面没什么好说的,主要看蓝牙部分开始.
结合这张图往下看.
ble_stack_init初始化协议栈的内存分配,设置回调函数为ble_evt_handler,后续的操作都通过回调方法接入,至于ble_evt_handler怎么做,后续再讨论.
static void ble_stack_init(void) { ret_code_t err_code; err_code = nrf_sdh_enable_request(); APP_ERROR_CHECK(err_code); // Configure the BLE stack using the default settings. // Fetch the start address of the application RAM. uint32_t ram_start = 0; err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); APP_ERROR_CHECK(err_code); // Enable BLE stack. err_code = nrf_sdh_ble_enable(&ram_start); APP_ERROR_CHECK(err_code); // Register a handler for BLE events. NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); }
蓝牙BLE建立GATT连接前,必须经过GAP协议,GAP用来控制设备的连接广播等,GAP使设备广播或可别发现,可被连接等.
gap_params_init就是进行GAP初始化,设定连接参数,各种广播信息.产品经理最爱的改产品名也在这里实现.不过实际上都宏定义了.
static void gap_params_init(void) { ret_code_t err_code; ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)); APP_ERROR_CHECK(err_code); memset(&gap_conn_params, 0, sizeof(gap_conn_params)); gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; gap_conn_params.slave_latency = SLAVE_LATENCY; gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; err_code = sd_ble_gap_ppcp_set(&gap_conn_params); APP_ERROR_CHECK(err_code); }
接下来才是真正的GATT部分.
static void gatt_init(void) { ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL); APP_ERROR_CHECK(err_code); }
这部分虽然很短,m_gatt是什么东西,没必要在这里浪费时间.
我们知道他是一个全局变量:
NRF_BLE_GATT_DEF(m_gatt);
关键展开在这里,只是告诉你,这个是很必要的东西,至于具体,我不管.
至此,蓝牙中间层搞定~
接下来应用层才是关键,我们开发的重点,而重点中的重点,就是Services.
static void services_init(void) { ret_code_t err_code; ble_lbs_init_t init = {0}; nrf_ble_qwr_init_t qwr_init = {0}; // Initialize Queued Write Module. qwr_init.error_handler = nrf_qwr_error_handler; err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init); APP_ERROR_CHECK(err_code); // Initialize LBS. init.led_write_handler = led_write_handler; err_code = ble_lbs_init(&m_lbs, &init); APP_ERROR_CHECK(err_code); }
这里重点是LBS,Led and Button Services?
uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init) { uint32_t err_code; ble_uuid_t ble_uuid; ble_add_char_params_t add_char_params; // Initialize service structure. p_lbs->led_write_handler = p_lbs_init->led_write_handler; // Add service. ble_uuid128_t base_uuid = {LBS_UUID_BASE}; err_code = sd_ble_uuid_vs_add(&base_uuid, &p_lbs->uuid_type); VERIFY_SUCCESS(err_code); ble_uuid.type = p_lbs->uuid_type; ble_uuid.uuid = LBS_UUID_SERVICE; err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_lbs->service_handle); VERIFY_SUCCESS(err_code); // Add Button characteristic. memset(&add_char_params, 0, sizeof(add_char_params)); add_char_params.uuid = LBS_UUID_BUTTON_CHAR; add_char_params.uuid_type = p_lbs->uuid_type; add_char_params.init_len = sizeof(uint8_t); add_char_params.max_len = sizeof(uint8_t); add_char_params.char_props.read = 1; add_char_params.char_props.notify = 1; add_char_params.read_access = SEC_OPEN; add_char_params.cccd_write_access = SEC_OPEN; err_code = characteristic_add(p_lbs->service_handle, &add_char_params, &p_lbs->button_char_handles); if (err_code != NRF_SUCCESS) { return err_code; } // Add LED characteristic. memset(&add_char_params, 0, sizeof(add_char_params)); add_char_params.uuid = LBS_UUID_LED_CHAR; add_char_params.uuid_type = p_lbs->uuid_type; add_char_params.init_len = sizeof(uint8_t); add_char_params.max_len = sizeof(uint8_t); add_char_params.char_props.read = 1; add_char_params.char_props.write = 1; add_char_params.read_access = SEC_OPEN; add_char_params.write_access = SEC_OPEN; return characteristic_add(p_lbs->service_handle, &add_char_params, &p_lbs->led_char_handles); }
找到这里面几个关键的定义.
#define LBS_UUID_BASE {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00} #define LBS_UUID_SERVICE 0x1523 #define LBS_UUID_BUTTON_CHAR 0x1524 #define LBS_UUID_LED_CHAR 0x1525
下载到板子中打开手机发现LBS_UUID_SERVICE + LBS_UUID_BASE就是主服务UUID,其他的也是如此类推.不在叙述.
我们现在发现了,这些都是服务,那么这些服务看起来不是main里面的,说明不是我们致力于研究的,实际上也是如此,NRF为我们提前开发了大量服务,拿来主义就行.
(除非,我们要做自己的Services)
接下来就是广播配置(超时时间,广播周期),链接参数(需要看蓝牙文档了),然后开始广播.. 这个初始逻辑就完成了.
实际的操作都在回调里面.
static void led_write_handler(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t led_state) { if (led_state) { bsp_board_led_on(LEDBUTTON_LED); NRF_LOG_INFO("Received LED ON!"); } else { bsp_board_led_off(LEDBUTTON_LED); NRF_LOG_INFO("Received LED OFF!"); } }
到这,就算是入门理清一下逻辑了.