pc-ble-driver 搭建环境

本文基于pc-ble-driver v4.1.1,介绍如何在Windows上搭建开发环境。 以前写过一个入门教程(链接),最近的版本更新改动很大,那篇文章中介绍的方法已经过时。我仍然建议预先读它,然后再看本文。 pc-ble-driver是什么 它是一个PC端的BLE开发平台。 我们在开发板中烧录一个中间层固件,通过PC给固件发指令,与外部BLE设备(比如手机或一个BLE从设备)进行通信。 PC端可以用C/C++、Python、Javascript 语言进行编程。本文介绍的方案是基于C语言。 pc-ble-driver 部署在github中,可以从该页面获得它的全部信息。 生成静态库文件 首先要安装Visual Studio 2019,它有多个开发语言,我们这里只需要C++。 安装好以后,我们能够打开VS的项目文件(.sln)和工程文件(.vcxproj),同时也得到了一个编译工具MSBuild.exe,它的路径应该在:C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe 然后安装cmake工具。 下载pc-ble-driver的源文件:https://github.com/NordicSemiconductor/pc-ble-driver/releases 页面中有多个选项: nrf-ble-driver-4.1.1-win_x86_32.zip nrf-ble-driver-4.1.1-win_x86_64.zip Source code (zip) Source code (tar.gz) 需要下载Source Code (zip)那一项。 将源文件解压到一个开发目录下,我这里是:C:\repo\pc-ble-driver-repos\pbd 开始之前需要明确我们要干什么! pc-ble-driver 在PC端实现了一个库,这个库可以调用softdevice,实现BLE的功能。我们写的代码直接调用这个库文件暴露的接口。 在上面的下载页面,下载nrf-ble-driver-4.1.1-win_x86_64.zip文件,里面就包含了这个库,理想情况是我们能够拿它直接使用,但奇怪的是我这里使用它的库会报错。无奈只能自己生成一个库来用。 所以我们的目标是,编译源文件,生成一个静态库文件(.lib)。 下载必要的第三方VC库: asio catch2 (只有一个catch.hpp文件) spdlog 将它们放在目录:C:\repo\pc-ble-driver-repos\pbd\ext 注意,官方页面上使用vcpkg来下载它们,还要设置一堆环境变量,把问题复杂化,这里我们直接下需要的库,不管vcpkg。 在pbd下面创建一个build文件夹: C:\repo\pc-ble-driver-repos\pbd\build。打开cmd窗口,cd到该文件夹,然后执行: 值得看一下各个参数: -DXXX表示增加一个变量,也可以通过设置环境变量的方式进行(有坑,不推荐) CONNECTIVITY_VERSION和NRF_BLE_DRIVER_VERISON 按实际版本进行配置,因为可能过几天又有新版本了 -G “Visual Studio … Read more

BLE 发现过程介绍

BLE 主从设备建立连接以后,先执行发现服务和特征的过程(Discovery Procedure),然后对着指定特征执行数据收发。 本文以一个Nordic LED Button服务为例(如下),介绍服务和特征发现过程的协议细节。 1. 发现服务 对于主机而言,首先找到目标服务的句柄范围,然后再在句柄范围内找下面的特征。 发现服务有两个方案: 发现全部服务 发现指定UUID的服务 1.1 发现全部服务 发现一个服务的基本思路是搜索Primary Service的UUID(0x2800),如果找到,即可获得它的句柄范围。 第一次我们从0x0001开始搜索,找到一个服务后,从该服务的句柄末尾开始继续搜索,可以找到第二个服务的句柄范围。 反复执行发现服务操作,就可以依次找到全部服务的句柄范围。 执行发现服务时,GATT协议层主机会产生一个请求(Read By Group Type Request),从机收到后返回一个响应(Read By Group Type Response)。 (为了说明一些问题,这个图片不对应上面的Nordic LED Button服务) 图中,请求包中包含了起始和末尾的句柄,第一次发现默认是0x0001~0xFFFF。 响应帧中包含了两个服务的UUID,代表它发现的两个服务。(为啥一次性发现两个?原因未知,它仅限于Generic Access和Generic Attribute)。 第二次的响应帧就比较正常,返回了发现的一个服务UUID。如果检查该数据帧的细节,能够看到该服务的起始和结束UUID。 SDK 15.3中执行发现全部服务的函数是: sd_ble_gattc_primary_services_discover(uuid),注意要将uuid参数设置为NULL。 在该函数的回调事件中即可获得发现的服务的UUID和句柄范围。 1.2 发现指定UUID服务 发现指定UUID的服务,顾名思义,除了查找0x2800,还查找指定的UUID。 通过它可以快速找到目标服务,对于不关心的服务项可以快速略过,加快整体发现过程的速度。 当执行发现指定UUID服务时,主机发出一个请求(Find By Type Value Request),从机返回一个响应(Find By Type Value Response)。 观察请求包,它包含了一个UUID的输入参数。响应包中,返回数据中的11就是该服务的句柄。 SDK 15.3中执行发现全部服务的函数是: … Read more

