(1)简介

pc-ble-driver是Nordic提供的一套开源的上位机BLE 开发软件库,配合nrf52832开发板,可以在上位机实现BLE中央设备和外设的功能。

上位机与硬件板之间通过UART通信,通信协议类似于蓝牙的HCI。在硬件设备中,运行基于softdevice的connectivity程序,与上位机的UART进行交互。

架构如下图所示:

pc-ble-driver提供C/C++、Python和Node.js三种源码库,托管在github网站上:

本文以C/C++语言为例,介绍pc-ble-driver的使用。

(2)准备工作

必要的工具包括:

工具 版本
nRF52开发板 PCA10040
SDK 14.x
pc-ble-driver 3
boost >1.54 (no 1.66.0)
cmake 3.11.0
Visual Studio 2017 or 2015

pc-ble-driver提供两个版本:v2和v3。v2版本支持nRF52和nRF51,v3版本仅支持nRF52。本文使用v3版本,相应的使用nRF52的开发板PCA10040。

Boost是一个C++通用软件库,为pc-ble-driver库提供了基础函数。pc-ble-driver要求Boost库版本要高于1.54。在测试发现1.66.0版本有一点问题,本文使用1.64.0版本。

cmake是一个生成VC++工程文件的工具,它可以与Boost配合,生成Visual C++工程文件(*.vcxjproj)。cmake\Modules\FindBoost.cmake文件中定义了cmake可以识别的Boost库版本范围,比如cmake 3.11可以识别Boost 1.70,而早期的cmake 3.9则最高仅能识别到Boost 1.65。所以推荐使用最新版的cmake。

Visual Studio 2017可以自定义安装组件以减小安装体积,以下为最小配置:

也可以使用命令行工具msbuild 2015msbuild 2017代替Visual Studio IDE,这样仍然能够编译cmake所生成的Visual C++工程,只是没有IDE功能。

上述软件安装后,需要添加环境变量:

  • C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin
  • C:\Program Files\CMake\bin

为了验证环境变量有效性,可以在cmd窗口中运行以下命令:

> where msbuild
> where cmake

正确情况下,能够打印出相关路径信息。

(3)编译Boost

将boost v1.64.0源码放到C:\root\boost\1.64.0。

在环境变量中加入 BOOST_ROOT=C:\root\boost\1.64.0,在cmd中执行echo %BOOST_ROOT%,应该能够输出路径。

前面已经安装好了Visual Studio 2017,在开始菜单可以看到几个快捷方式,如下:

快捷方式 说明
x64 Native Tools Command Prompt for VS 2017 本机和目标机器都是x64
x64_x86 Cross Tools Command Prompt for VS 2017 本机是x64,目标机器是x86
x86 Native Tools Command Prompt for VS 2017 本机和目标机器都是x86
x86_x64 Cross Tools Command Prompt for VS 2017 本机是x86,目标机器是x64

我们希望生成x86的应用程序,于是选择x86 Native Tools Command Prompt for VS 2017,打开它:

注意,这里不要使用系统的cmd窗口,会提示找不到cl.exe或者其他错误,使用这几个快捷方式,则会在启动时自动配置。

如果希望生成x64应用程序,则应该选择x86_x64 Cross Tools Command Prompt for VS 2017或x64 Native Tools Command Prompt for VS 2017。

然后依次执行以下命令:

> cd %BOOST_ROOT%
> ./bootstrap.bat
> ./b2 toolset=msvc-14.1 address-model=32 link=static --with-thread --with-system --with-regex --with-date_time --with-chrono

如果是Visual Studio 2017,对应着toolset=msvc-14.1,如果是Visual Studio 2015,对应着toolset=msvc-14.0。address-model=32/64分别对应着x86和x64,这跟上面选择的快捷方式要对应一致。link=static表示生成静态链接库。–with-xxx表示将生成thread、system、regex、date_time和chrono这几个库文件。

编译完毕后的提示如下:

