本文将讲述Nordic nRF5 SDK的主要调试手段,以帮助大家快速定位问题,并解决问题。一般来说,你可以通过打log方式,IDE的debug模式,SDK自带的app_error_check函数,以及命令行方式等多种手段来调试你的代码。

1. 通过打log方式进行调试

nRF5 SDK支持UART和SWD J-Link(RTT)两种底层通信方式来打印日志,SDK14之后日志也可以通过蓝牙或者Flash进行输出和存储打印,一般来说,UART和SWD用得比较多,其中UART使用串口助手来查看日志,SWD使用J-Link RTT Viewer(仅适用Windows)或者J-Link RTT Client(Windows/Mac/Linux系统)来查看打印日志。由于UART日志打印方式会占用一个UART口,而大部分nRF5芯片都只有一个UART口,从而导致资源冲突,为此推荐大家使用RTT方式来打印日志,从而可以将UART口留给正常应用,更重要的是RTT打印方式功耗非常低(几乎可以忽略不计),大家可以在正式release的产品中也使能它,从而发现产品部署后有可能会出现的问题(UART打印方式功耗非常高,在正式release产品中必须关掉它)。

如果使用SWD接口进行日志打印,那么你可以使用J-Link RTT Viewer或者RTTClient来显示日志,二者选其一即可。在Windows平台推荐使用RTT viewer,否则使用RTT Client。

1.1 J-Link RTT Viewer

RTT viewer配置方式如下所示:

RTT viewer日志打印窗口如下所示:(使用的是SDK15.3中的ble_app_hrs例子,下同)

1.2 J-Link RTTClient

RTT client配置方式如下所示(直接使用jlink命令进行配置):

RTT client打印窗口如下所示:

1.3 UART串口助手

串口助手配置方式如下所示:

  • Baud rate: 115200
  • 8 data bits
  • 1 stop bit
  • No parity
  • HW flow control: None

随便选择一款你熟悉的串口助手,比如Putty或者Termite,Termite打印窗口如下所示:

Putty打印窗口如下所示:

1.4 日志打印模块nRF_Log

nRF5 SDK日志打印功能是通过nRF_Log模块实现的(上面展示的日志都是通过nRF_Log打印出来的),SDK包含的大部分例子都自带打印功能,也就是说包含了nRF_Log模块。一般来说,例子都是默认使用UART进行打印的,如果需要改为RTT进行打印,需要对nRF_Log模块进行配置。在具体讲述nRF_Log模块的配置选项之前,先大概讲述一下nRF_Log的工作原理。

nRF_Log工作原理

nRF_Log模块包含前端和后端两部分代码。前端是面向用户的打印接口,比如NRF_LOG_INFO,它把用户要打印的数据放在一块RAM中。后端用来具体实现打印功能,即把前端RAM中的数据通过不同的后端接口打印出去。目前nRF_Log支持的后端接口有:UART,RTT,Flash和蓝牙。不管采用哪一种后端接口,对用户来说,其调用的前端API是一样的,nRF_Log模块会根据用户的配置自动适配相应后端接口,而且用户可以同时使能多个后端接口,即把日志同时打印到多个后端端口上。nRF_Log模块可以单独使能或禁止某一个模块的打印功能,比如advertising模块,当你调试advertising模块的时候,可以把log打开,调试完毕,把log关闭以让log界面变得更清爽。nRF_Log模块还可以设置打印的级别(Level),如果不分级别的话,打印界面会包含很多打印信息,让我们每次调试都要花费很多时间去寻找自己想要的日志上。设定级别后,我们可以有选择的打印需要的日志信息,没问题时,我们只打印info级别的日志;有问题时,我们就可以把所有debug级别的日志都打印出来。nRF_Log还有一个功能:Deferred,如果不使能Deferred,那么调用NRF_LOG_INFO等API的时候,立马就Flush,即把日志打印出去;如果使能了Deferred,那么调用NRF_LOG_INFO等API的时候,只是把打印数据放在RAM中,真正的打印由main函数中的NRF_LOG_PROCESS完成,这样可以最大程度降低打印对应用本身的影响,尤其在执行一些时序很关键的操作的时候。nRF_Log还支持时间戳打印,即在每条日志之前加上时间戳信息。

nRF_Log配置

从SDK12以后,nRF_Log模块的配置主要放在sdk_config.h文件中,以工程nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_hrs\pca10040\s132\arm5_no_packs为例,nRF_Log的配置选项如下所示:

注意:nRF5 SDK v11.0.0及以前版本是没有sdk_config.h文件的,此时你需要到options for target->C/C++->define里面定义一个宏(Keil工程),如果定义“NRF_LOG_USES_UART=1”选择UART日志打印;如果定义”NRF_LOG_USES_RTT=1” 则选择RTT日志打印,如下:

还是以nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_hrs\pca10040\s132\arm5_no_packs为例,当nRF_Log配置为info级别,无timestamp,打印信息如下(与前面配置一样)

当nRF_Log配置为debug级别,打开timestamp,打印信息如下所示:

2. 使用IDE调试界面进行调试

Nordic产品支持单步,断点,寄存器查看,内存查看,call stack查看等所有常规调试手段。需要注意的是,由于softdevice在底层要维持一定的时钟以及处理很多中断事件,一旦蓝牙跑起来后,只能用一个断点进行全速跑,也就是说,执行完一个断点后,程序内部逻辑和时序已经紊乱,此时不能再继续全速跑第二个断点。如果要看第二个断点的内容,只能删掉第一个断点,重新开始执行。

Keil IDE

通过以下方式打开nRF5 SoC内部寄存器查看窗口:

用得比较多的几个Keil窗口:

Segger embedded studio IDE

SES的调试界面如下所示(跟Keil很像):

这里要特别强调一下,所有SES工程都包含2个版本:Release版和Debug版,Release是没有包含debug信息以节省代码空间,所以如果你要debug一个工程,请先选择debug版本,然后再进行debug,如下所示:

3. nRF5 SDK自带的APP_ERROR_CHECK函数

APP_ERROR_CHECK是nRF5 SDK定义的一个用来检查API返回值是否正确的函数,在nRF5 SDK中,NRF_SUCCESS(0)为正确返回值,其它返回值皆为错误值。nRF5 SDK所有协议栈API调用,以及SDK库函数调用,都会用APP_ERROR_CHECK去检查调用的返回值。当出现非法调用时,比如传入的实参不对,API返回值就不会为NRF_SUCCESS,此时APP_ERROR_CHECK就会派上大用场。通过查看APP_ERROR_CHECK函数定义,如下所示:

NRF_BREAKPOINT_COND;

    // On assert, the system can only recover with a reset.

#ifndef DEBUG

    NRF_LOG_WARNING("System reset");

    NVIC_SystemReset();

#else

    app_error_save_and_stop(id, pc, info);

#endif // DEBUG

你会发现APP_ERROR_CHECK行为受宏DEBUG和有没有挂仿真器两个因素的影响,当没有定义DEBUG宏时,系统将直接产生软复位;当定义了DEBUG宏并且没有挂仿真器时,系统将把错误信息保存在call stack中。不管怎么配置,APP_ERROR_CHECK都会把相应的错误信息打印出来,以方便你去排查问题,如下所示。通过打印出来的错误信息,你就可以知道是哪个文件哪一行代码出了什么类型的错误。

注意在SDK14以前,app_error_check不会主动把错误信息打印出来,而是把错误信息存在RAM中,然后通过debug模式可以直接查看相关错误信息,如下所示:

默认情况下,nRF5 SDK是没有定义DEBUG宏的,所以一旦函数返回值不对,系统就会复位。这里要特别指出的是,在你开发调试过程中,经常会碰到复位的情况,这其中大部分都是由APP_ERROR_CHECK引起的。针对这种情况的复位,你只需要在options for target->C/C++->define里面定义一个宏:DEBUG,就可以快速找出是哪一个文件哪一行代码引出的复位,以及复位原因是什么,如下所示:

4.使用命令行(CLI)方式进行调试

打log方式只能单方面输出,而不能接受终端的输入,在很多情况下,我们需要动态调整log信息,比如动态更改log的级别,动态更改log的颜色等等,这个时候就需要用到nrf_cli模块,跟nRF_Log相似,nrf_cli同时支持5种后端接口:UART,RTT,BLE,Flash和USB CDC。由于nrf_cli也有log输出功能,因此nRF_Log模块可以直接选择nrf_cli为其后端接口。这里再次强调一下,nrf_log和nrf_cli是两个完全独立的模块,但是nrf_log可以选用nrf_cli为其后端接口,由nrf_cli完成后端打印功能。SDK中提供了三个cli的例子,分别为:

  • \examples\peripheral\cli\pca10040\blank\arm5_no_packs
  • \examples\ble_peripheral\experimental\ble_app_cli\pca10040\s132\arm5_no_packs
  • \examples\ble_central_and_peripheral\experimental\ble_app_interactive\pca10040\s132\arm5_no_packs

