nRF52实践:BLE Blinky例程

203 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情

nRF52实践:BLE Blinky例程。

Blinky就是点灯程序,SDK 自带有若干的工程,此处说的是带 BLE 协议栈的点灯程序,BLE 分主从两种不同的示例,本例程为从机,即提供服务一方,通过该例程可以一窥 nRF52 BLE 的开发(套路)。

硬件说明

笔者购买的板子上有4个LED灯和4个按钮,与官方参考设计完全一致,文档提到的一些操作和LED指示状态,完全适用。因为要测试,所以买了2块板子,分一主一从。主用串口看日志;从用 RTT 看。使用串口或jlink线作电源线。

两块板子,加上烧写器,抓包器,花了近600大洋,的确贵。

工程功能

Blinky工程目录为nRF5_SDK_17.1.0_ddde560\examples\ble_peripheral\ble_app_blinky\pca10040\s132\arm5_no_packs。双击ble_app_blinky_pca10040_s132.uvprojx文件即可用 keil 打开工程,按 F7 编译,使用nRFgo Studio软件烧写,也可以在 keil 中按 F8 烧写。

Blinky 实现 LED Button 服务,可以用手机 APP连接对 LED进行操作(此时手机 APP是客户端),也可与另一烧写主机程序的板子连接。前者需要在手机搜索连接设备,后者上电自动搜索连接。

测试方法

LED 状态说明:

  • LED 1: 扫描时亮。
  • LED 2: 连接时亮,扫描时灭。

不同的测试方法,操作效果稍有不同。

使用另一板子烧写主机程序

使用两块不同板子分别烧写主、从机程序,上电后,扫描,接着自动连接,此时 LED2 亮。按任一板子的 Button1,另一板子的 LED 3 会亮。

使用 nRF Connect

烧写从主程序后,上电,在手机使能蓝牙,打开 nRF Connect,扫描到 Nordic_Blinky 设备,连接,此时 LED2 亮。在 CLIENT 界面右上角选Enable CCCDs,使能通知事件。按 Button1,APP 的 Button 服务会显示按下或释放事件。

源码分析

为节省篇幅,不列出长篇代码。可用 VS Code 打开工程分析(可方便查看相关的函数)。BLE 涉及概念十分多,可以先从应用代码及显示效果入手,逐一分析。

蓝牙实际开发中,主要是应用层代码的编写,比如必要的协议栈参数设置,各种功能函数的调用。其中又区别蓝牙从机和蓝牙主机两种设备。至于底层协议,nRF52 SDK 已经实现了,也无从深入研究。

两种设备的开发描述如下:

  • 蓝牙从机

    1. 相关硬件和基础服务初始化
    2. 设置广播参数:广播数据,广播间隔,扫描回应等参数或者数据。
    3. 设置Profile:添加从机服务、特征是,还有设置回调函数用于接收主机数据等。
    4. 设置绑定管理参数(可选)
    5. 启动广播,开始运行。
    6. 等待相关事件,及事件处理,例如收到主机发来的数据,被链接等等。
  • 蓝牙主机

    1. 相关硬件和基础服务初始化
    2. 设置扫描参数。
    3. 设置连接参数。
    4. 设置绑定管理参数(可选)
    5. 启动协议栈,开始运行。
    6. 等待相关事件,及事件处理,例如扫描事件,从机的Notify事件等等。

本文针对从机进行代码对应的分析(主机后续可能再更新)。需要说明的是,官方 SDK 的示例已经非常完备,一般情况下,只需进行需求进行修改即可,部分参数或函数,无需修改。

蓝牙从机

典型的从机程序代码如下:

int main(void)
{
    // Initialize.
    log_init();
    leds_init();
    timers_init();
    buttons_init();
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();
​
    // Start execution.
    NRF_LOG_INFO("Blinky example started.");
    advertising_start();
​
    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}

可以看到,主函数前面部分基本都是各类初始化,然后在死循环中调用idle处理函数。数据交互、接收处理等,均在相应的回调函数中实现。

初始化

初始化相关硬件和基础服务,包括一些定义。

// 设备名,搜索时展示的
#define DEVICE_NAME                     "Nordic_Blinky"
// 连接参数
#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.5 seconds). */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (1 second). */
#define SLAVE_LATENCY                   0                                       /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory time-out (4 seconds). */// 定义 LBS 服务和队列
BLE_LBS_DEF(m_lbs);                                                             /**< LED Button Service instance. */
NRF_BLE_GATT_DEF(m_gatt);                                                       /**< GATT module instance. */
NRF_BLE_QWR_DEF(m_qwr);                                                         /**< Context for the Queued Write module.*/
// 定义句连接句柄
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;

一般不需要改动的初始化函数:

log_init();  日志初始化
leds_init(); 板级LED初始化
timers_init();  定时器初始化 
buttons_init();板级 Button 初始化
power_management_init(); 电源管理初始化
ble_stack_init(); 协议栈初始化
gap_params_init(); gap 初始化
gatt_init(); gatt初始化

设置广播参数

广播数据,广播间隔,扫描回应,连接参数等数据。

advertising_init()
conn_params_init()

设置Profile

添加从机服务,还有设置回调函数用于接收主机数据等。

服务初始化:

services_init
{
init.led_write_handler = led_write_handler; // LED回调处理函数
err_code = ble_lbs_init(&m_lbs, &init);
APP_ERROR_CHECK(err_code);
}

Button初始化:

static void buttons_init(void)
{
    ret_code_t err_code;
    static app_button_cfg_t buttons[] =
    {
        {LEDBUTTON_BUTTON, false, BUTTON_PULL, button_event_handler}
    };
​
    err_code = app_button_init(buttons, ARRAY_SIZE(buttons),
                               BUTTON_DETECTION_DELAY);
    APP_ERROR_CHECK(err_code);
}

设置绑定管理参数(可选)

本文暂不涉及到。

启动广播

启动广播,开始正式运行。

advertising_start

回调处理

等待相关事件,及事件处理,例如收到主机发来的数据,被链接等等。

BLE事件回调处理:

static void ble_stack_init(void)
{
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

ble_evt_handler 函数中处理连接、断开等事件。

buttons_init 函数设置了 button_event_handler,用以处理 Button 事件,当有按键按下时,调用 ble_lbs_on_button_change 发送状态到主机端。

ble_lbs_init 函数设置了 led_write_handler,主要用来设置 LED状态(包括APP和主机板子发送的事件)。

小结

初看 BLE 示例的确头大,涉及很多函数和概念,本文没有长篇列出函数,只是简单介绍程序开发会涉及的知识点,方便有一个直观的认知。