01
前言
本文档指导用户快速地对 BlueNRG SDK 有一个直观、清晰的认识,了解其软件架构,以便顺利地学会利用 SDK 开发自己的用户固件。
本文档所述 SDK 为 BlueNRG-LP/LPS 芯片的 SDK。阅读本文档前,用户应先了解BlueNRG-LP/LPS 芯片的一些基本特性,以及其配套开发板的烧录方式。
02
SDK 目录
从 ST 官网下载 SDK 的安装包,成功安装后,即可获得一个 SDK 目录。见图 1:
图1.SDK 目录
各个目录的功能说明见表 1:
表1. SDK 目录说明
BlueNRG SDK 安装目录为用户工程师开发 BlueNRG 平台提供了一个便捷的入口,举例来说,有以下几个场景:
1. 硬件工程师设计 PCB 前,可通过 Docs 目录找到硬件设计指导文档。完成 PCBA 制作后,可自行使用 Navigator 工具通过串口烧录 Firmware 下的应用固件,验证板子功能。
2. 当工程师想用板子进行功耗、射频测试时,也可在 Firmware 目录下找到合适的已经编译好的固件(测 SOC 蓝牙功耗可用 Beacon,测射频参数可用 DTM)
3. 固件工程师可在 Projects 目录下找到丰富的例程,并且可使用 KEIL、IAR、WiSE 任一个IDE 打开工程、编译、下载。
03
SDK 例程
SDK Projects 目录包含了以下三类例程:
1. Periph_Examples:包含了芯片外设驱动例程。
2. External_Micro :包含了外部单片机的例程,应用于 BlueNRG 芯片在系统中作为协处理器的场景。
3. BLE_Examples :包含了蓝牙相关的所有例程,这些例程的工程特性展示如下:
表2. BLE 例程说明
04
快速实现用户固件功能
本章节指导用户如何快速地在 SDK 中找到相应的 API 接口和位置,以便掌握在 SDK 例程上添加自己的配置和用户逻辑代码的方式。
在对 SDK 提供的例程的功能有所了解之后,假设用户面临的一个开发任务是:
1. BLE 从机功能,包含以下配置:
a. 一个服务,一个特征,特征具备 Write、Notify 属性
b. 设备广播名为“Hello”
2. 使用手机 BLE 工具和设备通讯,打印通讯过程产生的数据
3. 自定义协议,实现以下功能:
a. 控制 LED 亮灭
b. 定时 1s 上传心跳包,内容为连接后的秒计数值
c. 每次按键上传按键事件通知
基于以上任务,我们可以选择 BLE_SerialPort 工程作为基础工程并以此来进行固件开发。
4.1. 验证原始工程
在添加用户代码之前,我们最好先验证了原始工程(BLE_SerialPort)的功能,确保开发环境正常。新建一个用户工程目录,比如,test_sdk1.3.0,然后从 SDK 目录拷贝以下文件到我们的用户工程目录,见图 2:
图2.用户工程目录
打开 Projects > BLE_SerialPort 的 Keil 工程,勾选“Browse Information”选项,以便使能工程内函数的跳转,同时选中 Server 工程配置,见图 3,图 4:
图3.Browse Information
图4.选中 Server 工程配置
编译、下载工程到开发板,工程运行起来后,应能见到以下打印信息:
图5.BLE_SerialPort 工程 LOG 信息
使用 STBLE Toolbox 工具扫描该设备,应能看到设备名为“Sport_LP”,见图 6:
图6.STBLE Toolbox 扫描页面
至此,原始工程已经正常运行起来了。该工程实现了自定义服务、特征的功能,并能通过串口和手机进行数据的收发。要完成此次开发任务,我们只需在特定位置修改一些代码即可。
4.2. 配置 BLE 从机功能
原始工程有两个特征,一个负责发(TX_CHR_UUID),一个负责收(RX_CHR_UUID)。按照要求,我们需要把他们合并为一个可以收、发的特征。图 6 演示了定义新的特征 UUID 并注释掉旧的两个特征 UUID 的方式:
图7.特征 UUID
协议栈提供了 ble_gatt_chr_def_t 类型结构体,用户可以定义一个结构体变量并赋值,以此来声明一个特征。图 7 展示了我们所需的特征配置的相关赋值过程,图中可见特征的 UUID 声明,特征 notify、write 属性声明,特征的特征描述符声明等信息。另外,旧的两个特征应该注释掉。
图8.定义特征
将新的特征配置赋值给服务声明,特征的数量修改为 1 个:
图9.特征声明赋值给服务声明
上述服务、特征相关的数据结构配置完毕,我们还需要将这些配置通过 API 传递给协议栈。
首先,需要先定义一个新的句柄,所谓句柄,简单地说,用户在进行数据收发的时候,需要选择在哪个特征上进行数据收发,此时便需要句柄来指定特征(句柄本质上便是 attribute 的handle 字段)。原来的两个句柄也要注释掉,见图 10
图10. 句柄
然后,将上文定义好的服务、特征通过 aci_gatt_srv_add_service 函数一次性传送给协议栈,协议栈对这些配置进行解析、构建完整的 ATT 属性表,保存在内存中。之后,用户可使用aci_gatt_srv_get_char_decl_handle 接口获取已分配好的句柄(见图 11),此后的数据交互过程将频繁使用该句柄。
图11. 获取句柄
至此,GATT 相关的配置已经完成。但是,由于上层大量引用了旧的两个句柄进行数据收发,因此此时编译会出现比较多的错误,此处暂不处理这些错误,先完成其他的 BLE 配置。
接下来修改蓝牙地址,并修改广播名,见图 12:
图12. 蓝牙地址和广播名
广播名的长度改变后,应注意指定其长度:
图13. 广播名长度
LOCAL_NAME 设置的是广播包里的设备名,当设备连接成功后,主机会从 GAP Profile 的device name 特征里获取另外一个设备名,此处应保持这两个名字一致:
图14. 设备名
图15. 设置设备名
至此,关于蓝牙的应用配置即告完毕。接下来可进行数据通讯相关的配置、实现。
4.3.和手机进行通讯
上一小节配置完从机功能后,编译会产生大量错误,是因为 BLE 的通讯过程会比较多地引用旧的句柄。循着解决这些编译错误的操作,我们能了解到 BLE 通讯的过程。具体操作如下:
全局搜索旧的发送句柄(TXCharHanlde),我们找到了用于数据发送的协议栈 API,修正之 :
图16. 发送 Notify
手机使能订阅后,会通过图 17 的回调函数通知上层。此时应该修改为新的特征句柄,同时,添加一些打印指示 notify 的使能、禁用状态:
图17. 使能订阅回调
添加新的特征句柄全局变量声明,注释掉旧的:
图18. 句柄声明
关于旧的发送句柄(TXCharHanlde)的问题已经全部解决。继续搜索旧的接收句柄(RXCharHanlde),我们应能找到设备接收手机数据的函数接口,将其中的句柄替换为新的特征句柄,见图 19:
图19. 数据接收回调函数
至此,我们应该能通过全部编译过程,并且已经找到了数据发送、接收的位置。此时可以在这些位置添加数据发送、接收的打印函数。接收数据的用户接口见图 20:
图20. 用户接收 BLE 数据
关于发送数据,原始工程实现了以下处理流程:
1. 从串口接收数据
2. 解析数据为命令并缓存这些命令
3. 通过轮询的方式,不断将命令缓冲区里的命令发送出去
该处理流程不适用于我们的任务要求,我们需要先取消这部分功能,见图 21
图21. 取消原有的数据处理流程
然后设计自己的发送函数,见图 22:
图22. 自定义 BLE 数据发送函数
至此,蓝牙的通讯功能已经全部实现完毕。用户可通过:
• Data_Received()接口接收数据
• user_send_data_over_ble()接口发送数据
4.4.添加其它功能
根据任务要求,我们还需实现下面三个功能:
1. 控制 LED 亮灭
2. 定时 1s 上传心跳包,内容为连接后的秒计数值
3. 每次按键上传按键事件通知下面开始逐个实现:
4.4.1. 控制 LED 亮灭
首先,实现 LED 亮灭处理函数,
图23. LED 命令处理函数
将其添加到 BLE 数据接收函数处,见图 24。LED 控制功能实现完毕。
图24. 接收数据后进行 LED 命令处理
4.4.2. 每秒上传心跳包
首先,实现心跳上传处理函数,实现当设备连接后,每秒上传一个 4 字节的计数值,并使用0xaa 作为命令字。见图 25:
图25. 心跳上传处理
将心跳处理函数添加到系统任务处理函数 App_Tick 中,App_Tick 会在 main loop 中不断地被调用。心跳包需要在蓝牙连接成功并且使能了订阅之后才可以发送。另外,使用 timeout_flag变量来控制其每秒只被调用一次,实现方法如下:
图26. 心跳处理
上述 timeout_flag 变量需要使用每秒循环的软件定时器来周期性置位。软件定时器的应用方式很简单。
首先,实例化一个定时器,并定义超时回调函数:
图27. 软件定时器实例化
然后,在 Serial_port_DeviceInit 函数的末端位置注册超时回调函数并启动定时器。
图28. 启动定时器
4.4.3. 上传按键事件
首先,实现按键回调函数,该函数在按键按下时被调用。发送按键事件前,应检查此时蓝牙是否处理连接、已订阅状态。见图 29:
图29. 按键回调
按键中断服务函数中调用:
图30. 按键中断服务函数
至此,开发任务的要求已经全部实现完毕。接下来进行功能验证。
4.5.验证功能
用户工程运行起来后,用 STBLE Toolbox 扫描,可见广播名已经修改过来了。
图31. 用户工程广播名
连上设备并点击 Notify 开关以使能订阅,可观察到底部已经开始接收到设备的心跳包数据(以 AA 开头的 5 字节数据),该数据每秒钟变化一次,见图 32:
图32. 使能订阅
通过 LOG 也能观察到心跳包发送情况,此时如果按动按键,也能观察到按键事件已经发送:
图33. 用户工程 LOG
05
小结
跑完了上述用户任务开发的流程后,相信用户对 BlueNRG SDK 的软件架构应有所理解了。BlueNRG SDK 的软件层次架构为 STM32 典型的三层架构,分别为驱动层、中间层、用户层:
图34. 软件层次架构
上述添加用户功能的整个过程,其实只改动到了用户层的功能,用户层包含以下几个文件:
图35. 用户层文件
这些文件的含义是:
• serial_port.c,用户应用逻辑的实现
• BLE_SerialPort_main.c,程序入口,程序主流程
• gatt_db.c,BLE GATT 层功能的实现
• rf_device_it.c,存放所有的中断服务函数
上述用户固件的功能,大多都在 serial_port.c 中实现。BLE_SerialPort_main.c 函数则实现了系统的主要流程。简单来说,BlueNRG SDK 的裸机系统即是一个前后台系统。蓝牙事件、按键中断等属于前台处理,负责置位相关标志位和状态,main 函数的 while1 属于后台处理,运行蓝牙协议栈、用户任务处理等后台任务,见图 36:
图36. 系统流程
BlueNRG SDK 中的绝大多数例程都使用了本文档所述的软件架构,即前后台系统。该软件架构比较简单,优点是用户能非常快速地掌握其流程,能够依据本文档的示例快速构建自己的用户功能。缺点是功能比较简单,用户需要在此基础上再添加一个调度器以应对复杂功能的要求。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !