陈拓 2022/07/28-2022/11/04

1. 概述

在《WSL构建nRF5 SDK + ARM GCC开发环境》

https://zhuanlan.zhihu.com/p/544907537

https://blog.csdn.net/chentuo2000/article/details/125933307?spm=1001.2014.3001.5502

一文中我们已经构建了nRF5 SDK + ARM GCC的基本开发环境。

本文我们用RTT打印调试日志。

nRF5芯片只有一个串口,如果串口已经被使用,或者手头的板子上串口没有引出,这时可以使用JLink仿真器的RTT输出调试信息。

在上文中我们为了兼顾以前的项目安装了nRF5 SDK 14.2,现在我们已经升级到了nRF5_SDK_17.1.0_ddde560。

2. 编译和烧写项目

我们以ble_app_blinky为例:

~/nrf/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_blinky

2.1 设置sdk_config.h

修改sdk_config.h文件。

~/nrf/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_blinky/pca10040/s132/config/sdk_config.h

  • 使能NRF日志NRF_LOG_ENABLED
#ifndef NRF_LOG_ENABLED
#define NRF_LOG_ENABLED 1
#endif
  • 使能RTT日志NRF_LOG_BACKEND_RTT_ENABLED
#ifndef NRF_LOG_BACKEND_RTT_ENABLED
#define NRF_LOG_BACKEND_RTT_ENABLED 1
#endif
  • 禁用串口输出日志NRF_LOG_BACKEND_UART_ENABLED
#ifndef NRF_LOG_BACKEND_UART_ENABLED
#define NRF_LOG_BACKEND_UART_ENABLED 0
#endif

2.2 初始化RTT打印功能

在主函数main中,第一步就是初始化打印日志功能 log_init()。

