CH58x/CH59x 系列芯片从机示例解析

复制代码
沁恒微 RISC-V 蓝牙芯片从机例程代码框架解析     ...... 矜辰所致

前言

学习 CH58x 有一段时间了,更多的都是忙着基础知识的学习,对于芯片的 Ble 从机角色的代码,我们还没有详细的学习过,想要更好的使用芯片做应用,代码框架我们理应有个清晰的认识,比如哪里开启广播,如何收发数据等等。

本文内容就是来学习一下沁恒微 RISC-V 蓝牙芯片外设从机角色的例程代码框架。

本文目的在于让大家对从机代码流程结构有个整体的认知,从机各部分细节功能会在后面逐个讲解 。

相关博文:
BLE 蓝牙连接参数详解
BLE 蓝牙空中报文格式与解析(广播包)
一文搞清 BLE 蓝牙 UUID
CH585 蓝牙 示例工程 Central 全解析
沁恒微 蓝牙 芯片 TMOS 使用

系列目录:
【导航】沁恒微 RISC-V 蓝牙 入门教程目录 【快速跳转】

.

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • [一、 上电初始化流程](#一、 上电初始化流程)
    • [1.1 CONFIG.h 说明](#1.1 CONFIG.h 说明)
  • [二、 从机初始化](#二、 从机初始化)
    • [2.1 设置广播包](#2.1 设置广播包)
    • [2.2 设置广播间隔](#2.2 设置广播间隔)
    • [2.3 设置配对绑定策略](#2.3 设置配对绑定策略)
    • [2.4 注册 GATT 服务](#2.4 注册 GATT 服务)
    • [2.5 必要回调和创建启动事件](#2.5 必要回调和创建启动事件)
  • 三、开启设备
    • [3.1 回调函数说明](#3.1 回调函数说明)
    • [3.2 从机工作流程](#3.2 从机工作流程)
    • [3.3 关于连接过程](#3.3 关于连接过程)
    • [3.4 从机应用层 TMOS 任务列表](#3.4 从机应用层 TMOS 任务列表)
  • 四、消息传递
  • 五、数据收发
    • [5.1 接收主机数据](#5.1 接收主机数据)
    • [5.2 给主机发送数据](#5.2 给主机发送数据)
  • 结语

写在前面:为什么前面要写那么多基础知识才能写示例解析,因为学习 Ble 势必有很多基础知识需要搞清楚,不可能一蹴而就,可能很多小伙伴上来就想一步到位,直接看从机工程代码,然后就觉得程序复杂、看不懂之类,开始抱怨,正所谓磨刀不误砍柴工,该补充的基础还是得补充,要不然后期使用起来总会有十万个为什么 = =!

小提示:对于本文的内容,有些功能可以搜索关键字快速查找,比如:广播包,开始广播,参数更新,数据收发之类。

一、 上电初始化流程

本文基于 CH585 官方 EVT 的从机例程进行说明(只要具备从机角色功能,从机部分应用层代码的框架是一致的。):

设备上电基本框架流程和主机是类似的,我们贴出来和主机的初始化进行对比一下:

开头的初始化 CH58x_BLEInit(); HAL_Init(); 主机和从机是一样的,在CH58x_BLEInit 里面确定 MAC 地址 和 发射功率,他们都与宏定义有关,这些配置宏定义都存在于CONFIG.h 文件中。

1.1 CONFIG.h 说明

CONFIG.h 文件是芯片使用过程中比较重要的一个文件,只要修改宏定义,就可以实现不同功能的配置!

这里为什么要单独讲这个,是因为这个配置确实很重要也很有用,实际上使用过程中的有一些功能需求,通过修改宏定义就可以实现!所有的宏定义含义,已经很详细的,都不需要我额外说明,如下图:

蓝牙 MAC 地址,发射功率,开启睡眠,RTC选择等等。需要说明一下,如果想要修改宏定义,建议统一在 MounRiver Studio 中配置而不是手动修改 CONFIG.h 文件,比如:

EVT 中的CONFIG.h 文件是公用的,直接修改 CONFIG.h 文件会导致其他工程也用同样的配置,但是如果你的工程是独立的,那倒是无所谓,这个可参考博主的 沁恒微 RISC-V 芯片开发工具 MounRiver Studio 使用 了解如何独立工程 。

完成了上电的基本配置,最后就是我们应用的从机初始化 Peripheral_Init(); ,我们跳转就到了 peripheral.c 文件中,这里涵盖了我们应用层实现的所有功能。

二、 从机初始化

我们从从机初始化函数 Peripheral_Init() 开始看起,这里呢我就不直接贴出官方代码占用篇幅了,大家都能从官网下载,真的想学习肯定是要下载官方 EVT 对着源码看,而且我在说明中也会有源码截图。

2.1 设置广播包

首先是配置从机广播内容、广播开关、以及连接参数,如下:

关于其他一些参数,跳转宏定义可以查看:

广播包与扫描响应,广播包类型的知识可以参考博客:BLE 蓝牙空中报文格式与解析(广播包)

连接参数可以参考博客: BLE 蓝牙连接参数详解

2.2 设置广播间隔

接下来就是设置广播间隔:

广播间隔是指 两次广播事件之间的间隔是 50 ms,不是指一次发 50ms。

一般情况真正单次发一包广播只需要 几百微秒到约 2 ms(由 PDU 长度、PHY 速率决定),远小于间隔 。

这个查看博主博文《沁恒微 CH585 芯片 Ble 传输速率测试说明》也会有所理解。

2.3 设置配对绑定策略

设置 配对/绑定 参数 :

关于配对的说明后期需要单独写文章说明。

2.4 注册 GATT 服务

再往下就是注册 GATT 服务了,说直白点,就是那些带有 UUID 的服务特征值,蓝牙数据的交互就是读写特征值。

UUID 为 0x1800 和 0x1801 的服务是 Ble 设备必带的服务:

服务和特征值的定义是在工程中的 Profile 文件夹下面:

蓝牙设备数据通信就是基于这里定义的服务中的特征值进行的,这部分有必要单独展开细说,比如如何添加自定义的服务,如何添加特征值,等等,这个后期会有专门的文章说明。

2.5 必要回调和创建启动事件

在初始化最后部分,完成了一些回调函数以及创建设备开启事件,如下:

Peripheral_Init 函数中,完成了从机的初始化工作,在最后新建了一个事件 SBP_START_DEVICE_EVT 用来开始设备(属于 TMOS 的应用,不懂的需要查看 沁恒微 蓝牙 芯片 TMOS 使用)。

三、开启设备

初始化完毕,进入 TMOS 调度,直接就执行 SBP_START_DEVICE_EVT 事件,这个事件是个一次性的事件,功能就是开启设备。

3.1 回调函数说明

使用库函数GAPRole_PeripheralStartDevice 开启设备,配置回调函数:

再看一个整体框架:

通过上面说明,我们知道了,在初始化完成以后,立即就开起了设备,在开启设备的库函数中,我们怕设置好了我们的回调函数。其中,RSSI 读取的回调函数 和 参数更新的回调函数都很简单,我们也很好理解:

当读到一个 RSSI 值就会触发一次 peripheralRssiCB,示例中打印出 RSSI 值,

当有参数更新的时候会触发一次 peripheralParamUpdateCB,示例中打印出更新后的连接间隔,同时会更新一下上面说的应用层自定义的连接状态管理标志位。

至于 peripheralStateNotificationCB 回调函数,是整个应用逻辑流程的关键, 只要从机状态发生变化,协议栈就会触发回调,告知应用层现在设备处于什么状态。应用层根据不同的设备状态进行不同的操作。

在示例中,可以看到有两处打印错误原因的地方,但是实际上,都是生效的GAPROLE_WAITING 中的打印,在这里面重新开启广播,再进入 GAPROLE_ADVERTISING

3.2 从机工作流程

上面图片已经把基本框架都标注出来了,下面再通过文字说明一下从机工作的流程:

  1. 首先上电初始化完成,开启设备,首先进入GAPROLE_STARTED ,打印 Initialized..\n

  2. 因为初始化时候设置了 GAPROLE_ADVERT_ENABLEDTRUE (即便没写默认也是 TRUE ) ,所以设备就自动开启广播,进入GAPROLE_ADVERTISING 状态,打印Advertising..\n

    如果没有设备连接,会按照自己设置的广播周期,一直处于这种状态。

  3. 如果有主机进行了连接,进入 GAPROLE_CONNECTED 状态,同时为了确保确实是 BLE 物理连接已经成功建立,判断一下是否为 GAP_LINK_ESTABLISHED_EVENT 事件,如果是就表示建立连接,执行下面函数操作然后打印Connected..\n

  4. 在连接期间可以进行数据的读写,我们会在下文说明,而且建立连接的时候我们创建的任务都会执行。

    其中参数更新是一次性事件,连接上以后会进行参数更新,然后两个周期性事件:

    一个是周期读取 RSSI 的值,通过 RSSI 的回调函数进行打印,

    一个是周期发送通过自定义特征值发送 Notify,主机开启接收就会一直收到来自从机的 Notify(数据)。

  5. 本示例不会进入GAPROLE_CONNECTED_ADV 在连接状态还广播的状态;

  6. 如果连接上断开,会进入 GAPROLE_WAITING 状态,然后根据不同的事件打印不同效果,对于示例而言,不会自动停止广播,所以不会进入GAP_END_DISCOVERABLE_DONE_EVENT 事件分支,最常见的就是 BLE 连接已经断开 的GAP_LINK_TERMINATED_EVENT 分支,立即执行下面函数,然后打印 Disconnected.. Reason:

3.3 关于连接过程

只要我们应用层配置好合理的参数 (就是初始化阶段完成的那些工作),连接建立是由协议栈库自动完成的。

我们只需要针对协议栈给我们的不同状态和事件进行不同的应用工作。

我们测试看一下 DEBUG 效果:

建立连接以后,会进行连接间隔协商,本示例从机刚连接上会发起一次协商(第一次),后续主机会根据主机的需求,再次协商,这里会打印出协商结果,Int 后面的数字就是连接间隔,单位为 1.25ms :

connection interval (units of 1.25ms, 6=7.5ms)

本示例从机不会主动发起 PHY 请求,所以这里的 PHY 请求是主机下发的。

连接后开启了周期读取 RSSI 值的事件,所以周期打印 RSSI 值。

3.4 从机应用层 TMOS 任务列表

我们这里再来看一下,说明一下 从机 TMOS 任务有哪些,来看一下整体的框架:

上面的 PHY 更新事件,是从机想向主机发起 PHY 请求时候可以使用的,本示例并没有用到这个事件。我们在测试示例的时候会发现有 Phy update Rx:2 Tx:2 .. 此类的打印,这个是主机连接后会发起 PHY 更新请求导致的打印。

上面我们基本上了解了从机代码整体的架构,以及从机的工作流程。接下来就针对基本的数据收发进行必要的说明。

四、消息传递

在将数据收发之前,还有一个部分需要说明一下,就是消息传递,这个消息传递,不是我们应用上层面的数据收发,数据收发在下面会说明,这是协议栈于应用层之间的通信。

我们做示例测试的时候,如果没有连接可以一直看到打印 ( 打印的是扫描过 自己的设备的 MAC 地址,注意是扫描本设备的那些主机的 MAC 地址 ):

我们可以很容易的查找到,这部分实现的代码在 Peripheral_ProcessGAPMsg 函数中,然后通过查找,可以找到对应的调用关系如下图:

这个地方的流程,有必要说明一下,防止小伙伴理不清,首先我们知道 蓝牙有 GAP 层和 GATT 层,在我们蓝牙设备开始工作以后,Ble 协议栈会自动的向我们应用层主动发送事件通知,我们应用层就是根据不同的事件进行应用处理的。( TMOS 会固定有一个消息传递事件,开启设备的时候已经告诉了协议栈我设备用的是哪一个 TMOS 任务)

当然,并不是所有类型的消息都是通过这个流程传递给应用层,还有一些是以回调函数的方式直接响应的。就比如 GAP_SCAN_REQUEST_EVENT GAP_LINK_TERMINATED_EVENT 都是 GAP 层面的消息,但是他们一个是通过 TMOS 消息传递给应用层,一个是状态回调函数通知给应用层。

其实事件是用回调通知还是消息传递,有一个关键点是看事件是否改变设备状态,如果改变设备状态会通过回调函数,不过,数据的通信也是通过回调函数 。

下面把文中的事件列个表格说明一下:

事件宏(官方名) 传递方式 示例处理位置 含义
GAP_LINK_ESTABLISHED_EVENT 回调函数 GAPROLE_CONNECTED 分支 连接建立完成,保存连接参数并启动周期任务
GAP_LINK_TERMINATED_EVENT 同上 GAPROLE_ADVERTISING 分支 连接断开,清除句柄与定时器,重新使能广播
GAP_MAKE_DISCOVERABLE_DONE_EVENT 同上 GAPROLE_ADVERTISING 分支 广播已开启并可被扫描/连接
GAP_END_DISCOVERABLE_DONE_EVENT 同上 GAPROLE_WAITING 分支 广播已停止,进入空闲等待
GAP_SCAN_REQUEST_EVENT TMOS事件 GAP_MSG_EVENT Peripheral_ProcessTMOSMsg 收到扫描请求,可获取对端 MAC
GAP_PHY_UPDATE_EVENT TMOS事件 GAP_MSG_EVENT 同上 PHY 更新完成,得知实际使用的 Rx/Tx PHY
ATT_MTU_UPDATED_EVENT TMOS事件 GATT_MSG_EVENT 同上 MTU 交换完成,保存新 MTU

上面东西也只是帮大家整理一下,官方已经把应用结构都已经设计好了,在应用中,只需要按照框架修改就可以完成自己的功能了。

五、数据收发

最后我们需要讲一下数据收发,我在很早的蓝牙博客中就说过,即便我们搞不清什么协议层面 GATT ,GAP,这的那的,也要记住一个点, 蓝牙数据收发,就是对蓝牙服务中的特征值的读写。

示例中使用的数据收发的服务是我们自定义的,是在 Profile 文件夹下面的:

这部分官方示例已经是很好的模板,博主上面也提到过这部分是有必要花时间单独写一篇文章来介绍说明的,但是我们这里可以先简单的测试一下,搞清这个数据收发流程。

5.1 接收主机数据

从机示例的数据接收部分代码是在最后部分的simpleProfileChangeCB 回调函数中,下图解释了数据如何接收的:

示例只能接收一个字节,是因为设定的特征值 1 的数据长度为 1 :

我们做个简单测试,直接上测试图 :

5.2 给主机发送数据

除了数据接收,数据发送也是给出了示例,在周期事件SBP_PERIODIC_EVT 中,执行了一个 performPeriodicTask 函数,这里面调用了 Notify 发送,也就是数据发送函数 peripheralChar4Notify,这个函数实际上是调用simpleProfile_Notify 函数,最后这个 simpleProfile_Notify 函数依然是在自定义服务那部分里面实现的。

这里什么是 Notify ,这是 Ble 蓝牙基础的概念,大家网上一查就知道是什么东西,列个表格简单说明一下:

特性 Notify Indicate
方向 从机→主机 从机→主机
要确认吗
可靠性 可能丢 协议保证送到
速度 稍慢(等确认)

在示例中是使用的 Notify 发送,通过代码我们可以明显的看出来,每次发送一个 0x88 ,每秒发送一次(1600 * 625 us),我们用图展示下这个流程:

数据发送流程也是一目了然,至于其他的从机应用,也跳脱不出这个框架流程。

OK! 收工!

结语

本文详细的说明了一下 沁恒微 RISC-V 芯片的蓝牙从机示例框架和工作流程,应用千变万化,万变不离其宗,只要是从机应用框架基本都大差不差的,所以大家还是有必要好好的全面的看一次整体的代码架构,这样才能在后期使用的时候得心应手。

当然,其中依然有一些需要单独拧出来说明的部分,这个博主也会尽可能的安排 (●'◡'●) 。

好了,本文就到这里。谢谢大家!

相关推荐
AI原吾14 天前
双手机 nRF Connect 蓝牙配网完整模拟教程
智能手机·ble
AI原吾15 天前
[特殊字符] 欢迎来到蓝牙便利店
microsoft·智能手机·ble
矜辰所致15 天前
沁恒微 CH585 芯片 Ble 传输速率测试说明
沁恒微蓝牙·ble 速率·蓝牙测试·ch58x ble·ble 传输速度
wotaifuzao1 个月前
(七)深入探讨BLE MAC 地址的隐私博弈:技术与隐私的较量
经验分享·物联网·macos·蓝牙·射频工程·ble
矜辰所致1 个月前
CH58x 蓝牙芯片 SysTick、RTC、TMRx
沁恒微·时钟·rtc·systick·ch58x
矜辰所致2 个月前
沁恒微 RISC-V 蓝牙芯片低功耗测试
低功耗·risc-v·ble 蓝牙·蓝牙低功耗·沁恒微蓝牙
矜辰所致3 个月前
【导航】沁恒微 RISC-V 蓝牙 入门教程目录 【快速跳转】
沁恒微·蓝牙·risc-v·ble·ch585
星野云联AIoT技术洞察3 个月前
OpenMQTTGateway 技术全解:统一多协议到 MQTT 的开源网关
lora·esp32·智能家居·ble·ir·iot网关·openmqttgateway
奔跑吧 android5 个月前
【android bluetooth 协议分析 01】【HCI 层介绍 30】【hci_event和le_meta_event如何上报到btu层】
ble·bluetooth·aosp13·br/edr·hci_event·le_meta_event