注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
zsbyg的个人空间 https://home.eeworld.com.cn/space-uid-1278566.html [收藏] [复制] [分享] [RSS]
日志

EFR32_BLE开发记录

已有 473 次阅读2024-1-16 19:18 |个人分类:嵌入式

什么是BLE



BLE:低功耗蓝牙,采用蓝牙4.0技术具有低成本,短距离特点。可以用于电子手环,蓝牙门锁等场景。


BLE如何进行通信



广播



BLE分为:中心设备和外设设备。


外设设备用GAP协议以广播形式告知中心设备,本机蓝牙可以被链接。中心设备可选择对发送过来的广播数据回复。回复的内容有设备名称,设备地址。也可不回复。


连接建立后就以client-server方式联系


中心设备(手机)作为client


外设设备(手环)作为serve


GATT协议



链接建立后,中心设备与外设设备用GATT协议通信


蓝牙应用层的协议,以profile文件的形式存储。


Profile文件结构如下


Service1


Characterisitic1


Characterisitic2


Service2


Characterisitic


….


每个profile包含若干个service,每个service包含若干个characteristic


而characteristic包含Properties ,value。


Properties 规定了cilent应以怎样的方式处理characteristic的内容


Properties的属性如下



       
               
                       
                       
               
               
                       
                       
               
               
                       
                       
               
               
                       
                       
               
               
                       
                       
               
               
                       
                       
               
       
属性名称描述
read支持 read 操作
write支持 write 操作
notify支持 notify 操作
indicate支持 indicate 操作
当service端的蓝牙 characteristicr是Indicate 属性

                        客户端就必须要以indicate方式来读写characteristic

                        不能用其他三种。
 


无论是service还是characteristic最后都被封装成(Attribute


)att数据包。




ATT协议包括三部分 handle ,type,value


Handle: Attribute句柄。Client要访问Server的Attribute,都是通过这个句柄来访问的


在蓝牙数据包中一般都有这个值


Type :由UUID来定义。蓝牙联盟有统一的UUID设备标识。


具体使用使用时可以自定义也可用蓝牙联盟的。


Value:蓝牙数据包具体携带的值。


EFR32的BLE配置工具



到此我们只关心三件事情。

1.创建我们自定义的service和characteristic


2.用自定义的的characteristic向client发送数据


3.client接收数据


而GAP 和GATT都由EFR32的蓝牙SDK完成。


为了方便起见,我们首先分析一下EFR32的温度传感器例程代码。


在使用IDE的配置工具配置自定义的BLE任务


EFR32把蓝牙程序分成了两部分,一部分是bootloader,另一部分是蓝牙程序。


在下载蓝牙例程,需要先下载蓝牙的bootloader




导入一个蓝牙温度的例程




下载




如图所示




打开串口




打开IDE




看到有BLE组件


EFR32开发蓝牙工程分析及自定义一个蓝牙任务发送数据



蓝牙工程分析



先来找蓝牙发送函数


搜索字符串“Temperature:”在工程中位置




在app.c的app_periodic_timer_cb函数里


这个函数是定时器的回调函数


周期性的获取温度值,并通过app_log_info和sl_bt_ht_temperature_measurement_indicate发送到串口与蓝牙。


sl_sensor_rht_get是温度获取函数


那么谁来调用app_periodic_timer_cb函数呢?


在app.的sl_bt_ht_temperature_measurement_indication_changed_cb


函数里调用用了app_periodic_timer_cb


通过源码可知,这个函数是在蓝牙连接建立后,client访问server中characteristic中的temperature_measurement子项时调用的。


调用方式是indicate。意思是server端发送完成后,client需要回复确认收到。


在sl_bt_ht_temperature_measurement_indication_changed_cb


进行了开启定时器,绑定回调函数操作


那sl_bt_ht_temperature_measurement_indication_changed_cb


是谁在调用?


是sl_bt_ht_on_event在调用


sl_bt_ht_on_event是蓝牙温度测量事件函数。


