使用SEGGER Embedded Studio越来越多,本文总结了一些有用的小技巧。
本文是对SEGGER Embedded Studio使用技巧一文的延续。

1. 列选择

在编辑器中,按住Alt键并用鼠标框选,将执行列模式选择。

2. 快速找到文件

可以利用这个功能在工程文件堆中快速找到某个文件,比如文件名中包含了uart,点击“漏斗”图标,或使用快捷键Ctrl + ;,调出过滤栏,输入uart,即可列出所有相关文件。
Project File Filter

3. 扫描结果过滤

搜索关键字可能搜出大量匹配项,可以在搜索结果中进一步过滤。

Find Result Filter

4. 隐藏无效代码

工程中经常用#if 0宏开关让代码失效,可以设置无效代码的透明度,获得更好的视觉体验。
Tools --> Options --> Text Editor --> Inactive Code Opacity中设置5%,就可以得到下面效果

Inactive Code Color

5. 编辑工程文件

SES的工程文件(*.emProject)实际上是一个XML文件,某些场景下可以直接编辑它。
点击Project --> Open Solution in Editor,即可打开emProject文件。

Edit emproject file

6. 自定义宏标签

SES支持常规的TODO, FIXME宏标签,还支持自定义宏标签。在调试和读其他人代码的时候,尤其有用。
Tools --> Options --> Text Editor --> Attention Tag List中添加一个标签isyq,在代码注释中以isyq开头,即可高亮显示。

Customized Macro Tag

7. 跳转到编译错误

假如编译时报了多个编译错误,这些报错项可能夹在大量的编译信息中,手动查找的方式需要滚动半天,可以点击工具栏按钮或快捷键F4快速跳转到编译错误位置。

Build Warning Jumper

8. 串口终端

SES内置了一个简易的串口打印窗口,在调试时候不用再开第三方串口工具。
使用时候点击Tools --> Terminal Emulator

Intern UART Terminal

9. 预编译代码

代码中有的宏代码经过层层嵌套和拼接操作,无法直观的获知宏背后的原型函数和参数。

SES可以输出预编译后的文件,在当前源文件中,右键 --> Tools --> Show Preprocessor Output,即可打开一个名为xxx_PP.c的文件,该文件即源文件被预处理后的文件。
在追踪代码时候很有用。

Show Preprocessor Output

10. 工具栏大图标

现在显示器分辨率越来越高,小图标看着吃力。SES的工具栏图标可以设置为大图标,护眼。
Tools --> Options --> Environment --> Show Large Icon In Toolbars设置为Yes。

Toolbar Big Icon

(未完待续)

从Application跳转到Bootloader中,可以采用按键(Button)触发,也可以直接用BLE命令,因此后者称为Buttonless。本文介绍Buttonless DFU服务的技术细节和使用方法。

(1)DFU服务

Buttonless DFU是一个自定义服务,它下面仅包含一个特征:

Attribute UUID Property
Secure DFU Service 0xFE59
Buttonless DFU Characteristic 0x8EC90003-F315-4F60-9FB8-838830DAEA50 Write, Indicate

Buttonless DFU特征主要职责是:从Application进入DFU Mode。

DFU Mode指芯片停驻在Bootloader中,准备或正在执行DFU相关动作。

执行这个过程,DFU Controller发送一个Enter DFU Mode的命令,该命令需要返回Response,然后从Application跳转进入Bootloader,进入DFU Mode。

在代码层面,Enter DFU Mode命令向GPREGRET寄存器写入一个标志位,然后重启。Bootloader启动时会检查该标志位。具体代码为:

uint32_t ble_dfu_buttonless_bootloader_start_finalize(void)
{
    err_code = sd_power_gpregret_clr(0, 0xffffffff);
    err_code = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
    ...
    NVIC_SystemReset();
}

(2)添加DFU服务

SDK 15提供了一个Buttonless的模板工程:ble_app_buttonless_dfu。用户可以基于该模板,开发自己的应用。

为了理解Buttonless服务,这里向ble_app_hrs工程手动添加Buttonless DFU服务。

打开<sdk>\examples\ble_peripheral\ble_app_hrs工程,确保该工程能够正常运行。

添加源文件

在工程中增加一个文件夹nRF_DFU,并添加以下文件:

  • <sdk>\components\ble\ble_services\ble_dfu\ble_dfu.c
  • <sdk>\components\ble\ble_services\ble_dfu\ble_dfu_bonded.c
  • <sdk>\components\ble\ble_services\ble_dfu\ble_dfu_unbonded.c
  • <sdk>\components\libraries\bootloader\dfu\nrf_dfu_svci.c

添加Include目录

在工程配置窗口中找到User Include Directories,添加以下路径:

  • ../../../../../../components/libraries/bootloader
  • ../../../../../../components/libraries/bootloader/ble_dfu
  • ../../../../../../components/libraries/bootloader/dfu
  • ../../../../../../components/libraries/svc

