打开主菜单

谷雨文档中心 β

NRF52832DK协议栈实验

Jinx讨论 | 贡献2019年7月8日 (一) 16:56的版本 源码讲解

目录

1 蓝牙协议简介

2 蓝牙协议实验

蓝牙协议实验部分,我们借由串口透传实验,一步一步拆分,给大家介绍蓝牙的协议方面。

2.1 低功耗实验

2.1.1 实验简介

低功耗实验1.0_ble_central_pm与2.0_ble_peripheral_pm,这两个实验给大家带来的是最精简的主机以及从机例程,精简到什么程度呢,只保留了协议栈初始化以及电源管理部分。利用此实验,大家可以测试一下我们的BLE工程进入低功耗模式下的功耗情况。

2.1.2 实验现象

我们将万用表串联到电路中,并且打到电流档,此时我们可以看到功耗如下。

2.1.3 工程及源码讲解

2.1.3.1 主机部分
2.1.3.1.1 main()函数

首先我们查看一下main.c文件,在此文件的mian()函数中,我们首先初始化了电源管理模块,然后初始化了BLE栈堆,最后在while大循环中我们调用空闲状态处理的函数。

接下来我们分别针对这三个部分进行介绍。

170 //******************************************************************
171 // fn : main
172 //
173 // brief : 主函数
174 //
175 // param : none
176 //
177 // return : none
178 int main(void)
179 {
180     // 初始化
181     power_management_init();// 初始化电源控制
182     ble_stack_init();       // 初始化BLE栈堆
183     
184     // 进入主循环
185     for (;;)
186     {
187         idle_state_handle();   // 空闲状态处理
188     }
189 }
2.1.3.1.2 ble_stack_init()函数及ble_evt_handler()回调函数

我们查看一下BLE协议栈初始化,这个部分是一个格式化的东西。

首先调用nrf_sdh_enable_request()函数请求使能softdevice,原因在于ble协议、时钟、错误的回调以及中断的配置等,都需要这个sd(softdevice)支持。

接下来我们要配置默认的ble协议,主要包含了RAM起始地址、本工程的角色(根据设备支持连接的角色来判断)、MTU的大小及UUID和属性表大小。

RAM的起始地址在下面的位置可以看到,我们打开\ble_ghostyu\1.0_ble_central_pm\LaunchIOT\s132\iar下的ble_app_ghostyu_iar_nRF5x.icf文件(将此文件拖到IAR中就可以打开,可以看到__ICFEDIT_region_RAM_start__ = 0x200029e0)。

然后我们携带RAM起始地址,使能BLE协议栈。

在最后,我们注册了一个ble_evt_handler回调,在这个回调中我们处理BLE的事件返回。

102 //******************************************************************
103 // fn : ble_stack_init
104 //
105 // brief : 用于初始化BLE协议栈
106 // details : 初始化SoftDevice、BLE事件中断
107 //
108 // param : none
109 //
110 // return : none
111 static void ble_stack_init(void)
112 {
113     ret_code_t err_code;
114 
115     // SD使能请求,配置时钟,配置错误回调,中断(中断优先级栈堆默认设置)
116     err_code = nrf_sdh_enable_request();
117     APP_ERROR_CHECK(err_code);
118 
119     // SD默认配置(如下),SD RAM起始地址配置(0x200029e0)
120     // 作为从机时的最大连接数量0
121     // 作为主机时的最大连接数据1(本工程是主机)
122     // 初始化MTU大小23
123     // 供应商特定的UUID数量1
124     // 属性表大小248(必须是4的倍数,以字节为单位)
125     // 配置服务更改特性数量0
126     uint32_t ram_start = 0;
127     err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
128     APP_ERROR_CHECK(err_code);
129 
130     // 使能BLE栈堆
131     err_code = nrf_sdh_ble_enable(&ram_start);
132     APP_ERROR_CHECK(err_code);
133 
134     // 注册BLE事件的处理程序,所有BLE的事件都将分派ble_evt_handler回调
135     NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
136 }

