芯片nRF52840蓝牙的5.0远距离是怎么实现的?

一口Linux
关注

以上这段代码循环处理动态的向外广播气体传感器数据,
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。

声明: 本文由入驻OFweek维科号的作者撰写,观点仅代表作者本人,不代表OFweek立场。如有侵权或其他问题,请联系举报。
侵权投诉

下载OFweek,一手掌握高科技全行业资讯

还不是OFweek会员,马上注册
打开app,查看更多精彩资讯 >
  • 长按识别二维码
  • 进入OFweek阅读全文
长按图片进行保存