如何调试nRF5 SDK

本文将讲述Nordic nRF5 SDK的主要调试手段,以帮助大家快速定位问题,并解决问题。

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

nRF5 SDK支持UART和SWD J-Link两种底层通信方式来打印日志,其中UART使用串口助手来查看日志,SWD J-Link使用J-Link RTT Viewer来查看打印日志。由于UART日志打印方式会占用一个UART口,而大部分nRF5芯片都只有一个UART口,从而导致资源冲突,为此推荐大家使用RTT方式来打印日志,从而可以将UART口留给正常应用。J-Link RTT Viewer配置方式如下所示:

 

RTT viewer日志打印窗口如下所示:

 

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

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

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

 

Putty打印窗口如下所示:

 

为了使能日志打印功能,每个版本的SDK配置稍有不同。

1.1 nRF5 SDK v11.0.0及以前版本

你需要到options for target->C/C++->define里面定义一个宏,如果定义“NRF_LOG_USES_UART=1”选择UART日志打印;如果定义”NRF_LOG_USES_RTT=1” 则选择RTT日志打印,如下:

 

1.2 nRF5 SDK v12.0.0及以后版本

从SDK12开始,使能日志打印功能,就不需要到Keil的C/C++页面,所有关于日志打印的配置都在sdk_config.h文件中,一般来说,你需要配置三个选项:一打开还是关闭log功能,二后端使用UART还是RTT,或者同时选择二者(从SDK14开始,可以同时通过UART和RTT把日志打印出来),三日志的级别以及SDK各模块日志单独控制。如下:

 

 

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

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

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

 

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

 

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函数定义,你会发现APP_ERROR_CHECK受宏DEBUG控制,当没有定义DEBUG宏时,系统将直接产生软复位;当定义了DEBUG宏时,系统将把错误信息显示在call stack中,比如是哪个文件哪一行出错,返回的错误码是什么,如下所示:

 

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

 

最后附上APP_ERROR_CHECK函数体代码:

__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
    __disable_irq();
    NRF_LOG_FINAL_FLUSH();

#ifndef DEBUG
    NRF_LOG_ERROR("Fatal error");
#else
    switch (id)
    {
#if defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT
        case NRF_FAULT_ID_SD_ASSERT:
            NRF_LOG_ERROR("SOFTDEVICE: ASSERTION FAILED");
            break;
        case NRF_FAULT_ID_APP_MEMACC:
            NRF_LOG_ERROR("SOFTDEVICE: INVALID MEMORY ACCESS");
            break;
#endif
        case NRF_FAULT_ID_SDK_ASSERT:
        {
            assert_info_t * p_info = (assert_info_t *)info;
            NRF_LOG_ERROR("ASSERTION FAILED at %s:%u",
                          p_info->p_file_name,
                          p_info->line_num);
            break;
        }
        case NRF_FAULT_ID_SDK_ERROR:
        {
            error_info_t * p_info = (error_info_t *)info;
            NRF_LOG_ERROR("ERROR %u [%s] at %s:%u\r\nPC at: 0x%08x",
                          p_info->err_code,
                          nrf_strerror_get(p_info->err_code),
                          p_info->p_file_name,
                          p_info->line_num,
                          pc);
             NRF_LOG_ERROR("End of error report");
            break;
        }
        default:
            NRF_LOG_ERROR("UNKNOWN FAULT at 0x%08X", pc);
            break;
    }
#endif

    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
}

猜你喜欢

转载自www.cnblogs.com/iini/p/9279618.html