NRF51822蓝牙服务(2)——按键通知

前言

上篇已经通过一个LED读写实验,学习了如何添加一个私有服务。现在,我们就在LED读写实验的基础上再增加一个按键通知的功能。

实验分析

首先,我们先看看开发板的硬件连接:

从这里可以看出,button0和button1分别对应P0.16~P0.17引脚,当按下按键的时候引脚电平会被拉低。熟悉单片机开发的同学应该了解,这种情况我们的IO需要配置成上拉以提高引脚的抗干扰能力。

这里我们可以直接使用官方提供的应用驱动文件:工程目录\components\libraries\button\app_button.c和app_button.h文件

app_button.h:

#define APP_BUTTON_ACTIVE_HIGH 1          /**< Indicates that a button is active high. */
#define APP_BUTTON_ACTIVE_LOW  0          /**< Indicates that a button is active low. */

/**@brief Button configuration structure. */
typedef struct
{
    uint8_t              pin_no;           /**< Pin to be used as a button. */
    uint8_t              active_state;     /**< APP_BUTTON_ACTIVE_HIGH or APP_BUTTON_ACTIVE_LOW. */
    nrf_gpio_pin_pull_t  pull_cfg;         /**< Pull-up or -down configuration. */
    app_button_handler_t button_handler;   /**< Handler to be called when button is pushed. */
} app_button_cfg_t;

这里可以看到分别定义了按键的电平和按键初始化结构体,这两个将会在c文件中被调用。

app_button.c:

uint32_t app_button_init(app_button_cfg_t *             p_buttons,
                         uint8_t                        button_count,
                         uint32_t                       detection_delay)
{
    uint32_t err_code;
    
    if (detection_delay < APP_TIMER_MIN_TIMEOUT_TICKS)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    if (!nrf_drv_gpiote_is_init())
    {
        err_code = nrf_drv_gpiote_init();
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    // Save configuration.
    mp_buttons          = p_buttons;
    m_button_count      = button_count;
    m_detection_delay   = detection_delay;

    m_pin_state      = 0;
    m_pin_transition = 0;
    
    while (button_count--)
    {
        app_button_cfg_t * p_btn = &p_buttons[button_count];

        nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(false);
        config.pull = p_btn->pull_cfg;
        
        err_code = nrf_drv_gpiote_in_init(p_btn->pin_no, &config, gpiote_event_handler);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    // Create polling timer.
    return app_timer_create(&m_detection_delay_timer_id,
                            APP_TIMER_MODE_SINGLE_SHOT,
                            detection_delay_timeout_handler);
}

c文件中,我们比较关心的是按键的初始化函数,有兴趣的同学可以了解下,这里不再做深入分析,知道官方提供这个函数即可。

接着,我们需要回到上篇我们添加的LED服务驱动文件里面,将按键的特征值加上即可,这里不再开新的服务。

ble_lbs.c:

/**@brief Function for adding the Button Characteristic.
 *
 * @param[in] p_lbs      LED Button Service structure.
 * @param[in] p_lbs_init LED Button Service initialization structure.
 *
 * @retval NRF_SUCCESS on success, else an error value from the SoftDevice
 */
static uint32_t button_char_add(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init)
{
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;
    
    memset(&char_md, 0, sizeof(char_md));
    
    char_md.char_props.read   = 1;
    char_md.char_props.notify = 1;
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;

    ble_uuid.type = p_lbs->uuid_type;
    ble_uuid.uuid = LBS_UUID_BUTTON_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = sizeof(uint8_t);
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = sizeof(uint8_t);
    attr_char_value.p_value      = NULL;

    return sd_ble_gatts_characteristic_add(p_lbs->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_lbs->button_char_handles);
}

这里先定义特征值添加函数,使能read和notify功能。这个函数将会在ble_lbs_init函数中被调用。

uint32_t ble_lbs_on_button_change(ble_lbs_t * p_lbs, uint8_t button_state)
{
    ble_gatts_hvx_params_t params;
    uint16_t len = sizeof(button_state);
    
    memset(&params, 0, sizeof(params));
    params.type = BLE_GATT_HVX_NOTIFICATION;
    params.handle = p_lbs->button_char_handles.value_handle;
    params.p_data = &button_state;
    params.p_len = &len;
    
    return sd_ble_gatts_hvx(p_lbs->conn_handle, &params);
}

最后,我们还要把按键处理回调函数添加上。这个函数将会在应用层被调用。其实,也可以使用sd_ble_gatts_value_set函数一次性设置特征值,但是这个函数是更新一个可读的值。当作为通知使用的时候,调用sd_ble_gatts_hvx不需要设置特征值和值的长度。

main.c:

 *
 * @param[in] pin_no        The pin that the event applies to.
 * @param[in] button_action The button action (press/release).
 */
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    uint32_t err_code;

    switch (pin_no)
    {
        case LEDBUTTON_BUTTON_PIN:
            err_code = ble_lbs_on_button_change(&m_lbs, button_action);
            if (err_code != NRF_SUCCESS &&
                err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
                err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        default:
            APP_ERROR_HANDLER(pin_no);
            break;
    }
}


/**@brief Function for initializing the button handler module.
 */
static void buttons_init(void)
{
    uint32_t err_code;

    //The array must be static because a pointer to it will be saved in the button handler module.
    static app_button_cfg_t buttons[] =
    {
        {LEDBUTTON_BUTTON_PIN, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, button_event_handler}
    };

    err_code = app_button_init(buttons, sizeof(buttons) / sizeof(buttons[0]),
                               BUTTON_DETECTION_DELAY);
    APP_ERROR_CHECK(err_code);
}

应用层中,我们主要做的是添加按键初始化函数和按键处理函数,按键处理函数在初始化函数中被注册。最后,将初始化函数添加到main函数中即可。

到这里,就已经完成了任务。

结果验证

  • 手机连接蓝牙,我们可以看到有一个新增的UUID为1523的服务,在其下包含一个UUID为1525和一个UUID为1524的特制值;
  • 1524特征值里包含了read和notify两个属性;
  • 打开测试软件的监听功能,按下button0按键,会发现按下收到0x01,释放收到0x00;

总结

通过这个实验我们可以了解如何使用官方提供的应用驱动代码,快速添加按键服务。其他的串口也可以使用官方的代码尝试添加fifo功能等。

猜你喜欢

转载自blog.csdn.net/SammySum/article/details/103378393