2.监测关键节点信息(有些问题会出现在一些特殊情况下,可能需要监测N多次,才会出现一次异常)
{{Note|text=注意:我们的LOG工程,仅针对RTT部分,没有介绍UART注意:我们的LOG工程,仅针对RTT部分,没有介绍UART,UART的使用大家可以查看基础实验部分|type=warning}}
==== 实验现象 ====
我们打开J-Link RTT Viewer,选择我们的Jlink仿真器,可以看到log打印如下。
[[文件:Nrf rtt 11.png|边框|居中|无框|656x656像素]]
==== 工程及源码讲解 ====
===== 主机部分 =====
log打印实验相对于前一章节的低功耗实验,新增的功能并不多,我们仅仅添加了LOG的RTT打印功能,这边====== 工程说明 ======我们在工程中,新增加了nRF_Log与nRF_Segger_RTT分组,这两个分组中分别包含了log打印的相关函数,以及RTT的相关函数。{{Note|text=使用RTT,可以从目标微控制器输出信息,并以非常高的速度向应用程序发送输入,而不会影响目标的实时行为。 SEGGER RTT可与任何J-Link模型和任何支持目标处理器一起使用,后者允许后台存储器访问,即Cortex-M和RX目标。 RTT在两个方向上支持多个通道,直到主机和目标,可以用于不同的目的,并为用户提供最大的自由。 默认实现每个方向使用一个通道,用于可打印的终端输入和输出。使用J-Link RTT Viewer,该通道可用于多个“虚拟”终端,允许使用一个目标缓冲区打印到多个窗口(例如一个用于标准输出,一个用于错误输出,一个用于调试输出)。例如,可以使用附加的up(到主机)通道来发送分析或事件跟踪数据。|type=info}} ====== main()函数 ======log打印实验相对于前一章节的低功耗实验,新增的功能并不多,我们仅仅添加了LOG的RTT打印功能,这边我们首先还是看下mian函数。 可以看到,我们新增了log_init()的LOG初始化函数。<syntaxhighlight lang="c" line="1" start="188">//******************************************************************// fn : main//// brief : 主函数//// param : none//// return : noneint main(void){ // 初始化 log_init(); // 初始化LOG打印,由RTT工作 power_management_init();// 初始化电源控制 ble_stack_init(); // 初始化BLE栈堆 // 打印例程名称 NRF_LOG_INFO("1.1_ble_central_log"); // 进入主循环 for (;;) { idle_state_handle(); // 空闲状态处理 }}</syntaxhighlight> ====== log_init()函数以及底层调用 ======我们查看到log_init()函数,先调用NRF_LOG_INIT()函数初始化LOG,然后我们调用NRF_LOG_DEFAULT_BACKENDS_INIT()函数来决定LOG的底层调用。<syntaxhighlight lang="c" line="1" start="140">//******************************************************************// fn : log_init//// brief : 初始化log打印//// param : none//// return : nonestatic void log_init(void){ ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT();}</syntaxhighlight>接下来我们追踪NRF_LOG_DEFAULT_BACKENDS_INIT()函数,在这个函数中,我们会引用到RTT功能。 首先我们go to define,找到NRF_LOG_DEFAULT_BACKENDS_INIT()函数的定义,sdk_config.h中我们定义了NRF_LOG_ENABLED为1,也就是使能LOG打印。<syntaxhighlight lang="C" line="1" start="61">/** * @def NRF_LOG_DEFAULT_BACKENDS_INIT * @brief Macro for initializing default backends. * * Each backend enabled in configuration is initialized and added as a backend to the logger. */#if NRF_LOG_ENABLED#define NRF_LOG_DEFAULT_BACKENDS_INIT() nrf_log_default_backends_init()#else#define NRF_LOG_DEFAULT_BACKENDS_INIT()#endif</syntaxhighlight>接下来我们追踪nrf_log_default_backends_init()函数,在sdk_config.h中,定义了NRF_LOG_BACKEND_RTT_ENABLED为1,也就是使能了RTT打印。 在这个地方,我们会调用nrf_log_backend_rtt_init()函数初始化RTT,然后调用nrf_log_backend_add()添加新的后端接口,并调用nrf_log_backend_enable()将这个新的后端接口使能。 这样处理之后,我们便可以使用面向RTT的log功能了。{{Note|text=RTT部分的源码,这个大家有兴趣的可以自行了解,我们使用RTT功能的时候,只需要将RTT分组下的SEGGER_RTT_XX.c的三个文件添加到工程即可,不一定要了解这个源码|type=warning}}<syntaxhighlight lang="c" line="1" start="58">void nrf_log_default_backends_init(void){ int32_t backend_id = -1; (void)backend_id;#if defined(NRF_LOG_BACKEND_RTT_ENABLED) && NRF_LOG_BACKEND_RTT_ENABLED nrf_log_backend_rtt_init(); backend_id = nrf_log_backend_add(&rtt_log_backend, NRF_LOG_SEVERITY_DEBUG); ASSERT(backend_id >= 0); nrf_log_backend_enable(&rtt_log_backend);#endif #if defined(NRF_LOG_BACKEND_UART_ENABLED) && NRF_LOG_BACKEND_UART_ENABLED nrf_log_backend_uart_init(); backend_id = nrf_log_backend_add(&uart_log_backend, NRF_LOG_SEVERITY_DEBUG); ASSERT(backend_id >= 0); nrf_log_backend_enable(&uart_log_backend);#endif}</syntaxhighlight> ====== NRF_LOG_XX()函数说明 ======上面说明了如何去初始化LOG向RTT打印的功能,下面我们将给大家介绍一下LOG打印的函数,毕竟这个才是我们真正要用到的部分。 LOG打印的函数一共有4个,分别为打印ERROR、WARNING、INFO、DEBUG。他们的功能看字面意思就可以明白,分别是打印错误、警告、用户信息、调试信息。<syntaxhighlight lang="c" line="1" start="111">#define NRF_LOG_ERROR(...) NRF_LOG_INTERNAL_ERROR(__VA_ARGS__)#define NRF_LOG_WARNING(...) NRF_LOG_INTERNAL_WARNING( __VA_ARGS__)#define NRF_LOG_INFO(...) NRF_LOG_INTERNAL_INFO( __VA_ARGS__)#define NRF_LOG_DEBUG(...) NRF_LOG_INTERNAL_DEBUG( __VA_ARGS__)</syntaxhighlight>底层的打印函数大家自己go to define查看,我这边给大家各举一个例子,方便大家使用。 其实他们的使用方式,都是和printf一样的,只是打印消息的级别不同而已。printf函数的使用大家不清楚的,可以百度查看一下。<syntaxhighlight lang="c" line="1">NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GAP.", nrf_strerror_get(ret_code)); NRF_LOG_WARNING("Change the RAM start location from 0x%x to 0x%x.", app_ram_start_link, *p_app_ram_start); NRF_LOG_INFO("Shutdown started. Type %d", m_pwr_mgmt_evt); NRF_LOG_DEBUG("BLE event: 0x%x.", p_ble_evt->header.evt_id);</syntaxhighlight>上面一段话和大家说了nordic协议栈中4个标准的LOG打印函数,他们其实拥有相同的功能,但是打印的级别不同(这个级别是认为规定的),我们我们如何控制这个打印的级别呢,也是在我们的sdk_config.h中,大家可以看到NRF_LOG_DEFAULT_LEVEL。默认的只能打印到info,如果大家需要使用debug打印,需要修改此处的值为4。<syntaxhighlight lang="c" line="1" start="7569">// <o> NRF_LOG_DEFAULT_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_LOG_DEFAULT_LEVEL#define NRF_LOG_DEFAULT_LEVEL 3#endif</syntaxhighlight>
===== 从机部分 =====
从机部分与主机部分新增log功能相同,这边不再赘述。
==== 实验总结 ====
LOG打印实验,大家需要掌握的三个要点如下:
1.了解什么是RTT。
2.了解nordic协议栈中LOG面向RTT功能的初始化、及打印函数的使用。
3.要善于使用log功能,这样有利于我们程序的流程开发及异常问题的查找和处理。
=== 通用扫描实验 ===
==== 实验简介 ====
通用扫描实验给大家带来的是主机的扫描功能展示,以及从机的广播功能展示。
大家都清楚,低功耗蓝牙主从机间交互数据的方式一般来说是有两种,一种是连接之后通信(这个是蓝牙的主要功能),另一种就是本实验带来的主机扫描获取从机的广播数据。而上述的两种方式,不管是哪一种,都是需要扫描功能的,毕竟连接通信之前,我们的主机也是需要扫描到从机设备才可以发起连接。
==== 实验现象 ====