一直被各种事情耽搁,直到现在才继续有空发东西.
在说之前,先吐槽一个,下载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!");
}
}
到这,就算是入门理清一下逻辑了.