NRF52832 BLE手机控制LED灯亮灭

手机APP使用到的是NORDIC官网下载的nRF Connect

BLE通信的载体都是通过数据文件或属性文件来实现。也就是profile。他们的层级关系是这样的
在这里插入图片描述在BLE协议栈里面可以定义很多的profile。定义很多的server,每个server也可以定义很多的characteristic。

--------------------------------------------------分割线-----------------------------------------
例程解析
main函数

	log_init();             // 初始化LOG打印,由RTT工作
    timers_init();          // 初始化定时器
    power_management_init();// 初始化电源控制
    ble_stack_init();       // 初始化BLE栈堆
    gap_params_init();      // 初始化GAP
    gatt_init();            // 初始化GATT
    services_init();        // 初始化Service
    
    advertising_init();     // 初始化广播

    // 初始化外设
    LED_Init();
    
    // 打印例程名称
    NRF_LOG_INFO("demo0: simple peripheral");

    advertising_start();    // 开启广播

1.在main函数中,也是正常的初始化GAP和GATT。
2.初始化完后就初始化service------这一步是重点
3.然后初始化硬件外设
4.初始化广播并开始广播。

第二步中的初始化service
这一步最终要实现的就是调用库函数的sd_ble_gatts_service_add()添加service。characteristic_add()为服务添加属性。

来具体看看。services_init和ble_led_init都是自定义函数。

//******************************************************************
// fn : services_init
//
// brief : 初始化复位(本例程展示NUS:Nordic Uart Service)
//
// param : none
//
// return : none
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 ,方便管理和代码。也可以不定义这个结构体。直接将handler函数传递给m_led变量。
  • 之后才是调用两个库函数(sd_ble_gatts_service_add和characteristic_add),将配置传入库里面。
//******************************************************************************
// fn :ble_led_init
//
// brief : 初始化LED服务
//
// param : p_led -> led服务结构体
//         p_led_init -> led服务初始化结构体
//
// return : uint32_t -> 成功返回SUCCESS,其他返回ERR NO.
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;

    // 初始化服务结构体
    p_led->led_write_handler = p_led_init->led_write_handler;
    
    // 添加服务(128bit UUID),uuid_type由此判断是128位
    ble_uuid128_t base_uuid = {LED_UUID_BASE};
    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;
    
    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;
    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;

    return characteristic_add(p_led->service_handle, &add_char_params, &p_led->led_char_handles);
}
  • 函数sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid,
    &p_led->service_handle);将UUID和事件处理函数绑定了。
  • 函数characteristic_add(p_led->service_handle, &add_char_params,
    &p_led->led_char_handles);将service和属性处理函数绑定了。

注意:通过跟踪发现led_char_handles其实是一个空指针。并没有具体的实函数。

属性的权限。上面例程使用了read和write。如果需要主动上报通知,则需要打开notify

    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;

工作的数据流向

1.通过以上的配置后。若收到手机发过来的数据。
2.根据UUID调用p_led->service_handle这个指针指向m_led->ble_led_on_ble_evt。具体是这样的指针p_led实体是m_led。定义m_led的时候传入了参数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;
    }
}

判断是否是WRITE事件。是的话就调用on_write自定义函数

//******************************************************************************
// 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);
    }
}

on_write会判断(p_evt_write->handle == p_led->led_char_handles.value_handle)。这里判断是哪一个属性。然后调用led_write_handler这个函数。此函数是操作LED的函数。

到这里。整个链路就通了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值