int main(void)
{// Initialize.log_init();

log_init()函数

static void log_init(void)
{ret_code_t err_code = NRF_LOG_INIT(NULL);APP_ERROR_CHECK(err_code);NRF_LOG_DEFAULT_BACKENDS_INIT();
}

初始化之后就可以用RTT打印了,像这样:

    NRF_LOG_INFO("Blinky example started.");

2.3 编译项目

cd ~/nrf/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_blinky/pca10040/s132/armgcc

编译:make

cc1: all warnings being treated as errors错误。

在Makefile文件中找到CFLAGS += -Wall -Werror

行首加#号注释掉:

#CFLAGS += -Wall -Werror

再编译:

2.4 烧写hex文件

  • 清除memory

nrfjprog -f NRF52 --eraseall

  • 下载烧录项目的hex文件

nrfjprog -f nrf52 --program nrf52832_xxaa.hex -sectorerase --verify

  • 重启nRF52832

nrfjprog -f nrf52 --reset

  • 下载烧录蓝牙协议栈

和上文中的项目不同的是本项目需要有协议栈的支持,协议栈所在的目录:

~/nrf/nRF5_SDK_17.1.0_ddde560/components/softdevice/s132/hex/s132_nrf52_7.2.0_softdevice.hex

要注意的是协议栈的版本和SDK版本以及芯片型号是配套的。

nrfjprog -f nrf52 --program s132_nrf52_7.2.0_softdevice.hex -sectorerase --verify

重启nRF52832

nrfjprog -f nrf52 --reset

3. 用JLink仿真器的RTT打印Log信息

3.1 RTT日志的打印方式

在程序中RTT使用NRF_LOG_INFO函数打印日志信息,和串口打印语句printf不同,RTT的NRF_LOG_INFO语句是否立即输出要在sdk_config.h文件中进行设置:

// <q> NRF_LOG_DEFERRED  - Enable deffered logger.// <i> Log data is buffered and can be processed in idle.#ifndef NRF_LOG_DEFERRED
#define NRF_LOG_DEFERRED 1
#endif

默认设置为NRF_LOG_DEFERRED 1

这时NRF_LOG_INFO语句并不立即输出打印,而是把打印数据放在RAM中,真正的打印由main函数中的NRF_LOG_PROCESS完成。

在官方的例程中是在CPU空闲时调用NRF_LOG_PROCESS函数打印,代码如下:

  • main函数
int main(void)
{// Initialize.log_init();leds_init();timers_init();buttons_init();power_management_init();ble_stack_init();gap_params_init();gatt_init();services_init();advertising_init();conn_params_init();// Start execution.NRF_LOG_INFO("Blinky example started.");advertising_start();// Enter main loop.for (;;){idle_state_handle();}
}
  • idle_state_handle函数
/**@简要说明 处理空闲状态的函数 (main loop)。 ** @详细说明 如果没有在等待的日志操作,则休眠到下一个事件发生。*/
static void idle_state_handle(void)
{if (NRF_LOG_PROCESS() == false){nrf_pwr_mgmt_run();}
}

语句NRF_LOG_PROCESS()执行打印输出。

这种打印策略可以减少在程序执行过程中打印输出对于程序正常运行的影响,因为打印输出是要耗费时间的。

3.2 日志级别

上面我们在程序中使用NRF_LOG_INFO函数打印输出调试信息。为了使打印的日志更具有针对性,比如只打印错误部分日志,或者只打印警告部分日志,便有了日志分级。

日志分5个级别,在sdk_config.h中设置:

// <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

<0=> Off:关闭日志输出

<1=> Error:输出错误信息,对应的Log输出函数为NRF_LOG_ERROR

<2=> Warning:输出警告信息,对应的Log输出函数为NRF_LOG_WARNING

<3=> Info:  输出基本信息,对应的Log输出函数为NRF_LOG_INFO

<4=> Debug:输出调试信息,对应的Log输出函数为NRF_LOG_DEBUG

高级别的日志输出包含低级别的输出:

如程序中的默认设置NRF_LOG_DEFAULT_LEVEL 3,这时1、2、3级别的日志就会输出。

如果设置NRF_LOG_DEFAULT_LEVEL 4,那么所有4个级别的日志都会输出。

3.3 用J-Link RTT Viewer打印信息

J-Link RTT的3个工具都可以用,我们用J-Link RTT Viewer。

点开J-Link RTT Viewer之后先进行设置:

OK

RTT LOG显示RTC初始化成功,之后的程序出现错误,我们不知道问题出在哪里。下面我们借助RTT来找出问题所在。

4. 让RTT打印程序出错的文件和行号

4.1 APP_ERROR_CHECK函数

APP_ERROR_CHECK是nRF5 SDK定义的一个用来检查API返回值是否正确的函数,在nRF5 SDK中,NRF_SUCCESS(0)为正确返回值,其它返回值皆为错误值。

  • 查看APP_ERROR_CHECK函数定义

在VSCode中用鼠标右击APP_ERROR_CHECK > 转到定义

查看APP_ERROR_CHECK的代码:

/**@摘要 如果提供的错误代码不是NRF_SUCCESS (0),则调用错误处理函数的宏。** @参数[in] ERR_CODE 提供给错误处理程序的错误代码。*/
#define APP_ERROR_CHECK(ERR_CODE)                           \do                                                      \{                                                       \const uint32_t LOCAL_ERR_CODE = (ERR_CODE);         \if (LOCAL_ERR_CODE != NRF_SUCCESS)                  \{                                                   \APP_ERROR_HANDLER(LOCAL_ERR_CODE);              \}                                                   \} while (0)

APP_ERROR_CHECK调用了错误处理程序函数的宏APP_ERROR_HANDLER。

用同样的方法找到APP_ERROR_HANDLER

/**@brief 用于调用错误处理函数的宏。** @param[in] ERR_CODE 提供给错误处理程序的错误代码。*/
#ifdef DEBUG
#define APP_ERROR_HANDLER(ERR_CODE)                                    \do                                                                 \{                                                                  \app_error_handler((ERR_CODE), __LINE__, (uint8_t*) __FILE__);  \} while (0)
#else
#define APP_ERROR_HANDLER(ERR_CODE)                                    \do                                                                 \{                                                                  \app_error_handler_bare((ERR_CODE));                            \} while (0)
#endif

如果定义了DEBUG就调用错误处理程序app_error_handler,它会显示行号。否则调用app_error_handler_bare,不显示行号,就像上面看到的那样。

为了调用app_error_handler,我们要在Makefile里设置一下。

4.2 让APP_ERROR_CHECK显示错误码、文件名和出错行号

在Makefile文件中找到CFLAGS的最后一行,添加一行:CFLAGS += -DDEBUG

编译烧写。

之后需要将J-Link RTT Viewer断开重新连接一下:

File > Disconnect

File > Disconnect

这样就可以看到新的信息了,错误码、文件名和行号都有了。

错误码7表示无效参数。

我们看183行前面一条出错的语句:

sd_ble_gap_device_name_set函数有3个参数:

&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)

其中sec_mode定义如下:

    ble_gap_conn_sec_mode_t sec_mode;BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

DEVICE_NAME定义如下:

strlen(DEVICE_NAME)是DEVICE_NAME长度长度。

这些都是官方例程中的代码,不应有错。

5. 消除编译器引起的错误