ble_evt_handler回调函数,用于处理BLE的事件回调,包含了COMMON、GAP、GATT Client、GATT Server、L2CAP等多种事件类型。在这个例程中,我们只给大家保留了最基础的两个GAP状态,分别为连接状态和断开连接状态。

 68 //******************************************************************
 69 // fn : ble_evt_handler
 70 //
 71 // brief : BLE事件回调
 72 // details : 包含以下几种事件类型:COMMON、GAP、GATT Client、GATT Server、L2CAP
 73 //
 74 // param : ble_evt_t  事件类型
 75 //         p_context  未使用
 76 //
 77 // return : none
 78 static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
 79 {
 80     ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
 81 
 82     switch (p_ble_evt->header.evt_id)
 83     {
 84         // 连接
 85         case BLE_GAP_EVT_CONNECTED:
 86             NRF_LOG_INFO("Connected. conn_handle: 0x%x",p_gap_evt->conn_handle);
 87             break;
 88 
 89         // 断开连接
 90         case BLE_GAP_EVT_DISCONNECTED:
 91 
 92             NRF_LOG_INFO("Disconnected. conn_handle: 0x%x, reason: 0x%x",
 93                          p_gap_evt->conn_handle,
 94                          p_gap_evt->params.disconnected.reason);
 95             break;
 96 
 97         default:
 98             break;
 99     }
100 }
2.1.3.1.3 power_management_init()及idle_state_handle()函数

power_management_init()函数调用底层的nrf_pwr_mgmt_init()函数去初始化电源管理的部分。

idle_state_handle()函数调用底层的nrf_pwr_mgmt_run()函数,用于处理空闲状态的功能(处理完所有的挂起事件,然后进入休眠,直到下一个事件发生)。

138 //******************************************************************
139 // fn : power_management_init
140 //
141 // brief : 初始化电源管理
142 //
143 // param : none
144 //
145 // return : none
146 static void power_management_init(void)
147 {
148     ret_code_t err_code;
149     err_code = nrf_pwr_mgmt_init();
150     APP_ERROR_CHECK(err_code);
151 }
152 
153 //******************************************************************
154 // fn : idle_state_handle
155 //
156 // brief : 处理空闲状态的功能(用于主循环)
157 // details : 处理任何挂起的日志操作,然后休眠直到下一个事件发生
158 //
159 // param : none
160 //
161 // return : none
162 static void idle_state_handle(void)
163 {
164     if (NRF_LOG_PROCESS() == false)
165     {
166         nrf_pwr_mgmt_run();
167     }
168 }
2.1.3.2 从机部分
2.1.3.2.1 ble_stack_init()函数

从机部分大体上是和主机一样的,在nordic的协议栈例程中,(如果大家对BLE协议有一定的了解或者使用过其他厂家的BLE芯片)我们可以发现,nordic为了简化BLE的开发难度,可谓是不择手段,他减掉了很多的ble协议相关的内容(这里的减掉指的是放到底层处理,不需要开发者去配置),这其中就包含了GAP Role,也就是蓝牙的角色。

ble_satck_init()函数在主机与从机中,唯一不同的点在初始化参数配置。

 99 //******************************************************************
100 // fn : ble_stack_init
101 //
102 // brief : 用于初始化BLE协议栈
103 // details : 初始化SoftDevice、BLE事件中断
104 //
105 // param : none
106 //
107 // return : none
108 static void ble_stack_init(void)
109 {
110     ret_code_t err_code;
111 
112     // SD使能请求,配置时钟,配置错误回调,中断(中断优先级栈堆默认设置)
113     err_code = nrf_sdh_enable_request();
114     APP_ERROR_CHECK(err_code);
115 
116     // SD默认配置(如下),SD RAM起始地址配置(0x20002a98)
117     // 作为从机时的最大连接数量1(本工程为从机)
118     // 作为主机时的最大连接数据0
119     // 初始化MTU大小23
120     // 供应商特定的UUID数量1
121     // 属性表大小248(必须是4的倍数,以字节为单位)
122     // 配置服务更改特性数量0
123     uint32_t ram_start = 0;
124     err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
125     APP_ERROR_CHECK(err_code);
126 
127     // 使能BLE栈堆
128     err_code = nrf_sdh_ble_enable(&ram_start);
129     APP_ERROR_CHECK(err_code);
130 
131     // 注册BLE事件的处理程序,所有BLE的事件都将分派ble_evt_handler回调
132     NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
133 }

主机初始化时设置的是作为主机时最大连接数量1,从机初始化时设置的是作为从机时最大连接数量1。这个配置的宏定义是在sdk_config.h文件中。我们go to define,进入nrf_sdh_ble_default_cfg_set()默认参数配置函数中查看,可以找到如下部分。

