以上这段代码循环处理动态的向外广播气体传感器数据,
if(start_adv_flag == 0xa5) 这个变量是uart_event_handle 串口接收中断里进行赋值的,因为52840和气体传感器通过串口进行相连,当此变量等于0xa5时,说明传感器又发来数据了,
(void)sd_ble_gap_adv_stop(m_advertising.adv_handle); 这句的意思是停止当前的广播,
nrf_uart_disable(NRF_UART0);这句的意思是暂时关了串口中断,
advertising_init();这句的意思是重新配置广播数据,其中就包含在uart_event_handle串口中断中接收的传感器数据。
advertising_start();然后就开始新的广播。
2. 核心函数详解1)advertising_init()
功能:此函数主要实现的是如何设置蓝牙的广播数据内容,
company_identifier:代表的是厂商ID,可以自己随意设置
adv_interval:表示的是广播间隔
adv_duration:表示的是广播超时
ble_adv_primary_phy:表示的是主广播物理通道
ble_adv_extended_enabled = true;表示的是开启蓝牙5.0扩展广播。
本次设置的是BLE_GAP_PHY_CODED,即低速远距离编码模式。
ble_adv_secondary_phy= BLE_GAP_PHY_CODED 表示的是第二广播物理通道。
*@brief Function for initializing the Advertising functionality.
static void advertising_init(void)
{
uint32_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 = false;
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
m_manuf_data.company_identifier = company_identifier; //0xaa55;
m_manuf_data.data.size = manuf_data_size;
m_manuf_data.data.p_data = manuf_data;
init.advdata.p_manuf_specific_data = &m_manuf_data;
// init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
// init.srdata.uuids_complete.p_uuids = m_adv_uuids;
init.config.ble_adv_fast_enabled = true;
init.config.ble_adv_fast_interval = adv_interval;
init.config.ble_adv_fast_timeout = adv_duration;
init.config.ble_adv_primary_phy = BLE_GAP_PHY_CODED; //BLE_GAP_PHY_1MBPS;
init.config.ble_adv_secondary_phy = BLE_GAP_PHY_CODED; //BLE_GAP_PHY_2MBPS;
init.config.ble_adv_extended_enabled = true;
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);
}
设置完成后,就可以使用支持蓝牙5.0的智能手机使用nrf_connectAPP进行扫描广播包,我们测试用的是华为Mate20,几乎支持蓝牙5.0的所有特性。
此函数主要设置的是蓝牙器件名,连接间隔和从机延时等等相关的参数,设置完成后,也可以使用nrf_connect APP进行查看。
2)services_init()
功能:此函数实现的是蓝牙服务初始化,向蓝牙协议栈底层注册了nrf_qwr_error_handler和nus_data_handler这两个回调函数,其中nus_data_handler是一个非常核心的函数,当52840接收到蓝牙数据时,此回调函数立即被调用,协议栈底层会上抛出应用数据的指针,以及数据的长度。
*@brief Function for initializing services that will be used by the application.
static void services_init(void)
{
uint32_t err_code;
ble_nus_init_t nus_init;
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);
// Initialize NUS.
memset(&nus_init, 0, sizeof(nus_init));
nus_init.data_handler = nus_data_handler;
err_code = ble_nus_init(&m_nus, &nus_init);
APP_ERROR_CHECK(err_code);
}
3)nus_data_handler()
功能:
函数里进行解析相关的功能。
具体如何解析由手机APP发送的数据格式决定,也就是开发者制定的应用层协议。
*@brief Function for handling the data from the Nordic UART Service.
*
* @details This function will process the data received from the Nordic UART BLE Service and send
* it to the UART module.
*
* @param[in] p_evt Nordic UART Service event.
*@snippet [Handling the data received over BLE]
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
if (p_evt->type == BLE_NUS_EVT_RX_DATA)
{
uint32_t err_code;
NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
{
do
{
err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
{
NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
APP_ERROR_CHECK(err_code);
}
} while (err_code == NRF_ERROR_BUSY);
}
if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '')
{
while (app_uart_put('') == NRF_ERROR_BUSY);
}
}
}
4) ble_evt_handler()
功能:
此函数主要用来检测蓝牙的连接或断开等相关事件。
*@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)
{
uint32_t err_code;
switch (p_ble_evt->header.evt_id)
{
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_DISCONNECTED:
NRF_LOG_INFO("Disconnected");
// LED indication will be changed when advertising starts.
m_conn_handle = BLE_CONN_HANDLE_INVALID;
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_GAP_EVT_SEC_PARAMS_REQUEST:
// Pairing not supported
err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
// No system attributes have been stored.
err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
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.
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;
}
}
5)uart_event_handle()
功能:uart_event_handle也是一个常用的回调函数,在串口初始化时对协议栈底层进行注册,当52840串口接收到数据时,此回调函数立即被调用。
大家可以在里面添加解析串口命令相关的功能代码,其实完全可以把它当作STM32串口中断服务函数来用。
只是特别说明的是52840的串口设计得更强大一些,带有256字节的硬件FIFO BUFF,而且支持Easy DMA进行数据搬移,这大大的减轻了CPU 内核的负担,节省了功耗。
所谓Easy DMA就是串口专用的DMA,只是不能用来传输flash的数据。
*@brief Function for handling app_uart events.
*
* @details This function will receive a single character from the app_uart module and append it to
* a string. The string will be be sent over BLE when the last character received was a
* 'new line' '' (hex 0x0A) or if the string has reached the maximum data length.
*@snippet [Handling the data received over UART]
void uart_event_handle(app_uart_evt_t * p_event)
{
static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
static uint8_t index = 0;
uint32_t err_code;
switch (p_event->evt_type)
{
case APP_UART_DATA_READY:
UNUSED_VARIABLE(app_uart_get(&data_array[index]));
if(data_array[0] == 0xfa)
{
index++;
if((data_array[1] == 0x01) && (data_array[2] > 3) && (index >= 12))
{
if(data_array[2] > 10)
{
adv_duration = data_array[2] * 19;
adv_interval = 320;
}
else
{
adv_duration = data_array[2] * 9;
adv_interval = 160;
}
company_identifier = ((uint16_t)*(data_array + 3) << 8) | *(data_array + 4) ;
memcpy(manuf_data,data_array + 5,6);
memset(data_array,0,12);
index = 0;
start_adv_flag = 0xa5;
}
}
if(index >= 12)
{
memset(data_array,0,12);
index = 0;
}
break;
case APP_UART_COMMUNICATION_ERROR:
APP_ERROR_HANDLER(p_event->data.error_communication);
break;
case APP_UART_FIFO_ERROR:
APP_ERROR_HANDLER(p_event->data.error_code);
break;
default:
break;
}
}
void uart_event_handle(app_uart_evt_t * p_event), 这个函数是串口接收中断回调函数,由NRF串口底层驱动库上抛的,
APP_UART_DATA_READY,表示串口数据已经准备好
UNUSED_VARIABLE(app_uart_get(&data_array[index])); 表示读取串口数据,并放到数组data_array,每读取一次,index+1次,此中断回调函数,每收到一个字节,就会被调用一次。
if(data_array[0] == 0xfa) 解析协议传感器数据头,传感器串口发送过来的数据是以0xfa开头的,后面就是长度,传感器数据,和校验码。
adv_duration代表广播数次数,具体广播多少次由传感器发送过来的数据指定的,一般是数据比较异常,广播次数就会比较多,
adv_interval = 320; 这个是设置广播间隔,数值越大,越省电。company_identifier表示公司ID,也会体现在广播数据内容里,手机APP可以观察到。
memcpy(manuf_data,data_array + 5,6);这句是提取数组里的传感器数据到manuf_data里,然后广播初始化的时候会提取manuf_data这个变量的数据,
start_adv_flag = 0xa5;启动新的广播标致,主循环里会不断检查这个标致。
if(index >= 12) index = 0; 由于协议数据长度是12个字节,接收完成了,就要清0。