同样的的代码在Win10系统Keil中编译时没问题。看来问题出在不同的编译器对代码的处理不同。这种错误可能和编译器的优化有关,我们关闭编译器的优化试试。

在Makefile中优化设置如下:

# Optimization flags
OPT = -O3 -g3

5.1 GCC优化选项

-O0:默认的优化选项,减少编译时间和生成完整的调试信息。

-O/-O1:这两个都是开启level 1的编译优化。开启编译优化会导致更长的编译时间,对于大函数还会消耗更多的内存空间。level1的编译优化下,编译器会尝试减少代码段大小和优化程序的执行时间,但不执行需要消耗大量编译时间的优化。

-O2:相比于-O1,-O2打开了更多的编译优化开关

-O3:在-O2的基础上,level 3级别优化

-Os:优化生成的目标文件的大小

-Ofast:为了提高程序的执行速度,GCC可以无视严格的语言标准。-Ofast会开启所有-O3的编译开关,且会对不符合标准的程序进行优化

-Og:优化调试信息。相对于-O0生成的调试信息,-Og是为了能够生成更好的调试信息。和-O0一样,-Og选项关闭了很多优化开关

-g0:不生成调试信息,相当于没有使用-g

-g1:生成最小的调试信息,足够在不打算调试的程序中进行堆栈查看。最小调试信息包括函数描述,外部变量,行数表,但不包括局部变量信息

-g2:默认-g的调试级别

-g3:相对-g,生成额外的信息,例如所有的宏定义

5.2 修改Makefile的优化级别

将3级优化-O3改为-O0:

OPT = -O0 -g3

再编译烧写测试:

可以了。

如果想只对特定的代码设置优化级别可以参考下面的链接:

https://devzone.nordicsemi.com/f/nordic-q-a/88778/wierd-error-7-happening-in-init

6. 获取蓝牙的MAC地址

下面的内容来自后面列出的参考文档1。

6.1 添加代码

在main.c中添加。

  • 头文件
#include "ble_gap.h"

在main函数中添加下面的代码:

    // 读取蓝牙MAC地址。头文件#include "ble_gap.h"ble_gap_addr_t bleAddr; // 定义结构体变量sd_ble_gap_addr_get(&bleAddr); // 获取MAC地址uint8_t address[6];uint8_t i;for(i = 0; i < 6; i++) {address[i] = bleAddr.addr[5 - i];}NRF_LOG_INFO("MAC Address %02x:%02x:%02x:%02x:%02x:%02x", address[0], address[1], address[2], address[3], address[4], address[5]);

代码的位置:

6.2 编译烧写测试

显示MAC地址:

6.3 用nRF Connect查看MAC地址

NORDIC的工具软件nRF Connect可以用来测试蓝牙的广播和连接。有PC版和移动版。我们在手机上用移动版测试。

  • iphone手机

  • 安卓手机

对比可以看出苹果手机只显示设备名称Nordic_Blinky,不显示MAC地址。

7. J-Link RTT Viewer多虚拟终端

见《嵌入式芯片调试神器-J-Link RTT详解》

https://blog.csdn.net/suxiang198/article/details/126534730

  • 示例代码
int main()
{// 初始化RTTSEGGER_RTT_Init();// 进入主循环while (1) {// 设置虚拟端口0,并输出日志,如果不设置,默认都会用Terminal 0SEGGER_RTT_SetTerminal(0);SEGGER_RTT_printf(0, "Hello, SEGGER RTT Terminal 0!\r\n");// 设置虚拟端口1,并输出日志SEGGER_RTT_SetTerminal(1);SEGGER_RTT_printf(0, "Hello, SEGGER RTT Terminal 1!\r\n");// 设置虚拟端口2,并输出日志SEGGER_RTT_SetTerminal(2);SEGGER_RTT_printf(0, "Hello, SEGGER RTT Terminal 2!\r\n");Delay_ms(1000);// 另外可以输出在RTT Viewer不同颜色的日志,颜色定义可参考SEGGER_RTT.h// 如下为通过红色字体输出日志// SEGGER_RTT_printf(0, RTT_CTRL_TEXT_RED"Hello, SEGGER RTT Terminal 0!\r\n");}
}

8. SEGGER_RTT_printf的高级用法见

见《nRF52832闪存FDS使用(SDK17.1.0)》

nRF52832闪存FDS使用(SDK17.1.0)_晨之清风的博客-CSDN博客

参考文档

  1. NRF52832学习笔记(11)——蓝牙MAC地址
    https://www.jianshu.com/p/f56e0d9e9432
  2. GCC编译优化和调试选项
    https://blog.csdn.net/jinchengzhou/article/details/120703911

