zsbyg 发表于 2024-1-16 19:18

EFR32_BLE开发记录

<h2>什么是BLE</h2>

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

<h2>BLE如何进行通信</h2>

<h3>广播</h3>

<div>BLE分为:中心设备和外设设备。</div>

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

<div>连接建立后就以client-server方式联系</div>

<div>中心设备(手机)作为client</div>

<div>外设设备(手环)作为serve</div>

<h3>GATT协议</h3>

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

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

<div>Profile文件结构如下</div>

<div>Service1</div>

<div>Characterisitic1</div>

<div>Characterisitic2</div>

<div>Service2</div>

<div>Characterisitic</div>

<div>&hellip;.</div>

<div>每个profile包含若干个service,每个service包含若干个characteristic</div>

<div>而characteristic包含Properties ,value。</div>

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

<div>Properties的属性如下</div>

<table border="1">
        <tbody>
                <tr>
                        <td><strong>属性名称</strong></td>
                        <td><strong>描述</strong></td>
                </tr>
                <tr>
                        <td>read</td>
                        <td>支持 read 操作</td>
                </tr>
                <tr>
                        <td>write</td>
                        <td>支持 write 操作</td>
                </tr>
                <tr>
                        <td>notify</td>
                        <td>支持 notify 操作</td>
                </tr>
                <tr>
                        <td>indicate</td>
                        <td>支持 indicate 操作</td>
                </tr>
                <tr>
                        <td>当service端的蓝牙 characteristicr是Indicate 属性<br />
                        客户端就必须要以indicate方式来读写characteristic<br />
                        不能用其他三种。</td>
                        <td>&nbsp;</td>
                </tr>
        </tbody>
</table>

<div>无论是service还是characteristic最后都被封装成(Attribute</div>

<div>)att数据包。</div>

<div></div>

<div>ATT协议包括三部分 handle ,type,value</div>

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

<div>在蓝牙数据包中一般都有这个值</div>

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

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

<div>Value:蓝牙数据包具体携带的值。</div>

<h2>EFR32的BLE配置工具</h2>

<div>到此我们只关心三件事情。<br />
1.创建我们自定义的service和characteristic</div>

<div>2.用自定义的的characteristic向client发送数据</div>

<div>3.client接收数据</div>

<div>而GAP 和GATT都由EFR32的蓝牙SDK完成。</div>

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

<div>在使用IDE的配置工具配置自定义的BLE任务</div>

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

<div>在下载蓝牙例程,需要先下载蓝牙的bootloader</div>

<div></div>

<div>导入一个蓝牙温度的例程</div>

<div></div>

<div>下载</div>

<div><a href="https://www.silabs.com/documents/public/software/android-efr-connect.apk">https://www.silabs.com/documents/public/software/android-efr-connect.apk</a></div>

<div>如图所示</div>

<div></div>

<div>打开串口</div>

<div></div>

<div>打开IDE</div>

<div></div>

<div>看到有BLE组件</div>

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

<h3>蓝牙工程分析</h3>

<div>先来找蓝牙发送函数</div>

<div>搜索字符串&ldquo;Temperature:&rdquo;在工程中位置</div>

<div></div>

<div>在app.c的app_periodic_timer_cb函数里</div>

<div>这个函数是定时器的回调函数</div>

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

<div>sl_sensor_rht_get是温度获取函数</div>

<div>那么谁来调用app_periodic_timer_cb函数呢?</div>

<div>在app.的sl_bt_ht_temperature_measurement_indication_changed_cb</div>

<div>函数里调用用了app_periodic_timer_cb</div>

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

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

<div>在sl_bt_ht_temperature_measurement_indication_changed_cb</div>

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

<div>那sl_bt_ht_temperature_measurement_indication_changed_cb</div>

<div>是谁在调用?</div>

<div>是sl_bt_ht_on_event在调用</div>

<div>sl_bt_ht_on_event是蓝牙温度测量事件函数。</div>

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

<div></div>

<div>关心这几个部分。</div>

<div></div>

<div>sl_bt_gatt_server_write_attribute_value</div>

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

<div></div>

<div>gattdb_temperature_measurement</div>

<div></div>

<div>&nbsp; &nbsp; case sl_bt_evt_gatt_server_characteristic_status_id:</div>

<div>*@brief表示本地客户端特征配置</div>

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

<div>*远程GATT客户端在成功接收</div>

<div>*指示</div>