GATT详细介绍

我们经常看到下图,读图时候我们会讲一个GATT Profile包含多个服务,一个服务包含多个特征,一个特征包含多个描述符,但是真正落到细节层面上,又有些模棱两可,比如为什么特征里面有个Declaration,又有个Value。当我们发数据时候是发给Declaration还是发给Value? 本文介绍GATT的技术细节。 1. Attribute BLE 中的GATT 指Generic Attribute, ATT 指Attribute。 BLE 协议栈有ATT层和GATT层,ATT 层定义了一套基础数据结构,GATT 层基于ATT 层定义了一套通信交互规范。蓝牙协议规范的Vol 3的Part F和Part G有详尽介绍。 Attribute不能翻译成属性或特征,会与Property和Characteristic冲突,有的文档将其翻译成特性,读起来总觉词不达意,这里尽可能保持使用Attribute或其简写形式(ATT)。 ATT 是BLE 协议对现实世界实体的数据描述形式,不同的BLE 设备,其实就是它们的ATT 定义不同。 一个ATT 包含以下元素: 类型(UUID) 句柄(Handle) 权限(Permission) 值(Value) UUID是一个16字节的唯一码,如果两个ATT 拥有相同的UUID,就认为它们是同一类ATT,因此使用UUID 来表征ATT 的类型。 蓝牙协议规范设定了一个UUID 模板(0000xxxx-0000-1000-8000-00805f9b34fb),第三四字节为占位符,这样只要拿一个双字节数填充占位符,即可产生一个16字节UUID。于是我们看到了许多双字节UUID。 常见的ATT 有:服务(Primary Service)、特征(Characteristic)和描述符。 他们拥有各自固定的UUID,比如服务的UUID为0x2800,特征的UUID为0x2803。 等下!特征的UUID不是用户自己定义的吗,为什么是固定的0x2803? 2. Declaration of Attribute 特征其实是个集合,特征包含以下子元素: 特征声明(Characteristic Declaration) 特征值声明(Characteristic Value Declaration) 特征描述符声明(Characteristic Descriptor Declaration) … Read more

FDS的掉电分析

如果芯片在上一次操作Flash时遭遇了意外掉电 ,会发生什么呢? 1. 写入过程掉电 一个Record的写入按照如下顺序执行: TL Part ID Part Content IC Part 如果在前三步意外掉电,IC Part由于没有开始写入,所以File ID仍然是原始值0xFFFF。 File ID = 0xFFFF的数据也会被视为脏数据,在读取、更新、删除操作的时候,脏数据会被忽略。GC 过程会删除脏数据,收回空间。 如果在第四步掉电,IC Part有两种可能: 写入失败:File ID仍然是0xFFFF,情况同上 写入错误:CRC 校验将无法通过,应用代码可以得知 所以一旦我们开启了CRC校验,即可解决第四步出现的意外掉电情况。 (经过我有限的测试,在第四步掉电时,只遇到“写入失败”,完全没有遇到“写入错误”,一个猜测是芯片电源管脚上的电容余电保护Flash将单个Word写入完毕。) 2. 删除、更新过程掉电 FDS 的删除操作实际是将Record Key写成0x0000,使该数据成为脏数据。所以删除操作本质上仍然是写操作。 如果写Record Key失败,它并未变成0x0000,那么下次运行时FDS 会把它识别成有效数据。应用层程序应该有意识的去处理这种“删除失败”的情况。 更新操作实际是先删除原数据,再写入新数据。它的掉电情况与删除和写入一致。 3. 读取过程掉电 无需分析,没有任何影响。 4. GC过程掉电 GC 过程共有四个步骤,在第一步时掉电,GC 尚未开始,没有任何影响。 在其他三个步骤时掉电,都会影响到FDS 数据页和交换页的存在情况。这里使用一个标志位flag来标记各个页的类型,枚举出全部可能的情况: 如果是空白页,则flag = PAGE_ERASED 如果不是空白页 ,页头有效,是数据页,则flag = PAGE_DATA 如果不是空白页,页头有效,是交换页,页内空白,则flag … Read more