同时会在.\stage\lib目录下生成十个库文件:

  • libboost_chrono-vc141-mt-1_64.lib
  • libboost_chrono-vc141-mt-gd-1_64.lib
  • libboost_date_time-vc141-mt-1_64.lib
  • libboost_date_time-vc141-mt-gd-1_64.lib
  • libboost_regex-vc141-mt-1_64.lib
  • libboost_regex-vc141-mt-gd-1_64.lib
  • libboost_system-vc141-mt-1_64.lib
  • libboost_system-vc141-mt-gd-1_64.lib
  • libboost_thread-vc141-mt-1_64.lib
  • libboost_thread-vc141-mt-gd-1_64.lib

编译pc-ble-driver的api库时会使用到这些文件。

(4)编译pc-ble-driver库

将pc-ble-driver源码放到C:\nordic\pc-ble-driver\3.0.1。

打开一个cmd,并依次执行以下命令:

> cd <pc-ble-driver dir>
> mkdir build & cd build
> cmake -G "Visual Studio 15 2017" ..
> msbuild ALL_BUILD.vcxproj

编译完毕后给出提示信息:

同时在.\build\Debug目录下生成一系列库文件:

  • pc_ble_driver_shared_sd_api_v2.dll
  • pc_ble_driver_shared_sd_api_v2.lib
  • pc_ble_driver_shared_sd_api_v5.dll
  • pc_ble_driver_shared_sd_api_v5.lib
  • pc_ble_driver_static_sd_api_v2.lib
  • pc_ble_driver_static_sd_api_v5.lib

在后续工程中,会用到这些库文件。实际上只会用到pc_ble_driver_static_sd_api_v5.lib这一个。

注意到,在.\build\Debug目录下有个test_uart.exe文件,在cmd窗口中运行它,看到如下输出表明正常:

在某些情况下,运行test_uart.exe可能没有任何输出,通常是因为系统缺少ucrtbased.dll,可以从系统路径(C:\Program Files (x86)\Windows Kits\10\bin\x64\ucrt\ucrtbased.dll)中复制一份放在exe所在目录。

默认情况下,cmake是采用x86架构,如果要使用x64需要显式声明。即如果上个步骤中生成的是x64的Boost库,这里的cmake命令行变成:

cmake -G "Visual Studio 15 2017 Win64" .. -T host=x64

如果架构不一致,将无法正确读取到Boost库。

 

至此,开发环境准备完毕。

(5)测试示例程序

打开cmd窗口,并依次执行下列命令:

> cd <pc-ble-driver dir>\examples\heart_rate_monitor
> mkdir build & cd build
> cmake -G "Visual Studio 15 2017" ..

成功后看到如下提示:

此时在build目录下将生成Project.sln等文件,可以使用Visual Studio 2017打开和编译它,如下:

也可以使用msbuild进行编译:

> msbuild ALL_BUILD.vcxproj

编译完毕,将在build\Debug目录下生成hrm_v5.exe。下面我们运行测试这个hrm_v5.exe。

准备PCA10040,擦除芯片后烧录<pc-ble-driver dir>\hex\sd_api_v5\connectivity_2.0.1_1m_with_s132_5.0.hex,其中文件名中的1m表示波特率为1000000。

打开cmd窗口,并依次执行以下命令,其中COMx表示该开发板在系统中对应的串口号。

> cd <hrm_v5.exe dir>
> hrm_v5 COMx

运行成功则可以看到设备开始广播的日志信息:

(6)创建VS工程

有些时候我们希望脱离示例工程,创建一个全新的Visual Studio工程,进行pc-ble-driver开发。

打开Visual Studio 2017,在<pc-ble-driver dir>\examples下创建一个空VC++工程,命名为my_demo。

工程中新建一个源码文件main.c,并将\examples\heart_rate_collector\main.c中的内容复制到这里。

运行一下,会报许多错误。

在Visual Studio的Solution Explorer中,将下列文件夹中的*.h头文件全部拖入Header Files目录:

  • <pc-ble-driver dir>\include\common
  • <pc-ble-driver dir>\include\common\sdk_compat
  • <pc-ble-driver dir>\src\sd_api_v5\sdk\components\softdevice\s132\headers

