2024-07-02
GR5526 GUI专题(4) - Lvgl字体的位图数组合并转换为bin文件的说明
写技术文章
精选推荐
chang
文章主要介绍连接参数更新API的使用,关于蓝牙连接参数的介绍请参考这里。
文中提到的所有API,均基于 GR551x_SDK_V1.7.0 版本。
一、更新连接参数的API
1、更新连接参数
路径:ble_gapc.h
uint16_t ble_gap_conn_param_update (uint8_t conn_idx, const gap_conn_update_param_t *p_conn_param);
typedef struct{
uint16_t interval_min; //最小连接间隔
uint16_t interval_max; //最大连接间隔
uint16_t slave_latency; //从设备延迟
uint16_t sup_timeout; //监控超时
uint16_t ce_len; //LE连接所需的连接事件长度。
//取值范围:0x0002~0xFFFF,单位:0.625 ms,时间范围:1.25 ms ~ 40.9 s。
//推荐值:1M phy为0x0002, coded phy为0x0006
} gap_conn_update_param_t;
主从设备均可使用该函数更新连接参数。要实现连接参数的更新,程序中还需实现回调函数app_gap_connection_update_req_cb():
static void app_gap_connection_update_req_cb(uint8_t conn_idx,
const gap_conn_param_t *p_conn_param_update_req)
{
ble_gap_conn_param_update_reply(conn_idx, true);
}
使用ble_gap_conn_param_update()进行连接参数更新时,连接参数需要符合《蓝牙连接参数更新 --(1)连接参数说明和连接参数更新流程》中连接参数说明部分的要求。更新连接参数时,连接间隔参数优先选择interval_max的值。允许interval_max和interval_min的取值相同,如果需要将连接间隔修改为某一指定值,可将该值同时赋给interval_max和interval_min。
2、更新连接参数函数调用现象说明
表格持续补充中......
二、判断连接参数是否更新成功
1、通过连接参数更新完成的回调函数
这个回调函数将在连接更新完成时被调用。
static void app_gap_connection_update_cb(uint8_t conn_idx, uint8_t status,
const gap_conn_update_cmp_t *p_conn_param_update_info)
{
if(BLE_SUCCESS == status)
{
APP_LOG_INFO("conn_idx:%d connection update completed, intvl %0.2fms, ltcy %d, to %dms",
conn_idx,
p_conn_param_update_info->interval * 1.25,
p_conn_param_update_info->latency,
p_conn_param_update_info->sup_timeout * 10);
}
}
2、通过抓包记录的时间分布判断
注:该方法适用于连接间隔发生改变时的判断。从设备延迟和监控超时参数,可以根据抓包内容判断。
3、通过芯片电流曲线判断
使用PPK板和GRPPK工具,监控芯片的电流变化。在蓝牙数据交互的过程中,电流会随着连接事件的出现,产生周期性的峰值变化,峰值之间的时间即为连接间隔。
注:该方法适用于连接间隔发生改变时的简单判断。
三、更新连接参数时,导致连接断开的情况
1、参数设置不合理。如监控超时设置过短,降低链路的容错能力,在链路信号较差或有干扰的情况下,出现监控超时并断开连接。
2、主设备发送连接参数更新指令(LL_CONNECTION_UPDATE_IND)时,如果链路质量不好,出现了很多重传,那么很可能在预期的时间点(Instant),从设备还没有接收到包,等接收到了后,更新连接参数的时间点都错过了,导致从设备上报 Instant Passed,并断开连接。
3、从设备收到连接参数更新指令(LL_CONNECTION_UPDATE_IND)后,更新连接参数过程中,在信号较差或有干扰的情况下,从设备没有在更改连接参数的位置(Instant)接收到对端的数据,导致连接断开。
4、从设备没有实现app_gap_connection_update_req_cb()回调函数,无法响应主设备的更新连接参数请求,导致主设备断开连接。
四、更新连接参数的建议
1、连接参数可根据实际应用场景调整,但要符合规范。
2、设备建立连接后,需要进行服务发现的交互流程。设备连接时,连接间隔参数不宜设置过大,过大的连接间隔,会使设备完成服务发现的过程花费很长时间。建议建立连接时的连接间隔为30ms - 50ms,如果需要更快的完成交互过程,也可将连接间隔设为最小值7.5ms。
3、连接成功后不能太早的去更新连接参数,应该要确保设备完成服务发现后,再更新连接参数。连接参数的更新时间,建议在连接成功后1.5S - 5S。
4、设备在进行较大量的数据交互过程中,应避免更新连接参数。可在需要发送大量数据前更新连接参数(传输速度更快的参数),和发送完成后更新连接参数(更加省电的参数)。
五、参考例程说明
关于连接参数更新的使用,可以参考SDK中的鼠标示例,工程路径:
SDK_Folder\projects\ble\ble_peripheral\ble_app_hids_mouse
在这里只说明实例中连接参数更新的部分,关于该示例的具体使用方法请参考《GR551x鼠标示例手册》。
在这个例程中,作为从设备的鼠标会在建立连接后或主设备端更新连接参数后,判断当前使用的连接参数是否符合程序中指定的范围,如果不符合便会启动定时器,在定时器定时时间到期后,主动更新连接参数。主要函数流程如下:
1、初始化函数app_conn_init()中,指定连接参数。并调用函数ble_connect_init()。
路径:工程目录下的user_app\user_app.c
#define APP_PREF_CONN_INTERVAL_MAX 160
#define APP_PREF_CONN_INTERVAL_MIN 160
#define APP_PREF_CONN_SLAVE_LATENCY 0
#define APP_PREF_CONN_TIMEOUT 400
static void app_conn_init(void)
{
sdk_err_t err_code;
ble_conn_init_t conn_init;
//指定连接参数
gap_conn_param_t gap_conn_param = {
.interval_min = APP_PREF_CONN_INTERVAL_MIN,
.interval_max = APP_PREF_CONN_INTERVAL_MAX,
.slave_latency = APP_PREF_CONN_SLAVE_LATENCY,
.sup_timeout = APP_PREF_CONN_TIMEOUT };
memset(&conn_init, 0, sizeof(conn_init));
conn_init.conn_param_manage_enable = true;
conn_init.p_conn_param = &gap_conn_param;
conn_init.first_conn_param_update_delay = APP_FIRST_CONN_PARAM_UPDATE_DELAY;
conn_init.next_conn_param_update_delay = APP_SECOND_CONN_PARAM_UPDATE_DELAY;
conn_init.max_conn_param_update_count = APP_CONN_PARAM_UPDATE_TIMES;
conn_init.disconnect_on_fail = true;
conn_init.evt_handler = ble_conn_evt_handler;
conn_init.err_handler = ble_conn_err_handler;
//连接初始化
err_code = ble_connect_init(&conn_init);
APP_ERROR_CHECK(err_code);
}
2、在函数ble_connect_init()中,将连接参数赋值给“优先连接参数”,并创建更新连接参数用的定时器。
路径:工程目录下的ble_module\ble_connect.c
sdk_err_t ble_connect_init(ble_conn_init_t *p_conn_init)
{
......省略部分代码
for (uint32_t i = 0; i < BLE_CONN_LINK_CNT_MAX; i++)
{
//将指定的连接参数赋值给“优先连接参数”
memcpy(&s_conn_env.link_info[i].pref_conn_param, p_conn_init->p_conn_param, sizeof(gap_conn_param_t));
//创建更新连接参数用的定时器。
error_code = app_timer_create(&s_conn_env.link_info[i].timer_id, ATIMER_ONE_SHOT,update_timeout_handler);
RET_VERIFY_SUCCESS(error_code);
}
......
}
3、先来看建立连接后的情况。
当连接建立成功后,会进入函数ble_conn_established():
路径:工程目录下的ble_module\ble_connect.c
//从延迟中可接受的最大偏差
#define BLE_CONN_PARAM_MAX_SLAVE_LATENCY_DEVIATION 10
//监督超时时可接受的最大偏差(10ms单位)。
#define BLE_CONN_PARAM_MAX_SUPERVISION_TIMEOUT_DEVIATION 100
static void ble_conn_established(uint8_t conn_idx, void *arg)
{
......
//判断当前的连接参数是否符合更新规则
s_conn_env.link_info[conn_idx].param_ok = is_conn_param_ok(&s_conn_env.link_info[conn_idx].pref_conn_param,
&conn_update_param,
BLE_CONN_PARAM_MAX_SLAVE_LATENCY_DEVIATION,
BLE_CONN_PARAM_MAX_SUPERVISION_TIMEOUT_DEVIATION);
//启用了连接参数自动更新。
if (s_conn_env.conn_param_manage_enable)
{
//调用连接参数协商函数
conn_param_negotiation(conn_idx);
}
......
}
在ble_conn_established()函数中,通过函数is_conn_param_ok(),判断当前的连接参数是否符合更新规则,然后调用conn_param_negotiation()函数。
4、再来看主设备端更新连接参数后的情况
当主设备端更新连接参数成功后,会进入函数ble_conn_param_updated():
路径:工程目录下的ble_module\ble_connect.c
//从延迟中可接受的最大偏差
#define BLE_CONN_PARAM_MAX_SLAVE_LATENCY_DEVIATION 10
//监督超时时可接受的最大偏差(10ms单位)。
#define BLE_CONN_PARAM_MAX_SUPERVISION_TIMEOUT_DEVIATION 100
static void ble_conn_param_updated(uint8_t conn_idx, void *arg)
{
......
//判断当前的连接参数是否符合更新规则
s_conn_env.link_info[conn_idx].param_ok = is_conn_param_ok(&s_conn_env.link_info[conn_idx].pref_conn_param,
&conn_update_param,
BLE_CONN_PARAM_MAX_SLAVE_LATENCY_DEVIATION,
BLE_CONN_PARAM_MAX_SUPERVISION_TIMEOUT_DEVIATION);
//启用了连接参数自动更新。
if (s_conn_env.conn_param_manage_enable)
{
//调用连接参数协商函数
conn_param_negotiation(conn_idx);
}
......
}
在ble_conn_param_updated()函数中,也是通过函数is_conn_param_ok(),判断当前的连接参数是否符合更新规则,然后调用conn_param_negotiation()函数。
5、is_conn_param_ok()函数路径,判断连接参数是否符合更新规则
路径:工程目录下的ble_module\ble_connect.c
static bool is_conn_param_ok(gap_conn_param_t const * p_preferred_conn_param,
gap_conn_update_cmp_t const * p_actual_conn_param,
uint16_t max_slave_latency_err,
uint16_t max_sup_timeout_err)
{
uint32_t max_allowed_sl = p_preferred_conn_param->slave_latency + max_slave_latency_err;
uint32_t min_allowed_sl = p_preferred_conn_param->slave_latency - \
MIN(max_slave_latency_err, p_preferred_conn_param->slave_latency);
uint32_t max_allowed_to = p_preferred_conn_param->sup_timeout + max_sup_timeout_err;
uint32_t min_allowed_to = p_preferred_conn_param->sup_timeout - \
MIN(max_sup_timeout_err, p_preferred_conn_param->sup_timeout);
// 检查连接间隔是否在可接受范围内。
// NOTE: Using max_conn_interval in the received event data because this contains
// the client's connection interval.
if ((p_actual_conn_param->interval < p_preferred_conn_param->interval_min) ||
(p_actual_conn_param->interval > p_preferred_conn_param->interval_max))
{
return false;
}
// 检查从设备延迟是否在可接受范围内。
if ((p_actual_conn_param->latency < min_allowed_sl)||
(p_actual_conn_param->latency > max_allowed_sl))
{
return false;
}
// 检查监控超时是否在可接受范围内。
if ((p_actual_conn_param->sup_timeout < min_allowed_to) ||
(p_actual_conn_param->sup_timeout > max_allowed_to))
{
return false;
}
return true;
}
6、在函数conn_param_negotiation()中,当连接参数不符合更新规则时,启动更新连接参数用的定时器。
路径:工程目录下的ble_module\ble_connect.c
static void conn_param_negotiation(uint8_t conn_idx)
{
//当判断连接参数不符合设定的更新规则
if (!s_conn_env.link_info[conn_idx].param_ok)
{
sdk_err_t error_code;
uint32_t timeout_ticks;
if (0 == s_conn_env.link_info[conn_idx].update_count)
{
// First connection parameter update
timeout_ticks = s_conn_env.first_conn_param_update_delay;
}
else
{
timeout_ticks = s_conn_env.next_conn_param_update_delay;
}
//启动定时器
app_timer_stop(s_conn_env.link_info[conn_idx].timer_id);
error_code = app_timer_start(s_conn_env.link_info[conn_idx].timer_id, timeout_ticks, (void *)(uint32_t)conn_idx);
ble_connect_err_on_ble_capture(error_code);
}
......
}
7、在定时器定时时间到期后,最终调用ble_gap_conn_param_update()函数,进入更新连接参数流程。
路径:工程目录下的ble_module\ble_connect.c
//定时器回调
static void update_timeout_handler(void *p_context)
{
......
//连接参数更新
bool update_sent = send_update_request(conn_idx, &s_conn_env.link_info[conn_idx].pref_conn_param);
......
}
static bool send_update_request(uint8_t conn_idx, gap_conn_param_t * p_new_conn_param)
{
sdk_err_t error_code;
gap_conn_update_param_t conn_update_param;
memcpy(&conn_update_param, p_new_conn_param, sizeof(gap_conn_param_t));
conn_update_param.ce_len = 0;
//调用连接参数更新API
error_code = ble_gap_conn_param_update(conn_idx, &conn_update_param);
if ((error_code != SDK_SUCCESS) && (error_code != SDK_ERR_BUSY))
{
ble_connect_err_on_ble_capture(error_code);
}
return (error_code == SDK_SUCCESS);
}
六、默认使用逻辑链路控制和适配协议(L2CAP)来更新连接参数
在GR5515_SDK中,作为从设备,当调用ble_gap_conn_param_update()更新连接参数时,会先通过链路层控制程序(LLCP)来更新连接参数,当发送连接参数更新请求(LL_CONNECTION_PARAM_REQ)时,如果主设备返回LL_UNKNOWN_RSP,那就再通过逻辑链路控制和适配协议(L2CAP)来更新连接参数。
我们可以在调用ble_gap_conn_param_update()函数更新连接参数前,先调用函数ble_gap_update_conn_param_method_set(),修改更新连接参数的默认流程,跳过LLCP层,直接使用L2CAP层来更新连接参数。
路径:ble_gapm.h
uint16_t ble_gap_update_conn_param_method_set(uint8_t conn_idx, bool use_l2cap_flag);
该函数的用法,如修改第五节鼠标示例ble_app_hids_mouse中的send_update_request()函数:
static bool send_update_request(uint8_t conn_idx, gap_conn_param_t * p_new_conn_param)
{
sdk_err_t error_code;
gap_conn_update_param_t conn_update_param;
memcpy(&conn_update_param, p_new_conn_param, sizeof(gap_conn_param_t));
conn_update_param.ce_len = 0;
//默认使用逻辑链路控制和适配协议(L2CAP)来更新连接参数
ble_gap_update_conn_param_method_set(conn_idx, true);
//更新连接参数
error_code = ble_gap_conn_param_update(conn_idx, &conn_update_param);
if ((error_code != SDK_SUCCESS) && (error_code != SDK_ERR_BUSY))
{
ble_connect_err_on_ble_capture(error_code);
}
return (error_code == SDK_SUCCESS);
}
打开微信,使用“扫一扫”即可关注