如果大家的应用需要命令行交互方式,可以参考上面3个例子,以examples\peripheral\cli\pca10040\blank\arm5_no_packs为例,交互成功后的界面如下所示:

如何调试nRF5 SDK的更多相关文章

  1. nRF5 SDK软件架构及softdevice工作原理

    本文将介绍Nordic nRF5 SDK软件架构以及softdevice工作原理,以加深大家对Nordic产品开发的理解,这样开发过程中碰到问题时,大家也知道如何去调试. 如果你刚开始接触nRF5 S ...

  2. nRF5 SDK for Mesh(四) 源码编译

    官方文档教程编译源码: http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk%2Fdita%2Fs ...

  3. nRF5 SDK for Mesh(一) 介绍和下载源码

    一: 官网介绍地址:http://www.nordicsemi.com/Products/Bluetooth-low-energy/nRF5-SDK-for-Mesh Nordic offers a ...

  4. nRF5 SDK for Mesh(三) Installing the mesh toolchain 安装编译工具链

    Installing the mesh toolchain To build the example applications, a toolchain based on either CMake o ...

  5. nRF5 SDK for Mesh(二) Getting started 快速开始

    Getting started To get started, take a look at the Light switch demo. It shows how a simple applicat ...

  6. Nordic nRF5 SDK和softdevice介绍

    SDK和Softdevice的区别是什么?怎么选择SDK和softdevice版本?芯片,SDK和softdevice有没有版本兼容问题?怎么理解SDK目录结构?SDK帮助文档在哪里?Softdevi ...

  7. 转-nRF5 SDK for Mesh(六) BLE MESH 的 基础概念

    nRF5 SDK for Mesh(六) BLE MESH 的 基础概念 Basic Bluetooth Mesh concepts The Bluetooth Mesh is a profile s ...

  8. nRF5 SDK for Mesh(五) Light switch demo 点灯例子

    Light switch demo  灯开demo   Purpose This demo project consists of four sub examples - The light swit ...

  9. Flash访问模块FDS用法及常见问题—nRF5 SDK模块系列一

    FDS,全称Flash Data Storage,用来访问芯片内部Flash的.当你需要把数据存储在Flash中,或者读取Flash中的用户数据,或者更新或者删除Flash中的数据,那么FDS模块是你 ...

随机推荐

  1. 使用selenium找出外卖点餐次数最多的10个顾客

    大锅在做外卖,给我说能否统计出这半年点餐次数最多的10个顾客,我不知道APP本身是否有这个功能,想了下最近用selenium较多,就用selenium尝试下吧. 1 定义一个类,这里先描述需要的属性和 ...

  2. Java 运算符及优先级

    运算符 分割符: , ; [] () 算数运算符: + - * / % ++ -- 关系运算符: > < >= <= == != 逻辑运算符: ! & | ^ & ...

  3. python学习笔记(六)time、datetime、hashlib模块

    一.hashlib模块 python中的hashlib模块用来进行hash或者md5加密,而且这种加密是不可逆的,所以这种算法又被称为摘要算法.在python3的版本里,代替了md5和sha模块,主要 ...

  4. LNMP环境修改上传文件大小

    LNMP环境修改上传文件大小限制     如果网页上传文件大小大于nginx或php设定的值,就会造成无法上传,显示IO error错误. 如何解决这个问题呢?我们需要更改两个配置: 1.  ngin ...

  5. 在虚拟机中还原GHO镜像系统

    前置知识 本文精简干练,全是干货.首先要知道,一个原版的Windows系统下载下来就是一个iso格式的文件,也被称为一个镜像.另外还有一种镜像是使用ghost软件制作的,格式为gho的镜像,而ghos ...

  6. [Python] logging.logger

    <1>. mylogger = logging.getLogger("abc") logging.debug()/logging.info()/logging.warn ...

  7. 微信小程序组件navigator

    导航navigator:官方文档 Demo Code: // redirect.js navigator.js Page({ onLoad: function(options) { this.setD ...

  8. 下一个亿万市场:企业级SaaS服务谁能独领风骚

    注:SaaS是Software-as-a-Service(软件即服务)的简称,一种完全创新的软件应用模式,简单来说SaaS即为提供商基于互联网为企业提供软件服务. ​对中小型企业来说:SaaS是采用先 ...

  9. hdu2609 How many

    地址:http://acm.hdu.edu.cn/showproblem.php?pid=2609 题目: How many Time Limit: 2000/1000 MS (Java/Others ...

  10. java.lang.OutOfMemoryError: PermGen space异常及解决

    PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被 ...