NRF52832蓝牙的Profile之 LED灯读写 自己理解的

NRF52832蓝牙的Profile之 LED灯读写 自己理解的

关于自定义服务的一般流程

首先我们可以看到在main函数里面有一个ServiceInit()函数。

这个函数实现了我们自定义服务的初始化。

函数实现如下:

static void services_init(void)
{
    uint32_t           err_code;
    ble_led_init_t     led_init;
    //led状态默认值
    uint8_t led_value[LED_UUID_CHAR_LEN] = {1,1,1,1};

	//清除赋值
    memset(&led_init, 0, sizeof(led_init));

    //设置特征值初始取值。
    led_init.p_led_value = led_value;
    //设置处理主机(GATTC)Write事件的处理函数,用于接收数据。
    led_init.led_write_handler = led_write_handler;
    //函数参数是指针的形式,因此携带的参数要使用&符号,取变量地址。
    err_code = ble_led_init(&m_led, &led_init);
    APP_ERROR_CHECK(err_code);
}

其中,ble_led_init_t 类型是我们自己定义的一个结构体,里面一般包含一些简单的信息,例如led的初始值,和用于写led的回调函数。

ble_led_init_t的声明如下:

typedef void (*ble_led_write_handler_t) (uint8_t * new_state); 
typedef struct
{
    uint8_t *p_led_value;
    ble_led_write_handler_t led_write_handler; //我们自己定义的回调函数,一般包含自己需要接收的参数
} ble_led_init_t;

我们回到ServiceInit函数,接着往下看:

初始化了led_value[LED_UUID_CHAR_LEN]这个数组的值,其中LED_UUID_CHAR_LEN这个宏表示接收到特征值的长度。

接下来为我们自己定义的结构体赋值,接着就调用了ble_led_init()这个函数。

我们主要的工作都是在这个函数里面完成的。我们可以先看一下给这个函数的2个参数 &m_led,&led_init;

led_init是我们刚刚上面定义的结构体,里面基本上是一个回调函数的指针。这里不用管他。

最主要的是我们的第一个参数,m_led。

我们可以进行跟踪,看到m_led是在main函数外面用一个宏来声明的。

BLE_LED_DEF(m_led);                 // LED实例

我们来分解一下,分解后BLE_LED_DEF(m_led)这个实际上是

static ble_led_t m_led;
NRF_SDH_BLE_OBSERVER( m_led_obs,
    		          BLE_LED_BLE_OBSERVER_PRIO,
        		      ble_led_on_ble_evt,&m_led);

NRF_SDH_BLE_OBSERVER这个函数是像系统注册一个观察者,

其中底层我们不必深究,这里我们只需要知道,通过这个函数注册了一个BLE_OBSERVER观察者,

当系统发生BLE事件的时候。就会调用我们这里定义的ble_led_on_ble_evt回调方法,ble_led_t这个结构体中

我们定义了一些系统事件的句柄。原型如下:

// LED服务结构体
struct ble_led_s
{
    uint8_t                     uuid_type;           //UUID的类型
    uint16_t                    service_handle;      //服务的句柄
    ble_gatts_char_handles_t    led_char_handles;    //特征的句柄
    ble_led_write_handler_t     led_write_handler;   //当特征接收到写入值的时候,调用的回调函数。
};

我们接下来进入ble_led_init函数,这个函数是我们自己写的

这里的第一个参数p_led,就是我们上面讲的m_led结构体。第2个参数是我们定义另外一个结构体。

uint32_t ble_led_init(ble_led_t * p_led, const ble_led_init_t * p_led_init)
{
    uint32_t              err_code;
    ble_uuid_t            ble_uuid;
    ble_add_char_params_t add_char_params;

    // 初始化服务结构体
    // 给我们的m_led结构体中的回调函数赋值
    p_led->led_write_handler = p_led_init->led_write_handler;
    
    // 添加服务(128bit UUID),uuid_type由此判断是128位
    ble_uuid128_t base_uuid = {LED_UUID_BASE};
    //这个函数的功能是得到一个uuid的类型。写法基本上是固定的。第二个参数是得到uuid_type的类型
    err_code = sd_ble_uuid_vs_add(&base_uuid, &p_led->uuid_type);
    
    VERIFY_SUCCESS(err_code);

    ble_uuid.type = p_led->uuid_type;  //type value = 2(BLE_UUID_TYPE_VENDOR_BEGIN), is 128bit uuid;  value = 1(BLE_UUID_TYPE_BLE), is 16bit uuid
    ble_uuid.uuid = LED_UUID_SERVICE;
    //注册服务,作为主服务,传入我们刚才生成的uuid结构体,第3个参数为返回的服务的句柄,我们保存到我们定义的m_led结构体中
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_led->service_handle);
    VERIFY_SUCCESS(err_code);

    // 添加LED特征值(属性是Write和Read、长度是4)
    memset(&add_char_params, 0, sizeof(add_char_params));
    add_char_params.uuid             = LED_UUID_CHAR;				//特征的uui
    add_char_params.uuid_type        = p_led->uuid_type;
    add_char_params.init_len         = LED_UUID_CHAR_LEN;				//数据的初始长度
    add_char_params.max_len          = LED_UUID_CHAR_LEN;				//数据的最大
    add_char_params.p_init_value     = p_led_init->p_led_value;			//数据的初始值
    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;					//安全性为开放

    //调用特征添加方法,把我们的特征添加到服务里面去,所以这里的第一个参数为我们建立的服务的句柄,上面得到的
    //第2个参数为我们为特征配置的一些基本信息,第3个参数为返回值,得到的是特征的句柄,保存到我们的结构体中。
    return characteristic_add(p_led->service_handle, &add_char_params, &p_led->led_char_handles);
}

这样就完成了服务的添加。

因为我们已经向系统注册了一个观察者对象。所以在系统发生一些BLE的事件时,都会调用我们上面写的回调函数

ble_led_on_ble_evt()这个回调函数。

这个回调函数的写法如下:

//******************************************************************************
// fn :ble_led_on_ble_evt
//
// brief : BLE事件处理函数
//
// param : p_ble_evt -> ble事件
//         p_context -> ble事件处理程序的参数(暂时理解应该是不同的功能,注册时所携带的结构体参数)

// return : none
void ble_led_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    ble_led_t * p_led = (ble_led_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        // GATTClient的Write事件,本从机里对应为GATTS(Server端)
        case BLE_GATTS_EVT_WRITE:
            on_write(p_led, p_ble_evt);
            break;

        default:
            break;
    }
}

//******************************************************************************
// fn :on_write
//
// brief : 处理Write事件的函数。该事件来自主机(GATTC)的Write写特征值
//
// param : p_led -> led服务结构体
//         p_ble_evt -> ble事件
//
// return : none
static void on_write(ble_led_t * p_led, ble_evt_t const * p_ble_evt)
{
    ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

    //参数判断和筛选
    if (   (p_evt_write->handle == p_led->led_char_handles.value_handle)
        && (p_evt_write->len <= LED_UUID_CHAR_LEN)
        && (p_led->led_write_handler != NULL))
    {
        //调用mainc中service_init函数设置的处理函数。并传递从无线端接收到的数据。
        p_led->led_write_handler((uint8_t*)p_evt_write->data);
    }
}

我们可以看到第二个参数为一个上下文的指针,这里我们可以理解为我们注册的时候,给的那个结构体对象。也就是上面所说的m_led这个结构体。

第一个参数为发生的事件,我们可以用switch语句对我们感兴趣的事件进行处理。

这样我们的整个流程就已经完成了!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值