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();
}
}
如此可以在每一帧广播发射之后,动态的进行更新。