蓝牙SDK的example 文件夹提供了开发BLE的模板工程,它具有通用性,可以为自己开发工程提供参考。打开examples\ble_peripheral\ble_app_template文件夹下的main.c文件,主函数main的内容为:
/**@brief Function for application main entry.
*/
int main(void)
{
bool erase_bonds;
// Initialize.
log_init();
timers_init();
buttons_leds_init(&erase_bonds);
power_management_init();
ble_stack_init();
gap_params_init();
gatt_init();
advertising_init();
services_init();
conn_params_init();
peer_manager_init();
// Start execution.
NRF_LOG_INFO("Template example started.");
application_timers_start();
advertising_start(erase_bonds);
// Enter main loop.
for (;;)
{
idle_state_handle();
}
}
main函数的初始化部分主要分为三部分:
一是外设的初始化如定时器,按键,LED灯,串口等
二是协议层的初始化,如协议栈初始化,GAP和GATT初始化,广播初始化,连接参数初始化等。
三是应用层初始化,如服务初始化,蓝牙服务初始化。
一、外设
1.1、log打印初始化
log_init()函数就是打印初始化函数,内容如下:
/**@brief Function for initializing the nrf log module.
*/
static void log_init(void)
{
ret_code_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
}
1.2、定时器
蓝牙协议栈下的定时器是软件定时器,并采用软件中断来进行触发操作。Nordic官方库关于定时器的驱动库定义在app_timer.c和app_timer.h文件中
1.2.1、定时器初始化
main函数中的 timers_init()是定时器初始化函数
/**@brief Function for the Timer initialization.
*
* @details Initializes the timer module. This creates and starts application timers.
*/
static void timers_init(void)
{
// Initialize timer module.
ret_code_t err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
// Create timers.
/* YOUR_JOB: Create any timers to be used by the application.
Below is an example of how to create a timer.
For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by
one.
ret_code_t err_code;
err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
APP_ERROR_CHECK(err_code); */
}
调用的API函数有app_timer_init()和app_timer_create()。app_timer_init()函数就是初始化计数器的相关工作。app_timer_create()函数为
ret_code_t app_timer_create(app_timer_id_t const * p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler);
p_timer_id是定时器标识符指针
mode是定时器模式
timeout_handler是定时器超时溢出的中断 处理函数
1.2.2、定时器开始
main函数中的定时器开始函数为application_timers_start()
/**@brief Function for starting timers.
*/
static void application_timers_start(void)
{
/* YOUR_JOB: Start your timers. below is an example of how to start a timer.
ret_code_t err_code;
err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL);
APP_ERROR_CHECK(err_code); */
}
app_timer_start函数是定时器开始函数,其参数具体为:
ret_code_t app_timer_start(app_timer_t * p_timer, uint32_t timeout_ticks, void * p_context)
第一个参数p_timer为前面的定时器ID
第二个参数timeout_ticks是超时的时间间隔
第三个参数为p_context,一般可以设置为NULL。
1.3、LED和按键
Nordic的蓝牙协议栈中的库函数提供了一个BSP来支持按键和LED灯。
1.3.1、按键与LED的初始化
按键与LED的初始化函数为
/**@brief Function for initializing buttons and leds.
*
* @param[out] p_erase_bonds Will be true if the clear bonding button was pressed to wake the application up.
*/
static void buttons_leds_init(bool * p_erase_bonds)
{
ret_code_t err_code;
bsp_event_t startup_event;
err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
APP_ERROR_CHECK(err_code);
err_code = bsp_btn_ble_init(NULL, &startup_event);
APP_ERROR_CHECK(err_code);
*p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}
bsp_init函数实现按键和LED的初始化,同时触发对应的设备任务。
二、能源管理
能源管理初始化函数为power_management_init(),一般不变动。
三、协议栈
ble_stack_init()是协议初始化函数,协议栈初始化主要做以下工作:
1、协议栈回复使能应答,主要工作是协议栈时钟初始化配置
2、初始化协议栈,设置协议栈相关处理函数,
3、使能协议栈
4、注册蓝牙处理调度事件
具体代码如下:
/**@brief Function for initializing the BLE stack.
*
* @details Initializes the SoftDevice and the BLE event interrupt.
*/
static void ble_stack_init(void)
{
ret_code_t err_code;
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// Configure the BLE stack using the default settings.
// Fetch the start address of the application RAM.
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
// Enable BLE stack.
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
3.1、协议栈使能应答
协议栈使能应答代码为
ret_code_t nrf_sdh_enable_request(void)
{
ret_code_t ret_code;
if (m_nrf_sdh_enabled)
{
return NRF_ERROR_INVALID_STATE;
}
m_nrf_sdh_continue = true;
// Notify observers about SoftDevice enable request.
if (sdh_request_observer_notify(NRF_SDH_EVT_ENABLE_REQUEST) == NRF_ERROR_BUSY)
{
// Enable process was stopped.
return NRF_SUCCESS;
}
// Notify observers about starting SoftDevice enable process.
sdh_state_observer_notify(NRF_SDH_EVT_STATE_ENABLE_PREPARE);
nrf_clock_lf_cfg_t const clock_lf_cfg =
{
.source = NRF_SDH_CLOCK_LF_SRC,
.rc_ctiv = NRF_SDH_CLOCK_LF_RC_CTIV,
.rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
.accuracy = NRF_SDH_CLOCK_LF_ACCURACY
};
CRITICAL_REGION_ENTER();
#ifdef ANT_LICENSE_KEY
ret_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler, ANT_LICENSE_KEY);
#else
ret_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler);
#endif
m_nrf_sdh_enabled = (ret_code == NRF_SUCCESS);
CRITICAL_REGION_EXIT();
if (ret_code != NRF_SUCCESS)
{
return ret_code;
}
m_nrf_sdh_continue = false;
m_nrf_sdh_suspended = false;
// Enable event interrupt.
// Interrupt priority has already been set by the stack.
softdevices_evt_irq_enable();
// Notify observers about a finished SoftDevice enable process.
sdh_state_observer_notify(NRF_SDH_EVT_STATE_ENABLED);
return NRF_SUCCESS;
}
这个函数是用于观察者初始化协议栈是否开始使能。观察者就是开发者,代码中设置了交互方式,如串口等对设备状态进行观察,并且给观察者分配了回调函数
3.2、协议栈默认配置设置
协议栈默认配置设置,也是协议栈的初始化。函数代码如下:
ret_code_t nrf_sdh_ble_default_cfg_set(uint8_t conn_cfg_tag, uint32_t * p_ram_start)
{
uint32_t ret_code;
ret_code = nrf_sdh_ble_app_ram_start_get(p_ram_start);
if (ret_code != NRF_SUCCESS)
{
return ret_code;
}
#if defined (S112) || defined(S312)
STATIC_ASSERT(NRF_SDH_BLE_CENTRAL_LINK_COUNT == 0, "When using s112, NRF_SDH_BLE_CENTRAL_LINK_COUNT must be 0.");
#endif
// Overwrite some of the default settings of the BLE stack.
// If any of the calls to sd_ble_cfg_set() fail, log the error but carry on so that
// wrong RAM settings can be caught by nrf_sdh_ble_enable() and a meaningful error
// message will be printed to the user suggesting the correct value.
ble_cfg_t ble_cfg;
/*********************************设置连接数目和角色****************************************************************/
#if (NRF_SDH_BLE_TOTAL_LINK_COUNT != 0)
// Configure the connection count.设置连接数目
memset(&ble_cfg, 0, sizeof(ble_cfg));
ble_cfg.conn_cfg.conn_cfg_tag = conn_cfg_tag;//设置标号
ble_cfg.conn_cfg.params.gap_conn_cfg.conn_count = NRF_SDH_BLE_TOTAL_LINK_COUNT;//总的连接数量
ble_cfg.conn_cfg.params.gap_conn_cfg.event_length = NRF_SDH_BLE_GAP_EVENT_LENGTH;//GAP事件长度
ret_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_cfg, *p_ram_start);//添加协议栈配置
if (ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GAP.",
nrf_strerror_get(ret_code));
}
// Configure the connection roles.配置连接角色
memset(&ble_cfg, 0, sizeof(ble_cfg));
#if !defined (S122)
ble_cfg.gap_cfg.role_count_cfg.periph_role_count = NRF_SDH_BLE_PERIPHERAL_LINK_COUNT;//从机角色数目
#endif // !defined (S122)
#if !defined (S112) && !defined(S312) && !defined(S113)
ble_cfg.gap_cfg.role_count_cfg.central_role_count = NRF_SDH_BLE_CENTRAL_LINK_COUNT;//主机角色数目
ble_cfg.gap_cfg.role_count_cfg.central_sec_count = MIN(NRF_SDH_BLE_CENTRAL_LINK_COUNT,
BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT);
#endif // !defined (S112) && !defined(S312) && !defined(S113)
ret_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_cfg, *p_ram_start);
if (ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GAP_CFG_ROLE_COUNT.",
nrf_strerror_get(ret_code));
}
/*********************************设置MTU协商值****************************************************************/
// Configure the maximum ATT MTU.
#if (NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23)//如果MTU协商值不是23
memset(&ble_cfg, 0x00, sizeof(ble_cfg));
ble_cfg.conn_cfg.conn_cfg_tag = conn_cfg_tag;//设置连接编号
ble_cfg.conn_cfg.params.gatt_conn_cfg.att_mtu = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;//设置MTU字节长度
ret_code = sd_ble_cfg_set(BLE_CONN_CFG_GATT, &ble_cfg, *p_ram_start);//协议栈配置的添加
if (ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GATT.",
nrf_strerror_get(ret_code));
}
#endif // NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23
#endif // NRF_SDH_BLE_TOTAL_LINK_COUNT != 0
/*********************************设置UUID数量****************************************************************/
// Configure number of custom UUIDS.
memset(&ble_cfg, 0, sizeof(ble_cfg));
ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = NRF_SDH_BLE_VS_UUID_COUNT;//配置UUID主服务数量
ret_code = sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &ble_cfg, *p_ram_start);
if (ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_COMMON_CFG_VS_UUID.",
nrf_strerror_get(ret_code));
}
/*********************************设置GATTS属性表的大小****************************************************************/
// Configure the GATTS attribute table.
memset(&ble_cfg, 0x00, sizeof(ble_cfg));
ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE;
ret_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_cfg, *p_ram_start);
if (ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GATTS_CFG_ATTR_TAB_SIZE.",
nrf_strerror_get(ret_code));
}
/*********************************使能服务变化特征值****************************************************************/
// Configure Service Changed characteristic.
memset(&ble_cfg, 0x00, sizeof(ble_cfg));
ble_cfg.gatts_cfg.service_changed.service_changed = NRF_SDH_BLE_SERVICE_CHANGED;
ret_code = sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &ble_cfg, *p_ram_start);
if (ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GATTS_CFG_SERVICE_CHANGED.",
nrf_strerror_get(ret_code));
}
return NRF_SUCCESS;
}
该函数的主要功能有:
一、配置连接数目和角色
二、配置MTU协商值,最大传输单元(Maximum Transmission Unit , MTU)是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。因为协议数据单元的包头和包尾的长度是固定的,MTU越大,则一个协议数据单元的承载的有效数据就越长,通信效率也越高。传送相同数据所需的数据包个数也越少。但是,传送一个数据包的延迟也越大,包中bit位发生错误的概率也越大。因此在蓝牙5.0协议下限定了MTU的最大值。
在蓝牙4.0里面定义了一个最大23字节的传输包,ATT默认MTU为23个bytes,除去ATT的opcode一个字节以及ATT的handle两个字节之后,剩下的 20个字节便是留给GATT的了。
三、配置定制的UUID数目,这个数目指的是私有任务的UUID数目,也就是自定义的128bit的UUID数目。SIG定义的公有任务不计入其中。
四、GATTS属性表大小.
五、使能服务变化特征值
3.3、使能协议栈
配置完协议栈的参数后,使用nrf_sdh_ble_enable函数使能协议栈。程序在运行时,芯片内部的RAM被分为两个区域:协议栈设备RAM区域位于0X20000000和APP_RAM_BASE-1之间,应用程序的RAM位于APP_RAM_BASE和调用堆栈的开始之间。
3.4、注册蓝牙处理事件
注册蓝牙处理事件是以一种观察者的模式来注册蓝牙处理的回调函数,简单来说就是:注册蓝牙处理事件的回调函数ble_evt_handler。注册蓝牙处理事件就是处理GAP层的事件。
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ret_code_t err_code = NRF_SUCCESS;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected.");
// LED indication will be changed when advertising starts.
break;
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected.");
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
APP_ERROR_CHECK(err_code);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);//将连接句柄分配给队列写模块
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
} break;
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
NRF_LOG_DEBUG("GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
NRF_LOG_DEBUG("GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
default:
// No implementation needed.
break;
}
}
蓝牙处理事件函数主要是根据协议栈触发的蓝牙事件进行对应的动作,主要有一下几个蓝牙GAP层的事件:
BLE_GAP_EVT_CONNECTED:连接事件
BLE_GAP_EVT_DISCONNECTED:断开连接事件
BLE_GAP_EVT_PHY_UPDATE_REQUEST:PHY更新应答
BLE_GATTC_EVT_TIMEOUT:客户端超时
BLE_GATTS_EVT_TIMEOUT:服务端超时
BLE_GATTS_EVT_TIMEOUT服务端超时和BLE_GATTC_EVT_TIMEOUT客户端超时这两个操作表示主机和从机发生GATT事件超时,主机和从机链路没有按时交流数据包从而引发通信超时。这个时候,从机会调用sd_ble_gap_disconnect函数断开蓝牙连接。
GAP事件的ID定义为:
/**@brief GAP Event IDs.
* IDs that uniquely identify an event coming from the stack to the application.
*/
enum BLE_GAP_EVTS
{
BLE_GAP_EVT_CONNECTED = BLE_GAP_EVT_BASE, /**< Connected to peer. \n See @ref ble_gap_evt_connected_t */
BLE_GAP_EVT_DISCONNECTED = BLE_GAP_EVT_BASE + 1, /**< Disconnected from peer. \n See @ref ble_gap_evt_disconnected_t. */
BLE_GAP_EVT_CONN_PARAM_UPDATE = BLE_GAP_EVT_BASE + 2, /**< Connection Parameters updated. \n See @ref ble_gap_evt_conn_param_update_t. */
BLE_GAP_EVT_SEC_PARAMS_REQUEST = BLE_GAP_EVT_BASE + 3, /**< Request to provide security parameters. \n Reply with @ref sd_ble_gap_sec_params_reply. \n See @ref ble_gap_evt_sec_params_request_t. */
BLE_GAP_EVT_SEC_INFO_REQUEST = BLE_GAP_EVT_BASE + 4, /**< Request to provide security information. \n Reply with @ref sd_ble_gap_sec_info_reply. \n See @ref ble_gap_evt_sec_info_request_t. */
BLE_GAP_EVT_PASSKEY_DISPLAY = BLE_GAP_EVT_BASE + 5, /**< Request to display a passkey to the user. \n In LESC Numeric Comparison, reply with @ref sd_ble_gap_auth_key_reply. \n See @ref ble_gap_evt_passkey_display_t. */
BLE_GAP_EVT_KEY_PRESSED = BLE_GAP_EVT_BASE + 6, /**< Notification of a keypress on the remote device.\n See @ref ble_gap_evt_key_pressed_t */
BLE_GAP_EVT_AUTH_KEY_REQUEST = BLE_GAP_EVT_BASE + 7, /**< Request to provide an authentication key. \n Reply with @ref sd_ble_gap_auth_key_reply. \n See @ref ble_gap_evt_auth_key_request_t. */
BLE_GAP_EVT_LESC_DHKEY_REQUEST = BLE_GAP_EVT_BASE + 8, /**< Request to calculate an LE Secure Connections DHKey. \n Reply with @ref sd_ble_gap_lesc_dhkey_reply. \n See @ref ble_gap_evt_lesc_dhkey_request_t */
BLE_GAP_EVT_AUTH_STATUS = BLE_GAP_EVT_BASE + 9, /**< Authentication procedure completed with status. \n See @ref ble_gap_evt_auth_status_t. */
BLE_GAP_EVT_CONN_SEC_UPDATE = BLE_GAP_EVT_BASE + 10, /**< Connection security updated. \n See @ref ble_gap_evt_conn_sec_update_t. */
BLE_GAP_EVT_TIMEOUT = BLE_GAP_EVT_BASE + 11, /**< Timeout expired. \n See @ref ble_gap_evt_timeout_t. */
BLE_GAP_EVT_RSSI_CHANGED = BLE_GAP_EVT_BASE + 12, /**< RSSI report. \n See @ref ble_gap_evt_rssi_changed_t. */
BLE_GAP_EVT_SEC_REQUEST = BLE_GAP_EVT_BASE + 14, /**< Security Request. \n Reply with @ref sd_ble_gap_authenticate
. \n See @ref ble_gap_evt_sec_request_t. */
BLE_GAP_EVT_SCAN_REQ_REPORT = BLE_GAP_EVT_BASE + 16, /**< Scan request report. \n See @ref ble_gap_evt_scan_req_report_t. */
BLE_GAP_EVT_PHY_UPDATE_REQUEST = BLE_GAP_EVT_BASE + 17, /**< PHY Update Request. \n Reply with @ref sd_ble_gap_phy_update. \n See @ref ble_gap_evt_phy_update_request_t. */
BLE_GAP_EVT_PHY_UPDATE = BLE_GAP_EVT_BASE + 18, /**< PHY Update Procedure is complete. \n See @ref ble_gap_evt_phy_update_t. */
BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST = BLE_GAP_EVT_BASE + 19, /**< Data Length Update Request. \n Reply with @ref sd_ble_gap_data_length_update. \n See @ref ble_gap_evt_data_length_update_request_t. */
BLE_GAP_EVT_DATA_LENGTH_UPDATE = BLE_GAP_EVT_BASE + 20, /**< LL Data Channel PDU payload length updated. \n See @ref ble_gap_evt_data_length_update_t. */
BLE_GAP_EVT_ADV_SET_TERMINATED = BLE_GAP_EVT_BASE + 22, /**< Advertising set terminated. \n See @ref ble_gap_evt_adv_set_terminated_t. */
};
四、GAP和GATT
4.1、GAP初始化
通用访问配置文件(Generic Access Profile,GAP),该Profile保证不同的蓝牙产品可以互相发现对方并建立连接。
GAP初始化函数为gap_params_init,代码如下 :
/**@brief Function for the GAP initialization.
*
* @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
* device including the device name, appearance, and the preferred connection parameters.
*/
static void gap_params_init(void)
{
ret_code_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);
/* YOUR_JOB: Use an appearance value matching the application's use case.
err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
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);
}
主要完成三个工作:
一、GAP初始化安全模式配置,安全模式指连接时是否需要身份验证。
二、蓝牙设备名称设置,即设置蓝牙广播的名字,注意名称长度有限制,最好不要超过十八个字节
三、连接参数设置,连接参数设置,主要是设置:最小连接间隔、最大连接间隔、从机设备潜伏周期、连接超时时间。这些值代表了外围设备针对连接的期望参数。
4.2、GATT初始化
GATT称为通用属性规范(Generic Attribute profile,GATT),GATT层是传输真正数据所在的 层。包括一个数据传输和存储框架以及其基本操作。其大部分设置是在服务中进行的,在主函数中只需要初始化数据长度这个参数。
/**@brief Function for initializing the GATT module.
*/
static void gatt_init(void)
{
ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
APP_ERROR_CHECK(err_code);
}
gatt_init函数中调用了nrf_ble_gatt_init函数
ret_code_t nrf_ble_gatt_init(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_handler_t evt_handler)
{
VERIFY_PARAM_NOT_NULL(p_gatt);
p_gatt->evt_handler = evt_handler;
p_gatt->att_mtu_desired_periph = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;
p_gatt->att_mtu_desired_central = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;
p_gatt->data_length = NRF_SDH_BLE_GAP_DATA_LENGTH;
for (uint32_t i = 0; i < NRF_BLE_GATT_LINK_COUNT; i++)
{
link_init(&p_gatt->links[i]);
}
return NRF_SUCCESS;
}
五、广播
一个广播数据最多可以携带31字节的数据,它通常包含用户可读的名字、关于设备发送数据包的有关信息、用于表示此设备是否可被发现的标志等。当主机接收到广播包后,它可能发送请求更多数据包的请求,称为扫描回应,如果他被设置成主动扫描,从机设备将会发送一个扫描回应作为对主机请求的回应,扫描回应最多也可以携带31字节的数据。广播扫描回应包的数据结构类型可以和广播包一致。
5.1、广播初始化
初始化函数如下:
/**@brief Function for initializing the Advertising functionality.
*/
static void advertising_init(void)
{
ret_code_t err_code;
ble_advertising_init_t init;
memset(&init, 0, sizeof(init));
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;//广播时的名称显示
init.advdata.include_appearance = true;//是否需要图标
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;//蓝牙设备模式
init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);//广播UUID
init.advdata.uuids_complete.p_uuids = m_adv_uuids;
init.config.ble_adv_fast_enabled = true;//广播类型
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;//广播间隔
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;//广播超时
init.evt_handler = on_adv_evt;
err_code = ble_advertising_init(&m_advertising, &init);//初始化广播
APP_ERROR_CHECK(err_code);
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);//设置广播识别号
}
广播初始化实际上就是初始化两个结构体。一个是advdata广播数据,一个是config选择项
5.2、开始广播
开始广播内容如下
/**@brief Function for starting advertising.
*/
static void advertising_start(bool erase_bonds)
{
if (erase_bonds == true)
{
delete_bonds();
// Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event
}
else
{
ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
}
}
5.3、广播响应包
广播包有两种:广播包(Advertising Data)和响应包(Scan Response),其中广播包是每个广播必须的,而响应包是可选的。每个包都是31个字节,数据包中分为有效数据和无效数据两部分。
有效数据部分:包含若干个广播数据单元,这些数据单元的组成是:第一个字节是长度值Len,表示接下来的Len个字节是数据部分。数据部分的第一个字节表示数据的类型,剩下的字节是真正的数据。其中数据类型非常关键,决定了数据代表的是什么和怎样解析。
无效数据部分:因为广播包的长度必须是31个字节,如果有效部分不到31字节,剩下的就用0补全。
六、服务初始化
服务初始化就是建立一个服务声明,分配一个RAM空间。
/**@brief Function for initializing services that will be used by the application.
*/
static void services_init(void)
{
ret_code_t err_code;
nrf_ble_qwr_init_t qwr_init = {0};
// Initialize Queued Write Module.
qwr_init.error_handler = nrf_qwr_error_handler;
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
APP_ERROR_CHECK(err_code);
/* YOUR_JOB: Add code to initialize the services used by the application.
ble_xxs_init_t xxs_init;
ble_yys_init_t yys_init;
// Initialize XXX Service.
memset(&xxs_init, 0, sizeof(xxs_init));
xxs_init.evt_handler = NULL;
xxs_init.is_xxx_notify_supported = true;
xxs_init.ble_xx_initial_value.level = 100;
err_code = ble_bas_init(&m_xxs, &xxs_init);
APP_ERROR_CHECK(err_code);
// Initialize YYY Service.
memset(&yys_init, 0, sizeof(yys_init));
yys_init.evt_handler = on_yys_evt;
yys_init.ble_yy_initial_value.counter = 0;
err_code = ble_yy_service_init(&yys_init, &yy_init);
APP_ERROR_CHECK(err_code);
*/
}
七、连接参数更新
连接参数更新初始化,并没有设置连接参数值,连接参数值已经在GAP初始化中设置了。连接参数更新主要是为了功耗和数据传输之间的一个动态妥协的策略。代码如下
/**@brief Function for initializing the Connection Parameters module.
*/
static void conn_params_init(void)
{
ret_code_t err_code;
ble_conn_params_init_t cp_init;
memset(&cp_init, 0, sizeof(cp_init));
cp_init.p_conn_params = NULL;
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = false;
cp_init.evt_handler = on_conn_params_evt;
cp_init.error_handler = conn_params_error_handler;
err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);
}
八、电源待机
主函数中,最后一个循环等待,调用了idle_state_handle函数,意思为无效状态操作
/**@brief Function for handling the idle state (main loop).
*
* @details If there is no pending log operation, then sleep until next the next event occurs.
*/
static void idle_state_handle(void)
{
if (NRF_LOG_PROCESS() == false)
{
nrf_pwr_mgmt_run();
}
}