<div>*</div>

<div>来梳理下sl_bt_ht_on_event完成的事情</div>

<div>刚开始进入蓝牙温度任务</div>

<div>先执行&nbsp;health_thermometer_init();</div>

<div>往蓝牙gatt数据库中写入temperature_type的属性。</div>

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

<div>之后case sl_bt_evt_gatt_server_characteristic_status_id</div>

<div>说temperature_type的属性的蓝牙通信建立。</div>

<div>gattdb_temperature_measurement == evt-&gt;data.evt_gatt_server_characteristic_status.characteristic</div>

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

<div>(sl_bt_gatt_server_client_config == (sl_bt_gatt_server_characteristic_status_flag_t)evt-&gt;data.evt_gatt_server_characteristic_status.status_flags</div>

<div>客户端的characteristic已经改变</div>

<div>执行sl_bt_ht_temperature_measurement_indication_changed_cb</div>

<div>当蓝牙温度att通信关闭执行</div>

<div>&nbsp;case sl_bt_evt_connection_closed_id:</div>

<div>&nbsp; &nbsp; &nbsp; sl_bt_connection_closed_cb(evt-&gt;data.evt_connection_closed.reason,</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;evt-&gt;data.evt_connection_closed.connection);</div>

<div>&nbsp; &nbsp; &nbsp; break;</div>

<div>接下来看看sl_bt_ht_on_event是由谁调用</div>

<div>在sl_bluebooth.c的sl_bt_process_event里调用。</div>

<div>这是处理蓝牙任务函数包括</div>

<div>&nbsp; sl_bt_in_place_ota_dfu_on_event(evt);</div>

<div>&nbsp; sl_gatt_service_device_information_on_event(evt);</div>

<div>&nbsp; sl_bt_ht_on_event(evt);</div>

<div>&nbsp; sl_bt_on_event(evt);//这是个虚函数里面没有代码</div>

<div>与配置工具对应</div>

<div></div>

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

<div>在sl_event_handler.c的sl_stack_process_action调用sl_bt_step</div>

<div>在sl_system_process_action.c的sl_system_process_action调用sl_stack_process_action</div>

<div>sl_system_process_action进行系统初始化和操作处理</div>

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

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

<div>&nbsp;* &nbsp; - platform_init &nbsp; &nbsp; &nbsp;-&gt; sl_platform_init()</div>

<div>&nbsp;* &nbsp; - driver_init &nbsp; &nbsp; &nbsp; &nbsp;-&gt; sl_driver_init()</div>

<div>&nbsp;* &nbsp; - service_init &nbsp; &nbsp; &nbsp; -&gt; sl_service_init()</div>

<div>&nbsp;* &nbsp; - stack_init &nbsp; &nbsp; &nbsp; &nbsp; -&gt; sl_stack_init()</div>

<div>&nbsp;* &nbsp; - internal_app_init &nbsp;-&gt; sl_internal_app_init()</div>

<div>sl_system_process_action()在main函数while中调用</div>

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

<div>如图</div>

<div></div>

<div>我们只需要关心</div>

<div>修改sl_bt_ht_on_event函数变成我们自己的</div>

<div></div>

<div>仿照修改即可</div>

<div>接下来看看发送函数</div>

<div>&nbsp; sc = sl_bt_ht_temperature_measurement_indicate(app_connection,</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;temperature,</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;false);</div>

<div>app_connection,蓝牙句柄</div>

<div>temperature温度值</div>

<div>false是摄氏度/true是华氏温度</div>

<div>在sl_bt_ht_temperature_measurement_indicate里进行了温度的数据封装操作</div>

<div>temperature_measurement_val_to_buf</div>

<div>把温度数据封装到五个字节的数据包里</div>

<div>第一个字节是温度类型</div>

<div>其余是温度值</div>

<div>&nbsp; uint32_t tmp_value = ((uint32_t)value &amp; 0x00ffffffu) \</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| ((uint32_t)(-3) &lt;&lt; 24);</div>

<div>&nbsp; buffer = fahrenheit ? TEMPERATURE_MEASUREMENT_FLAG_UNITS : 0;</div>

<div>&nbsp; buffer = tmp_value &amp; 0xff;</div>

<div>&nbsp; buffer = (tmp_value &gt;&gt; 8) &amp; 0xff;</div>

<div>&nbsp; buffer = (tmp_value &gt;&gt; 16) &amp; 0xff;</div>

<div>&nbsp; buffer = (tmp_value &gt;&gt; 24) &amp; 0xff;</div>

<div>要转回去</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; a= (data&lt;&lt;8)|data;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; a=(a&lt;&lt;8)|data;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; a=(a&lt;&lt;8)|data;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; a=a&amp;0xffffff;</div>

<div>用最底层sl_bt_gatt_server_send_indication发送</div>

<div>&nbsp; sc = sl_bt_gatt_server_send_indication(</div>

<div>&nbsp; &nbsp; connection,</div>

<div>&nbsp; &nbsp; gattdb_temperature_measurement,</div>

<div>&nbsp;&nbsp; sizeof(buf),</div>

<div>&nbsp; &nbsp; buf);</div>

<div>connection句柄</div>

<div>gattdb_temperature_measurement gatt的属性</div>

<div>&nbsp; sizeof(buf),数据包大小</div>

<div>buf数据包 是一个指针类型const uint8_t* value</div>

<div>自定义GATT的service characteristic 并发送</div>

<h3>进入blue配置工具</h3>

<div>配置GATT数据库</div>

<div></div>

<div>如图</div>

<div></div>

<div></div>

<div></div>

<div>GATT数据库</div>

<div></div>

<div>再添加一个</div>

<div></div>

<div>接下修改即可</div>

<div>在Health_Thermometer添加</div>

<div><strong>void</strong> <strong>demo_init</strong>()</div>

<div>{</div>

<div>}</div>

<div><strong>void</strong> <strong>sl_bt_demo_on_event</strong>(sl_bt_msg_t *evt)</div>

<div>{</div>

<div>// Handle stack events</div>

<div><strong>switch</strong> (SL_BT_MSG_ID(evt-&gt;header)) {</div>

<div><strong>case</strong> sl_bt_evt_system_boot_id:</div>

<div>demo_init();</div>

<div><strong>break</strong>;</div>

<div><strong>case</strong> sl_bt_evt_connection_closed_id:</div>

<div>sl_bt_demo_connection_closed_cb(evt-&gt;data.evt_connection_closed.reason,</div>

<div>evt-&gt;data.evt_connection_closed.connection);</div>

<div><strong>break</strong>;</div>

<div><strong>case</strong> sl_bt_evt_gatt_server_characteristic_status_id:</div>

<div><strong>if</strong> (gattdb_test == evt-&gt;data.evt_gatt_server_characteristic_status.characteristic) {</div>

<div>// client characteristic configuration changed by remote GATT client</div>

<div><strong>if</strong> (<em>sl_bt_gatt_server_client_config</em> == (sl_bt_gatt_server_characteristic_status_flag_t)evt-&gt;data.evt_gatt_server_characteristic_status.status_flags) {</div>

<div>sl_bt_demo_indication_changed_cb(</div>

<div>evt-&gt;data.evt_gatt_server_characteristic_status.connection,</div>

<div>(sl_bt_gatt_client_config_flag_t)evt-&gt;data.evt_gatt_server_characteristic_status.client_config_flags);</div>

<div>}</div>

<div>// confirmation of indication received from remove GATT client</div>

<div><strong>else</strong> <strong>if</strong> (<em>sl_bt_gatt_server_confirmation</em> == (sl_bt_gatt_server_characteristic_status_flag_t)evt-&gt;data.evt_gatt_server_characteristic_status.status_flags) {</div>

<div>sl_bt_demo_indication_confirmed_cb(</div>

<div>evt-&gt;data.evt_gatt_server_characteristic_status.connection);</div>

<div>} <strong>else</strong> {</div>

<div>}</div>

<div>}</div>

<div><strong>break</strong>;</div>

<div><strong>default</strong>:</div>

<div><strong>break</strong>;</div>

<div>}</div>

<div>}</div>

<div>App.c里添加</div>

<div>//demo code</div>

<div><strong>#include</strong> &quot;gatt_db.h&quot;</div>

<div><strong>static</strong> app_timer_t app_periodic_timer1;</div>

<div><strong>static</strong> uint8_t app_connection1 = 0;</div>

<div><strong>void</strong> <strong>demo_timer_cb</strong>()</div>

<div>{printf(&quot;demo_timer_cb()&quot;);</div>

<div>uint8_t c=10;</div>

<div>sl_bt_gatt_server_send_indication(</div>

<div>app_connection1,</div>

<div>gattdb_test,//要发送数据的characteristic</div>

<div>1,</div>

<div>&amp;c);</div>

<div>}</div>

<div>/**************************************************************************//**</div>

<div>demo</div>

<div>* Indication changed callback</div>

<div>*</div>

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

<div>* the client.</div>

<div>*****************************************************************************/</div>

<div><strong>void</strong> <strong>sl_bt_demo_indication_changed_cb</strong>(uint8_t connection,</div>

<div>sl_bt_gatt_client_config_flag_t client_config)</div>

<div>{</div>

<div>app_connection1 = connection;</div>

<div>// Indication or notification enabled.</div>

<div><strong>if</strong> (<em>sl_bt_gatt_disable</em> != client_config) {</div>

<div>// Start timer used for periodic indications.</div>

<div>app_timer_start(&amp;app_periodic_timer1,</div>

<div>1 * 1000,</div>

<div>demo_timer_cb,</div>

<div>NULL,</div>

<div>true);</div>

<div>// Send first indication.</div>

<div>demo_timer_cb();</div>

<div>}</div>

<div>// Indications disabled.</div>

<div><strong>else</strong> {</div>

<div>// Stop timer used for periodic indications.</div>

<div>(<strong>void</strong>)app_timer_stop(&amp;demo_timer_cb);</div>

<div>}</div>

<div>}</div>

<div><strong>void</strong> <strong>sl_bt_demo_connection_closed_cb</strong>(uint16_t reason, uint8_t connection)</div>

<div>{</div>

<div>(<strong>void</strong>)reason;</div>

<div>(<strong>void</strong>)connection;</div>

<div>sl_status_t sc;</div>

<div>// Stop timer.</div>

<div>sc = app_timer_stop(&amp;app_periodic_timer1);</div>

<div>}</div>

<div>并在<strong>sl_bt_process_event</strong><strong>添加</strong><strong>sl_bt_demo_on_event</strong></div>

<h3>结果如图</h3>

<div></div>

<div>主要参考</div>

<div>Silabs的蓝牙demo文档</div>

<div><a href="https://www.silabs.com/documents/public/user-guides/ug103-14-fundamentals-ble.pdf">UG103.14: Bluetooth&reg; LE Fundamentals (silabs.com)</a></div>

<div><a href="https://www.silabs.com/documents/public/quick-start-guides/qsg169-bluetooth-sdk-v3x-quick-start-guide.pdf">QSG169: Bluetooth&reg; Quick-Start Guide for SDK v3.x and Higher (silabs.com)</a></div>

<div><a href="https://www.silabs.com/documents/public/user-guides/ug434-bluetooth-c-soc-dev-guide-sdk-v3x.pdf">ug434-bluetooth-c-soc-dev-guide-sdk-v3x.pdf (silabs.com)</a></div>

<div>博客园<a href="https://www.cnblogs.com/yongdaimi">夜行过客</a>专栏</div>

<div><a href="https://www.cnblogs.com/yongdaimi/category/1543239.html" target="_blank">https://www.cnblogs.com/yongdaimi/category/1543239.html</a></div>

<div>龙言飞语</div>

<div><a href="https://zhuanlan.zhihu.com/p/346972549" target="_blank">https://zhuanlan.zhihu.com/p/346972549</a></div>

Jacktang 发表于 2024-1-17 07:45

<p>EFR32的BLE配置工具的三件事情介绍的比较清楚明白</p>

zsbyg 发表于 2024-1-17 10:01

Jacktang 发表于 2024-1-17 07:45
EFR32的BLE配置工具的三件事情介绍的比较清楚明白

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/handshake.gif" width="48" /></p>

damiaa 发表于 2024-1-21 18:32

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan88.gif" width="59" />上次改了下esp32蓝牙配置wifi的那个例子,搞了android上的配置软件,还学了一段时间的android。 现在又差不多忘光了<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan34.gif" width="48" /></p>

zsbyg 发表于 2024-1-22 21:41

damiaa 发表于 2024-1-21 18:32
上次改了下esp32蓝牙配置wifi的那个例子,搞了android上的配置软件,还学了一段时间的android。 现在又差不 ...

<p>我之前准备在Android上做BLE上位机。看了安卓开发课后觉得安卓上BLE开发太麻烦了。转而用python</p>

<p>&nbsp;</p>
页: [1]
查看完整版本: EFR32_BLE开发记录