八、基于GD32 Embedded Builder开发GD32VW553(蓝牙广播)
前言:
笔者最近使用GD32VW553进行开发时,遇到了很多问题,现在记录下来,供GD32开发者参考。
已有:
GD32 Embedded Builder界面中文化(保姆级教程)-CSDN博客
一、基于GD32 Embedded Builder开发GD32VW553(编译环境搭建)-CSDN博客
二、基于GD32 Embedded Builder开发GD32VW553(点亮一个Led灯)_gd32embeddedbuilder-CSDN博客
三、基于GD32 Embedded Builder开发GD32VW553(裸机开发流程)-CSDN博客
四、基于GD32 Embedded Builder开发GD32VW553(GD32VW553K-START开发板代码烧录)_gd32g553ccu7-CSDN博客
五、基于GD32 Embedded Builder开发GD32VW553(萤火工场 GD32VW553-IOT代码烧录下载)-CSDN博客
六、基于GD32 Embedded Builder开发GD32VW553(基本指令用户指南)-CSDN博客
七、基于GD32 Embedded Builder开发GD32VW553(AT指令)-CSDN博客
本文介绍如何进行蓝牙广播。
一、蓝牙广播
根据gd32vw553 ble开发指南和官方的sdk实现广播的流程。
3个步骤:
1应用层初始化函数,也就是开发指南里面的蓝牙三层架构的最上层app,用户自己创建的函数。
2.事件处理回调函数,当广播被别的设备扫描到时,就调用这个回调函数进行处理。
3广播启动函数。
找到MSDK/app/main.c,然后再主函数的后面把下面的函数粘贴过去,然后在主函数里调用初始函数,在freertos任务里一直恢复ble_stack最底层的蓝牙协议栈。
// 前向声明函数
static void app_adv_evt_handler(ble_adv_evt_t evt, void *p_data, void *p_ctx);
static void app_adv_start(uint8_t adv_idx);/*** 广播上下文结构体* 用于在回调函数中保存当前广播的索引和状态*/
static struct {uint8_t idx; // 广播实例索引uint8_t state; // 当前广播状态
} s_adv_ctx;/*** 应用层初始化函数 - 在main()中调用* 功能:初始化BLE协议栈并创建广播实例*/
void ble_adv_app_init(void) {ble_status_t ret;/* 1. 初始化BLE协议栈 */// 恢复BLE协议栈任务ble_stack_task_resume(false);// ble_app_task_resume(false); // 未使用应用任务/* 2. 直接创建广告,不再调用ble_adv_init */ble_adv_param_t param = {0};// 配置广播参数param.param.type = BLE_GAP_ADV_TYPE_LEGACY; // 使用传统广播类型param.param.prop = BLE_GAP_ADV_PROP_UNDIR_CONN; // 可连接可扫描的未定向广播param.param.own_addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; // 使用公网地址param.param.adv_intv_min = 160; // 最小广播间隔100ms (160 * 0.625ms)param.param.adv_intv_max = 160; // 最大广播间隔100msparam.param.primary_phy = BLE_GAP_PHY_1MBPS; // 使用1Mbps PHYparam.param.ch_map = BLE_GAP_ADV_CHANN_37; // 仅使用37信道// 创建广播实例并注册事件回调函数ret = ble_adv_create(¶m, app_adv_evt_handler, &s_adv_ctx);if (ret != BLE_ERR_NO_ERROR) {dbg_print(ERR, "ble_adv_create fail! 0x%04X\r\n", ret);return;}dbg_print(NOTICE, "ble_adv_create triggered\r\n");
}/*** 广播事件回调函数* 功能:处理各种广播相关事件*/
static void app_adv_evt_handler(ble_adv_evt_t evt, void *p_data, void *p_ctx) {(void)p_ctx; // 未使用上下文参数switch (evt) {case BLE_ADV_EVT_STATE_CHG: {// 广播状态变化事件ble_adv_state_chg_t *chg = (ble_adv_state_chg_t*)p_data;dbg_print(NOTICE,"ADV state 0x%02X->0x%02X reason=0x%04X\r\n",s_adv_ctx.state, chg->state, chg->reason);// 更新上下文状态和索引s_adv_ctx.state = chg->state;s_adv_ctx.idx = chg->adv_idx;/* 创建完成后启动广播 */if (chg->state == BLE_ADV_STATE_CREATE) {app_adv_start(chg->adv_idx);}break;}case BLE_ADV_EVT_DATA_UPDATE_INFO: {// 广播数据更新事件ble_adv_data_update_info_t *info = (ble_adv_data_update_info_t*)p_data;dbg_print(NOTICE,"ADV data update idx=%d type=%d status=0x%04X\r\n",info->adv_idx, info->type, info->status);break;}case BLE_ADV_EVT_SCAN_REQ_RCV: {// 接收到扫描请求事件ble_adv_scan_req_rcv_t *req = (ble_adv_scan_req_rcv_t*)p_data;dbg_print(NOTICE,"ScanReq from %02X:%02X:%02X:%02X:%02X:%02X\r\n",req->peer_addr.addr[5], req->peer_addr.addr[4],req->peer_addr.addr[3], req->peer_addr.addr[2],req->peer_addr.addr[1], req->peer_addr.addr[0]);break;}default:break;}
}/*** 启动广播函数* 功能:配置广播数据并启动广播*/
static void app_adv_start(uint8_t adv_idx) {/* 构造广播包:Flags + 完整本地名 */static uint8_t adv_payload[] = {0x02, 0x01, 0x06, /* Flags字段: LE General Discoverable Mode + BR/EDR Not Supported */0x0A, 0x09, /* 完整本地名字段: 长度=10, 类型=0x09 */'G','D','3','2','-','B','L','E' /* 设备名称: GD32-BLE */};// 配置广播数据ble_data_t raw = {.p_data = adv_payload,.len = sizeof(adv_payload),};ble_adv_data_set_t adv_set = {.data_force = true, // 强制设置数据.data.p_data_force = &raw // 指向广播数据};// 启动广播ble_status_t ret = ble_adv_start(adv_idx, &adv_set, NULL, NULL);if (ret != BLE_ERR_NO_ERROR) {dbg_print(ERR, "ble_adv_start fail! 0x%04X\r\n", ret);} else {dbg_print(NOTICE, "ble_adv_start OK\r\n");}
}
/* 启动广播示例 */ble_adv_app_init();void *task_handle = sys_task_create(NULL, // 静态任务控制块,这里使用动态分配(const uint8_t *)"PA0_Control_Task", // 任务名称NULL, // 栈基地址,这里使用动态分配128, // 栈大小,单位为字(4字节)0, // 消息队列大小0, // 消息队列项大小1, // 任务优先级pa0_control_task, // 任务入口函数NULL // 任务参数);if (task_handle == NULL) {// 任务创建失败dbg_print(ERR, "task_handle init failed\r\n");}sys_os_start();void pa0_control_task(void *pvParameters)
{while (1) {// 置高PA0gpio_bit_set(GPIOA, GPIO_PIN_0);//
// // 置低PA0
// gpio_bit_reset(GPIOA, GPIO_PIN_0);
// sys_us_delay(500000); // 延时500msble_stack_task_resume(false);sys_us_delay(500000); // 延时500ms// sys_us_delay(100000);}
}
设置广播扫描响应数据,总体是按照广播数据进行设置的。
1、扫描响应的作用与限制
作用:广播包(Advertising Data)受 31 字节限制,扫描响应可扩展数据传输。当其他设备主动扫描本设备时,本设备会发送扫描响应(类似 “一问一答”)。
格式:与广播数据相同,采用 AD Structure 格式:[长度(1B)][类型(1B)][数据(nB)],总长度≤31 字节。
2、扫描响应的常用 AD Type(数据类型)
AD Type | 名称 | 说明 |
0x01 | Flags | 设备发现模式和功能标志(如是否支持 BLE 和 BR/EDR) |
0x02 | Incomplete List of 16-bit Service Class UUIDs | 部分 16 位服务 UUID 列表(若超过 31 字节) |
0x03 | Complete List of 16-bit Service Class UUIDs | 完整 16 位服务 UUID 列表 |
0x04 | Incomplete List of 32-bit Service Class UUIDs | 部分 32 位服务 UUID 列表 |
0x05 | Complete List of 32-bit Service Class UUIDs | 完整 32 位服务 UUID 列表 |
0x06 | Incomplete List of 128-bit Service Class UUIDs | 部分 128 位服务 UUID 列表 |
0x07 | Complete List of 128-bit Service Class UUIDs | 完整 128 位服务 UUID 列表 |
0x08 | Shortened Local Name | 短设备名称(截断版,如 “GD32”) |
0x09 | Complete Local Name | 完整设备名称(如 “GD32-BLE”) |
0x0A | TX Power Level | 发送功率(如 0dBm、-4dBm 等,用于估算距离) |
0x10 | Device ID | 设备标识符(如序列号、产品 ID) |
0x16 | Service Data - 16-bit UUID | 基于 16 位 UUID 的服务数据(自定义数据) |
0xFF | Manufacturer Specific Data | 厂商自定义数据(如电池电量、温度等) |
总结:蓝牙广播主要是流程,然后需要查阅大量的相关的源文件,找到函数定义。