510
个编辑
更改
→源码讲解
我们将万用表串联到电路中,并且打到电流档,此时我们可以看到功耗如下。
==== 源码讲解 工程及源码讲解 ==== ===== 主机部分 ===== ====== main()函数 ======首先我们查看一下main.c文件,在此文件的mian()函数,在mian函数中,我们首先初始化了电源管理模块,然后初始化了BLE栈堆,最后在while大循环中我们调用空闲状态处理的函数。函数中,我们首先初始化了电源管理模块,然后初始化了BLE栈堆,最后在while大循环中我们调用空闲状态处理的函数。 接下来我们分别针对这三个部分进行介绍。<syntaxhighlight lang="c" line="1" start="170">
//******************************************************************
// fn : main
}
</syntaxhighlight>
====== 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的事件返回。<syntaxhighlight lang="c" line="1" start="102">
//******************************************************************
// fn : ble_stack_init
//
// brief : 用于初始化BLE协议栈
// details : 初始化SoftDevice、BLE事件中断
//
// param : none
//
// return : none
static void ble_stack_init(void)
{
ret_code_t err_code;
// SD使能请求,配置时钟,配置错误回调,中断(中断优先级栈堆默认设置)
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// SD默认配置(如下),SD RAM起始地址配置(0x200029e0)
// 作为从机时的最大连接数量0
// 作为主机时的最大连接数据1(本工程是主机)
// 初始化MTU大小23
// 供应商特定的UUID数量1
// 属性表大小248(必须是4的倍数,以字节为单位)
// 配置服务更改特性数量0
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
// 使能BLE栈堆
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// 注册BLE事件的处理程序,所有BLE的事件都将分派ble_evt_handler回调
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
</syntaxhighlight>ble_evt_handler回调函数,用于处理BLE的事件回调,包含了COMMON、GAP、GATT Client、GATT Server、L2CAP等多种事件类型。在这个例程中,我们只给大家保留了最基础的两个GAP状态,分别为连接状态和断开连接状态。<syntaxhighlight lang="c" line="1" start="68">
//******************************************************************
// fn : ble_evt_handler
//
// brief : BLE事件回调
// details : 包含以下几种事件类型:COMMON、GAP、GATT Client、GATT Server、L2CAP
//
// param : ble_evt_t 事件类型
// p_context 未使用
//
// return : none
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
switch (p_ble_evt->header.evt_id)
{
// 连接
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected. conn_handle: 0x%x",p_gap_evt->conn_handle);
break;
// 断开连接
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected. conn_handle: 0x%x, reason: 0x%x",
p_gap_evt->conn_handle,
p_gap_evt->params.disconnected.reason);
break;
default:
break;
}
}
</syntaxhighlight>
====== power_management_init()及idle_state_handle()函数 ======
power_management_init()函数调用底层的nrf_pwr_mgmt_init()函数去初始化电源管理的部分。
idle_state_handle()函数调用底层的nrf_pwr_mgmt_run()函数,用于处理空闲状态的功能(处理完所有的挂起事件,然后进入休眠,直到下一个事件发生)。<syntaxhighlight lang="c" line="1" start="138">
//******************************************************************
// fn : power_management_init
//
// brief : 初始化电源管理
//
// param : none
//
// return : none
static void power_management_init(void)
{
ret_code_t err_code;
err_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(err_code);
}
//******************************************************************
// fn : idle_state_handle
//
// brief : 处理空闲状态的功能(用于主循环)
// details : 处理任何挂起的日志操作,然后休眠直到下一个事件发生
//
// param : none
//
// return : none
static void idle_state_handle(void)
{
if (NRF_LOG_PROCESS() == false)
{
nrf_pwr_mgmt_run();
}
}
</syntaxhighlight>
===== 从机部分 =====
====== ble_stack_init()函数 ======
从机部分大体上是和主机一样的,在nordic的协议栈例程中,(如果大家对BLE协议有一定的了解或者使用过其他厂家的BLE芯片)我们可以发现,nordic为了简化BLE的开发难度,可谓是不择手段,他减掉了很多的ble协议相关的内容(这里的减掉指的是放到底层处理,不需要开发者去配置),这其中就包含了GAP Role,也就是蓝牙的角色。
ble_satck_init()函数在主机与从机中,唯一不同的点在初始化参数配置。<syntaxhighlight lang="c" line="1" start="99">
//******************************************************************
// fn : ble_stack_init
//
// brief : 用于初始化BLE协议栈
// details : 初始化SoftDevice、BLE事件中断
//
// param : none
//
// return : none
static void ble_stack_init(void)
{
ret_code_t err_code;
// SD使能请求,配置时钟,配置错误回调,中断(中断优先级栈堆默认设置)
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// SD默认配置(如下),SD RAM起始地址配置(0x20002a98)
// 作为从机时的最大连接数量1(本工程为从机)
// 作为主机时的最大连接数据0
// 初始化MTU大小23
// 供应商特定的UUID数量1
// 属性表大小248(必须是4的倍数,以字节为单位)
// 配置服务更改特性数量0
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
// 使能BLE栈堆
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// 注册BLE事件的处理程序,所有BLE的事件都将分派ble_evt_handler回调
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
</syntaxhighlight>主机初始化时设置的是作为主机时最大连接数量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外部设备为从机(广播等待被连接的设备)。<syntaxhighlight lang="c" line="1" start="103">
ret_code_t nrf_sdh_ble_default_cfg_set(uint8_t conn_cfg_tag, uint32_t * p_ram_start)
{
uint32_t ret_code;
...
// Configure the connection roles.
memset(&ble_cfg, 0, sizeof(ble_cfg));
ble_cfg.gap_cfg.role_count_cfg.periph_role_count = NRF_SDH_BLE_PERIPHERAL_LINK_COUNT;
#ifndef S112
ble_cfg.gap_cfg.role_count_cfg.central_role_count = NRF_SDH_BLE_CENTRAL_LINK_COUNT;
ble_cfg.gap_cfg.role_count_cfg.central_sec_count = MIN(NRF_SDH_BLE_CENTRAL_LINK_COUNT,
BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT);
#endif
...
return NRF_SUCCESS;
}
</syntaxhighlight>
==== 实验总结 ====
在低功耗主从机的学习中,我们可以了解到最精简的主从机工程。也就是只包含了softdevice与ble协议栈初始化、以及电源管理初始化。
重点问题:
1.了解如何定义一个BLE工程是主机还是从机,或者其他的属性(主从一体等等)。
2.了解如何进行低功耗处理(main()函数中while循环的idle_state_handle空闲任务处理函数)。
=== LOG打印实验 ===
==== 实验简介 ====
LOG打印实验是在低功耗实验的基础上,新增了LOG打印部分。
那么为什么我们需要添加log功能,主要是因为在开发的过程中,我们几乎很难一次调通我们需求的功能,这个时候我们就需要一种好的方式去帮助我们调通,我们出现问题点在哪(查找bug),以及我们的流程进行到哪边了(流程监视)。说到这里,大家会说为什么不使用在线调试的方式解决问题呢,下面谈一谈我个人的使用感觉(仅针对BLE)。
何时使用在线调试:
1.针对细小的问题点,例如某个参数的数值是否正确
2.针对不影响程序整机蓝牙功能运行的问题点,例如外设功能异常
何时使用log:
1.不能使用在线调试功能的时候(例如蓝牙连接状态下的通信调试,由于连接参数的限制,如果在线打断点调试,会导致异常断开)
2.监测关键节点信息(有些问题会出现在一些特殊情况下,可能需要监测N多次,才会出现一次异常)
{{Note|text=注意:我们的LOG工程,仅针对RTT部分,没有介绍UART|type=warning}}
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ===== 主机部分 =====log打印实验相对于前一章节的低功耗实验,新增的功能并不多,我们仅仅添加了LOG的RTT打印功能,这边 ===== 从机部分 ===== ==== 实验总结 ====
=== 通用扫描实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== 过滤扫描实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== 白名单扫描实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== 通用连接实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== 过滤连接实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== 连接参数更新实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== MTU更新实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== NUS服务获取实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====
=== NUS通信实验 ===
==== 实验现象 ====
==== 源码讲解 工程及源码讲解 ==== ==== 实验总结 ====