Nordic 的自定义广播和动态广播实现(2)

4 篇文章 4 订阅
3 篇文章 0 订阅

Nordic 的动态广播实现

1 切换广播A和广播B

第一种采用最简单的广播切换,广播一段时间A,然后再广播一段时间B,然后再切换为A广播,动态的转换。
可以采用定时器的方式,循环定时切换。

BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */

//停止广播
void advertising_stop(ble_advertising_t *const p_advertising) {

  uint32_t err_code = sd_ble_gap_adv_stop(p_advertising->adv_handle);
  APP_ERROR_CHECK(err_code);
}
//重新初始化,并开启广播
void adv_updata(void) {

  uint32_t err_code;

  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);

  err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
  APP_ERROR_CHECK(err_code);
}
//定时器中断函数
static void adv_test_timeout_handler(void *p_context)
 {
	static uint8_t change_flag = 0;
  	NRF_LOG_INFO("advertising changed !");
  	advertising_stop(&m_advertising);
		
		if(change_flag == 0){
 	 memset(test_array_adv, 255, sizeof(test_array_adv) / sizeof(test_array_adv[0]));
  	memset(test_array_scan, 255, sizeof(test_array_scan) / sizeof(test_array_scan[0]));
  	change_flag = 1;
  	}
  	else{
	 memset(test_array_adv, 0, sizeof(test_array_adv) / sizeof(test_array_adv[0]));
  	memset(test_array_scan, 0, sizeof(test_array_scan) / sizeof(test_array_scan[0]));
  	change_flag = 0;
}
  	

  adv_updata();
}

//定时器初始化
static void timers_init(void) {

  NRF_LOG_INFO("timer init");
  ret_code_t err_code = app_timer_init();
  APP_ERROR_CHECK(err_code);
  APP_ERROR_CHECK(app_timer_create(&adv_test_timer_id, APP_TIMER_MODE_REPEATED,
                                   adv_test_timeout_handler));
}

//开启定时器
static void adv_timer_start(void) {

   APP_ERROR_CHECK(
       app_timer_start(adv_test_timer_id, APP_TIMER_TICKS(1000), NULL));
}


传建一个1s的周期定时器,然后在主函数中打开,这样每秒钟都会产生定时器中断,之后会进入回调函数adv_test_timeout_handler,在这里我们修改广播的制造商信息的数组内容然后重新开启;这样广播内容会在0x00,和0xff之间不断被切换;当然也可以回调函数里修改 init 结构体的其他配置,通过调用 adv_updata 函数,都会重新装载进去;

处理流程: 1) advertising_stop() 停止广播
2)修改广播内容和参数( ble_advertising_init_t init 结构体);
3) adv_updata() 重新初始化并开启广播

2 广播超时事件

在上节的广播初始化里面会有这样的设置

    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;

分别设置了,快速广播,和它的广播间隔,持续时间,和广播事件;这里对应存在的是慢速广播,二者没有本质的区别,仅仅是广播间隔 APP_ADV_INTERVAL 的不同。

注意这个参数的设置
#define APP_ADV_DURATION 18000 /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
这里是快速广播持续的事件180s,超过180s ,广播会进入观察者的回调函数

#define BLE_ADVERTISING_DEF(_name)                                                                  \
static ble_advertising_t _name;                                                                     \
NRF_SDH_BLE_OBSERVER(_name ## _ble_obs,                                                             \
                     BLE_ADV_BLE_OBSERVER_PRIO,                                                     \
                     ble_advertising_on_ble_evt, &_name);                                           \
NRF_SDH_SOC_OBSERVER(_name ## _soc_obs,                                                             \
                     BLE_ADV_SOC_OBSERVER_PRIO,                                                     \
                     ble_advertising_on_sys_evt, &_name)

void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    ble_advertising_t * p_advertising = (ble_advertising_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connected(p_advertising, p_ble_evt);
            break;

        // Upon disconnection, whitelist will be activated and direct advertising is started.
        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnected(p_advertising, p_ble_evt);
            break;

        // Upon terminated advertising (time-out), the next advertising mode is started.
        case BLE_GAP_EVT_ADV_SET_TERMINATED:
            on_terminated(p_advertising, p_ble_evt);
            break;

        default:
            break;
    }
}

/**@brief Function for handling the Timeout event.
 *
 * @param[in] p_advertising Advertising module instance.
 * @param[in] p_ble_evt Event received from the BLE stack.
 */
