Nrf51822 回顾:
Starup Code中存放的是启动文件,设置NRF51822的启动项和系统时钟。
Source codecun存放的是main.c 文件。
Services中存放的是蓝牙的服务代码。
Libraies中放的是nrf51822提供的一些库函数。
带有BLE前缀的就是蓝牙相关的函数,带有app前缀的文件是和app应用有关系的文件夹。
控制层:
1、主机控制器(HCI),也称设备管理器
设备管理器是基带中的一个功能模块,控制一般的蓝牙设备行为。他负责所有与数据无关的蓝牙系统操作,如:扫描,连接,被发现等的设置。
2、链路层(LL)
链路层负责链路管理,链路控制。包括负责创建,维护和释放逻辑链路已经更新设备之间物理链路的相关参数。
3、物理层:
物理层模块负责从物理信道传输和接受信息数据包。在基带和物理之间,一条控制路允许基带模块控制物理层的时隙和频率载波。同时,物理层模块向物理信道和基带发送和几首符合格式要求的数据流。
主协议层:
GAP(通用访问规范)
通用访问配置文件GAP,该profile保证不同的bluetooth产品可以相互发现对方并建立连接。它定义了蓝牙设备如何发现和建立与其他设备的安全(或不安全)连接。他处理一些一般模式的业务(如询问,命名,搜索)和一些安全性问题,同时还处理一些有关连接的业务,如链路的建立、信道和连接建立。
GATT层是传输真正数据所在的层。包括了一个数据传输和存储的框架。
GATT定义了两类角色。服务器和客户端。通常情况下我们的开发板是作为服务器的。手机作为客户端。
一个GATT服务器通过一个称为属性表的表格组织数据,这些数据就是用于真正发送的数据。
属性:一个属性包含句柄,UUID,值。
特性:一个特性包含2个属性,一个属性用于声明,一个属性用于存放特性的值。(我的理 解就是功能。串口服务中接收是一个特性,发送是一个特性。LED服务中,LED只 有接收数据的功能,所以只有一个特性。接收到1是亮,接收到0是灭。按键服务中, 只有发送的功能,按下就发送通知。所以也只有一个特性)服务:一个服务包含一个 或多个特性。
Profile(数据配置文件):一个profile可以包含一个或者多个服务
客户端特性配置描述符(Client Characteristic Configuration Descriptor,CCCD),这个描述符是给任何支持通知或指示功能的特性额外增加的。在CCCD中写入“1”使能通知功能,写入“2”使能指示功能,写入“0”同时禁止通知和指示功能这个描述符是服务器来配置的,通过写事件来向其中写入值。
空中操作性质:
写和没有回应的写,允许GATT客户端写入一个值到GATT服务器的一个特性中。它们之间的不同的地方在于一个没有回应的写事件没有任何应用层上的确认或回应。
读,读性质表明一个GATT客户端可以读取在GATT服务器中特性的值。
通知和指示性质允许GATT服务器在其某个特性改变的时候对GATT客户端进行提醒,通知和指示之间不同支出在于指示有应用蹭上的确认,而通知没有。
服务可以给通过sd_ble_gatts_service_add()进行添加。Service_uuid就是你想用于服务的UUID。变量service_handle是一个输出变量,当创建服务的时候会返回一个唯一的句柄值,这个句柄可以在以后用于识别不同的服务。
特性可以通过sd_ble_gatts_characteristic_add()函数进行添加。它有四个参数。第一个是特性要加入的服务的句柄,第二个参数是特性的结构体,它是一个全局变量,它包含了特性可能用到的性质(读,写和通知等)。第3个参数的值属性的描述包含了它的UUID,长度和初始值。第4个参数是返回的特性和描述符的唯一句柄。这个句柄可以在以后用于识别不同的特性。例如识别拿过一个特性被写入。
私有服务的实现需要定义的参数:
1、服务的句柄(用来存放函数的返回值,不需要自己初始化)
2、特性的句柄(用来存放函数的返回值,不需要自己初始化)
3、连接的句柄(在sd_ble_gatts_hvx()中用返回,不用自己初始化)
4、UUID类型(用来存放函数sd_ble_uuid_vs_add()的返回值,不用自己初始化)
5、事件发生的回调函数
要明白协议栈怎么运作,首先就要理解51822的协议栈是基于100%的事件驱动的。就是说协议栈向app发送的任何数据都是基于事件的。
比如设备收到手机发来的链接请求,或是手机发过来的数据等等。协议栈首先收到这些数据后做一些处理,然后将这些数据(比如链接请求,或是普通数据等)打包成一个结构体,并附上事件ID,比如BLE_GAP_EVT_CONNECTED或BLE_GATTS_EVT_WRITE来分别告诉上层app这个事件结构体代表的事件。
比如BLE_GAP_EVT_CONNECTED代表链接事件,那么这个事件结构体中包含的数据就是连接参数等数据。而BLE_GATTS_EVT_WRITE代表写事件,那么结构体中的数据就是对端设备(比如手机)写给板子的数据。
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
ble_conn_params_on_ble_evt(p_ble_evt);
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
on_ble_evt(p_ble_evt);
}
在任何与BLE相关的事件被协议栈上抛上来给app时,ble_evt_dispatch就会被调用。从而将事件抛给各个服务函数或处理模块,这里是将事件抛给了
连接参数管理处理函数ble_conn_params_on_ble_evt
Uart服务的事件处理函数ble_nus_on_ble_evt (nus为Nordicuart server)
通用的事件处理函数on_ble_evt
不同的事件在事件结构体ble_evt_t中通过id来区别。不同是事件处理函数通常也只是处理自己感兴趣的事件
蓝牙没有接收函数,蓝牙的数据接收在底层,接收完后会返回事件给上层的 ble_evt_dispatch 分发函数,它将事件分发给各个服务或者事件处理函数。 服 务或处理函数会捕获是否存在写事件case BLE_GATTS_EVT_WRITE: 存在就做 相应的处理。收到的数据都在返回的事件结构体里
其实看完这三个问题基本上上面的问题其实已经解决差不多了。作为从设备,BLE的发送数据给手机是有API接口的,就是上面问到的sd_ble_gatts_hvx(),可以通过参数来设置是以通知方式发送还是指示方式发送(通知不需要回复确认,指示需要)。但是手机发过来数据却是没有接收函数,为什么?因为协议栈是基于事件驱动的!所以收到数据后协议栈会给上层app一个写事件(指示对端设备写数据过来了),而写过来的数据时在这个事件结构体中。我们只要提取出来就行了。所以没有接收函数API。
关于NRF51822的休眠:
程序中进行休眠的地方是在事件管理中进行休眠的,在on_ble_evt中,如下:
通过case BLE_GAP_EVT_TIMEOUT:中的err_code = sd_power_system_off();