这是一篇基于应用的说明文章,
目的在于让大家能够快速的理解沁恒微蓝牙 GATT 的应用开发框架 ...... 矜辰所致
前言
对于刚开始接触 BLE 蓝牙的小伙伴来说,学习的时候往往会去看一些基础介绍,那必然会遇到 BLE 蓝牙协议栈的说明,协议栈分为多层,这对于初学者来说很难区分,即便所有的资料教材可能都告诉了你,每个层是干什么用的,但如果只看理论缺少实际的应用支持,还是会有很多小伙伴觉得难以理解。这是正常的,实际上如果不是专门的想研究协议栈,我们可以不需要了解那么清楚,有个大概认知再结合实际示例也可以正常的开发应用。
GATT 的使用简单来说就是如何实现设备间的数据交互,对于我们应用而言,我们更加关注的是我们如何使用代码实现我们的功能。
所以本文我们以快速应用为目的,结合沁恒微 EVT 示例,给家梳理一下 GATT 在实际应用上的体现和框架。
相关博文: CH58x/CH59x 系列芯片从机示例解析 CH585 蓝牙 主机示例 Central 解析 一文搞清 BLE 蓝牙 UUID 沁恒微 蓝牙 芯片 TMOS 使用
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
写在最前面:
GATT 核心就是 BLE 设备之间用服务和特征值做数据交互,应用上就是定义服务(Service),添加特征值(Characteristic),处理读写和通知。
一、基本说明
在刚接触 BLE 蓝牙协议栈查看资料的时候,大家估计都能接触到类似下面的协议栈框架图:

然后在后面的介绍可能就会有一些文字类的描述来表示每一层都是干什么的,博主之前的时候也是有博文说明:

我们通过理论大体上可以确定的是,GATT 管理数据的交互,但是最近用起沁恒微的芯片,遇到带有专业术语的说明,还是容易犯迷糊,即便我知道哪部分代码是数据收发的接口,起初也很难将其与 GATT 联系起来,所以这也是想写本片博客的原因之一。
在沁恒微官方 EVT 里面,有一份《沁恒低功耗蓝牙软件开发参考手册》:
(基础的文档资料沁恒微已经做得很好很全了,只是往往大家并不习惯去看官方,宁愿自己网上搜,但是又不知道如何筛选,还可能搞得一头雾水,其实官方文档说明其实是很好的入门文档,再配合示例,加上自己的测试验证,这样才能更快的上手一块芯片。)

里面也有关于 GATT 层应用的说明:

文档博主也不是看一次就明白了,也看了好些遍的,随着自己学习的深入回头来看,很多东西就是手册中说明过的, 本文也是参考该文档中的内容。
GATT(Generic Attribute Profile)就是一套标准化的层级结构 + 操作规则,让两个 BLE 设备(客户端 / 服务器)能有结构、有语义地交换数据。GATT 核心就是:BLE 设备之间用 服务 (Service) + 特征值 (Characteristic) 做结构化数据交互 。
GATT 使用对于应用来说来说就是:定义服务 → 添加特征 → 实现读写 / 通知 / 指示 → 处理事件。下面我们会详细介绍。
二、GATT 层的应用
本小节我们就基于官方 EVT 的工程,来说明一下官方 GATT 层的代码框架。
当两个设备连接时,它们将各自扮演以下两种角色之一:
- • GATT 服务器 – 该设备提供GATT客户端读取或写入特征数据库。(通常应用来说就是 Peripheral(从机))
- • GATT 客户端 – 该设备从GATT服务器读写数据。(通常应用来说就是Central(主机))
上面的服务器对应从机,客户端对应主机不是绝对的,只是我们通常应用都是 Peripheral(从机)扮演 GATT Server,Central(主机)扮演 GATT Client,官方的标准从机主机示例也是如此。
为了方便大家对照工程代码学习,本文下面的介绍,以从机部分说明 GATT 服务器, 主机部分说明 GATT 客户端的操作。
再一次说明,GATT 使用对于应用来说来说就是:定义服务 → 添加特征 → 实现读写 / 通知 / 指示 → 处理事件。
2.1 从机(做 GATT 服务器)
博主之前的文章 CH58x/CH59x 系列芯片从机示例解析 有过从机流程的初步介绍,本文我们主要介绍与 GATT 有关的使用部分,从机上面要实现定义服务,添加特征,实现通知,处理事件等工作。
我们只要做过基础测试,手机与从机建立连接以后,就能够看到设备的服务和特征值,这些服务和特征值的实现都是在代码中有体现的,下面列出了示例中的服务和特征值:

工程中相关代码在 Profile 文件夹里面:

这里需要说明的是,我们看到从机示例的 4 个 服务 ,前面 2 个 UUID 为 0x1800 和 0x1801 的是必带服务,所有 Ble 设备都必须要有的。对于 CH58x 来说,这两个服务并不需要我们上层应用层去设计,因为已经封装在库里面了。从机示例的下面两个服务,都是需要我们应用层去定义的。

所以对我们今后的应用,可以想象的是,只要是做 GATT 服务器,下面的两条代码必带:
GGS_AddService(GATT_ALL_SERVICES); // GAP
GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes
后面的代码,自己需要做什么应用服务,就自己添加什么服务,这个在官方的 蓝牙 OTA 代码中也有体现:

