源码剖析
main.c
main函数中所有使用extern引用外部的函数均无源码,具体细节不可知。
可以看到源码函数有以下几个:
hal_rfphy_init:
相关参数:
hal_init:
jump_table.c
因为rom的code是不开源的,rom代码调用app就需要一个跳转表。
OSAL_SimpleBLEPeripheral.c
这个文件中包含应用所包含的所有任务初始化。注意在所有任务初始化函数中,初始化每个任务都传入了一个序号,所有所有函数都要按顺序排列。
具体按顺序排列指的是和上面任务函数指针数组中函数指针的顺序:
在osalInitTasks函数中对于我们最重要的是应用初始化:SimpleBLEPeripheral_Init函数
simpleBLEPeripheral.c
此文件才是应用的重点,在这个文件中定义了大量可修改参数,这些参数控制着我们需要实现的功能。同时定义了很多回调函数,来处理不同事件触发时的处理流程。
在我们应用初始化函数中,初始化了很多GAP和GATT的参数:
关于一些可设置的GAP层参数如下:
除去一些GAP参数设置外,比较重要的是广播数据和扫描应答数据设置:
根据上面设置类型的注释,已经可以知道这两个数据的最大长度都是31,他们的内容都保存在全局数组里:
广播与扫描应答数据的格式都是length+type+value,其中length包含type这一个字节,type一般使用蓝牙协议联盟规定的,value可以有多个字节组成长度=length-1。
当使能绑定管理器时,还回初始化一些配对与绑定的参数:
和我们应用最贴近的设置是添加服务的函数:
不同服务有各自的添加函数。而不同服务实质上是一个个属性数组。
gapgattserver.c
添加服务很简单,主要就是注册属性表和回调函数。
这里回调函数是一个结构体,包含三个函数:
而属性表则是一个结构体数组,包含该服务所有属性:
type其实就是uuid,蓝牙中使用uuid表示不同数据的类型,type中包含了两个部分,一个是len表示长度,因为uuid可以使用完整的16个字节,也可以使用缩写的2个字节。另一个是uuid,这里使用的uuid已经由未开源的代码中定义好了,我们可以直接使用。默认我们使用缩写的2字节的uuid即可。缩写的2字节uuid其实就是完整uuid的byte12、13,因为其他字节完全相同,所以可以缩写。
已经内置的一些uuid(gatt_uuid.h):
他们的具体数值为:
另外权限分为以下几种:
而局部默认都为0,因为句柄由属性服务器分配。
最后值指向的是一个数组,最大为512字节。
最后完整的看一下GAP层的属性表(请注意属性数据组织方式):
下面还包含一些其他特征,其实组织方式都是一样的不再赘述。这里主要看一下两个可读的区别。在特征声明这条属性里的”pValue”可以是以下属性的一个或多个:
注意,这里这个权限描述的是这个特征所允许的一些操作。而属性中的”permissions”则是描述的这条属性的权限。
回过头看一下回调函数,在读取函数里有:
这里读取回调函数看起来是通过传入的gattAttribute_t*指针判断读取的属性,注意这个指针是完整的一个属性,它的pValue就是我们需要读取的值,我们所需要做的就是将这个属性的pValue复制到函数入参的pValue里。属性的pValue值初始化就是在上面提到的属性表里。
在写回调中修改属性表的方式其实就是把读回调中的操作反过来:
同样的,也是判断gattAttribute_t*的uuid,只不过复制数据的方向和读刚好相反。
不过根据前文分析,其实GAP中的特征都是只读的,所以写回调函数理论上是不会触发的。
除了读写这两个比较重要的函数,还有一下函数:
上面两个函数用来设置和获取GGS的属性值。
和注册服务函数相对应的,用来删除服务。
设备名称被更改的时候用来回调通知应用。在write函数中被调用。
设置属性的权限。
sbpProfile_ota.c
GATTServApp_AddService( GATT_ALL_SERVICES );和gap差不多,而且内容不用修改所以不再展开。
SimpleProfile_AddService( GATT_ALL_SERVICES );才是真正添加此demo服务的函数,其实大部分内容和GGS相似,但是除了读写还有通知,这里主要提一下通知。
这里Characteristic 6就是用来指示是否通知。
这个通知也是通过write函数 simpleProfile_WriteAttrCB 写入的:
这里直接调用现成的API设置此handle可以被通知即可,然后在需要通知的时候使用:
其实最底层调用的是: