一、简介:
BLE的Long Range(长距离)是一种扩展的蓝牙低功耗技术,旨在提供比传统BLE更远的通信距离。传统BLE主要用于短距离无线通信,而BLE Long Range则通过一系列技术改进和优化,使得蓝牙设备能够在更远的距离上保持可靠的连接和数据传输。那靠什么实现更远距离传输呢?
主要就是靠不同编码的方式,以提高信号的传输效率和稳定性。这种调制方式能够更有效地利用无线频谱资源,降低干扰和误码率,从而实现更远的通信距离。那是什么样的编码方式呢?
普通的ble原本在1M速率下,ble每一秒钟可以传输1M个bit,每一个实际的bit就是一个信息位,但是在long range中是可以选择的,因为long range的PHY层为coded,也就是编码使用多个实际的bit位去标识一个信息位,举一个例子:
现在以两个bit位标识一个信息位,那么2bit就有四种可能,00,01,10,11,同时可以规定当接收端接受到的数据位为00和10的时候都表示信息位0,接受数据位为01和11的时候都表示数据1,这样就大大提高了容错率,比如实际发送端要发送一个信息位是1,其编码为01发出,但是受到了干扰,导致 数据被接受后解析出来了11(01中的0变为了1),但是因为我们规定了01和11都是标识信息1,所以虽然受到了干扰导致数据出现看错误,但是由于编码的方式不同,接收端还是正确的解析得到了正确的信息是1。大大提高了抗干扰能力。
当然实际编码会更复杂,在coded中一个信息位由几个码元表示(用一个符号s表示),是可以选择的,S=2表示,一个信息位的码元是2bit,S=8表示一个信息位的码元为8bit。因为coded是基于1M速率修改编码方式得来,所以在S=2的时候,实际的信息速率就是1M/2为500K;S=8的时候,实际的信息速率就是1M/8为125K,这也是coded速率为什么是125K和500K的原因。
总结来说,long range以速率换距离。
二、从机代码
-
环境
SDK:GR551x_SDK_V2.0.2
例子:GR551x_SDK_V2.0.2\projects\ble\ble_peripheral\ble_app_uart
2、代码添加
打开工程后再user_app.c中进行修改:
找到gap_params_init函数对广播进行配置,需要配置广播为拓展广播,然后设置PHY为coded,并且设置广播时候的TX功率:
完整修改代码截图如下:
在s_adv_data_set中应该设置包含广播名字,作为拓展广播包的数据,可以让主机读取到并进行过滤:
代码如下:
static const uint8_t s_adv_data_set[] = /**< Advertising data. */
{
0x11, // Length of this data
BLE_GAP_AD_TYPE_COMPLETE_LIST_128_BIT_UUID,
GUS_SERVICE_UUID,
0x0e, // Length of this data
BLE_GAP_AD_TYPE_COMPLETE_NAME,
'G', 'o', 'o', 'd', 'i', 'x', '_', 'U', 'A', 'R', 'T','_', 'p',
};
static void gap_params_init(void)
{
sdk_err_t error_code;
error_code = ble_gap_device_name_set(BLE_GAP_WRITE_PERM_DISABLE, (uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME));
#if coded_test
ble_gap_ext_adv_param_t m_code_test;
memset(&m_code_test,0,sizeof(m_code_test));
ble_gap_pair_enable(false);
m_code_test.type = BLE_GAP_ADV_TYPE_EXTENDED;
m_code_test.disc_mode = BLE_GAP_DISC_MODE_GEN_DISCOVERABLE;
m_code_test.prop = BLE_GAP_ADV_PROP_CONNECTABLE_BIT;
m_code_test.max_tx_pwr = 15; //广播功率设置
m_code_test.filter_pol = BLE_GAP_ADV_ALLOW_SCAN_ANY_CON_ANY;
memset(&m_code_test.peer_addr, 0, sizeof(ble_gap_bdaddr_t));
m_code_test.prim_cfg.adv_intv_max = 64;
m_code_test.prim_cfg.adv_intv_min = 64;
m_code_test.prim_cfg.chnl_map = BLE_GAP_ADV_CHANNEL_37_38_39;
m_code_test.prim_cfg.phy = BLE_GAP_PHY_CODED_VALUE;
m_code_test.second_cfg.max_skip = 0;
m_code_test.second_cfg.phy = BLE_GAP_PHY_CODED_VALUE;
m_code_test.second_cfg.adv_sid = 0x00;
m_code_test.period_cfg.adv_intv_min = 0;
m_code_test.period_cfg.adv_intv_max = 0;
error_code = ble_gap_ext_adv_param_set(0,BLE_GAP_OWN_ADDR_STATIC,&m_code_test);
APP_ERROR_CHECK(error_code);
#endif
/*拓展包数据设置*/
error_code = ble_gap_adv_data_set(0, BLE_GAP_ADV_DATA_TYPE_DATA, s_adv_data_set, sizeof(s_adv_data_set));
APP_ERROR_CHECK(error_code);
s_gap_adv_time_param.duration = 0;
s_gap_adv_time_param.max_adv_evt = 0;
}
然后因为我们为了最求最远的距离,所以设置了TX功率为15dBm,这是基于GR5332的硬件来确定的(RF电路需要使用15dBm的设计),为了使GR5332支持15dBm,软件也需要修改一个宏来启用:
把宏#define RF_TX_PA_SELECT的值设置为3
然后在ble_evt_handler事件回调中改或者添加如下几个事件(没有的就加入)
- 在开始事件BLE_GAPM_EVT_ADV_START中确定广播功率是不是初始化时设置的15dBm
if (p_evt->evt_status)
{
APP_LOG_DEBUG("Adverting started failed(0X%02X).", p_evt->evt_status);
}else{
uint8_t index=p_evt->evt.gapc_evt.index;
int8_t txpwr_dbm=0;
error_code = ble_gap_tx_power_get(BLE_GAP_ACTIVITY_ROLE_ADV,index,&txpwr_dbm);
APP_ERROR_CHECK(error_code);
APP_LOG_DEBUG("广播功率:%d | %d dBm",index,txpwr_dbm);
APP_LOG_DEBUG("广播开始");
}
- 在连接事件BLE_GAPC_EVT_CONNECTED中设置连接的TX功耗,确定在连接后RF依然使用15dBm进行数据发送
2、设置更新coded的S为8
{
uint8_t index = p_evt->evt.gapc_evt.index;
int8_t txpwr_dbm = 15;
int8_t txpwr_dbm_reg=0;
error_code = ble_gap_tx_power_get(BLE_GAP_ACTIVITY_ROLE_CON,index,&txpwr_dbm_reg);
APP_ERROR_CHECK(error_code);
APP_LOG_INFO("连接功率:index:%d | %d dBm",index,txpwr_dbm_reg);
if(txpwr_dbm_reg != 15)
{
APP_LOG_INFO("设置连接功率为15dBm");
error_code = ble_gap_tx_power_set(BLE_GAP_ACTIVITY_ROLE_CON,index,txpwr_dbm);
APP_ERROR_CHECK(error_code);
}
app_connected_handler(&(p_evt->evt.gapc_evt.params.connected));
break;
}
static bool phy_set_flag = true;
case BLE_GAPC_EVT_PHY_UPDATED:
APP_LOG_INFO("PHY 更新完成\n");
break;
case BLE_GAPC_EVT_CONN_PARAM_UPDATED:
APP_LOG_INFO("连接参数更新\n");
if(phy_set_flag)
{
if(ble_gap_phy_update(0,BLE_GAP_PHY_CODED_S8,BLE_GAP_PHY_CODED_S8,BLE_GAP_PHY_OPT_S8_CODING) == SDK_SUCCESS)
{
APP_LOG_INFO("PHY 设置成功");
phy_set_flag =false;
}
}
break;
至此从机代码修改完毕。
三、主机代码
主机的代码相对复杂,需要实现启动拓展扫描,扫描的PHY为coded,同时对拓展包的过滤,便于找到正确的从机设备(过滤可选方式为名字,UUID,MAC地址),在找到正确的设备后需要使用拓展连接方式去连接从机。
1、环境
SDK:GR551x_SDK_V2.0.2
例子:GR551x_SDK_V2.0.2\projects\ble\ble_central\ble_app_uart_c
2、代码添加
首先修改TX的功率,让其可以支持15dBm,依然是在custom_config中进行修改
在user_app中的参数初始化中,修改gap_params_init中的ble_gap_pref_phy_set函数参数为BLE_GAP_PHY_LE_CODED:
然后重新编写扫描开始函数,让扫描的包为拓展包,并且PHY为coded,本次选择名字过滤,
void ext_connect_start(void)
{
sdk_err_t error_code;
ble_gap_ext_init_param_t ext_conn_param;
ble_scanner_init_t scan_init;
ble_scanner_filter_data_t filter_data;
ble_gap_ext_scan_param_t s_scan_param;
memset(&scan_init, 0x00, sizeof(ble_scanner_init_t));
memset(&filter_data, 0x00, sizeof(ble_scanner_filter_data_t));
/*扫描初始化*/
scan_init.connect_auto = true;
scan_init.err_handler = NULL;
scan_init.evt_handler = ble_scanner_evt_handler;
error_code = ble_scanner_init(&scan_init);
APP_ERROR_CHECK(error_code);
/*拓展包扫描初始化*/
s_scan_param.type = BLE_GAP_EXT_SCAN_TYPE_OBSERVER;
s_scan_param.prop = BLE_GAP_EXT_SCAN_PROP_PHY_CODED_BIT | BLE_GAP_EXT_SCAN_PROP_FILT_TRUNC_BIT ;
s_scan_param.dup_filt_pol = BLE_GAP_EXT_DUP_FILT_DIS;
s_scan_param.scan_param_coded.scan_intv = APP_SCAN_INTERVAL;
s_scan_param.scan_param_coded.scan_wd =APP_SCAN_WINDOW;
s_scan_param.duration= 0;
s_scan_param.period= 0;
error_code = ble_gap_ext_scan_param_set(BLE_GAP_OWN_ADDR_STATIC, &s_scan_param);
APP_ERROR_CHECK(error_code);
/*过滤初始化*/
// filter_data.svr_uuid.length = UART_UUID_LEN;
// filter_data.svr_uuid.p_data = s_target_uuid;
filter_data.dev_name.length = strlen(s_target_name);
filter_data.dev_name.p_data = (uint8_t *)s_target_name;
// filter_data.target_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
// memcpy(filter_data.target_addr.gap_addr.addr, s_target_addr, SYS_BD_ADDR_LEN);
ble_scanner_filter_set(BLE_SCANNER_NAME_FILTER, &filter_data);
ble_scanner_filter_enable(BLE_SCANNER_FILTER_ALL_MATCH);
error_code = ble_gap_scan_start();
APP_ERROR_CHECK(error_code);
}
因为设置了名字过滤,所以s_target_name需要填写从机拓展广播包的中的名字,否则无法正确连接,因为名字不正确的话,会被过滤掉。本次测试,名字设置如下。
static const char s_target_name[] = "Goodix_UART_p";
同时在ble_evt_handler回调中的BLE_GAPM_EVT_SCAN_START事件中修改TX的功率为15dBm,
case BLE_GAPM_EVT_SCAN_START:
if (BLE_SUCCESS != p_evt->evt_status)
{
APP_LOG_DEBUG("Scan started failed(0X%02X).", p_evt->evt_status);
}else
{
uint8_t index = p_evt->evt.gapm_evt.index;
int8_t txpwr_dbm;
ble_gap_tx_power_get(BLE_GAP_ACTIVITY_ROLE_SCAN_INIT,index,&txpwr_dbm);
APP_LOG_INFO("扫描开始,功率设置为:%d dBm ( index:%d )\n",txpwr_dbm,index);
}
break;
经过上面的设置,已经可以按照名字过滤拓展广播包了,那接下来就是连接了,在ble_scanner.c的on_scanner_adv_report函数中需要记录已经经过过滤的对端从机设备的MAC地址
变量为ble_gap_bdaddr_t类型:
static ble_gap_bdaddr_t m_conn_addr={0};
然后需要在on_scanner_stoped函数中修改连接为拓展包链接,原来为普通广播连接:
修改的连接函数ext_conning需要我们自定义,代码如下:
#define APP_CONNECTION_MIN_INTERVAL 6 /**< The connection min interval (in units of 1.25 ms. This value corresponds to 7.5 ms). */
#define APP_CONNECTION_MAX_INTERVAL 10 /**< The connection max interval (in units of 1.25 ms. This value corresponds to 12.5 ms). */
#define APP_CONNECTION_SLAVE_LATENCY 1 /**< Slave latency. */
#define APP_CONNECTION_MAX_TIMEOUT 100 /**< Link supervision timeout (in unit of 10ms. This value corresponds to 1000 ms). */
uint16_t ext_conning(void)
{
APP_LOG_INFO("2--ext_conn_event");
uint16_t error_code;
ble_gap_ext_init_param_t ext_conn_param;
memset(&ext_conn_param, 0 , sizeof(ext_conn_param));
ext_conn_param.type = BLE_GAP_INIT_TYPE_DIRECT_CONN_EST;
ext_conn_param.prop = BLE_GAP_INIT_PROP_CODED_BIT;
ext_conn_param.conn_to = 0;
ext_conn_param.scan_param_coded.scan_intv = 15;
ext_conn_param.scan_param_coded.scan_wd = 15;
ext_conn_param.conn_param_coded.conn_intv_min = APP_CONNECTION_MIN_INTERVAL;
ext_conn_param.conn_param_coded.conn_intv_max = APP_CONNECTION_MAX_INTERVAL;
ext_conn_param.conn_param_coded.conn_latency = APP_CONNECTION_SLAVE_LATENCY;
ext_conn_param.conn_param_coded.supervision_to = APP_CONNECTION_MAX_TIMEOUT;
ext_conn_param.conn_param_coded.ce_len = 0;
ext_conn_param.peer_addr.addr_type = m_conn_addr.addr_type;
APP_LOG_INFO("即将连接的拓展广播MAC地址 %02X:%02X:%02X:%02X:%02X:%02X.",
ext_conn_param.peer_addr.gap_addr.addr[5],
ext_conn_param.peer_addr.gap_addr.addr[4],
ext_conn_param.peer_addr.gap_addr.addr[3],
ext_conn_param.peer_addr.gap_addr.addr[2],
ext_conn_param.peer_addr.gap_addr.addr[1],
ext_conn_param.peer_addr.gap_addr.addr[0]);
memcpy(&ext_conn_param.peer_addr.gap_addr.addr,&m_conn_addr.gap_addr, 6);
APP_LOG_INFO("MAC地址 %02X:%02X:%02X:%02X:%02X:%02X.",
ext_conn_param.peer_addr.gap_addr.addr[5],
ext_conn_param.peer_addr.gap_addr.addr[4],
ext_conn_param.peer_addr.gap_addr.addr[3],
ext_conn_param.peer_addr.gap_addr.addr[2],
ext_conn_param.peer_addr.gap_addr.addr[1],
ext_conn_param.peer_addr.gap_addr.addr[0]);
error_code = ble_gap_ext_connect(BLE_GAP_OWN_ADDR_STATIC, &ext_conn_param);
return error_code;
}
因为在ble_scanner.c中加入了打印,所以要加入头文件#include "app_log.h",如果不需要打印,可以不加入,代码截图如下:
至此我们已经完成扫描过滤和连接了,但是还需要确定连接后的TX功率为15dBm,需要再ble_evt_handler函数的连接回调事件中进行设置:
case BLE_GAPC_EVT_CONNECTED:
if (BLE_SUCCESS == p_evt->evt_status)
{
uint16_t error_code;
uint8_t index = p_evt->evt.gapm_evt.index;
int8_t txpwr_dbm;
ble_gap_tx_power_get(BLE_GAP_ACTIVITY_ROLE_CON,index,&txpwr_dbm);
APP_LOG_DEBUG("连接功率:%d dBm ( index:%d )",index,txpwr_dbm);
if(txpwr_dbm != 15)
{
int8_t txpwr_dbm_val = 15;
APP_LOG_INFO("设置连接功率为15dBm");
error_code = ble_gap_tx_power_set(BLE_GAP_ACTIVITY_ROLE_CON,index,txpwr_dbm_val);
APP_ERROR_CHECK(error_code);
}
APP_LOG_DEBUG("Connected.");
}
break;
至此从机代码添加完毕。