这个函数完成了温度测量的 characteristic从开始发送到发送完成关闭定时器的操作。




关心这几个部分。




sl_bt_gatt_server_write_attribute_value


在本地 GATT 数据库中写入属性值。如果本地 GATT 数据库中的属性具有指示或通知属性,且客户端已启用通知或指示功能,则写入该属性的值不会触发向远程 GATT 客户端发送通知或指示。




gattdb_temperature_measurement




    case sl_bt_evt_gatt_server_characteristic_status_id:


*@brief表示本地客户端特征配置


*描述符被远程GATT客户端更改,或者来自的确认


*远程GATT客户端在成功接收


*指示


*


来梳理下sl_bt_ht_on_event完成的事情


刚开始进入蓝牙温度任务


先执行 health_thermometer_init();


往蓝牙gatt数据库中写入temperature_type的属性。


温度数据发送都是按照这个属性(att)发送的


之后case sl_bt_evt_gatt_server_characteristic_status_id


说temperature_type的属性的蓝牙通信建立。


gattdb_temperature_measurement == evt->data.evt_gatt_server_characteristic_status.characteristic


判断蓝牙句柄拿到的uuid是否是gatt数据库中有的


(sl_bt_gatt_server_client_config == (sl_bt_gatt_server_characteristic_status_flag_t)evt->data.evt_gatt_server_characteristic_status.status_flags


客户端的characteristic已经改变


执行sl_bt_ht_temperature_measurement_indication_changed_cb


当蓝牙温度att通信关闭执行


 case sl_bt_evt_connection_closed_id:


      sl_bt_connection_closed_cb(evt->data.evt_connection_closed.reason,


                                 evt->data.evt_connection_closed.connection);


      break;


接下来看看sl_bt_ht_on_event是由谁调用


在sl_bluebooth.c的sl_bt_process_event里调用。


这是处理蓝牙任务函数包括


  sl_bt_in_place_ota_dfu_on_event(evt);


  sl_gatt_service_device_information_on_event(evt);


  sl_bt_ht_on_event(evt);


  sl_bt_on_event(evt);//这是个虚函数里面没有代码


与配置工具对应




在在sl_bluebooth.c的sl_bt_step里调用。sl_bt_step轮询蓝牙堆栈中的事件并进行处理


在sl_event_handler.c的sl_stack_process_action调用sl_bt_step


在sl_system_process_action.c的sl_system_process_action调用sl_stack_process_action


sl_system_process_action进行系统初始化和操作处理


*此函数调用一组自动生成的函数,这些函数位于`autogen/sl_event_handler.c`中。


可以使用事件处理程序组件提供的事件处理程序API为以下事件注册处理程序:


 *   - platform_init      -> sl_platform_init()


 *   - driver_init        -> sl_driver_init()


 *   - service_init       -> sl_service_init()


 *   - stack_init         -> sl_stack_init()


 *   - internal_app_init  -> sl_internal_app_init()


sl_system_process_action()在main函数while中调用


到这里已经把应用相关代码过了一遍


如图




我们只需要关心


修改sl_bt_ht_on_event函数变成我们自己的




仿照修改即可


接下来看看发送函数


  sc = sl_bt_ht_temperature_measurement_indicate(app_connection,


                                                 temperature,


                                                 false);


app_connection,蓝牙句柄


temperature温度值


false是摄氏度/true是华氏温度


在sl_bt_ht_temperature_measurement_indicate里进行了温度的数据封装操作


temperature_measurement_val_to_buf


把温度数据封装到五个字节的数据包里


第一个字节是温度类型


其余是温度值


  uint32_t tmp_value = ((uint32_t)value & 0x00ffffffu) \


                       | ((uint32_t)(-3) << 24);


  buffer[0] = fahrenheit ? TEMPERATURE_MEASUREMENT_FLAG_UNITS : 0;


  buffer[1] = tmp_value & 0xff;


  buffer[2] = (tmp_value >> 8) & 0xff;


  buffer[3] = (tmp_value >> 16) & 0xff;


  buffer[4] = (tmp_value >> 24) & 0xff;


要转回去


        a= (data[4]<<8)|data[3];


        a=(a<<8)|data[2];


        a=(a<<8)|data[1];


        a=a&0xffffff;


用最底层sl_bt_gatt_server_send_indication发送


  sc = sl_bt_gatt_server_send_indication(


    connection,


    gattdb_temperature_measurement,


   sizeof(buf),


    buf);


connection句柄


gattdb_temperature_measurement gatt的属性


  sizeof(buf),数据包大小


buf数据包 是一个指针类型const uint8_t* value


自定义GATT的service characteristic 并发送


进入blue配置工具



配置GATT数据库




如图








GATT数据库




再添加一个




接下修改即可


在Health_Thermometer添加


void demo_init()


{


}


void sl_bt_demo_on_event(sl_bt_msg_t *evt)


{


// Handle stack events


switch (SL_BT_MSG_ID(evt->header)) {


case sl_bt_evt_system_boot_id:


demo_init();


break;


case sl_bt_evt_connection_closed_id:


sl_bt_demo_connection_closed_cb(evt->data.evt_connection_closed.reason,


evt->data.evt_connection_closed.connection);


break;


case sl_bt_evt_gatt_server_characteristic_status_id:


if (gattdb_test == evt->data.evt_gatt_server_characteristic_status.characteristic) {


// client characteristic configuration changed by remote GATT client


if (sl_bt_gatt_server_client_config == (sl_bt_gatt_server_characteristic_status_flag_t)evt->data.evt_gatt_server_characteristic_status.status_flags) {


sl_bt_demo_indication_changed_cb(


evt->data.evt_gatt_server_characteristic_status.connection,


(sl_bt_gatt_client_config_flag_t)evt->data.evt_gatt_server_characteristic_status.client_config_flags);


}


// confirmation of indication received from remove GATT client


else if (sl_bt_gatt_server_confirmation == (sl_bt_gatt_server_characteristic_status_flag_t)evt->data.evt_gatt_server_characteristic_status.status_flags) {


sl_bt_demo_indication_confirmed_cb(


evt->data.evt_gatt_server_characteristic_status.connection);


} else {


}


}


break;


default:


break;


}


}


App.c里添加


//demo code


#include "gatt_db.h"


static app_timer_t app_periodic_timer1;


static uint8_t app_connection1 = 0;


void demo_timer_cb()


{printf("demo_timer_cb()");


uint8_t c=10;


sl_bt_gatt_server_send_indication(


app_connection1,


gattdb_test,//要发送数据的characteristic


1,


&c);


}


/**************************************************************************//**


demo


* Indication changed callback


*


* Called when indication of temperature measurement is enabled/disabled by


* the client.


*****************************************************************************/


void sl_bt_demo_indication_changed_cb(uint8_t connection,


sl_bt_gatt_client_config_flag_t client_config)


{


app_connection1 = connection;


// Indication or notification enabled.


if (sl_bt_gatt_disable != client_config) {


// Start timer used for periodic indications.


app_timer_start(&app_periodic_timer1,


1 * 1000,


demo_timer_cb,


NULL,


true);


// Send first indication.


demo_timer_cb();


}


// Indications disabled.


else {


// Stop timer used for periodic indications.


(void)app_timer_stop(&demo_timer_cb);


}


}


void sl_bt_demo_connection_closed_cb(uint16_t reason, uint8_t connection)


{


(void)reason;


(void)connection;


sl_status_t sc;


// Stop timer.


sc = app_timer_stop(&app_periodic_timer1);


}


并在sl_bt_process_event添加sl_bt_demo_on_event


结果如图





主要参考


Silabs的蓝牙demo文档








博客园夜行过客专栏




龙言飞语



本文来自论坛,点击查看完整帖子内容。

全部作者的其他最新日志
评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章