今天来分析一个例程《BLE实验1:蓝牙工程样例》,注意这里并不是十分专业的讲解,完全是本人的一点小笔记,并没有太大的参考价值,但是如果幸运的话,能让我们有点小的灵感
这是一个最简单的BLE程序了,keil工程文件在这个目录下:\Board\nrf6310\s110\ble_app_template\arm
主函数如下:
int main(void)
{
// Initialize
leds_init();
timers_init();
gpiote_init();
buttons_init();
ble_stack_init();
scheduler_init();
gap_params_init();
advertising_init();
services_init();
conn_params_init();
sec_params_init();
// Start execution
timers_start();
advertising_start();
// Enter main loop
for (;;)
{
app_sched_execute();
power_manage();
}
}
leds_init()和gpiote_init()以及buttons_init()函数暂且不说,timers_init()函数如下:
static void timers_init(void)
{
// Initialize timer module, making it use the scheduler
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true);
/* err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
APP_ERROR_CHECK(err_code); */
}
这个函数里的 APP_TIMER_INIT的app_timer_init函数里初始化了mp_nodes变量,如下:
// Initialize timer node array初始化定时器节点数组
m_node_array_size = max_timers;
mp_nodes = p_buffer;
这个变量在后期会使用
ble_stack_init() 初始化协议栈函数:
static void ble_stack_init(void)
{
uint32_t err_code;
// Initialize the SoftDevice handler module.
SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
// Register with the SoftDevice handler module for BLE events.
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
APP_ERROR_CHECK(err_code);
// Register with the SoftDevice handler module for BLE events.
err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
APP_ERROR_CHECK(err_code);
}
其中SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);初始化了协议栈,最主要的工作就是分配事件内存和调用sd函数初始化时钟等,最后生效语句是:err_code = sd_softdevice_enable(clock_source, softdevice_assertion_handler);
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);函数注册了调度器ble事件调度函数,这个是协议栈回调的函数,当然具体函数由用户编写,内容如下:
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
on_ble_evt(p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
/*
YOUR_JOB: Add service ble_evt handlers calls here, like, for example:
ble_bas_on_ble_evt(&m_bas, p_ble_evt);
*/
}
这其中的on_ble_evt(p_ble_evt);函数定义了蓝牙协议栈发生相关事件的时候的相应操作,如下:
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
uint32_t err_code;
static ble_gap_evt_auth_status_t m_auth_status;
ble_gap_enc_info_t * p_enc_info;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
nrf_gpio_pin_set(CONNECTED_LED_PIN_NO);
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
break;
case BLE_GAP_EVT_DISCONNECTED:
nrf_gpio_pin_clear(CONNECTED_LED_PIN_NO);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
advertising_start();
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_SUCCESS,
&m_sec_params);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0);
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_AUTH_STATUS:
m_auth_status = p_ble_evt->evt.gap_evt.params.auth_status;
break;
case BLE_GAP_EVT_SEC_INFO_REQUEST:
p_enc_info = &m_auth_status.periph_keys.enc_info;
if (p_enc_info->div == p_ble_evt->evt.gap_evt.params.sec_info_request.div)
{
err_code = sd_ble_gap_sec_info_reply(m_conn_handle, p_enc_info, NULL);
APP_ERROR_CHECK(err_code);
}
else
{
// No keys found for this device
err_code = sd_ble_gap_sec_info_reply(m_conn_handle, NULL, NULL);
APP_ERROR_CHECK(err_code);
}
break;
case BLE_GAP_EVT_TIMEOUT:
if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISEMENT)
{
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
// Configure buttons with sense level low as wakeup source.
nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
BUTTON_PULL,
NRF_GPIO_PIN_SENSE_LOW);
// Go to system-off mode (this function will not return; wakeup will cause a reset)
err_code = sd_power_system_off();
APP_ERROR_CHECK(err_code);
}
break;
default:
// No implementation needed.
break;
}
}
其中有两个地方现在可以看看:
case BLE_GAP_EVT_CONNECTED:
nrf_gpio_pin_set(CONNECTED_LED_PIN_NO);
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
break;
case BLE_GAP_EVT_TIMEOUT:
if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISEMENT)
{
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
// Configure buttons with sense level low as wakeup source.
nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
BUTTON_PULL,
NRF_GPIO_PIN_SENSE_LOW);
// Go to system-off mode (this function will not return; wakeup will cause a reset)
err_code = sd_power_system_off();
APP_ERROR_CHECK(err_code);
}
break;
这两个地方有一个现阶段比较重要的一句话:
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
本实验中连接上蓝牙后,本来点亮的D1灭了,而不亮的D2点亮了,作用的的语句就在这里。而点亮D1的在advertising_start函数中。
scheduler_init() 函数初始化调度器,其最终调用的函数是APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE),其中的SCHED_MAX_EVENT_DATA_SIZE定义了最大的事件数据大小,SCHED_QUEUE_SIZE定义了一共有多少个数据,定义如下:
#define SCHED_MAX_EVENT_DATA_SIZE sizeof(app_timer_event_t) /**< Maximum size of scheduler events. Note that scheduler BLE stack events do not contain any data, as the events are being pulled from the stack in the event handler. */
#define SCHED_QUEUE_SIZE 10 /**< Maximum number of events in the scheduler queue. */
gap_params_init() GAP参数初始化函数 内容如下:
static void gap_params_init(void)
{
uint32_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参数,这里不多说
advertising_init广播初始化函数,内容如下:
static void advertising_init(void)
{
uint32_t err_code;
ble_advdata_t advdata;
uint8_t flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
// YOUR_JOB: Use UUIDs for service(s) used in your application.
ble_uuid_t adv_uuids[] = {{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}};
// Build and set advertising data
memset(&advdata, 0, sizeof(advdata));
advdata.name_type = BLE_ADVDATA_FULL_NAME;
advdata.include_appearance = true;
advdata.flags.size = sizeof(flags);
advdata.flags.p_data = &flags;
advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
advdata.uuids_complete.p_uuids = adv_uuids;
err_code = ble_advdata_set(&advdata, NULL);
APP_ERROR_CHECK(err_code);
}
这里最关键的是设置了uuid,这里我们可以分析一下uuid:
uuid的定义:0000xxxx-0000-1000-8000-00805F9B34FB
其中的x是每种设备唯一的id,我们可以查看到定义如下:
#define BLE_UUID_BATTERY_SERVICE 0x180F /**< Battery service UUID. */
这个可以从手机客户端查看到,后面再说
services_init 本例子中该函数没有任何内容
conn_params_init函数都是蓝牙协议的函数,这里先不讲
sec_params_init 安全参数设置函数,内容如下:
static void sec_params_init(void)
{
m_sec_params.timeout = SEC_PARAM_TIMEOUT;
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm = SEC_PARAM_MITM;
m_sec_params.io_caps = SEC_PARAM_IO_CAPABILITIES;
m_sec_params.oob = SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
}
这些参数在ble调度器函数on_ble_evt中使用,如下代码:
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_SUCCESS,
&m_sec_params);
timers_start函数本例程没有使用
advertising_start开始广播函数,这里开始蓝牙广播,要注意的一句话是打开了LED1
nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);
app_sched_execute 函数是事件切换函数,内筒如下:
void app_sched_execute(void)
{
void * p_event_data;
uint16_t event_data_size;
app_sched_event_handler_t event_handler;
// Get next event (if any), and execute handler
while ((app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
{
event_handler(p_event_data, event_data_size);
}
}
最后是电源管理函数,内容如下:
static void power_manage(void)
{
uint32_t err_code = sd_app_evt_wait();
APP_ERROR_CHECK(err_code);
}