整理一下实际应用中蓝牙参数的设定和修改方式 ...... 矜辰所致
前言
之前的文章,我们讲了很多理论基础,例程分析,在实际应用中,根据不同的需求我们需要设定修改不同的蓝牙参数。
所以本文我们就来整理下 实际应用中 工程里 常用的参数设置与修改方式 。
相关博文:
与本文有关的博文比较多,所以大家可通过专栏目录自行查找对应知识点的文章介绍。
沁恒微蓝牙专栏目录:
【导航】沁恒微 RISC-V 蓝牙 入门教程目录 【快速跳转】.
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
目录
- 前言
- [一、 广播相关](#一、 广播相关)
-
- [1.1 关闭/开启广播](#1.1 关闭/开启广播)
- [1.2 发射功率](#1.2 发射功率)
- [1.3 MAC 地址](#1.3 MAC 地址)
- [1.4 广播间隔](#1.4 广播间隔)
-
- [1.4.1 一次广播时长说明](#1.4.1 一次广播时长说明)
- [1.5 广播内容](#1.5 广播内容)
-
- [1.5.1 修改广播名称](#1.5.1 修改广播名称)
- [1.6 广播类型](#1.6 广播类型)
- 二、连接相关
-
- [2.1 连接参数](#2.1 连接参数)
- [2.2 MTU](#2.2 MTU)
- [2.3 PHY](#2.3 PHY)
- [2.4 断开当前连接](#2.4 断开当前连接)
- 三、扫描相关
-
- [3.1 扫描时间](#3.1 扫描时间)
- [3.2 扫描间隔](#3.2 扫描间隔)
- [3.3 扫描窗口](#3.3 扫描窗口)
- 结语
本文以 CH585 EVT 为例说明,仅供参考,同系列芯片大致相同,但是也不排除有某些特定型号的区别,具体以大家实际测试为准。
原理、流程、框架相关的东西都在以前博文中讲过了,不知道代码添加在哪里的朋友自行查阅上面相关博文。
一、 广播相关
1.1 关闭/开启广播
关闭广播:
c
uint8_t initial_advertising_enable = FALSE;
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
开启广播:
c
/*
如果当前已经在广播模式, 不要再次开启广播了
如果不确定当前状态,可以操作前先获取当前广播状态
非 0 为广播
uint8_t adv_status;
GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &adv_status );
*/
uint8_t initial_advertising_enable = TRUE;
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
注意!对于与广播参数有关的动态修改,需要先关闭广播 -> 修改配置 -> 再开启广播。
这个 关闭/开启 广播步骤对应用实际效果来说,基本是无感的,并不会有明显的延时,在实际运行中需要修改广播参数的情况,必须按照 关闭广播 -> 修改配置 -> 再开启广播的流程。(首先,大部分是需要这个步骤的,即便存在有什么东西修改不需要这个步骤的按照这个来,也不会出错,反正我自己目前测试是不知道有哪个步骤不需要重新开启广播,所以实际使用按照这个步骤,准没错。)
此流程的示例如下:

不能直接在设置完以后的代码处直接开启广播,必须按照上面示例,在协议栈回复了状态以后才能再次开启广播!!!
1.2 发射功率
初始发功功率:
设置默认发射功率,如下操作(如果不设置,EVT 默认发射功率为:LL_TX_POWEER_0_DBM):

动态修改发射功率:
如果要在广播期间修改发射功率使用如下函数(此过程需要关闭广播 - 设置 - 再开启广播):
c
/*#define LL_TX_POWEER_4_DBM 0x3B
第二个参数根据上面那个对应表格来即可
需要先关闭广播,然后设置,最后在状态回调里面开启广播
*/
GAP_SetParamValue(TGAP_ADV_TX_POWER,0x3B);
连接时候的发射功率修改,后期遇到了再来更新。
1.3 MAC 地址
初始MAC地址:
芯片上电默认使用芯片本身的公共地址。
如果想修改上电的 MAC 地址用于测试,需要定义一个宏BLE_MAC == TRUE ,便可以在代码中自己设定自定义地址,如下图:

动态修改 MAC 地址:
如果要动态修改 MAC 地址使用如下函数(此过程需要关闭广播 - 设置 - 再开启广播):
c
/*
// GAP_ADDR_TYPE_DEFINES GAP Address Types
#define ADDRTYPE_PUBLIC 0x00 //!< Use the BD_ADDR
#define ADDRTYPE_STATIC 0x01 //!< Static address
#define ADDRTYPE_PRIVATE_NONRESOLVE 0x02 //!< Generate Non-Resolvable Private Address
#define ADDRTYPE_PRIVATE_RESOLVE 0x03 //!< Generate Resolvable Private Address
第一个参数选上面宏定义
需要先关闭广播,然后设置,最后在状态回调里面开启广播
*/
bStatus_t GAP_ConfigDeviceAddr( uint8_t addrType, uint8_t *pStaticAddr );
//示例:
uint8_t myAddr[6] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x00};
GAP_ConfigDeviceAddr(ADDRTYPE_STATIC, myAddr);
详细说明可参考博主之前的博文:BLE 蓝牙 MAC 地址相关说明
1.4 广播间隔
BLE 广播间隔的标准范围是 20 ms ~ 10.24 s,设置范围为 32~16348(以 0.625 ms 单位)。
示例默认为 80:
c
// What is the advertising interval when device is discoverable (units of 625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL 80
修改方式如下(此过程需要关闭广播 - 设置 - 再开启广播):
c
uint16_t advInt = 100;//想要修改的间隔(32~16348)
// Set advertising interval
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);
减少广播间隔时,可以使得建立连接速度更快,但是这样也会使得芯片功耗增大。
1.4.1 一次广播时长说明
一次广播事件,就是在 3 个广播信(37/38/39)上各发一遍广播包,发完就停,直到下一个广播间隔再发一轮,不是持续发射。
如果有扫描响应,就回广播响应,一次广播事件流程如下:
信道 37:发广播 → 看有没有扫描请求 → 有就回扫描响应
信道 38:发广播 → 看有没有扫描请求 → 有就回扫描响应
信道 39:发广播 → 看有没有扫描请求 → 有就回扫描响应
单次广播事件时长 是很短的 ,一般时间最长 可连接广播+扫描响应 的广播时间估算下来 4ms 左右 ,甚至更少 。剩余的时间就可以用来休眠节约功耗 ,比如 20ms 广播间隔,剩余的 16ms 就可以休眠 。
1.5 广播内容
根据示例有两个数组,表示广播包和广播响应的内容 scanRspData[] 和advertData[]
修改方式如下(此过程需要关闭广播 - 设置 - 再开启广播):
c
GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
或者通过下面函数,直接生效:
c
/*
参数1:任务 ID
参数2:广播包类型:确定是修改广播数据还是扫描响应数据
TRUE - advertisement data,
FALSE - scan response data
参数3:数据长度
参数4:指向广播内容缓冲区的指针(要修改成的数据内容)
*/
bStatus_t GAP_UpdateAdvertisingData( uint8_t taskID, uint8_t adType, uint16_t dataLen, uint8_t *pAdvertData );
广播包格式需要遵循标准格式,可参考之前博文:BLE 蓝牙空中报文格式与解析(广播包)
1.5.1 修改广播名称
广播名称是属于上面广播内容的一部分的,我们使用上面方式二做个示例说明:
c
if(events & MY_ChangePower_EVT)
{
PRINT("Set Set...\r\n");
GAP_UpdateAdvertisingData(Peripheral_TaskID,FALSE,sizeof(my_change_scanRspData), my_change_scanRspData);
return (events ^ MY_ChangePower_EVT);
}
测试图:

额外说明,在工程里面涉及到名字的地方有两个,一个就是上面的广播包/扫描响应包中,还有一个是需要存在服务特征值中,链接以后通过特征值读取的。
如下:
c
// GAP GATT Attributes
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Peripheral";
// Set the GAP Characteristics
GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
虽然这两个地方的名字没有明文规定必须一致,但是强烈建议实际产品中保持一致!!
苹果 IOS 中如果不一致,会存在一些问题。
1.6 广播类型
广播类型一般不会在使用中更改,在初始化的时候设置,例程并没有设置,默认广播类型为 GAP_ADTYPE_ADV_IND (可连接、非定向广播),设置其他广播类型,如下 :
c
// GAP_ADVERTISEMENT_TYPE_DEFINES GAP Advertising Event Types
#define GAP_ADTYPE_ADV_IND 0x00 //!< Connectable undirected event typet
#define GAP_ADTYPE_ADV_HDC_DIRECT_IND 0x01 //!< Connectable high duty cycle directed event type
#define GAP_ADTYPE_ADV_SCAN_IND 0x02 //!< Scannable undirected event type
#define GAP_ADTYPE_ADV_NONCONN_IND 0x03 //!< Non-Connectable undirected event type
#define GAP_ADTYPE_ADV_LDC_DIRECT_IND 0x04 //!< Connectable low duty cycle directed event type
/*
依次为:
可连接、非定向广播 可被扫描 + 可被连接最常用
高占空比、可连接、定向广播 设备需要瞬间秒连时用
可扫描、非定向、不可连接 只发数据、不做连接的设备展
不可连接、不可扫描、纯广播 iBeacon / 传感器
低占空比、可连接、定向广播 低功耗定向连接
*/
uint8_t my_test_adv_type = GAP_ADTYPE_ADV_SCAN_IND;
GAPRole_SetParameter(GAPROLE_ADV_EVENT_TYPE, sizeof(uint8_t), &my_test_adv_type);
二、连接相关
2.1 连接参数
连接参数相关详细说明可参考博文:BLE 蓝牙连接参数详解
从机端
下面是从机例程与连接参数有关的宏定义:
c
// Minimum connection interval (units of 1.25ms, 6=7.5ms)
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 6
// Maximum connection interval (units of 1.25ms, 100=125ms)
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 100
// Slave latency to use parameter update
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms, 100=1s)
#define DEFAULT_DESIRED_CONN_TIMEOUT 100
必须满足:
Supervision Timeout > (1 + Slave Latency) × Connection Interval × 2
超时时间 (ms)>(1+延迟次数)×连接间隔 (ms)×2
c
uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);
GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);
这只是从机端希望的连接间隔,主机会在这个范围内,给一个合适的值(由主机决定),如果这个范围主机给不了,主机也会给从机一个连接间隔,从机不会拒绝 。
连接上以后从机还能发起协商请求:
c
if(events & SBP_PARAM_UPDATE_EVT)
{
// Send connect param update request
// When the current connection parameters already meet the requirements for update, return 0x18(InvalidRange)
GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
DEFAULT_DESIRED_MIN_CONN_INTERVAL,
DEFAULT_DESIRED_MAX_CONN_INTERVAL,
DEFAULT_DESIRED_SLAVE_LATENCY,
DEFAULT_DESIRED_CONN_TIMEOUT,
Peripheral_TaskID);
return (events ^ SBP_PARAM_UPDATE_EVT);
}
不管协商几次,主机协商还是从机自己协商,协商的结果会在从机回调函数 peripheralParamUpdateCB 里打印,连接间隔以最后一次协商结果为最终结果,如下图 :

主机端
c
// Minimum connection interval (units of 1.25ms)
#define DEFAULT_UPDATE_MIN_CONN_INTERVAL 20
// Maximum connection interval (units of 1.25ms)
#define DEFAULT_UPDATE_MAX_CONN_INTERVAL 100
// Slave latency to use parameter update
#define DEFAULT_UPDATE_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms)
#define DEFAULT_UPDATE_CONN_TIMEOUT 600
if(events & START_PARAM_UPDATE_EVT)
{
// start connect parameter update
GAPRole_UpdateLink(centralConnHandle,
DEFAULT_UPDATE_MIN_CONN_INTERVAL,
DEFAULT_UPDATE_MAX_CONN_INTERVAL,
DEFAULT_UPDATE_SLAVE_LATENCY,
DEFAULT_UPDATE_CONN_TIMEOUT);
return (events ^ START_PARAM_UPDATE_EVT);
}
2.2 MTU
关于 MTU 交互, 一些理论知识请参考博主之前博文:沁恒微 CH585 芯片 Ble 传输速率测试说明 里面 MTU,BLE_BUFF_MAX_LEN 和 单包数据长度小节相关说明。
这里举几个例子,先看看最大的情况:
如果 BLE_BUFF_MAX_LEN = 516 字节
那么:MTU 最大为 512 字节
用户一包数据最大为 509 字节,可以定义一个 509 字节大小的数组用于发送
但是因为空中物理帧限制,一包空中数据最大 251 字节,减去 2 字节 LL header,减去 4 字节 L2CAP header,再减去 3 字节 ATT header,用户数据一帧最大发出 244 字节。
所以这种勤快它会分成 3 包 : 244 B + 244 B + 21 B 发送
对于适合单帧不分包的情况:
BLE_BUFF_MAX_LEN = 251
MTU 最大为 247
用户一包数据最大为 244 字节 (通过特征值或者通知数据交互)
对于默认的情况:
BLE_BUFF_MAX_LEN = 27
MTU 最大为 23
用户一包数据最大为 20
设置 MTU ,需要主从机均支持修改 MTU,对于应用来说,就是 主机从机的 BLE_BUFF_MAX_LEN 要足够,如果我们想协商 512 的 MTU ,那么需要 BLE_BUFF_MAX_LEN 至少为 516 。
从机端
首先是宏定义 BLE_BUFF_MAX_LEN :

然后通过手机连接从机,手机端发起 MTU 交互,就能够成功:

主机端
主机端同样的,和从机一样的方式先定义 BLE_BUFF_MAX_LEN 。
然后再程序需要设置的地方手动调用如下代码:
c
// Update MTU,最大为 BLE_BUFF_MAX_LEN - 4
attExchangeMTUReq_t req = {
.clientRxMTU = BLE_BUFF_MAX_LEN - 4,//这只是最大值
};
GATT_ExchangeMTU(centralConnHandle, &req, centralTaskId);
从机主动交互 MTU
如果想要从机主动交互 MTU ,我们可以使用如下方式:
- 先定义
BLE_BUFF_MAX_LEN; - 从机初始化的地方
Peripheral_Init加上GATT_InitClient();; - 和主机示例手动调用 MTU 协商一样的方式。
c
1。
BLE_BUFF_MAX_LEN=516(>=516 都可以)
2.
//...
// Register receive scan request callback
GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);
//增加代码
GATT_InitClient();
// Setup a delayed profile startup
tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT);
//...
3.
if(events & MY_ChangePower_EVT)
{
if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT)//如果存在连接状态,才设置
{
PRINT("Set Set...\r\n");
attExchangeMTUReq_t req = {
.clientRxMTU = 512,//这只是最大值
};
GATT_ExchangeMTU(peripheralConnList.connHandle, &req, Peripheral_TaskID);
}
else{
PRINT("no connected...\r\n");
}
return (events ^ MY_ChangePower_EVT);
}
测试结果:

2.3 PHY
PHY 协商,主机和从机一样的函数:
c
// start phy update
PRINT("PHY Update %x...\n", GAPRole_UpdatePHY(peripheralConnList.connHandle, 0,
GAP_PHY_BIT_LE_2M, GAP_PHY_BIT_LE_2M, 0));
2.4 断开当前连接
c
bStatus_t GAPRole_TerminateLink( uint16_t connHandle );
上面传入的是句柄,这个在主机从机示例里面可以找到,比如主机端使用的句柄变量是 centralConnHandle ,从机使用的句柄变量是 peripheralConnList.connHandle 。
三、扫描相关
扫描相关,是针对主机或者观察者角色而言的。
3.1 扫描时间
表示设备扫描持续时间,扫描多久后停止:
c
// Scan duration in 0.625ms
#define DEFAULT_SCAN_DURATION 2400
/*
扫描持续时间
Default 10.24seconds.
Setting this parameter to 0 turns off the timeout
设置为0,永不超时
*/
GAP_SetParamValue( TGAP_DISC_SCAN, DEFAULT_SCAN_DURATION );
3.2 扫描间隔
表示 多久扫一次:
c
/*
Default 16. (n * 0.625 mSec)
扫描间隔,需要>=扫描窗口
*/
GAP_SetParamValue(TGAP_DISC_SCAN_INT, 64);
3.3 扫描窗口
表示 每次扫多久:
c
/*
Default 16. (n * 0.625 mSec)
*/
GAP_SetParamValue(TGAP_DISC_SCAN_WIND, 32);
上面 3 个参数是主机扫描的核心参数,举几个组合示例 :
快速发现设备(功耗大)
c
TGAP_DISC_SCAN = 0; // 无限扫描
TGAP_DISC_SCAN_INT = 10ms; // 扫描间隔
TGAP_DISC_SCAN_WIND = 10ms; // 窗口 = 间隔 → 一直扫描
低功耗扫描(省电)
c
TGAP_DISC_SCAN = 5000ms; // 扫5秒自动停
TGAP_DISC_SCAN_INT = 100ms; // 每100ms扫一次
TGAP_DISC_SCAN_WIND = 10ms; // 每次只扫10ms
结语
本文整理了一些蓝牙应用中需要用到的功能接口函数以供大家查阅。
在后期如果有更新或者新的一些说明,博主也会更新博文内容。
好了,本文就到这里。谢谢大家!