金牌会员
- 积分
- 1765
- 金钱
- 1765
- 注册时间
- 2015-9-21
- 在线时间
- 544 小时
|
- /*
- * @功能:
- * @入口参数说明:
- * @返回参数说明:
- * @日期:
- *
-
- 通过LED灯的遥控了解主机与从机的GATT Profile通信原理
- 了解服务service,特征值characteristic与profile的关系
- 主机向从机写入write数据和读取read数据
- 通俗的讲,我们将从机具有的数据或者属性特征,称之为profile,profile可翻译为“配置文件”
- 从机中添加profile,作为GATT的server端,主机作为client端
- profile包含一个或者多个服务service,每个服务又包含一个或者多个特征值characteristic “通讯的最小单元”
- 通过特征值的读/写/通知,实现主从之间的通讯
- LED是读写 button是读+通知
- 采用观察者模式,即我们应用端所操作的每一个动作,协议栈都会抛出一个事件,每一个观察者都会接收到这样的一个事件,在决定是否需要执行响应的操作,
- 一个服务可以包含几个特征
- nrf52832的寄存器分为下面三种类型:
- task:任务寄存器,可以由程序或事件触发
- event:事件寄存器,事件可以产生中断或触发任务
- register:普通寄存器
- 通过蓝牙的事件触发芯片脱离低功耗模式,进入运行状态。
- ①程序下载。程序下载包括2步:先下载softdevice,再下载应用。Softdevice是Nordic蓝牙协议栈的名称,整个开发过程中只需下载一次。
- 应用就是我们这里的blinky程序
- 蓝牙协议栈下载(整个开发周期只下载一次)。在Keil ‘select target’下拉列表中,默认选择的是Keil工程对应的Target,即‘nrf52832_xxaa’。
- 但我们还可以选择另一个target ‘flash_s132_nrf52_6.1.1_softdevice’,即softdevice对应的target,然后点击“下载download”(不需要编译哦!),
- 此时会把softdevice下载到开发板中。
-
- 应用下载。重新选择Target:‘nrf52832_xxaa’,点击“下载Download”,此时会把Blinky程序下载到开发板中。
- 此时开发板的LED1常亮,表示程序运行正常。
- */
- /**
- * @brief Blinky Sample Application main file.Blinky样例应用程序主文件
- *
- * This file contains the source code for a sample server application using the LED Button service.
- * 该文件包含使用LED Button服务的示例服务器应用程序的源代码。
- */
- /*
- 函数名被使用时总是由编译器把它转换为函数指针
- */
- /*
- 使用回调函数就是把函数当作参数引入一个新的函数中,可方便的分离底层和上层的开发,或方便复杂系统集成
- */
- /*
- task 任务 :置位、清零、反转
- event 事件 :上升沿、下降沿、任何变化
- task任务通过OUT[0]–OUT[3]设置三种触发状态;
- event 事件可以通过检测信号产生PORT enevt事件,也能产生 IN[n] event事件;
- */
- #include <stdint.h>//定义了整数类型和宏
- #include <string.h>//字符数组的函数定义
- #include "nordic_common.h"//固件的通用定义和宏
- #include "nrf.h"//硬件的定义
- #include "app_error.h"//应用程序常见的错误处理程序
- #include "ble.h"//BLE程序(设备)的模块、事件、类型定义和API调用
- #include "ble_err.h"//BLE API的一般错误代码定义
- #include "ble_hci.h"//HOST和CONTROLLER通讯之间的接口
- #include "ble_srv_common.h"//公共服务定义
- #include "ble_advdata.h"//广播和扫描响应数据编码
- #include "ble_conn_params.h"//链接参数的协商
- #include "nrf_sdh.h"//nrf初始化和应用api
- #include "nrf_sdh_ble.h"//包含BLE堆栈所需的类型和函数的声明
- #include "boards.h"//电路板上硬件的定义
- #include "app_timer.h"//应用程序定时器
- #include "app_button.h"//app_button按钮的处理程序
- #include "ble_lbs.h"//ble_lbs LED按键服务服务器
- #include "nrf_ble_gatt.h"//nrf_ble_gatt GATT module
- #include "nrf_ble_qwr.h"//nrf_ble_qwr排队写模块
- #include "nrf_pwr_mgmt.h"//nrf_pwr_mgmt电源管理
- #include "nrf_log.h"
- #include "nrf_log_ctrl.h"
- #include "nrf_log_default_backends.h"
- #include "SEGGER_RTT.h"
- //定义LED灯的状态来标识链接的状态和操纵
- //广播LED
- #define ADVERTISING_LED BSP_BOARD_LED_0 /**< Is on when device is advertising. 当设备正在播放广播时打开*/
- //链接状态LED
- #define CONNECTED_LED BSP_BOARD_LED_1 /**< Is on when device has connected. 设备连接时打开*/
- //服务连接后可以操作的LED
- #define LEDBUTTON_LED BSP_BOARD_LED_2 /**< LED to be toggled with the help of the LED Button Service. 在LED按钮服务的帮助下切换LED*/
- //服务连接后可以操作的BUTTON
- #define LEDBUTTON_BUTTON BSP_BUTTON_0 /**< Button that will trigger the notification event with the LED Button Service 用LED按钮触发通知事件*/
- //设备广播的名字
- #define DEVICE_NAME "Nordic_Blinky" /**< Name of device. Will be included in the advertising data. 设备名称,将包含在广播数据中*/
- //#define DEVICE_NAME "Things for shuo" /**< Name of device. Will be included in the advertising data. 设备名称,将包含在广播数据中*/
- //观察者优先级
- #define APP_BLE_OBSERVER_PRIO 3 /**< Application's BLE observer priority. You shouldn't need to modify this value.应用程序的优先级,不需要修改此值 */
- //BLE配置软件标识
- #define APP_BLE_CONN_CFG_TAG 1 /**< A tag identifying the SoftDevice BLE configuration. 标识软设备配置的标签*/
- //设备广播间隔
- #define APP_ADV_INTERVAL 64 /**< The advertising interval (in units of 0.625 ms; this value corresponds to 40 ms). 广播间隔,以0.625毫秒为单位,*/
- //设备广播超时设置
- #define APP_ADV_DURATION BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED /**< The advertising time-out (in units of seconds). When set to 0, we will never time out. 广播超时,以秒为单位,设置为0时为永不超时*/
- //最小链接间隔
- #define MIN_CONN_INTERVAL MSEC_TO_UNITS(100, UNIT_1_25_MS) /**< Minimum acceptable connection interval (0.5 seconds). 最小可接受连接 0.5秒*/
- //最大链接间隔
- #define MAX_CONN_INTERVAL MSEC_TO_UNITS(200, UNIT_1_25_MS) /**< Maximum acceptable connection interval (1 second). 可接受的最大连接间隔 1秒*/
- //从机延时参数
- #define SLAVE_LATENCY 0 /**< Slave latency. 从设备延迟*/
- //超时管理参数
- #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection supervisory time-out (4 seconds). 连接监控超时 4秒*/
- //第一次链接参数的更新时间
- #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(20000) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (15 seconds). 从启动事件到第一次调用*/
- //下一次链接参数的更新时间
- #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (5 seconds). 在第一调用后,每隔5秒调用。。。。*/
- //最大链接参数更新时间
- #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. 放弃连接参数协商之前的尝试次数*/
- //按键消抖时间
- #define BUTTON_DETECTION_DELAY APP_TIMER_TICKS(50) /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). 从GPIOE时间到按钮被报告为按下的延迟(以计时器计时的次数表示)*/
- /*
- “DEADBEEF”是什么?可能很多人都没有听说过。DEADBEEF不是“死牛肉”的意思,而是一个十六进制数字,即0xDEADBEEF。
- 最初使用它的是IBM的RS/6000系统。在该系统中,已分配但还未初始化的内存中用该数字来填充,使
- 某些其他系统也使用DEADBEEF,例如运行在32位PowerPC处理器上的Mac OS和RS/6000一样将其用于填充已分配但未初始化的内存;
- Solaris(操作系统)则用它来标记内核的空闲内存;在嵌入式系统中,经常用DEADBEEF来表示程序出错奔溃或发生了死锁。等等。
- 类似于DEADBEEF这样的特殊数字还有很多,有一个专门的术语来称呼它们,即“魔数”,Magic Number。
- */
- #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. 值用作堆栈的错误代码,可用于标识堆栈展开时的堆栈位置 */
- //在主函数中,通过BLE_LBS_DEF(_name) 声明一个观察者。具体的定义是在 ble_lbs.h 中完成。
- //在蓝牙点灯中,会通过 BLE_LBS_DEF(m_bls) 来声明一个 m_lbs的观察者
- //当LED的特性被写入时,会产生一个GATT写事件。这时候就会回调上面 的事件派发函数 ble_lbs_on_ble_evt ()。
- /*在蓝牙点灯中,会通过 BLE_LBS_DEF(m_bls) 来声明一个 m_lbs的观察者,当LED的特性被写入时,会产生一个GATT写事件。
- 这时候就会回调上面 的事件派发函数 ble_lbs_on_ble_evt ()。*/
- //主机(手机)通过需要蓝牙写入,必须使用此宏注册处理处理函数!如果不使用,导致写入数据后SOC无法处理写入的数据.
- // BLE_LBS_DEF 在此宏中调用的NRF_SDH_BLE_OBSERVER,
- //BLE_LBS_DEF(m_lbs);/**< LED Button Service instance. LED按钮服务实列*/
- /*
- 宏的形参无需类型定义,因为宏不存在类型的问题,宏名无类型,宏的形参也无类型,他们都只是一串字符。
- 用宏可以设法得到几个结果。
- LED Button Service structure. This structure contains various status information for the service.
- LED按键业务结构。该结构包含服务的各种状态信息。
- struct ble_lbs_s
- {
- //服务句柄
- uint16_t service_handle; 服务的句柄
- //指向LED特征句柄
- ble_gatts_char_handles_t led_char_handles; 特征的句柄
- //指向BUTTON特征句柄
- ble_gatts_char_handles_t button_char_handles; 特征的句柄
- //服务UUDI信息
- uint8_t uuid_type; UUID的类型
- //定义LED特征被写入时,回调的事件句柄
- ble_lbs_led_write_handler_t led_write_handler; 当特征接收到写入值的时候,调用的回调函数
- };
- m_lbs是结构体类型的变量
- 结构体是一种构造数据类型,在使用之前必须先声明,结构体类型是一种复杂的数据类型,是数目固定,类型不同(也可以相同)的若干有序变量的集合。
- 声明的结构体类型相当于一个新的数据类型,系统并不会分配实际的内存空间。在定义了结构体变量后,系统会为之分配内存空间,结构体变量的各个成员在内存中占用连续的存储区域,结构体变量所占内存大小为结构体中每个成员所占内存长度之和。
- */
- //声明一个结构体类型的变量m_lbs
- static ble_lbs_t m_lbs;
- /*
- NRF_SDH_BLE_OBSERVER(观察者,观察者事件处理程序的优先等级,GATT事件派发函数(BLE事件处理程序),观察者参数设置事件处理程序(把观察者参数传递))
- NRF_SDH_BLE_OBSERVER这个函数是向系统注册一个观察者,通过这个函数注册了一个BLE_OBSERVER观察者,
- 当系统发生BLE事件的时候。就会调用我们这里定义的ble_led_on_ble_evt回调方法
- */
- NRF_SDH_BLE_OBSERVER(m_lbs_obs,BLE_LBS_BLE_OBSERVER_PRIO,ble_lbs_on_ble_evt, &m_lbs); //BLE的观察者
- NRF_BLE_GATT_DEF(m_gatt);/**< GATT module instance. GATT模块实列*/
- NRF_BLE_QWR_DEF(m_qwr);/**< Context for the Queued Write module. 队列写入模块的上下文*/
- //当前链接的句柄 = 无效句柄
- static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;/**< Handle of the current connection. 当前连接的句柄*/
- //广播有关的句柄 = 广播句柄没有设置
- static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;/**< Advertising handle used to identify an advertising set.用于标识广播集的广播句柄 */
- //广播编码数组 [31]
- static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX];/**< Buffer for storing an encoded advertising set. 用于存储编码广播级的缓冲区 广播数据的数组*/
- //扫描响应数据数组[31]
- static uint8_t m_enc_scan_response_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX];/**< Buffer for storing an encoded scan data. 用于存储编码扫描数据的缓冲区 扫描响应的数组*/
- /**@brief Struct that contains pointers to the encoded advertising data. 包含指向编码广播数据的指针的结构*/
- /*
- 该Profile保证不同的Bluetooth产品可以互相发现对方并建立连接
- GAP定义了蓝牙设备如何发现和建立与其他设备的安全/不安全连接
- 它处理一些一般模式的业务(如询问、命名和搜索)和一些安全性问题(如担保)
- 同时还处理一些有关连接的业务(如链路建立、信道和连接建立)
- GAP规定的是一些一般性的运行任务;因此,它具有强制性,并作为所有其它蓝牙应用规范的基础
- GAP主要对linklayer层(standby state、advertising state、initiating state、connection state)的状态进行了抽象,转化成上层的概念
- 对广播包数据进行封装,运用同一的格式和类型,已达到互联的目的。比如我们扫描设备的时候,会发先设备的名称,设备的名称就是运用统一的格式封装在adv的报文中,支持GAP的设备都能明白数据包的内容的意思。
- GAP主要功能包括定义了广播协议,以及几个服务,包括
- Device Name characteristic,这个也可以包含在广播数据中
- Appearance characteristic,这个可以告诉observer显示出什么样的图标
- Peripheral PreferredConnectionParameters (PPCP) characteristic
- 广播里可能会包含SIG定义的UUID,这样中心设备就不需要连接,就知道外设有哪些服务。
- 配置广播数据
- 它在用来控制设备连接和广播。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。例如 Beacon 设备就只是向外广播,不支持连接,小米手环就等设备就可以与中心设备连接。
- */
- /*
- typedef struct
- {
- ble_data_t adv_data;广播数据
- ble_data_t scan_rsp_data;扫描响应
-
- } ble_gap_adv_data_t;
- typedef struct
- {
- uint8_t *p_data;指向提供给/来自应用程序的数据缓冲区的指针。
- uint16_t len; 数据缓冲区的长度,以字节为单位
- } ble_data_t;
- */
- static ble_gap_adv_data_t m_adv_data =
- {
- .adv_data = //广播数据赋值
- {
- .p_data = m_enc_advdata,//广播句柄
- .len = BLE_GAP_ADV_SET_DATA_SIZE_MAX //广播数据的最大长度
- },
- .scan_rsp_data = //扫描响应数据赋值
- {
- .p_data = m_enc_scan_response_data,//扫描响应数据数组[31]
- .len = BLE_GAP_ADV_SET_DATA_SIZE_MAX
- }
- };
- /**@brief Function for assert macro callback.用于断言宏回调的函数
- *
- * [url=home.php?mod=space&uid=168459]@Details[/url] This function will be called in case of an assert in the SoftDevice.
- *
- * [url=home.php?mod=space&uid=163200]@warning[/url] This handler is an example only and does not fit a final product. You need to analyze
- * how your product is supposed to react in case of Assert. 此处理程序只是一个示列,不适合最终产品,你需要分析你的产品应该如何反应的情况下断言
- * @warning On assert from the SoftDevice, the system can only recover on reset.从设备断言时,系统只能在重置时恢复
- *
- * @param[in] line_num Line number of the failing ASSERT call.失败的AAERT调用的行号
- * @param[in] p_file_name File name of the failing ASSERT call.失败的AAERT调用的文件名
- * 出现错误时的处理
- */
- //DEAD_BEEF 错误代码
- void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
- {
- app_error_handler(DEAD_BEEF, line_num, p_file_name); //
- }
- /**@brief Function for the LEDs initialization.LED功能初始化
- *
- * @details Initializes all LEDs used by the application.初始化应用程序使用的所有LED
- */
- static void leds_init(void)
- {
- //BSP_INIT_LEDS = 1
- bsp_board_init(BSP_INIT_LEDS);
- }
- /**@brief Function for the Timer initialization.定时器初始化
- *
- * @details Initializes the timer module.计时器初始化(无效)
- */
- static void timers_init(void)
- {
- // Initialize timer module, making it use the scheduler 初始化定时器模块,使其使用调用程序
- ret_code_t err_code = app_timer_init();//初始化定时器
- APP_ERROR_CHECK(err_code); //返回错误参数
- }
- /**@brief Function for the GAP initialization.用于GAP初始化的函数。
- *
- * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
- * device including the device name, appearance, and the preferred connection parameters. 此函数用于设置设备的所有必须的GAP(通用访问配置文件)参数,包括设备名称,外观和首选连接参数
- * 设置必要的设备的GAP参数
- */
- /*
- 蓝牙BLE建立GATT连接前,必须经过GAP协议,GAP用来控制设备的连接广播等,GAP使设备广播或可被发现、可被连接等。
- gap_params_init就是进行GAP初始化,设定连接参数
- */
- /*
- GAP四种广播:
- 通用广播:通用广播是用途最广的广播方式。进行通用广播的设备能够被扫描设备扫描到,或者在接收到连接请求时作为从设备进入一个连接。通用广播可以在没有连接的情况下发出,换句话说,没有主从设备之分。
- 定向广播:为了尽可能快的建立连接。这种报文包含两个地址:广播者的地址和发起者的地址。发起设备收到发绐自己的定向广播报文后,可以立即发送连接请求作为回应。
- 不可连接广播:只广播数据,不能被扫描或者连接。只能根据主机的要求在广播态和就绪态之间切换。
- 可发现广播:不能用于发起连接,但允许其他设备扫描该广播设备。设备可以被发现,既可以广播数据,又可以响应扫描,但不能建立连接。这是一种适用于广播数据的广播形式,动态数据可以包含于广播数据之中,
- 而静态数据可以包含于扫描响应数据之中。可发现广播不会进入连接态,而只能在停止后回到就绪态。
- */
- static void gap_params_init(void)
- {
- ret_code_t err_code;//存放错误参数的变量
- ble_gap_conn_params_t gap_conn_params; //结构体变量 GAP链接参数
- ble_gap_conn_sec_mode_t sec_mode; //结构体变量 GAP安全链接(链接模式)
- //设置设备名的写权限为普通模式,则手机扫描到设备连接上后可以在第一个服务Geneic Access Service中改为Device name
- BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);//模式设置 加密级别为Security Mode 1 Level 1
- //设置设备名,该设备名就是在手机app扫描蓝牙设置时显示的名字
- //广播名字其实是通过如下API来完成修改的
- err_code = sd_ble_gap_device_name_set(&sec_mode,
- (const uint8_t *)DEVICE_NAME,
- strlen(DEVICE_NAME));//名称设置
- APP_ERROR_CHECK(err_code);//错误参数
- //设置GAP外围设备首选连接参数
- //申请内存 用0填充
- memset(&gap_conn_params, 0, sizeof(gap_conn_params));
- /*
- 设置外围设备连接首选参数,同device name一样,手机连上某个蓝牙设备后,可以从generic access service中看到设置的这些参数,这个参数主要是让中央
- 设备在首次连接外设时可以读取她们以即使调整连接参数,或者当中央设备以后重连该外设,并且之前保留了这些参数那么就免去了连接后可能需要的修改连接
- 参数的麻烦
- */
- gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;//最小间隔时间
- gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;//最大间隔时间
- gap_conn_params.slave_latency = SLAVE_LATENCY;//从设备延迟
- gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;//链接超时时间
- err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
- APP_ERROR_CHECK(err_code);
- }
- /**@brief Function for initializing the GATT module.
- 它定义两个 BLE 设备通过叫做 Service 和 Characteristic 的东西进行通信。GATT 就是使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service, Characteristic对应的数据保存在一个查找表中,次查找表使用 16 bit ID 作为每一项的索引。
- 一旦两个设备建立起了连接,GATT 就开始起作用了,这也意味着,你必需完成前面的 GAP 协议。这里需要说明的是,GATT 连接,必需先经过 GAP 协议。实际上,我们在 Android 开发中,可以直接使用设备的 MAC 地址,发起连接,可以不经过扫描的步骤。
- 这并不意味不需要经过 GAP,实际上在芯片级别已经给你做好了,蓝牙芯片发起连接,总是先扫描设备,扫描到了才会发起连接。
- GATT 连接需要特别注意的是:GATT 连接是独占的。也就是一个 BLE 外设同时只能被一个中心设备连接。一旦外设被连接,它就会马上停止广播,这样它就对其他设备不可见了。当设备断开,它又开始广播。
- 中心设备和外设需要双向通信的话,唯一的方式就是建立 GATT 连接。
- */
- /*
- 这就没什么说的了,根本就是直接调用了SDK中的ret_code_t nrf_ble_gatt_init(nrf_ble_gatt_t * p_gatt,nrf_ble_gatt_evt_handler_t evt_handler ),
- 很好理解,问题是,m_gatt的定义并不是简单的nrf_ble_gatt_t m_gatt;,而是使用宏:NRF_BLE_GATT_DEF(m_gatt);进行定义
- */
- static void gatt_init(void)
- {
- ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
- APP_ERROR_CHECK(err_code);
- }
- /**@brief Function for initializing the Advertising functionality.
- * 广播参数初始化 用于初始化广播功能
- * @details Encodes the required advertising data and passes it to the stack.
- * Also builds a structure to be passed to the stack when starting advertising.
- * 对所需的广告数据进行编码并将其传递给堆栈,
- * 还构建了一个在开始广告时传递给堆栈的结构
- advertising_init()中包括了蓝牙相关服务uuid的初始化,这个是我们在具体使用时创建多个服务时就需要依次用一个UUID进行匹配。
- 在一个蓝牙设备中可以有多个服务,每一个服务可以包含多个特征值,每个特征值都可以有属于它的多个属性,例如长度、宽度等
- */
- //广播初始化,设定广播周期,广播超时时间等参数,其中还会配置广播的uuid信息
- static void advertising_init(void)//广播参数初始化
- {
- ret_code_t err_code;
- ble_advdata_t advdata;
- ble_advdata_t srdata;
- ble_uuid_t adv_uuids[] = {{LBS_UUID_SERVICE, m_lbs.uuid_type}};
- // Build and set advertising data.构建和设置广播数据
- //memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
- memset(&advdata, 0, sizeof(advdata));
- advdata.name_type = BLE_ADVDATA_FULL_NAME; //设备名称的类型
- advdata.include_appearance = true;//确定是否应包括外观
- advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; //
- //memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
- memset(&srdata, 0, sizeof(srdata));
- srdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);//UUDI相关大小
- srdata.uuids_complete.p_uuids = adv_uuids; //广播UUID
- err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
- APP_ERROR_CHECK(err_code);//用于编码和设置广告数据和扫描响应数据的功能
- err_code = ble_advdata_encode(&srdata, m_adv_data.scan_rsp_data.p_data, &m_adv_data.scan_rsp_data.len);
- APP_ERROR_CHECK(err_code);
- ble_gap_adv_params_t adv_params;
- // Set advertising parameters.初始化广播参数(在开始广播时使用)
- memset(&adv_params, 0, sizeof(adv_params));
- adv_params.primary_phy = BLE_GAP_PHY_1MBPS; //主要的PHY参数
- adv_params.duration = APP_ADV_DURATION;//持续的时间 广播超时
- adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; //属性的类型
- adv_params.p_peer_addr = NULL;//地址优先级
- adv_params.filter_policy = BLE_GAP_ADV_FP_ANY;//筛选原则
- adv_params.interval = APP_ADV_INTERVAL;//间隔
- err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &adv_params);
- APP_ERROR_CHECK(err_code);
- }
- /**@brief Function for handling Queued Write Module errors.函数,用于处理排队写模块错误
- *
- * @details A pointer to this function will be passed to each service which may need to inform the
- * application about an error.
- *指向这个函数的指针将被传递给每个可能需要通知应用程序错误的服务。
- * @param[in] nrf_error Error code containing information about what went wrong.包含出错信息的错误代码
- */
- static void nrf_qwr_error_handler(uint32_t nrf_error)
- {
- APP_ERROR_HANDLER(nrf_error);
- }
- /**@brief Function for handling write events to the LED characteristic.函数用于处理LED特性的写事件。
- *
- * @param[in] p_lbs Instance of LED Button Service to which the write applies.写入所应用的LED按钮服务实例。
- * @param[in] led_state Written/desired state of the LED.LED的写入/期望状态。
- */
- static void led_write_handler(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t led_state)
- {
- SEGGER_RTT_printf(0,"led_state = %x\r\n",led_state);
- if (led_state)
- {
- bsp_board_led_on(LEDBUTTON_LED);//点亮板子上的指示灯
- NRF_LOG_INFO("Received LED ON!");
- SEGGER_RTT_printf(0,"Received LED ON!\r\n");
- }
- else
- {
- bsp_board_led_off(LEDBUTTON_LED);//熄灭板子上的指示灯
- NRF_LOG_INFO("Received LED OFF!");
- SEGGER_RTT_printf(0,"Received LED OFF!\r\n");
- }
- }
- /**@brief Function for initializing services that will be used by the application. 应用程序服务
- 在 services_init() 中添加上述的ble_lbs_init();并在main函数调用services_init() 进行初始化,
- 完成整一个蓝牙LED灯读写服务的添加。
- */
- static void services_init(void)
- {
- ret_code_t err_code;
- ble_lbs_init_t init = {0};//定义一个结构体变量init,里面基本上是一个回调函数的指针,参数初始化为0
- nrf_ble_qwr_init_t qwr_init = {0};//参数初始化为0
- // Initialize Queued Write Module.初始化排队写模块。
- qwr_init.error_handler = nrf_qwr_error_handler;
- ////设置特征值初始取值。
- err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);//把一个函数 led_write_handler 送给了 m_qwr
- APP_ERROR_CHECK(err_code);
- // Initialize LBS.初始化 LBS
- //其中 led_write_handler(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t led_state)
- //是通过led_state开关灯而所谓送 关键是 ble_lbs_init
- ////设置处理主机(GATTC)Write事件的处理函数,用于接收数据。
- init.led_write_handler = led_write_handler;
- //函数参数是指针的形式,因此携带的参数要使用&符号,取变量地址。
- err_code = ble_lbs_init(&m_lbs, &init);
- APP_ERROR_CHECK(err_code);
- }
- /**@brief Function for handling the Connection Parameters Module.函数用于处理连接参数模块。
- *
- * @details This function will be called for all events in the Connection Parameters Module that
- * are passed to the application.
- * 对于传递给应用程序的连接参数模块中的所有事件,将调用此函数。
- * [url=home.php?mod=space&uid=60778]@note[/url] All this function does is to disconnect. This could have been done by simply
- * setting the disconnect_on_fail config parameter, but instead we use the event
- * handler mechanism to demonstrate its use.
- * 这个函数所做的就是断开连接。这可以通过简单地设置disconnect_on_fail配置参数来完成,
- * 但是我们使用事件处理程序机制来演示它的使用。
- * @param[in] p_evt Event received from the Connection Parameters Module.
- */
- static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
- {
- ret_code_t err_code;
- if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
- {
- err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
- APP_ERROR_CHECK(err_code);
- }
- }
- /**@brief Function for handling a Connection Parameters error. 函数处理连接参数错误。
- *
- * @param[in] nrf_error Error code containing information about what went wrong.包含出错信息的错误代码。
- */
- static void conn_params_error_handler(uint32_t nrf_error)
- {
- APP_ERROR_HANDLER(nrf_error);
- }
- /**@brief Function for initializing the Connection Parameters module.函数用于初始化连接参数模块。
- 蓝牙连接的一些参数配置信息,详细需要参考蓝牙文档了
- */
- static void conn_params_init(void)
- {
- ret_code_t err_code;
- ble_conn_params_init_t cp_init;
- memset(&cp_init, 0, sizeof(cp_init));
- cp_init.p_conn_params = NULL;
- cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;//事件启动到第一次调用的延时
- cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;//第一次调用后的间隔事件延时
- cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; //连接尝试的次数定义
- cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
- cp_init.disconnect_on_fail = false;
- cp_init.evt_handler = on_conn_params_evt;
- cp_init.error_handler = conn_params_error_handler;
- err_code = ble_conn_params_init(&cp_init);
- APP_ERROR_CHECK(err_code);
- }
- /**@brief Function for starting advertising.启动广告功能。
- 调用sd_ble_gap_adv_start开始广播,同时点亮LED表示广播状态
- */
- static void advertising_start(void)
- {
- ret_code_t err_code;
- err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);//APP_BLE_CONN_CFG_TAG 标识
- APP_ERROR_CHECK(err_code);
- bsp_board_led_on(ADVERTISING_LED);//点亮广播灯
- }
- /**@brief Function for handling BLE events. 用于处理BLE事件的函数。
- *
- * @param[in] p_ble_evt Bluetooth stack event.
- * @param[in] p_context Unused.
- */
- static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
- {
- ret_code_t err_code;
- //当我们的主机设备进行链接或者断开链接的时候,BLE的事件回调中,都会给我们状态事件提示
- switch (p_ble_evt->header.evt_id)
- {
- case BLE_GAP_EVT_CONNECTED: //链接成功的状态,我们可以在这边获取到链接的设备的MAC,链接参数等等
- NRF_LOG_INFO("Connected");
- bsp_board_led_on(CONNECTED_LED);//连接了就点亮连接指示灯
- bsp_board_led_off(ADVERTISING_LED);//关闭广播灯
- m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
- err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
- APP_ERROR_CHECK(err_code);
- err_code = app_button_enable();
- APP_ERROR_CHECK(err_code);
- break;
- case BLE_GAP_EVT_DISCONNECTED://断开链接的状态,我们可以获取到断开链接的原因等等
- NRF_LOG_INFO("Disconnected");
- bsp_board_led_off(CONNECTED_LED);//没有连接就关闭连接指示灯
- m_conn_handle = BLE_CONN_HANDLE_INVALID;
- err_code = app_button_disable();
- APP_ERROR_CHECK(err_code);
- advertising_start();
- break;
- //回复配对请求
- case BLE_GAP_EVT_SEC_PARAMS_REQUEST://当手机发来配对请求时就会触发这个事件,调用回复API回复配对参数
- // Pairing not supported
- err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
- BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
- NULL,
- NULL);
- APP_ERROR_CHECK(err_code);
- break;
- /*
- BLE_GAP_EVT_PHY_UPDATE_REQUEST 蓝牙5.0提出了一个高速率模式,即引入了一个新的调制解调符号率2M,即一秒可以传2M个bit
- 只有手机和蓝牙设备同时支持2M模式,并且一方主动要求更新物理层为2M时,蓝牙通信才会采用2M,上述的速率翻倍才可能实现。
- 所有的nrf52设备都支持高速率2M模式,而手机是否支持2M模式,可以通过手机中的device information菜单查看
- 硬件已经满足,那软件如何修改呢?2M和1M模式,这个是物理概念,对应用层来说完全是透明的,也就是说,不管采用2M还是1M模式,
- 应用的代码都是一样的,不需要修改。
- 这里已经支持2M模式了
- phy参数有4种:
- BLE_GAP_PHY_1MBPS,强制选择1M模式
- BLE_GAP_PHY_2MBPS,强制选择2M模式
- BLE_GAP_PHY_CODED,强制选择低速率的长距离模式
- BLE_GAP_PHY_AUTO,协议栈自动选择合适的phy层,一般会选择最快最合适的那个phy
- 请注意:BLE_GAP_EVT_PHY_UPDATE_REQUEST是响应主机端的phy update请求的,如果主机端不发phy update请求,那么phy仍然维持默认的1M模式。
- 碰到这种情况,设备端需要主动发起phy update请求,怎么做呢?
- 只需在连接成功事件中,调用sd_ble_gap_phy_update这个函数即可,这样哪怕手机端不发起phy update请求,设备端也会主动请求phy update。
- */
-
- /*
- 手机连接设备后,设备会立刻发送安全请求。因为手机和设备没有绑定,所以手机收到设备发过来的安全请求后机会发送配对请求给设备。
- 设备,继而回复配对请求(没有设置绑定标志),设备后续的配对过程由协议栈自动完成,并最终返回给上层配对完成事件。判断配对是否成功,
- 如果失败就断开连接,从而阻止他人随意连接设备。
- */
- case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
- {
- NRF_LOG_DEBUG("PHY update request.");
- ble_gap_phys_t const phys =
- {
- .rx_phys = BLE_GAP_PHY_AUTO,
- .tx_phys = BLE_GAP_PHY_AUTO,
- };
- err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
- APP_ERROR_CHECK(err_code);
- } break;
- case BLE_GATTS_EVT_SYS_ATTR_MISSING:
- // No system attributes have been stored.未存储系统属性。
- err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
- APP_ERROR_CHECK(err_code);
- break;
- case BLE_GATTC_EVT_TIMEOUT:
- // Disconnect on GATT Client timeout event.断开GATT客户端超时事件。
- NRF_LOG_DEBUG("GATT Client Timeout.");
- err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
- BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
- APP_ERROR_CHECK(err_code);
- break;
- case BLE_GATTS_EVT_TIMEOUT:
- // Disconnect on GATT Server timeout event.断开GATT服务器超时事件。
- NRF_LOG_DEBUG("GATT Server Timeout.");
- err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
- BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
- APP_ERROR_CHECK(err_code);
- break;
- default:
- // No implementation needed.不需要实现。
- break;
- }
- }
- /**@brief Function for initializing the BLE stack.
- * 用于初始化BLE堆栈的功能
- * @details Initializes the SoftDevice and the BLE event interrupt.
- * 初始化softdevice和BLE事件中断
- * ble_stack_init初始化协议栈的内存分配,设置回调函数为ble_stack_init
- */
- static void ble_stack_init(void)
- {
- ret_code_t err_code;
- err_code = nrf_sdh_enable_request();
- APP_ERROR_CHECK(err_code);
- // Configure the BLE stack using the default settings.使用默认设置配置BLE堆栈
- // Fetch the start address of the application RAM.获取应用程序RAM的起始地址
- uint32_t ram_start = 0;
- err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);//APP_BLE_CONN_CFG_TAG 标识
- APP_ERROR_CHECK(err_code);
- // Enable BLE stack. 启用BLE堆栈
- err_code = nrf_sdh_ble_enable(&ram_start);
- APP_ERROR_CHECK(err_code);
- // Register a handler for BLE events.为BLE事件注册一个处理程序。
- NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);//APP_BLE_OBSERVER_PRIO 优先级
- }
- /**@brief Function for handling events from the button handler module.函数用于处理按钮处理程序模块中的事件。
- *
- * @param[in] pin_no The pin that the event applies to.事件所应用的大头针。
- * @param[in] button_action The button action (press/release).按钮动作(按/释放)。
- */
- static void button_event_handler(uint8_t pin_no, uint8_t button_action)
- {
- ret_code_t err_code;
- switch (pin_no)
- {
- case LEDBUTTON_BUTTON://按键事件?按键通知?
- NRF_LOG_INFO("Send button state change.");
- //通过ble_lbs_on_button_change来更新Button characteristic的值的,
- err_code = ble_lbs_on_button_change(m_conn_handle, &m_lbs, button_action);
- if (err_code != NRF_SUCCESS &&
- err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
- err_code != NRF_ERROR_INVALID_STATE &&
- err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
- {
- APP_ERROR_CHECK(err_code);
- }
- break;
- default:
- APP_ERROR_HANDLER(pin_no);
- break;
- }
- }
- /**@brief Function for initializing the button handler module.函数用于初始化按钮处理程序模块。
- */
- static void buttons_init(void)
- {
- ret_code_t err_code;
- //The array must be static because a pointer to it will be saved in the button handler module.
- //数组必须是静态的,因为指向它的指针将保存在按钮处理程序模块中。
- static app_button_cfg_t buttons[] =
- {
- //button_event_handler是一个函数,这里把函数的指针(地址)作为参数传递给另一个函数
- /*
- 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,
- 当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
- */
- /*
- 用作按键的引脚 -- LEDBUTTON_BUTTON
- 初始化引脚的活动状态 -- false
- 上下拉配置 -- BUTTON_PULL
- 按键按下要调用的函数 -- button_event_handler
- */
- {LEDBUTTON_BUTTON, false, BUTTON_PULL, button_event_handler} //按键功能初始化 按键中断
- };
-
- //#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 计算数组大小
- //ARRAY_SIZE(buttons)计算按键数量
- err_code = app_button_init(buttons, ARRAY_SIZE(buttons),
- BUTTON_DETECTION_DELAY);//按键消抖时间
- APP_ERROR_CHECK(err_code);
- }
- static void log_init(void)
- {
- ret_code_t err_code = NRF_LOG_INIT(NULL);
- APP_ERROR_CHECK(err_code);
- NRF_LOG_DEFAULT_BACKENDS_INIT();
- }
- /**@brief Function for initializing power management.用于初始化电源管理。
- */
- static void power_management_init(void)
- {
- ret_code_t err_code;
- err_code = nrf_pwr_mgmt_init();
- APP_ERROR_CHECK(err_code);
- }
- /**@brief Function for handling the idle state (main loop).函数处理空闲状态(主循环)。
- *
- * @details If there is no pending log operation, then sleep until next the next event occurs.
- * 如果没有挂起的日志操作,则休眠,直到下一个事件发生。
- */
- /*
- 打开 idle_state_handle() 函数,该函数是处理空闲状态的函数。通过 if 语句,判断调试缓冲区没有更多日志的时候,
- 就进入 nrf_pwr_mgmt_run() 函数,这个函数就会进入到低功耗模式,直到下一个事件发生。
- */
- static void idle_state_handle(void)
- {
- if (NRF_LOG_PROCESS() == false) // 如果调试缓冲区没有更多日志
- {
- nrf_pwr_mgmt_run();
- }
- }
- /**@brief Function for application main entry.用于应用程序主入口的函数。
- 实现任务调度和电源管理
- */
- int main(void)
- {
- // Initialize.
- SEGGER_RTT_printf(0,"nRF52832 Test \r\n");
-
- //外设驱动初始化
- log_init();//消息打印
- //初始化LED指示灯,用来指示广播和链接状态
- leds_init();//LED
- //初始化软件定时器
- timers_init();//定时器创建
- buttons_init();//按键
-
- //蓝牙协议栈初始化
- power_management_init();//电源管理
- ble_stack_init();//BLE协议栈初始化
- gap_params_init();//GAP参数设置初始化
- gatt_init();//GATT服务初始化
- services_init();//添加私有服务,需要初始化在广播初始化之前 服务初始化
- advertising_init();//广播初始化
- conn_params_init();//连接参数初始化
- //开启应用
- // Start execution.
- NRF_LOG_INFO("Blinky example started.");
- advertising_start();//开启广播
- // Enter main loop.
- for (;;)
- {
-
- idle_state_handle();
-
- }
- }
- /**
- * @}
- */
复制代码
|
|