
01前言 本文档指导用户快速地对 BlueNRG SDK 有一个直观、清晰的认识,了解其软件架构,以便顺利地学会利用 SDK 开发自己的用户固件。 0 F) Q5 W3 d( m7 E 本文档所述 SDK 为 BlueNRG-LP/LPS 芯片的 SDK。阅读本文档前,用户应先了解BlueNRG-LP/LPS 芯片的一些基本特性,以及其配套开发板的烧录方式。+ p- w/ p' H- y+ o: \! F 02SDK 目录6 d* W& U8 X9 n# _$ p8 W# Q, } 从 ST 官网下载 SDK 的安装包,成功安装后,即可获得一个 SDK 目录。见图 1:, F X) E0 u$ a, r0 i 4 d( l- Y4 j2 E+ B+ D# i ![]() 图1.SDK 目录 " {9 Y/ _& t( h% F+ ]( }8 i* n8 {) ^: B( W R* m . Z. t! X5 T* T* U0 P 各个目录的功能说明见表 1:6 _9 e* K( z$ H4 H; W ![]() 表1. SDK 目录说明 BlueNRG SDK 安装目录为用户工程师开发 BlueNRG 平台提供了一个便捷的入口,举例来说,有以下几个场景:9 B5 t" {% v8 C. | 1. 硬件工程师设计 PCB 前,可通过 Docs 目录找到硬件设计指导文档。完成 PCBA 制作后,可自行使用 Navigator 工具通过串口烧录 Firmware 下的应用固件,验证板子功能。 1 c/ T: Y t3 e9 U- f 2. 当工程师想用板子进行功耗、射频测试时,也可在 Firmware 目录下找到合适的已经编译好的固件(测 SOC 蓝牙功耗可用 Beacon,测射频参数可用 DTM) + g* U) V0 g1 ? 3. 固件工程师可在 Projects 目录下找到丰富的例程,并且可使用 KEIL、IAR、WiSE 任一个IDE 打开工程、编译、下载。 U n& Y) W8 P& l) M) m/ S. K: X 4 }. H# q8 `0 q2 X- q 03SDK 例程, X6 @; q4 t% u! d& D( F- c V SDK Projects 目录包含了以下三类例程:+ m4 c. q0 _$ ] 1. Periph_Examples:包含了芯片外设驱动例程。 2. External_Micro :包含了外部单片机的例程,应用于 BlueNRG 芯片在系统中作为协处理器的场景。 3. BLE_Examples :包含了蓝牙相关的所有例程,这些例程的工程特性展示如下: 6 v% F6 }) ]; v. C" | x0 i/ t7 Y ![]() 表2. BLE 例程说明 d) o$ p- H$ g/ ?4 b) q9 `2 d 04快速实现用户固件功能# p# Z7 P7 Q1 D6 }/ D8 E6 y 本章节指导用户如何快速地在 SDK 中找到相应的 API 接口和位置,以便掌握在 SDK 例程上添加自己的配置和用户逻辑代码的方式。 在对 SDK 提供的例程的功能有所了解之后,假设用户面临的一个开发任务是: 1. BLE 从机功能,包含以下配置:6 C/ C! h$ ]" _5 E a. 一个服务,一个特征,特征具备 Write、Notify 属性) M& _5 F: w Q b. 设备广播名为“Hello” 2. 使用手机 BLE 工具和设备通讯,打印通讯过程产生的数据 3. 自定义协议,实现以下功能:: g# U8 @- S4 ^0 y3 V& f6 _; r a. 控制 LED 亮灭 b. 定时 1s 上传心跳包,内容为连接后的秒计数值. M9 ^! S4 b' G! U }4 {" s6 w c. 每次按键上传按键事件通知 基于以上任务,我们可以选择 BLE_SerialPort 工程作为基础工程并以此来进行固件开发。/ d6 F4 `. g r $ `1 z9 T1 u& }" u 4.1. 验证原始工程 在添加用户代码之前,我们最好先验证了原始工程(BLE_SerialPort)的功能,确保开发环境正常。新建一个用户工程目录,比如,test_sdk1.3.0,然后从 SDK 目录拷贝以下文件到我们的用户工程目录,见图 2: % u' R0 q2 \$ L l; k ![]() 图2.用户工程目录 7 K% f8 J9 V2 R1 w% k: c( j, ?# Q0 z$ l. p& }1 g& v 打开 Projects > BLE_SerialPort 的 Keil 工程,勾选“Browse Information”选项,以便使能工程内函数的跳转,同时选中 Server 工程配置,见图 3,图 4: 4 s% I( b2 z% ]) q/ S- E ![]() 图3.Browse Information 0 o# t* {- [6 s/ c, H![]() 图4.选中 Server 工程配置 - M' g# R) g( |编译、下载工程到开发板,工程运行起来后,应能见到以下打印信息: ![]() 图5.BLE_SerialPort 工程 LOG 信息 5 M6 T' C8 x3 ? 使用 STBLE Toolbox 工具扫描该设备,应能看到设备名为“Sport_LP”,见图 6:+ ~3 T2 Q9 G6 N- h* }6 q$ z. V & l& o* J# M. F/ g( {2 W ![]() 图6.STBLE Toolbox 扫描页面 1 a! z, e q. b至此,原始工程已经正常运行起来了。该工程实现了自定义服务、特征的功能,并能通过串口和手机进行数据的收发。要完成此次开发任务,我们只需在特定位置修改一些代码即可。 4 _! @/ J3 x# j2 e 4.2. 配置 BLE 从机功能 原始工程有两个特征,一个负责发(TX_CHR_UUID),一个负责收(RX_CHR_UUID)。按照要求,我们需要把他们合并为一个可以收、发的特征。图 6 演示了定义新的特征 UUID 并注释掉旧的两个特征 UUID 的方式: & h- m9 X: ^; H5 Q9 T R ![]() 图7.特征 UUID 8 k: Y5 H1 p9 U5 S7 a- H0 D协议栈提供了 ble_gatt_chr_def_t 类型结构体,用户可以定义一个结构体变量并赋值,以此来声明一个特征。图 7 展示了我们所需的特征配置的相关赋值过程,图中可见特征的 UUID 声明,特征 notify、write 属性声明,特征的特征描述符声明等信息。另外,旧的两个特征应该注释掉。 ![]() 图8.定义特征 : ]8 Q* [4 O& u) H; O将新的特征配置赋值给服务声明,特征的数量修改为 1 个:6 t% n y3 g4 K+ S- ^ a ; {. `& P& Q5 X9 q ![]() 图9.特征声明赋值给服务声明 , ]2 b* T, s$ {3 S2 ?上述服务、特征相关的数据结构配置完毕,我们还需要将这些配置通过 API 传递给协议栈。 首先,需要先定义一个新的句柄,所谓句柄,简单地说,用户在进行数据收发的时候,需要选择在哪个特征上进行数据收发,此时便需要句柄来指定特征(句柄本质上便是 attribute 的handle 字段)。原来的两个句柄也要注释掉,见图 10 ![]() 图10. 句柄 " ]4 ^( s9 N5 H1 [- a2 M; J; R 然后,将上文定义好的服务、特征通过 aci_gatt_srv_add_service 函数一次性传送给协议栈,协议栈对这些配置进行解析、构建完整的 ATT 属性表,保存在内存中。之后,用户可使用aci_gatt_srv_get_char_decl_handle 接口获取已分配好的句柄(见图 11),此后的数据交互过程将频繁使用该句柄。/ q8 y9 M; E4 Y) j" i8 _8 l+ o3 M & Z; c4 ~+ M6 a ![]() 图11. 获取句柄 , x& d- u4 ~4 o. L至此,GATT 相关的配置已经完成。但是,由于上层大量引用了旧的两个句柄进行数据收发,因此此时编译会出现比较多的错误,此处暂不处理这些错误,先完成其他的 BLE 配置。6 h7 K6 \) p- {( x , N3 u8 d9 q( G. g! G 接下来修改蓝牙地址,并修改广播名,见图 12: B, i6 u. r2 K- a! o & S* r3 P. e8 y' S. A: y Y( u7 y ![]() 图12. 蓝牙地址和广播名 ! M+ L% m2 z5 |& Y广播名的长度改变后,应注意指定其长度:/ u0 o9 K8 s; `" C( A0 i 9 |3 m5 I* @9 {% Z y ![]() 图13. 广播名长度 , J5 [% ?, J! w0 v! CLOCAL_NAME 设置的是广播包里的设备名,当设备连接成功后,主机会从 GAP Profile 的device name 特征里获取另外一个设备名,此处应保持这两个名字一致:2 \! A+ y* M$ w: F% O4 } ![]() 图14. 设备名 & U. ~7 U$ m w% W* e6 c3 M![]() 图15. 设置设备名 7 o0 y( U0 e5 m3 J& h- B 至此,关于蓝牙的应用配置即告完毕。接下来可进行数据通讯相关的配置、实现。4 b, R4 [. Z! T! S 4.3.和手机进行通讯3 S. V2 c# u4 g& c( Z 上一小节配置完从机功能后,编译会产生大量错误,是因为 BLE 的通讯过程会比较多地引用旧的句柄。循着解决这些编译错误的操作,我们能了解到 BLE 通讯的过程。具体操作如下: 全局搜索旧的发送句柄(TXCharHanlde),我们找到了用于数据发送的协议栈 API,修正之 :9 o$ z9 e6 \/ P- x0 r" T+ R ![]() 图16. 发送 Notify " {. U. P2 x+ v @% i" |" K( ^" x( @6 L5 g$ g! s: i8 W y* A 手机使能订阅后,会通过图 17 的回调函数通知上层。此时应该修改为新的特征句柄,同时,添加一些打印指示 notify 的使能、禁用状态: ![]() 图17. 使能订阅回调 8 R% G% Y& r5 }+ s, V) s# g( r- y- {# X 添加新的特征句柄全局变量声明,注释掉旧的: ![]() 图18. 句柄声明 ; X8 H% s+ M, p4 ~ u关于旧的发送句柄(TXCharHanlde)的问题已经全部解决。继续搜索旧的接收句柄(RXCharHanlde),我们应能找到设备接收手机数据的函数接口,将其中的句柄替换为新的特征句柄,见图 19:1 F2 h& d) @6 m- T' A ![]() 图19. 数据接收回调函数 至此,我们应该能通过全部编译过程,并且已经找到了数据发送、接收的位置。此时可以在这些位置添加数据发送、接收的打印函数。接收数据的用户接口见图 20:1 G6 V/ Q' t( E2 g. e: L' e; c ![]() 图20. 用户接收 BLE 数据 % W* Z1 W# F/ X - L/ C- c/ g+ j* ?7 k5 F1 T关于发送数据,原始工程实现了以下处理流程:1 S. i0 k5 i. N6 w 1. 从串口接收数据 2. 解析数据为命令并缓存这些命令 3. 通过轮询的方式,不断将命令缓冲区里的命令发送出去 该处理流程不适用于我们的任务要求,我们需要先取消这部分功能,见图 21 ![]() 图21. 取消原有的数据处理流程 2 N" Q/ b4 M' y4 B. w# X/ J3 @1 L+ } 然后设计自己的发送函数,见图 22:9 J' M. S. z+ f 2 q1 n0 _5 b" I5 M ![]() 图22. 自定义 BLE 数据发送函数 1 j& e. P; e. a2 O* G s. J w 至此,蓝牙的通讯功能已经全部实现完毕。用户可通过: • Data_Received()接口接收数据 • user_send_data_over_ble()接口发送数据 4.4.添加其它功能. U; P( m0 H \- g- c) L+ f 根据任务要求,我们还需实现下面三个功能:6 x* |' A7 W/ D 1. 控制 LED 亮灭* [, G1 q2 {5 t 2. 定时 1s 上传心跳包,内容为连接后的秒计数值 3. 每次按键上传按键事件通知下面开始逐个实现:* k" k0 u' }$ m2 L1 B. F, \% T $ A% f# \& i1 D 4.4.1. 控制 LED 亮灭- V6 g* }6 ~) n; [+ i% P 首先,实现 LED 亮灭处理函数,3 T) o$ l+ o. g ![]() 图23. LED 命令处理函数 将其添加到 BLE 数据接收函数处,见图 24。LED 控制功能实现完毕。4 g8 R: _* V! b4 l) s" ? 1 d6 W( U: W5 f3 l# `$ | ![]() 图24. 接收数据后进行 LED 命令处理 : ]% @' d, q1 }4.4.2. 每秒上传心跳包" ]1 s+ K! g2 h: D& i) t, } 首先,实现心跳上传处理函数,实现当设备连接后,每秒上传一个 4 字节的计数值,并使用0xaa 作为命令字。见图 25:1 \# ~6 D! U! a; u ![]() 图25. 心跳上传处理 , e' I0 l! p* k7 @6 }5 W% r将心跳处理函数添加到系统任务处理函数 App_Tick 中,App_Tick 会在 main loop 中不断地被调用。心跳包需要在蓝牙连接成功并且使能了订阅之后才可以发送。另外,使用 timeout_flag变量来控制其每秒只被调用一次,实现方法如下: 8 W0 ~7 _0 p; Z' e( l ![]() 图26. 心跳处理 4 G0 h! s( {6 G5 {. z- E ; j) L! M4 }- d2 b- J$ L上述 timeout_flag 变量需要使用每秒循环的软件定时器来周期性置位。软件定时器的应用方式很简单。+ P6 P+ H! @/ f* m6 Z1 S 首先,实例化一个定时器,并定义超时回调函数: ![]() 图27. 软件定时器实例化 7 \) s7 k2 x% j9 P然后,在 Serial_port_DeviceInit 函数的末端位置注册超时回调函数并启动定时器。 % s6 Q, S6 ?5 ^ ![]() 图28. 启动定时器 - R% p/ z7 D& L4 P$ y4.4.3. 上传按键事件 首先,实现按键回调函数,该函数在按键按下时被调用。发送按键事件前,应检查此时蓝牙是否处理连接、已订阅状态。见图 29: ![]() 图29. 按键回调 按键中断服务函数中调用:- f0 G& ^; T, u$ F1 V/ ^0 c ![]() 图30. 按键中断服务函数 ! u8 ^4 W1 a4 w. `; l至此,开发任务的要求已经全部实现完毕。接下来进行功能验证。 4.5验证功能 用户工程运行起来后,用 STBLE Toolbox 扫描,可见广播名已经修改过来了。; b0 q. I; G o/ g7 ]' T ![]() 图31. 用户工程广播名 连上设备并点击 Notify 开关以使能订阅,可观察到底部已经开始接收到设备的心跳包数据(以 AA 开头的 5 字节数据),该数据每秒钟变化一次,见图 32:2 S) @2 |" \ A7 E( ]8 n# i ![]() 图32. 使能订阅 通过 LOG 也能观察到心跳包发送情况,此时如果按动按键,也能观察到按键事件已经发送:5 j0 M2 k; q2 a; T7 i5 Z ![]() 图33. 用户工程 LOG 05小结 跑完了上述用户任务开发的流程后,相信用户对 BlueNRG SDK 的软件架构应有所理解了。BlueNRG SDK 的软件层次架构为 STM32 典型的三层架构,分别为驱动层、中间层、用户层: # o9 x# z* y2 r, g9 j- b1 b+ _ ![]() 图34. 软件层次架构 + S9 l5 F4 c) }# c6 ?9 C+ a上述添加用户功能的整个过程,其实只改动到了用户层的功能,用户层包含以下几个文件:4 P+ I) E% D) A# h! e: t 9 G; G: t8 e2 W1 a ![]() 图35. 用户层文件 / f5 }- _1 T- I! Q# u- y 这些文件的含义是:; U9 v6 Z8 g5 }* b6 D • serial_port.c,用户应用逻辑的实现 • BLE_SerialPort_main.c,程序入口,程序主流程) s2 m' C, s: ^. V; }) X! l, O • gatt_db.c,BLE GATT 层功能的实现' }0 D$ V& Y4 v$ F • rf_device_it.c,存放所有的中断服务函数 上述用户固件的功能,大多都在 serial_port.c 中实现。BLE_SerialPort_main.c 函数则实现了系统的主要流程。简单来说,BlueNRG SDK 的裸机系统即是一个前后台系统。蓝牙事件、按键中断等属于前台处理,负责置位相关标志位和状态,main 函数的 while1 属于后台处理,运行蓝牙协议栈、用户任务处理等后台任务,见图 36: : }( d9 ^! o$ j% h ![]() ) B1 ?0 @) y8 M, o 2 v- E8 i4 ^' o1 o 图36. 系统流程 BlueNRG SDK 中的绝大多数例程都使用了本文档所述的软件架构,即前后台系统。该软件架构比较简单,优点是用户能非常快速地掌握其流程,能够依据本文档的示例快速构建自己的用户功能。缺点是功能比较简单,用户需要在此基础上再添加一个调度器以应对复杂功能的要求。 q7 z, J) ]/ t% s& z$ l % Y( m; Y& k8 x% }' g( }# @2 O * Y# @/ T% s; W% | 转载自: STM32单片机! @& ?! o) a4 Y5 M1 L, F/ |$ Q. s 如有侵权请联系删除 |
【2025·STM32峰会】GUI解决方案实训分享5-调通板载的NRF24L01 SPI接口并使用模块进行无线通信(发送和接收)
【2025·STM32峰会】GUI解决方案实训分享2-编译运行TouchGFX咖啡机例程(含桌面仿真)
实战经验 | Keil工程使用NEAI库的异常问题
STM32 ISP IQTune:真正零门槛的免费ISP调整软件
【经验分享】STM32 新建基于STM32F40x 固件库的MDK5 工程
意法半导体MCU双供应链策略,打消中国客户后顾之忧
【经验分享】基于STM32使用HAL库实现USB组合设备CDC+MSC
2024意法半导体工业峰会:赋能智能电源和智能工业,构筑可持续未来
ST推出灵活、面向未来的智能电表通信解决方案,助力能源转型
意法半导体 x Qu-Bit Electronix:推动新一轮的数字声音合成革命