如果使用SEGGER Embedded Studio,需要额外注意这些路径末尾的空格,可能导致不被正确识别。

添加宏开关

在工程配置窗口中找到Preprocessor Definitions,添加下列项:

  • NRF_DFU_SVCI_ENABLED
  • NRF_DFU_TRANSPORT_BLE=1
  • BL_SETTINGS_ACCESS_ONLY

配置sdk_config

在sdk_config.h中,做如下更改:

  • BLE_DFU_ENABLED = 1
  • NRF_SDH_BLE_VS_UUID_COUNT += 1

添加头文件

在main.c中添加以下头文件:

  • #include “nrf_dfu_ble_svci_bond_sharing.h”
  • #include “nrf_svci_async_function.h”
  • #include “nrf_svci_async_handler.h”
  • #include “ble_dfu.h”
  • #include “nrf_power.h”
  • #include “nrf_bootloader_info.h”

添加代码

打开Buttonless示例工程的main.c,将下列函数复制到当前工程的main.c里:

static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event){};
NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);
static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context){};
NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0) = {};
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event){};

添加Buttonless DFU服务

在main.c/services_init函数末尾添加DFU服务:

ble_dfu_buttonless_init_t dfus_init = {0};

// Initialize the async SVCI interface to bootloader.
err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);

dfus_init.evt_handler = ble_dfu_evt_handler;

err_code = ble_dfu_buttonless_init(&dfus_init);
APP_ERROR_CHECK(err_code);    

调整内存地址

按照上面步骤依次走下来,已经可以通过编译。烧录Softdevice和Application,打开串口工具,应该看到日志消息提示内存地址和内存大小需要调整,如下:

<warning> nrf_sdh_ble: Insufficient RAM allocated for the SoftDevice.
<warning> nrf_sdh_ble: Change the RAM start location from 0x20002B90 to 0x20002B98.
<warning> nrf_sdh_ble: Maximum RAM size for application is 0xD468.
<error> nrf_sdh_ble: sd_ble_enable() returned NRF_ERROR_NO_MEM.

在工程配置窗口中找到Section Placement Macro,参照日志调整RAM的起始地址和Size。

重新编译和下载工程。

此时串口工具不再提示内存问题,但是仍然提示Fatal error。这是因为Buttonless DFU服务会检测芯片中的Bootloader,如果我们没有预先烧录Bootloader,就会出现这个错误。

 

至此,为ble_app_hrs工程添加Buttonless DFU服务,代码层面工作全部结束。

(3)Bootloader Settings

直接烧录softdevice、bootloader和application,会发现application并未运行,芯片一直跑在Bootloader中。

芯片启动后先进入Bootloader,检测Bootloader Settings中的数据,如果这些数据指示Flash中有一个有效的Application,则跳转进入Application。Bootloader Settings是Flash中的一段区域,它包含了Application的大小、CRC等数据,执行DFU时也会在这里存储状态信息。

正常执行DFU时,Bootloader自动生成和维护Bootloader Settings信息。而烧录过程不同,需要手动写入。可以根据application.hex生成一个bl_settings.hex,以产生这些数据,然后烧录这个hex。

生成bl_settings.hex的命令为:

nrfutil settings generate 
    --family NRF52 
    --application app.hex 
    --application-version 2 
    --bootloader-version 2 
    --bl-settings-version 1 
    bl_settings.hex

注意–bl-settings-version只能是1,不可以是其他值。从源代码上看它应该是个预留位,没有作用。

通过命令nrfutil settings display bl_settings.hex可以查看Bootloader Settings的内容:

(4)烧录

每次我们修改应用程序代码,都会导致现有的Bootloader Settings信息失效,需要重新生成并烧录bl_settings.hex。可以利用批处理来完成生成、烧录的动作。

第一次烧录程序时,需要烧录softdevice和bootloader:

@echo off

set app=<your app.hex path>

nrfutil settings generate --family NRF52 --application %app% --application-version 2 --bootloader-version 2 --bl-settings-version 1 bl_settings.hex

nrfjprog -e
nrfjprog --program softdevice.hex
nrfjprog --program bootloader.hex
nrfjprog --program bl_settings.hex
nrfjprog --program %app%
nrfjprog --reset

pause

后续再次烧录,可以省略烧录Softdevice和Bootloader的步骤:

@echo off

set app=<your app.hex path>

nrfutil settings generate --family NRF52 --application %app% --application-version 2 --bootloader-version 2 --bl-settings-version 1 bl_settings.hex

nrfjprog --program bl_settings.hex --sectorerase
nrfjprog --program %app% --sectorerase
nrfjprog --reset

pause

nrfjprog命令中使用了--sectorerase参数,以保证只擦除并填写指定区域。

(5)调试

调试时候会频繁的修改代码,如果每次都要重新生成和下载一遍bl_settings.hex,会疯掉。

我们可以在代码中禁止Bootloader检测Bootloader Settings,让它直接跳转进入Application。

打开Bootloader工程main.c,注释掉main.c中的下面两行代码:

ret_val = nrf_bootloader_init(dfu_observer);
APP_ERROR_CHECK(ret_val);

芯片上电后,Bootloader完全不理会DFU Mode,直接进入Application。这样就如同一个没有Bootloader的工程,可以在Application中自由的调试,也无需生成bl_settings.hex。

 

(完)

Segger Embedded Studio(简称SES)有许多使用技巧,本文介绍几个我比较常用的。

(1)自定义快捷键

通过菜单项:Tools –> Options –> Keyboard,实现自定义快捷键。

我所改的几个功能:

  • DebugDownloadActive(F8):下载当前工程。
    在Keil中,下载功能使用快捷键F8,与Build功能(F7)毗邻,这里设置成相同快捷键。
  • EditGoToDeclaration(F11):跳转到函数声明。
    跳转到函数定义快捷键是Alt+G和F12,后者是隐藏快捷键,这里设置成F11与之毗邻。

另外,在Tools –> Options –> Text Editor中,将Ctrl + Left Click Action设为Go To Definition,即可实现类似Source Insight中的“Ctrl+点击”的方式跳转函数定义。

(2)外部工具

SES可以添加外部工具,但是入口很隐晦。

打开菜单项:File –> Open Studio Folder –> External Tool Configuration,将打开tools.xml文件。

在<tools></tools>之间加入如下代码,参考链接

<item name="Tool.CMSIS_Config_Wizard" wait="no">
    <menu>&amp;CMSIS Configuration Wizard</menu>
    <text>CMSIS Configuration Wizard</text>
    <tip>Open a configuration file in CMSIS Configuration Wizard</tip>
    <key>Ctrl+Y</key>
    <match>*config*.h</match>
    <message>CMSIS Config</message>
    <commands>
    java -jar &quot;$(CMSIS_CONFIG_TOOL)&quot; &quot;$(InputPath)&quot;
    </commands>
</item>

此时对sdk_config.h文件点右键,右键菜单中将多出一个CMSIS Configuration Wizard的菜单项,快捷键是Ctrl+Y。如下:

CMSIS Configuration Wizard是一个可视化的设置sdk_config.h的工具,SDK14.2中已经自带了该工具:

同时可以看到,右键菜单中还有我自定义的擦除芯片(Erase the Chip)的命令。

(3)定位注释中的变量

有的代码注释会引用外部变量,如下:

enum BLE_GAP_EVTS
{
  BLE_GAP_EVT_DISCONNECTED,     /**< See @ref ble_gap_evt_disconnected_t.*/
  BLE_GAP_EVT_CONN_PARAM_UPDATE,/**< See @ref ble_gap_evt_conn_param_update_t.*/
  // ...
}

注释中提到了ble_gap_evt_disconnected_t,我想查看该类型变量的定义,由于它位于注释中,无法直接按F12跳转。

最直观的方法是”全局搜索“,再从搜索结果中筛选,但这样做效率很慢(而且SES的全局搜索功能还有陈年bug)。

一个快速的方式是,调用菜单项:Navigate –> Find Symbol(Alt + Y),在符号查找窗口中输入ble_gap_evt_disconnected_t,然后就能够快速定位到该变量的定义,如下:

(4)移除Code/Data列

在项目文件浏览器中,显示了各文件的Code/Data大小,这两列宽度无法调小,在笔记本屏幕上非常浪费空间。

在工具栏的右边角落里有个设置菜单,可以在菜单中隐藏这两列,如下图:

(5)简化工程选项(Project Options)

打开工程选项的常规做法是先选中工程,再右键选择Edit Options项。但是Options中的条目太多,看着麻烦,希望能够只显示我们关心的条目。

操作方法是,打开(4)中提到菜单,做如下设置:

然后在项目文件浏览器中会自动增加一个Project Options目录,里面存放了我们修改过的选项。

如果手动修改了Project Options中的条目,被修改的条目会自动添加到这里。注意,不可以随意删除这些条目,删除操作意味着将其值恢复为默认值。

最终效果如下:

(6)按等号对齐

写代码时候经常遇到这样情况:

p_yqs->conn_handle = BLE_CONN_HANDLE_INVALID;
p_yqs->data_handler = p_yqs_init->data_handler;
p_yqs->is_notification_enabled = false;

希望这些赋值操作按等号对齐,实现如下效果:

p_yqs->conn_handle             = BLE_CONN_HANDLE_INVALID;
p_yqs->data_handler            = p_yqs_init->data_handler;
p_yqs->is_notification_enabled = false;

很简单,选中这几行,调用菜单项:Edit –> Column Tidy功能,即可自动以按等号对齐。

(7)跳转到头文件

假设当前打开的文件为peer_manager.c,希望跳转到peer_manager.h文件中。

调用菜单项:Navigate –> Go To Header (Alt + L)即可。

反过来,从头文件中跳转到源文件,只要找一个函数,按F12跳转到定义即可。

 

(未完待续)