nrf51822学习之第一个BLE程序分析

SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/

nrf51822学习之第一个BLE程序分析

今天来分析一个例程《BLE实验1:蓝牙工程样例》,注意这里并不是十分专业的讲解,完全是本人的一点小笔记,并没有太大的参考价值,但是如果幸运的话,能让我们有点小的灵感

这是一个最简单的BLE程序了,keil工程文件在这个目录下:\Board\nrf6310\s110\ble_app_template\arm
主函数如下:
int main(void)
{
    // Initialize
    leds_init();
    timers_init();
    gpiote_init();
    buttons_init();
    ble_stack_init();
    scheduler_init();    
    gap_params_init();
    advertising_init();
    services_init();
    conn_params_init();
    sec_params_init();
    // Start execution
    timers_start();
    advertising_start();
    // Enter main loop
    for (;;)
    {
        app_sched_execute();
        power_manage();
    }
}
leds_init()和gpiote_init()以及buttons_init()函数暂且不说,timers_init()函数如下:
static void timers_init(void)
{
    // Initialize timer module, making it use the scheduler
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true);


    /* err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
    APP_ERROR_CHECK(err_code); */
}
这个函数里的 APP_TIMER_INIT的app_timer_init函数里初始化了mp_nodes变量,如下:
    // Initialize timer node array初始化定时器节点数组
    m_node_array_size = max_timers;
    mp_nodes          = p_buffer;
这个变量在后期会使用


ble_stack_init() 初始化协议栈函数:
static void ble_stack_init(void)
{
    uint32_t err_code;


    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);


    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);
    
    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}
其中SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);初始化了协议栈,最主要的工作就是分配事件内存和调用sd函数初始化时钟等,最后生效语句是:err_code = sd_softdevice_enable(clock_source, softdevice_assertion_handler);
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);函数注册了调度器ble事件调度函数,这个是协议栈回调的函数,当然具体函数由用户编写,内容如下:
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    on_ble_evt(p_ble_evt);
    ble_conn_params_on_ble_evt(p_ble_evt);
    /*
    YOUR_JOB: Add service ble_evt handlers calls here, like, for example:
    ble_bas_on_ble_evt(&m_bas, p_ble_evt);
    */
}
这其中的on_ble_evt(p_ble_evt);函数定义了蓝牙协议栈发生相关事件的时候的相应操作,如下:
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    uint32_t                         err_code;
    static ble_gap_evt_auth_status_t m_auth_status;
    ble_gap_enc_info_t *             p_enc_info;


    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            nrf_gpio_pin_set(CONNECTED_LED_PIN_NO);
            nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;


        case BLE_GAP_EVT_DISCONNECTED:
            nrf_gpio_pin_clear(CONNECTED_LED_PIN_NO);
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
            advertising_start();
            break;


        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
                                                   BLE_GAP_SEC_STATUS_SUCCESS,
                                                   &m_sec_params);
            APP_ERROR_CHECK(err_code);
            break;


        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
            err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0);
            APP_ERROR_CHECK(err_code);
            break;


        case BLE_GAP_EVT_AUTH_STATUS:
            m_auth_status = p_ble_evt->evt.gap_evt.params.auth_status;
            break;


        case BLE_GAP_EVT_SEC_INFO_REQUEST:
            p_enc_info = &m_auth_status.periph_keys.enc_info;
            if (p_enc_info->div == p_ble_evt->evt.gap_evt.params.sec_info_request.div)
            {
                err_code = sd_ble_gap_sec_info_reply(m_conn_handle, p_enc_info, NULL);
                APP_ERROR_CHECK(err_code);
            }
            else
            {
                // No keys found for this device
                err_code = sd_ble_gap_sec_info_reply(m_conn_handle, NULL, NULL);
                APP_ERROR_CHECK(err_code);
            }
            break;


        case BLE_GAP_EVT_TIMEOUT:
            if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISEMENT)
            {
                nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);


                // Configure buttons with sense level low as wakeup source.
                nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
                                         BUTTON_PULL,
                                         NRF_GPIO_PIN_SENSE_LOW);
                
                // Go to system-off mode (this function will not return; wakeup will cause a reset)                
                err_code = sd_power_system_off();
                APP_ERROR_CHECK(err_code);
            }
            break;


        default:
            // No implementation needed.
            break;
    }
}
其中有两个地方现在可以看看:
case BLE_GAP_EVT_CONNECTED:
            nrf_gpio_pin_set(CONNECTED_LED_PIN_NO);
            nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;


case BLE_GAP_EVT_TIMEOUT:
            if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISEMENT)
            {
                nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);


                // Configure buttons with sense level low as wakeup source.
                nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
                                         BUTTON_PULL,
                                         NRF_GPIO_PIN_SENSE_LOW);
                
                // Go to system-off mode (this function will not return; wakeup will cause a reset)                
                err_code = sd_power_system_off();
                APP_ERROR_CHECK(err_code);
            }
            break;


