nRF5 蓝牙上手[ble_app_blinky]

/ 0评 / 0

一直被各种事情耽搁,直到现在才继续有空发东西.

在说之前,先吐槽一个,下载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!");

    }

}

到这,就算是入门理清一下逻辑了.