NRF_SDH_BLE_PERIPHERAL_LINK_COUNT与NRF_SDH_BLE_CENTRAL_LINK_COUNT,在定义最大连接设备数量的同时,也决定了它本身的角色属性。

BLE中我们称Central中心设备为主机(发起连接的设备)、Peripheral外部设备为从机(广播等待被连接的设备)。

103 ret_code_t nrf_sdh_ble_default_cfg_set(uint8_t conn_cfg_tag, uint32_t * p_ram_start)
104 {
105     uint32_t ret_code;
106 
107     ...
108 
109     // Configure the connection roles.
110     memset(&ble_cfg, 0, sizeof(ble_cfg));
111     ble_cfg.gap_cfg.role_count_cfg.periph_role_count  = NRF_SDH_BLE_PERIPHERAL_LINK_COUNT;
112 #ifndef S112
113     ble_cfg.gap_cfg.role_count_cfg.central_role_count = NRF_SDH_BLE_CENTRAL_LINK_COUNT;
114     ble_cfg.gap_cfg.role_count_cfg.central_sec_count  = MIN(NRF_SDH_BLE_CENTRAL_LINK_COUNT,
115                                                             BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT);
116 #endif
117 
118    ...
119 
120     return NRF_SUCCESS;
121 }

2.1.4 实验总结

在低功耗主从机的学习中,我们可以了解到最精简的主从机工程。也就是只包含了softdevice与ble协议栈初始化、以及电源管理初始化。

重点问题:

1.了解如何定义一个BLE工程是主机还是从机,或者其他的属性(主从一体等等)。

2.了解如何进行低功耗处理(main()函数中while循环的idle_state_handle空闲任务处理函数)。

2.2 LOG打印实验

2.2.1 实验简介

LOG打印实验是在低功耗实验的基础上,新增了LOG打印部分。

那么为什么我们需要添加log功能,主要是因为在开发的过程中,我们几乎很难一次调通我们需求的功能,这个时候我们就需要一种好的方式去帮助我们调通,我们出现问题点在哪(查找bug),以及我们的流程进行到哪边了(流程监视)。说到这里,大家会说为什么不使用在线调试的方式解决问题呢,下面谈一谈我个人的使用感觉(仅针对BLE)。

何时使用在线调试:

1.针对细小的问题点,例如某个参数的数值是否正确

2.针对不影响程序整机蓝牙功能运行的问题点,例如外设功能异常

何时使用log:

1.不能使用在线调试功能的时候(例如蓝牙连接状态下的通信调试,由于连接参数的限制,如果在线打断点调试,会导致异常断开)

2.监测关键节点信息(有些问题会出现在一些特殊情况下,可能需要监测N多次,才会出现一次异常)

注意:我们的LOG工程,仅针对RTT部分,没有介绍UART

2.2.2 实验现象

2.2.3 工程及源码讲解

2.2.3.1 主机部分

log打印实验相对于前一章节的低功耗实验,新增的功能并不多,我们仅仅添加了LOG的RTT打印功能,这边

2.2.3.2 从机部分

2.2.4 实验总结

2.3 通用扫描实验

2.3.1 实验简介

2.3.2 实验现象

2.3.3 工程及源码讲解

2.3.4 实验总结

2.4 过滤扫描实验

2.4.1 实验简介

2.4.2 实验现象

2.4.3 工程及源码讲解

2.4.4 实验总结

2.5 白名单扫描实验

2.5.1 实验简介

2.5.2 实验现象

2.5.3 工程及源码讲解

2.5.4 实验总结

2.6 通用连接实验

2.6.1 实验简介

2.6.2 实验现象

2.6.3 工程及源码讲解

2.6.4 实验总结

2.7 过滤连接实验

2.7.1 实验简介

2.7.2 实验现象

2.7.3 工程及源码讲解

2.7.4 实验总结

2.8 连接参数更新实验

2.8.1 实验简介

2.8.2 实验现象

2.8.3 工程及源码讲解

2.8.4 实验总结

2.9 MTU更新实验

2.9.1 实验简介

2.9.2 实验现象

2.9.3 工程及源码讲解

2.9.4 实验总结

2.10 NUS服务获取实验

2.10.1 实验简介

2.10.2 实验现象

2.10.3 工程及源码讲解

2.10.4 实验总结

2.11 NUS通信实验

2.11.1 实验简介

2.11.2 实验现象

2.11.3 工程及源码讲解

2.11.4 实验总结