1.0、
最近没什么繁忙的事情,自己有点闲时间 ,就自学了一些蓝牙方面的知识,用的是nRF51822的板子。做了一些笔记供大家分享。有什么问题,希望大家及时指出。
蓝牙芯片与普通芯片唯一的好处就是,它的大多数引脚可以配置成想要的功能(除了特殊引脚比如ADC等)。这种多种映射方式方便了硬件操作;软件上,nordic给出了许多库函数和模板供使用,也是很方便的,不过前期要多多熟悉这些代码。
我是先从普通引脚配置学起来,这些没什么要说的,用过任何一款的芯片的工程师都能够很快了解并应用,所以这里一笔带过,直接进入正题我们先看看蓝牙的GAP。在学习之前要先明白GAP是干什么的 --------GAP是发现设备,建立连接,断开连接;简单来说,GAP就是修路的,后面会看到还有一个GATT,这个就是运输数据的。我们进入正题,看下面的代码。
int main(void)
{
uint32_t err_code;
bool erase_bonds;
// Initialize.
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
uart_init();
buttons_leds_init(&erase_bonds);
ble_stack_init();
gap_params_init(); //GAP初始化
services_init();
advertising_init();
conn_params_init();
printf("\r\nUART Start!\r\n");
err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
// Enter main loop.
for (;;)
{
power_manage();
}
}
}
上面 用的是nRF51822的12.3.0 SDK的串口模板代码,上面的GAP初始化函数,我们可以跳 gap_params_init()过去看看。下面是代码
2.0、
/**@brief Function for the GAP initialization.
*
* @details This function will set up all the necessary GAP (Generic Access Profile) parameters of
* the device. It also sets the permissions and appearance.
*/
static void gap_params_init(void)
{
uint32_t err_code;
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);//模式安全设置
//初始化安全模式
err_code = sd_ble_gap_device_name_set(&sec_mode, //结构体
(const uint8_t *) DEVICE_NAME, //蓝牙名称
strlen(DEVICE_NAME));//长度
APP_ERROR_CHECK(err_code);
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; //最小连接间隔
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; //最大连接间隔
gap_conn_params.slave_latency = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; //连接超时
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
}
2.01、先谈谈安全模式的概要:
安全模式就是我们经常说的连接模式。两个设备之间的连接安全是GAP负责的部分之一,它允许通过身份验证的连接才能进行数据的可读可写,就相当于我们前面说的给设备铺路一样。一旦形成连接,两个设备可以通过一个被称为配对的过程 ,进行配对时候,密钥建立加密 和认证的连接。说到这里,大家应该有印象就是我们用手机直接连接蓝牙和输入配对密码连接。
2.02、我们现在分析一下代码 ,先看一下安全模式结构体。
uint32_t err_code;
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode; //结构体
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); //设置安全模式
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *)DEVICE_NAME,
strlen(DEVICE_NAME));
APP_ERROR_CHECK(err_code);
我们跳到结构体ble_gap_conn_sec_mode_t 里面看看定义
typedef struct
{
uint8_t sm : 4; /**< Security Mode (1 or 2), 0 for no permissions at all. */
uint8_t lv : 4; /**< Level (1, 2, 3 or 4), 0 for no permissions at all. */
} ble_gap_conn_sec_mode_t;
可以看出分为两种:安全模式和水平,它们不同的组合,形成不同的连接安全模式。看一下相关定义函数
/**@defgroup BLE_GAP_CONN_SEC_MODE_SET_MACROS GAP attribute security requirement setters
*
* See @ref ble_gap_conn_sec_mode_t.
* @{ */
/**@brief Set sec_mode pointed to by ptr to have no access rights.*/设置模式没有访问权限,0模式0水平
#define BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(ptr) do {(ptr)->sm = 0; (ptr)->lv = 0;} while(0)
/**@brief Set sec_mode pointed to by ptr to require no protection, open link.*/设置为无需密码保护,开放性连接,1模式1水平
#define BLE_GAP_CONN_SEC_MODE_SET_OPEN(ptr) do {(ptr)->sm = 1; (ptr)->lv = 1;} while(0)
/**@brief Set sec_mode pointed to by ptr to require encryption, but no MITM protection.*/需要密码连接,无MITM保护,1模式2水平
#define BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(ptr) do {(ptr)->sm = 1; (ptr)->lv = 2;} while(0)
/**@brief Set sec_mode pointed to by ptr to require encryption and MITM protection.*/加密连接和MITM保护 ,1模式3水平
#define BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(ptr) do {(ptr)->sm = 1; (ptr)->lv = 3;} while(0)
/**@brief Set sec_mode pointed to by ptr to require LESC encryption and MITM protection.*/签名 或加密。MITM保护
#define BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(ptr) do {(ptr)->sm = 1; (ptr)->lv = 4;} while(0)
/**@brief Set sec_mode pointed to by ptr to require signing or encryption, no MITM protection needed.*/签名 或加密。无MITM保护
#define BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(ptr) do {(ptr)->sm = 2; (ptr)->lv = 1;} while(0)
/**@brief Set sec_mode pointed to by ptr to require signing or encryption with MITM protection.*/MITM保护签名 或加密连接
#define BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(ptr) do {(ptr)->sm = 2; (ptr)->lv = 2;} while(0)
/**@} */
有代码可以看出,我们是选择了 开放连接。
2.03、设备名称修改
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *)DEVICE_NAME, //设备名称
strlen(DEVICE_NAME));//名称长度
APP_ERROR_CHECK(err_code);
设备名称是宏定义:
#define DEVICE_NAME "Nordic_UART" /**< Name of device. Will be included in the advertising data. */
#define MANUFACTURER_NAME "NordicSemiconductor" /**< Manufacturer. Will be passed to Device Information Service. */
#define APP_ADV_INTERVAL 40 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 25 ms). */
#define APP_ADV_TIMEOUT_IN_SECONDS 180 /**< The advertising timeout in units of seconds. */
这个名称在蓝牙扫描的时候 ,可以在手机app或者手机蓝牙扫描到。
2.04、最后看一下蓝牙里面比较重要的一步分,就是连接间隔,这个连接间隔影响到 蓝牙的连接速度和功耗问题,不过 ,具体还要结合广播 间隔来看。先看代码:
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; //最小连接间隔
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; //最大连接间隔
gap_conn_params.slave_latency = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; //监督超时
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
(1)、最小连接间隔单位是1.25ms,其中设定的最小值为6(也就是7.5ms)
(2)、最大连接间隔单位1.25ms,,其中设定的最大值为3200(也就是4s)
(3)、监督超时单位为10ms,其中最小值为10(100ms)到最大值3200(32s),超出设定的最大时间还没有连接,设备就会判断连接失败。