在官方文档也有对应说明,详细的大家自行查看官方文档 :

2.1.1 服务和特征值配置
然后就是服务和特征值的配置,我们可以查看示例中的gattprofile.c 文件,里面有一个结构体数组 simpleProfileAttrTbl[] ,就是对服务和特征值的配置,如下图:

在示例代码中,都有注释,我们先看一张对应图表:

其中 0x 2800 和 0x2803 的 UUID 的问题在我以前的文章 一文搞清 BLE 蓝牙 UUID 有过说明 :

其中服务和特征值的配置的各个元素的定义如下图所示:

具体的操作,可查看官方示例,当然后面博主肯定也会手把手带大家增加自定义服务,会有相关文章的。
2.1.2 服务注册和 CCCD 绑定
上面是服务和特征值的配置,我们已经知道在示例中的服务是通过 SimpleProfile_AddService(GATT_ALL_SERVICES); 函数添加的,这个函数在 gattprofile.c 文件中实现,里面绑定了 CCCD ,注册连接状态回调,然后根据前面 服务和特征值的配置注册服务:

2.1.3 应用层写特征值回调函数
在 Peripheral 工程中,当 GATT 客户端写入特征值时后,通过一个名为 simpleProfileChangeCB 的回调函数,可以获取写入的数值。
应用层调用的流程如下图:

应用层实现的代码是在 gattprofile.c 文件中:

2.1.4 协议层读写回调函数
上面回调函数是在客户端特征值被写入时后响应的,但是有的小伙伴看过代码,会发现还有几个关于读写特征值的回调函数,这里需要说明一下 :

gattServiceCBs_t 类型的回调函数是协议栈层面的, 上面 simpleProfileCBs_t 是我们应用层面的。
我们可以通过代码跳转,gattServiceCBs_t 跳转到的结构体是在库文件中定义的,我们应用层只负责绑定回调函数,其他操作库函数已经实现了,只要有特征值的读写操作,协议栈就会调用我们绑定的回调函数 (读操作 —> simpleProfile_ReadAttrCB 写操作 —> simpleProfile_WriteAttrCB ) 。
而应用层的回调函数只会发生在写数据的时候,我们在协议栈写操作回调函数中可以看到如下代码:

流程的框架图如下:

2.1.5 特征值的读取与设置
在示例中,应用层还提供了特征值的读取和设置函数:
bStatus_t SimpleProfile_SetParameter(uint8_t param, uint8_t len, void *value);
bStatus_t SimpleProfile_GetParameter(uint8_t param, void *value);
它们都是对特征的 值 进行设置和读取的操作,比如在 Peripheral 示例中,初始化的时候把自定义服务的 5 个特征值都给设置了,读取的时候就会显示设定的值(当然特征值得支持读属性):

2.1.6 Notify 发送函数
示例中还给我们提供了一个发送 Notify 的函数simpleProfile_Notify 。
示例在实现 Char4 发送通知的时候,调用了这个函数,给了我们一个使用示例:

Indicate(指示)发送在示例中并没有实现,但是博主也会在后期的文章中做个示例测试。
2.2 主机(做 GATT 客户端)
GATT 客户端我们通过主机 Central 例程来说明。
GATT 客户端没有属性表,因为客户端是接收信息而非提供信息,相互来说简单一点,一些接口函数都是蓝牙库封装好的。
GATT 层大多数接口直接来自应用程序。
2.2.1 初始化 GATT 客户端
首先是初始化客户端,和绑定接受消息的 TMOS 任务:

2.2.2 写特征值
写特征值就是向 GATT 服务器发送数据,函数是库函数提供好的:
bStatus_t GATT_WriteCharValue( uint16_t connHandle, attWriteReq_t *pReq, uint8_t taskId );
在使用的时候有必要好好看一下注释,在使用中有些问题看注释就知道答案的(后期计划会有专门的使用说明博文):

2.2.3 应用层的接受消息响应
因为开头我们绑定了 TMOS 任务,协议栈在收到读写数据的响应以后,会通过 TMOS 任务把消息传递给应用层,这个 TMOS 使用大家自己参考博主以前的博文 沁恒微 蓝牙 芯片 TMOS 使用 。
消息传递这部分在我以前的博文 CH58x/CH59x 系列芯片从机示例解析 中也有过部分说明:

对于我们 Central 例程来说,这个流程如下:

结语
本文根据官方示例把沁恒微蓝牙 GATT 应用层使用框架说明了一遍,最后还是建议大家看看官方的文档,博主在第一次看的时候,脑瓜子也是嗡嗡的,对自己很陌生得东西看了很快就忘记,和学习程序语言一样,得自己测试需改,回头再多看几次,才会豁然开朗。
不过话说回来,GATT 层只是一个概念,在实际应用上,即便我们分不清是哪个层面的东西,我们其实也能正常的进行开发,简而言之就是如何实现蓝牙设备之间的数据收发罢了。需不需要深入理解这个根据大家自己的需求学习就好,在后面博主也会有如何添加自定义服务,如何设置 128 bit UUID ,Indicate 发送,等应用相关的示例博文。
好了,本文就到这里。谢谢大家!
参考文献:《沁恒低功耗蓝牙软件开发参考手册》

评论区
登录后即可参与讨论
立即登录