这两个地方有一个现阶段比较重要的一句话:
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
本实验中连接上蓝牙后,本来点亮的D1灭了,而不亮的D2点亮了,作用的的语句就在这里。而点亮D1的在advertising_start函数中。


scheduler_init() 函数初始化调度器,其最终调用的函数是APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE),其中的SCHED_MAX_EVENT_DATA_SIZE定义了最大的事件数据大小,SCHED_QUEUE_SIZE定义了一共有多少个数据,定义如下:
#define SCHED_MAX_EVENT_DATA_SIZE       sizeof(app_timer_event_t)                   /**< Maximum size of scheduler events. Note that scheduler BLE stack events do not contain any data, as the events are being pulled from the stack in the event handler. */
#define SCHED_QUEUE_SIZE                10                                          /**< Maximum number of events in the scheduler queue. */


gap_params_init() GAP参数初始化函数 内容如下:
static void gap_params_init(void)
{
    uint32_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);
    /* YOUR_JOB: Use an appearance value matching the application's use case.
    err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
    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);
}
最主要的工作室设置蓝牙名称和初始化一些gap参数,这里不多说


advertising_init广播初始化函数,内容如下:
static void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;
    uint8_t       flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
    // YOUR_JOB: Use UUIDs for service(s) used in your application.
    ble_uuid_t adv_uuids[] = {{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}};
    // Build and set advertising data
    memset(&advdata, 0, sizeof(advdata));
    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance      = true;
    advdata.flags.size              = sizeof(flags);
    advdata.flags.p_data            = &flags;
    advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = adv_uuids;
    err_code = ble_advdata_set(&advdata, NULL);
    APP_ERROR_CHECK(err_code);
}
这里最关键的是设置了uuid,这里我们可以分析一下uuid:
uuid的定义:0000xxxx-0000-1000-8000-00805F9B34FB
其中的x是每种设备唯一的id,我们可以查看到定义如下:
#define BLE_UUID_BATTERY_SERVICE                                 0x180F     /**< Battery service UUID. */
这个可以从手机客户端查看到,后面再说


services_init 本例子中该函数没有任何内容
conn_params_init函数都是蓝牙协议的函数,这里先不讲


sec_params_init  安全参数设置函数,内容如下:
static void sec_params_init(void)
{
    m_sec_params.timeout      = SEC_PARAM_TIMEOUT;
    m_sec_params.bond         = SEC_PARAM_BOND;
    m_sec_params.mitm         = SEC_PARAM_MITM;
    m_sec_params.io_caps      = SEC_PARAM_IO_CAPABILITIES;
    m_sec_params.oob          = SEC_PARAM_OOB;
    m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
    m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
}
这些参数在ble调度器函数on_ble_evt中使用,如下代码:
        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
                                                   BLE_GAP_SEC_STATUS_SUCCESS,
                                                   &m_sec_params);


timers_start函数本例程没有使用


advertising_start开始广播函数,这里开始蓝牙广播,要注意的一句话是打开了LED1
nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);


app_sched_execute  函数是事件切换函数,内筒如下:
void app_sched_execute(void)
{
    void *                    p_event_data;
    uint16_t                  event_data_size;
    app_sched_event_handler_t event_handler;


    // Get next event (if any), and execute handler
    while ((app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
    {
        event_handler(p_event_data, event_data_size);
    }
}


最后是电源管理函数,内容如下:
static void power_manage(void)
{
    uint32_t err_code = sd_app_evt_wait();
    APP_ERROR_CHECK(err_code);
}

 

好了介绍,粗粗讲到这里

 

 

 

下载让我们看看实验图片:

这是青风开发板的实物图,未连接前:

nrf51822学习之第一个BLE程序分析_第1张图片

连接后实物:

在安卓手机端,可以看到下面的实验截图,搜索蓝牙,未连接:

nrf51822学习之第一个BLE程序分析_第2张图片

连接蓝牙,查看蓝牙具体内容:
nrf51822学习之第一个BLE程序分析_第3张图片

查看全局蓝牙内容:

nrf51822学习之第一个BLE程序分析_第4张图片

可以看到这里的uuid和我们在程序里设置的一样,都是0x801

 

下面的实验平台是BLE400开发板的实验结果,图片内容和上面的顺序是一样的,这里就不列出了

nrf51822学习之第一个BLE程序分析_第5张图片nrf51822学习之第一个BLE程序分析_第6张图片nrf51822学习之第一个BLE程序分析_第7张图片nrf51822学习之第一个BLE程序分析_第8张图片nrf51822学习之第一个BLE程序分析_第9张图片

 

 

 

 

 

下面上传本实验的工程代码:

http://download.csdn.net/detail/chengdong1314/9534594

 

题外话:查看文件用Source Insight软件是最好的软件了,本历程查看文件就用它,并且在工程的SI目录就是Source Insight工程目录,并不是BLE的目录,这里上传Source Insight软件安装包,十分的小:

http://download.csdn.net/detail/chengdong1314/9534611

你可能感兴趣的:(NORDIC,TI蓝牙)