static void on_terminated(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
{
    ret_code_t ret;

    if (p_ble_evt->header.evt_id != BLE_GAP_EVT_ADV_SET_TERMINATED)
    {
        // Nothing to do.
        return;
    }

    if (  p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT
        ||p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_LIMIT_REACHED)
    {
        // Start advertising in the next mode.
        ret = ble_advertising_start(p_advertising, adv_mode_next_get(p_advertising->adv_mode_current));

        if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
        {
            p_advertising->error_handler(ret);
        }
    }
}


超时之后,会自动切换模式:
adv_mode_next_get(p_advertising->adv_mode_current)
会在几种广播模式切换

typedef enum
{
    BLE_ADV_MODE_IDLE,               /**< Idle; no connectable advertising is ongoing. */
    BLE_ADV_MODE_DIRECTED_HIGH_DUTY, /**< Directed advertising (high duty cycle) attempts to connect to the most recently disconnected peer. */
    BLE_ADV_MODE_DIRECTED,           /**< Directed advertising (low duty cycle) attempts to connect to the most recently disconnected peer. */
    BLE_ADV_MODE_FAST,               /**< Fast advertising will connect to any peer device, or filter with a whitelist if one exists. */
    BLE_ADV_MODE_SLOW,               /**< Slow advertising is similar to fast advertising. By default, it uses a longer advertising interval and time-out than fast advertising. However, these options are defined by the user. */
} ble_adv_mode_t;


/**@brief Function for checking the next advertising mode.
 *
 * @param[in] adv_mode Current advertising mode.
 */
static ble_adv_mode_t adv_mode_next_get(ble_adv_mode_t adv_mode)
{
    return (ble_adv_mode_t)((adv_mode + 1) % BLE_ADV_MODES);
}

如果初始化是快速广播,那么它将切换为 – BLE_ADV_MODE_SLOW,最后切换为 BLE_ADV_MODE_IDLE;

这里也调用了ble_advertising_start()函数,你也可以在它之前更改广播内容,就能重新开始一段广播;
如果想要一种广播持续进行,APP_ADV_DURATION 为0 ,即可;

3 Radio Notification

radio_noyification 函数的作用是在发射完成一帧 radio 的某个时间点或位置,产生一个中断事件,进入中断函数,执行相应的操作。
首先要添加 ble_radio_notificatio .c和 .h 相关文件;


#include "ble_radio_notification.h"
#include "nrf_nvic.h"
#include <stdlib.h>


static bool                                 m_radio_active = false;  /**< Current radio state. */
static ble_radio_notification_evt_handler_t m_evt_handler  = NULL;   /**< Application event handler for handling Radio Notification events. */


void SWI1_IRQHandler(void)
{
   // m_radio_active = !m_radio_active;
    if (m_evt_handler != NULL)
    {
        m_evt_handler(m_radio_active);
    }
}


uint32_t ble_radio_notification_init(uint32_t                             irq_priority,
                                     uint8_t                              distance,
                                     ble_radio_notification_evt_handler_t evt_handler)
{
    uint32_t err_code;

    m_evt_handler = evt_handler;

    // Initialize Radio Notification software interrupt
    err_code = sd_nvic_ClearPendingIRQ(SWI1_IRQn);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    err_code = sd_nvic_SetPriority(SWI1_IRQn, irq_priority);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    err_code = sd_nvic_EnableIRQ(SWI1_IRQn);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Configure the event
    return sd_radio_notification_cfg_set(NRF_RADIO_NOTIFICATION_TYPE_INT_ON_INACTIVE, distance);
}


sd_radio_notification_cfg_set()函数

/**@brief Configures the Radio Notification signal.
 *

 * @param[in]  type      Type of notification signal, see @ref NRF_RADIO_NOTIFICATION_TYPES.
 *                       @ref NRF_RADIO_NOTIFICATION_TYPE_NONE shall be used to turn off radio
 *                       notification. Using @ref NRF_RADIO_NOTIFICATION_DISTANCE_NONE is
 *                       recommended (but not required) to be used with
 *                       @ref NRF_RADIO_NOTIFICATION_TYPE_NONE.
 *
 * @param[in]  distance  Distance between the notification signal and start of radio activity, see @ref NRF_RADIO_NOTIFICATION_DISTANCES.
 *                       This parameter is ignored when @ref NRF_RADIO_NOTIFICATION_TYPE_NONE or
 *                       @ref NRF_RADIO_NOTIFICATION_TYPE_INT_ON_INACTIVE is used.
 *
 * @retval ::NRF_ERROR_INVALID_PARAM The group number is invalid.
 * @retval ::NRF_ERROR_INVALID_STATE A protocol stack or other SoftDevice is running. Stop all
 *                                   running activities and retry.
 * @retval ::NRF_SUCCESS
 */
SVCALL(SD_RADIO_NOTIFICATION_CFG_SET, uint32_t, sd_radio_notification_cfg_set(uint8_t type, uint8_t distance));

相关传参: irq_priority - 设置中断的优先级
type - notification 信号的类型
distance - 中断事件生效的时间
evt_handler - 回调函数
参考 nrf_soc.h 对应的类型

/**@brief Radio notification distances. */
enum NRF_RADIO_NOTIFICATION_DISTANCES
{
  NRF_RADIO_NOTIFICATION_DISTANCE_NONE = 0, /**< The event does not have a notification. */
  NRF_RADIO_NOTIFICATION_DISTANCE_800US,    /**< The distance from the active notification to start of radio activity. */
  NRF_RADIO_NOTIFICATION_DISTANCE_1740US,   /**< The distance from the active notification to start of radio activity. */
  NRF_RADIO_NOTIFICATION_DISTANCE_2680US,   /**< The distance from the active notification to start of radio activity. */
  NRF_RADIO_NOTIFICATION_DISTANCE_3620US,   /**< The distance from the active notification to start of radio activity. */
  NRF_RADIO_NOTIFICATION_DISTANCE_4560US,   /**< The distance from the active notification to start of radio activity. */
  NRF_RADIO_NOTIFICATION_DISTANCE_5500US    /**< The distance from the active notification to start of radio activity. */
};


/**@brief Radio notification types. */
enum NRF_RADIO_NOTIFICATION_TYPES
{
  NRF_RADIO_NOTIFICATION_TYPE_NONE = 0,        /**< The event does not have a radio notification signal. */
  NRF_RADIO_NOTIFICATION_TYPE_INT_ON_ACTIVE,   /**< Using interrupt for notification when the radio will be enabled. */
  NRF_RADIO_NOTIFICATION_TYPE_INT_ON_INACTIVE, /**< Using interrupt for notification when the radio has been disabled. */
  NRF_RADIO_NOTIFICATION_TYPE_INT_ON_BOTH,     /**< Using interrupt for notification both when the radio will be enabled and disabled. */
};

/**@brief The Radio signal callback types. */
enum NRF_RADIO_CALLBACK_SIGNAL_TYPE
{
  NRF_RADIO_CALLBACK_SIGNAL_TYPE_START,             /**< This signal indicates the start of the radio timeslot. */
  NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0,            /**< This signal indicates the NRF_TIMER0 interrupt. */
  NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO,             /**< This signal indicates the NRF_RADIO interrupt. */
  NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED,     /**< This signal indicates extend action failed. */
  NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED   /**< This signal indicates extend action succeeded. */
};

在主函数的调用:

static __attribute__((unused)) void
ble_radio_notification_handdle(bool radio_state) {

  static uint8_t radio_flag = 0;
  int8_t power_leval = 0;
  
 if (radio_flag == 0) {
    NRF_LOG_INFO("radio A start");
     radio_flag = 1;
    adv_stop();

    memset(test_array_adv, 255,
           sizeof(test_array_adv) / sizeof(test_array_adv[0]));
    memset(test_array_scan, 255,
           sizeof(test_array_scan) / sizeof(test_array_scan[0]));

    init.advdata.p_tx_power_level = &power_leval;

    init.advdata.name_type = BLE_ADVDATA_NO_NAME;
    init.srdata.name_type = BLE_ADVDATA_FULL_NAME;
    sr_data.company_identifier = 0xBB00;
    sr_data.data.p_data = test_array_scan;
    sr_data.data.size = sizeof(test_array_scan) / sizeof(test_array_scan[0]);

     init.srdata.name_type = BLE_ADVDATA_FULL_NAME;

    init.srdata.p_manuf_specific_data = &sr_data;
    init.advdata.p_manuf_specific_data->company_identifier = 0xBB00;

    adv_updata();
  }

  else if (radio_flag == 1) {
    radio_flag = 0;
    NRF_LOG_INFO("change to radio B");
	  adv_stop();

    memset(test_array_adv, 0,
           sizeof(test_array_adv) / sizeof(test_array_adv[0]));
    memset(test_array_scan, 0,
           sizeof(test_array_scan) / sizeof(test_array_scan[0]));

    init.advdata.p_tx_power_level = &power_leval;

    init.advdata.name_type = BLE_ADVDATA_NO_NAME;
    init.srdata.name_type = BLE_ADVDATA_FULL_NAME;
    sr_data.company_identifier = 0xBB00;
    sr_data.data.p_data = test_array_scan;
    sr_data.data.size = sizeof(test_array_scan) / sizeof(test_array_scan[0]);

     init.srdata.name_type = BLE_ADVDATA_FULL_NAME;

    init.srdata.p_manuf_specific_data = &sr_data;
    init.advdata.p_manuf_specific_data->company_identifier = 0xAA00;

    adv_updata();
  } 
}

int main(void) {
  bool erase_bonds;

  // Initialize.
  // uart_init();
  log_init();
  timers_init();
  buttons_leds_init(&erase_bonds);
  power_management_init();
  ble_stack_init();
  gap_params_init();
  gatt_init();
  services_init();
   advertising_init();
  conn_params_init();
   APP_ERROR_CHECK(
   ble_radio_notification_init(6, NRF_RADIO_NOTIFICATION_DISTANCE_800US,
                            ble_radio_notification_handdle));
  //adv_init();
   advertising_start();
   adv_timer_start();

  // Enter main loop.
  for (;;) {
    idle_state_handle();
  }
}

如此可以在每一帧广播发射之后,动态的进行更新。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值