打开工程配置菜单:Project –> my_demo Properities…

在C/C++ –> All Options分支中,做如下配置:

Option Value
Additional Include Directories $(SolutionDir)..\..\include\common,

$(SolutionDir)..\..\include\common\sdk_compat,

$(SolutionDir)..\..\include\common\internal\transport,

$(SolutionDir)..\..\src\sd_api_v5\sdk\components\softdevice\s132\headers

Compile As Compile As C Code (/TC)
Preprocessor Definitions WIN32, _WINDOWS, NRF_SD_BLE_API=5, PC_BLE_DRIVER_STATIC, _CRT_SECURE_NO_WARNINGS

在Linker –> All Options分支中,做如下配置:

Option Value
Additional Dependencies pc_ble_driver_static_sd_api_v5.lib,

libboost_thread-vc140-mt-gd-1_64.lib,

libboost_system-vc140-mt-gd-1_64.lib,

libboost_regex-vc140-mt-gd-1_64.lib,

libboost_date_time-vc140-mt-gd-1_64.lib,

libboost_chrono-vc140-mt-gd-1_64.lib

Additional Library Directories $(SolutionDir)..\..\build,

$(SolutionDir)..\..\build\$(Configuration),

$(BOOST_ROOT)\stage\lib

保存并关闭配置窗口,重新Build一下工程,应该已经没有错误了。

此时就可以按照常规的VC++工程来编写和调试代码。

 

(完)

BLE协议栈规定物理层、链路层和DTM层属于控制器,其他协议层属于主机,主机与控制器之间的通信是通过主机控制器接口传输层完成的。

主机控制器接口常简称为HCI(Host Controller Interface)。

HCI定义了一套“命令-事件”机制,主机向控制器发送HCI命令,控制器向主机返回命令执行结果。应用层的所有操作都会转换成HCI命令传给控制器。

1. HCI通信

HCI接口物理形式可以是串口、SPI、USB和三线串口。

对于串口HCI,其通信模型如下:

HCI_UART_Interface

左侧蓝牙主机向右侧蓝牙控制器发送命令,控制器返回命令执行状态。当收到对端设备发送的消息,控制器会以事件形式发送给主机。

通过HCI的数据包括:HCI命令、HCI事件和连接数据。HCI层本身不能区分这三种类型,因此在发送HCI数据包前需要先发送该数据包的类型指示信息。串口HCI的数据包类型指示信息如下:

HCI包类型 指示信息
HCI命令 0x01
连接数据 0x02
HCI事件 0x04

指示信息中缺少0x03,该信息用于经典蓝牙概念。

包类型指示位在HCI包发送前发给给主机或控制器。

2. 连接数据

两个设备建立连接后相互收发数据,从主机将数据发送给控制器,再通过无线发送到对端设备,或控制器接收到对端设备数据后通过HCI发送给主机。连接数据的结构如下所示:

HCI_Data_Packet_Format

  • Handle:连接句柄。
  • FB Flag:数据边界标志(Packet Boundary Flag),表示当前数据包是一个完整数据包的开头片段或中间片段。
  • BC Flag:播报标志(Broadcast Flag),不用于BLE。
  • Data Total Length:数据总长度。
  • Data:有效数据。

3. HCI命令

HCI命令包包括:操作码OpCode、参数总长度和参数个数,如下所示:

HCI_Command_Packet_Format

为了避免控制器的缓冲区溢出,发送命令包时需要应用流程控制。主机向控制器发送一个命令,控制器返回命令执行状态事件,事件中包含参数Num HCI Command Packets,该参数指主机可以发送的最大命令包的数量。

控制器按接收顺序执行主机命令,但后面的命令可能提前执行完毕。

如果命令执行出错,将在控制器的状态事件中包含错误码。

HCI命令非常多,将近300个,BLE仅支持部分命令,所有BLE专属的命令OGF字段都等于0x08。

下面列出BLE支持的最基础的一部分HCI命令:

命令 描述
LE Add Device To White List Command 添加白名单
LE Clear White List Command 清空白名单
LE Read Buffer Size Command 读控制器缓存
LE Read Local Supported Features Command 读本设备支持的功能
LE Read Supported States Command 读本设备支持的状态
LE Read White List Size Command 读白名单空间
LE Remove Device From White List Command 从白名单移除设备
LE Set Event Mask Command 设置事件掩码
LE Test End Command 结束测试
Read BD_ADDR Command 读取设备地址
Reset Command 重启
LE Read Advertising Channel TX Power Command 读取广播发射功率
LE Transmitter Test Command 发送数据测试
LE Set Advertising Data Command 设置广播数据
LE Set Advertising Enable Command 开启广播
LE Set Advertising Parameters Command 设置广播参数
LE Set Random Address Command 设置随机地址
LE Receiver Test Command 接收数据测试
LE Set Scan Enable Command 开启扫描
LE Set Scan Parameters Command 设置扫描参数
Disconnect Command 断开连接

4. HCI事件

HCI事件包包括:时间代码, 参数总长度和具体参数,如下所示:

HCI_Event_Packet_Format

HCI事件包不强制要求流程控制,因为通常主机总是具有充足资源来处理控制器返回的事件。

当连接断开时,主机默认所有命令都已经执行完毕,将不再接收任何事件。

控制器收到不同的主机命令,可能返回以下类型事件:

  • 执行完毕事件
  • 状态信息事件

对于不涉及连接的命令,可以立即得到执行结果,执行完毕事件报告该命令执行成功或失败。

对于涉及连接的命令,无法立即得到执行结果,命令执行完毕后,先返回执行完毕事件,等命令最终结果产生,再返回新的执行完毕事件。比如LE Create Connection Command命令,执行命令时先返回执行完毕,表面链路层开始执行或加入执行队列,待两端设备建立连接,将返回连接完成事件。

部分读命令,比如LE Read Advertising Channel Tx Power Command,执行完毕后将读取结果存放在状态信息事件中返回。

HCI事件包括BLE专有事件和通用事件,通用事件适用于经典蓝牙和BLE。BLE专有事件称为“元事件(LE Meta Event)”,共有20个,它们的事件代码均为0x3E,事件参数的第一个字节为Subevent_code,用以区分不同的元事件。如下:

事件 Subevent_Code 描述
LE Connection Complete Event 0x01 建立连接完毕
LE Advertising Report Event 0x02 检测到广播数据或收到扫描响应数据
LE Connection Update Complete Event 0x03 连接参数更新完毕
LE Read Remote Features Complete Event 0x04 读取对端设备功能完毕
LE Long Term Key Request Event 0x05 控制器向主机发送LTK以加密链接
LE Remote Connection Parameter Request Event 0x06 对端设备发起更新连接参数请求
LE Data Length Change Event 0x07 控制器通知主机链路层数据长度发生了更新
LE Read Local P-256 Public Key Complete Event 0x08 控制器通知主机P-256密钥生成完毕
LE Generate DHKey Complete Event 0x09 控制器通知主机椭圆加密算法密钥生成完毕
LE Enhanced Connection Complete Event 0x0A 建立连接完毕(还支持扩展连接)
LE Directed Advertising Report Event 0x0B 检测到定向广播数据或扫描响应数据
LE PHY Update Complete Event 0x0C 物理层更新完毕
LE Extended Advertising Report Event 0x0D 检测到扩展广播数据或扫描响应数据
LE Periodic Advertising Sync Established Event 0x0E 建立周期广播同步完毕
LE Periodic Advertising Report Event 0x0F 检测到周期广播数据或扫描响应数据
LE Periodic Advertising Sync Lost Event 0x10 周期广播数据无法同步
LE Scan Timeout Event 0x11 扫描超时
LE Advertising Set Terminated Event 0x12 终止广播数据集事件
LE Scan Request Received Event 0x13 收到扫描请求
LE Channel Selection Algorithm Event 0x14 使用了信道选择算法

(完)