WSL构建nRF5 SDK + ARM GCC开发环境 – RTT打印调试日志相关推荐

  1. 在windows上配置VScode支持ARM GCC开发环境

    简单有效的在windows上,配置VS Code,以支持GCC开发环境.没有什么花里胡哨的. 需要用到的工具 Visual Studio Code :编辑工具 ARM GCC :            ...

  2. Eclipse+ADT+Android SDK 搭建安卓开发环境

    2019独角兽企业重金招聘Python工程师标准>>> Eclipse+ADT+Android SDK 搭建安卓开发环境 博客分类: Android 最近刚开始接触Android(安 ...

  3. Android:Eclipse+ADT+Android SDK 搭建安卓开发环境

    Eclipse+ADT+Android SDK 搭建安卓开发环境 要求 必备知识 windows 7 基本操作. 运行环境 windows 7(64位);  eclipse-jee-luna-SR2- ...

  4. 构建你的Office 365开发环境 - IOS版

    博客地址:http://blog.csdn.net/FoxDave 本文主要介绍在开始创建应用之前,如何构建你的Office 365开发环境去调用Office 365 API. 构建Office 36 ...

  5. eclipse+gcc STM32开发环境搭建及调试

    记录一下本人进行eclipse+gcc stm32开发环境的搭建过程,以便后续学习使用 参考资料如下: CubeMX+Eclipse+Jlink STM32开发环境搭建_小裘HUST的博客-CSDN博 ...

  6. .NET实践:构建iPhone程序虚机开发环境

    .NET实践:构建iPhone程序虚机开发环境 http://tech.it168.com/a2010/0921/1106/000001106670_all.shtml [IT168 技术文档] 看了 ...

  7. mac启动本地redis_通过 Laravel Sail 构建基于 Docker 的本地开发环境

    Laravel 官方最近发布了 Laravel Sail -- 一个轻量级的.基于 Docker 的 Laravel 本地集成开发环境,今天学院君就以 Mac 系统为例,给大家演示下如何基于 Lara ...

  8. suse linux c 编译环境,SUSE 11中安装GCC开发环境

    SUSE11中安装GCC开发环境 安装包下载网站:http://213.174.32.130/sles/distribution/11.0-SP1/repo/disk1/suse/x86_64/ RP ...

  9. 乐鑫Esp32学习之旅② 巧用eclipes编辑器,官方教程在Windows下搭建esp32开发环境,打印 “Hello World”。

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 " ...

最新文章

  1. linux目录默认权限是什么,linux文件目录默认权限(详解)
  2. Nature子刊:皮层内脑机接口的主导感
  3. 【商务智能】数据仓库 ( 多维数据模型 | 多维数据分析 )
  4. 为什么产品经理面试都喜欢问注册问题?
  5. JavaScript基础01【简介、js编写位置、基本语法(6种基本数据类型)】
  6. python 解压zip文件_Python 解压缩文件详解
  7. 网络时延——发送时延和传播时延
  8. Docker知识体系--从容器基础-微服务-DevOps-实战演习-Kubernetes简介-KBS基础架构-KBS核心组件-KBS集群
  9. 如何通过JS获取元素宽高
  10. 用指针实现对二维数组元素的访问
  11. 贾俊平统计学blog整理
  12. jxls对比_结合JXLS和POI技术开发Web报表
  13. 异速联大于400并发分散型项目应用介绍
  14. TTL电路和CMOS电路的区别和…
  15. 河南科技大学计算机科学与技术分数线,河南科技大学2017年河南省各专业录取分数线...
  16. C语言show用法,show的用法及句型
  17. iphone怎样关闭副屏_机情烩:联通eSIM主副卡业务上线 副卡套餐最低仅10元
  18. getopt Python
  19. Unity3D游戏开发从零单排(六) - 人物运动及攻击连击
  20. 经典简单的猜数字小游戏

热门文章

  1. Controller Area Network(CAN)简介
  2. 项目管理 : 如何入手分包合同管理
  3. 对于Ubuntu服务器杀毒的一次记录
  4. c语言计算机二级考试推荐用书,国家计算机二级C语言(考试用书方面)
  5. 移动互联网时代:圈子人李帅的兑变
  6. 联想工控机服务器型号,联想工控机IPC-600
  7. 蓝屏0x000000F4高峰,因为有恶意软件
  8. 后台管理页面布局、web页面布局
  9. linux 进程通信机制,LINUX内核进程高效通信机制研究
  10. js证件等校验校验规则