Nordic有2套并存的SDK:老的nRF5 SDK和新的NCS SDK,两套SDK相互独立,大家选择其中一套进行开发即可。一般而言,如果你选择的芯片是nRF51或者nRF52系列,那么推荐使用nRF5 SDK。如果你选择的是Nordic最新产品系列,比如nRF53或者nRF9160,那么请选择NCS SDK。还有一种特殊情况,虽然你选择的是nRF52芯片,但需要使用最新的一些射频技术,比如蓝牙测向,蓝牙Mesh v1.1,低功耗蓝牙音频,那么也需要使用NCS SDK,换句话说,最新的射频技术,Nordic都只会在NCS上进行开发,而nRF5 SDK将进入维护阶段不再增加新的特性(如发现bug,会对其进行修复的)。关于nRF5 SDK的介绍,请参考这篇博文之前的博文,基本上都是基于nRF5 SDK进行阐述的,尤其是这篇:https://www.cnblogs.com/iini/p/9043565.html,详细讲解了nRF5 SDK开发环境的搭建,这里不再赘述。下面将只对NCS SDK进行阐述,以期让大家快速了解这个Nordic最新SDK,并尽快熟悉和上手。

1. NCS SDK介绍

NCS全称nRF Connect SDK,是Nordic最新的SDK平台,该平台将支持Nordic所有产品线,包括低功耗蓝牙,蜂窝网,2.4G,蓝牙Mesh,Zigbee,Thread,CHIP等,换句话说,由于短距离无线网络和长距离无线网络共用同一个SDK,将使得你同时具备两种网络的开发经验,因为他们的框架是一样的,驱动是一样的,网络协议栈的编写风格也是相仿的。只要你熟悉了其中一种网络的开发,那么相关的经验可以迅速复制到新网络平台上。

NCS SDK内嵌Zephyr RTOS,并沿用了Zephyr project的编译系统。Zephyr Project是Linux基金会推出的一个Apache2.0开源项目,版权非常友好,适合用于商业项目开发。Zephyr Project是一个合作社区,其产品就是Zephyr,具体包括Zephyr RTOS,Zephyr组件以及Zephyr编译系统等。Zephyr很多地方都模拟了Linux,比如使用了DeviceTree和Kconfig,对Linux很熟的同学,阅读Zephyr代码会感到很亲切的。经常有人问:为什么NCS要使用Zephyr RTOS?其实答案就蕴含在Zephyr Project的愿景中:The Zephyr Project strives to deliver the best-in-class RTOS for connected resource-constrained devices, built be secure and safe。Zephyr的愿景跟Nordic的产品策略高度吻合,这也是为什么Nordic要选Zephyr的主要原因。NCS SDK和Zephyr Project两者最大的区别有3个:一是owner不同,NCS SDK由Nordic负责,Zephyr SDK由Linux基金会负责。NCS开发中碰到的所有问题,Nordic都将负责解决。二是产品管理不一样,NCS SDK将由Nordic完成所有相关测试和考核,并符合Nordic产品开发,测试,发布和质量控制流程,因此NCS有自己的版本,并跟Zephyr版本控制解耦。三是NCS具有很多增强特性,比如Nordic特有的蓝牙链路层等。所以,从产品角度看,NCS SDK和Zephyr SDK是两套完全不同的SDK。但是,如果从代码角度看,那么NCS SDK和Zephyr SDK又基本上是一样的。在本文章的下面部分,在不引起混淆的情况下,经常会把NCS和Zephyr两个概念换着使用,因为本质上他们是一个东西。

NCS使用了CMake编译系统,并使用了大量Python脚本以辅助生成一些头文件,代码和hex,这些都大大增加开发的可移植性和便利性。

NCS SDK放在GitHub上,里面包含多个代码库,其主代码库(Manifest)是nrf,同时还包含Zephyr,MCUBoot,mbedtls,nrfxlib等其他代码库。NCS SDK可以同时在Windows,macOS和Linux上运行。

由于Zephyr和NCS的build系统是一样的,如果你正在学习Zephyr RTOS,那么也可以参考本篇文章,从NCS SDK开始学习Zephyr。由于NCS SDK新增了很多特性,比如图形化的DEBUG,丰富的例程,你会发现从NCS SDK学习Zephyr是一条便捷通道。

2. NCS SDK安装

NCS SDK开发包比较大,目前大概4G(后续有可能会更大),由于GitHub网络带宽的问题,很多人下载的时候会出现超时错误,为此我们提供如下两种安装方案。如果你能在早上6点左右起床,请参考2.2方案(GitHub直接下载);如果你不想早点起来,请参考2.1方案(百度网盘下载)

2.1 百度网盘下载(仅适用于Windows)

进入目录“开发你的第一个NCS(Zephyr)应用程序”,选择相应的版本,推荐使用最新版本。本篇博文完成之时最新量产tag为:ncs_v1.4.1,后续还会有ncs_v1.5.0,ncs_v1.6.0等。直接把相应的压缩包下载下来并解压到本地目录,SDK即算安装完成。注意,这个压缩包只能在Windows系统上运行,不能在Linux和macOS上运行。

注:ncs_v1.4.99-dev1是专为nRF53准备的一个临时开发版本,量产版本需要等到v1.5.0或者更新

2.2 通过nRF connect桌面版直接GitHub下载(同时支持Windows,Linux和macOS)

采用这个方案,你必须早上6:00左右起床,切记!

首先,下载桌面版nRF connect (同时支持Windows/macOS/Linux平台)。下载链接为https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Connect-for-desktop/Download#infotabs,选择你的平台和版本。

桌面版nRF connect安装成功后,将如下所示:

在nRF connect桌面版中有2个app都可以完成NCS SDK安装,一个是Toolchain Manager,一个是Getting Started Assistant。如果你是GitHub老手,可以按照Getting Started Assistant的步骤一步一步来安装。如果你嫌一步一步安装麻烦,那么建议你使用Toolchain Manager一键安装。下面将会以Toolchain Manager的方式(支持Windows和macOS)来讲解安装过程Getting Started Assistant的方式自己有兴趣可以自己去摸索,而且Linux系统目前只能采用这种方式)

首先install Toolchain Manager,然后open,进入settings界面,选择安装目录,如下:

然后重新选择SDK ENVIRONMENTS页面,并选择SDK相应版本进行安装,如下所示:

根据网速不同,这个过程持续时间变化很大,我这边网络大概需要20分钟完成所有下载和安装,安装成功后你将得到如下目录内容:

2.3 nRF command line tools安装

下载完SDK压缩包,再下载“nRF-Command-Line-Tools_10_11_1_Installer.exe”,即nRF command line tools,nRF command line tools包括Jlink驱动以及Nordic自己开发的一些命令行工具,如nrfjprog,nrfutil以及mergehex等,这些工具对开发非常有帮助。nRF command line tools下载链接为:https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools/Download#infotabs

 

3. NCS SDK开发环境说明

NCS支持的工具链有2套:一套是SEGGER Embedded Studio,简称SES,图形化的IDE,一套是命令行方式(沿用了Zephyr工具链),其实就是GCC工具链。两套工具链选其一即可,推荐大家使用SES,因为它的debug功能非常好使。(GCC工具链的debug功能使用起来就比较复杂了)。下面将分别对SES和命令行方式两种开发环境进行介绍,大家可以参考其中一种或者两种结合一起使用。

3.1 SES开发环境搭建

通过百度网盘或者Toolchain Manager安装的SDK,必须进入如下目录:install folder\toolchain,并双击SEGGER Embedded Studio.cmd以打开SES,而不能在其他地方直接打开SES,如下所示:

SES启动成功后,进入Tools->Options,我们需要先对IDE进行配置。

如下两种方式的配置有可能都能工作起来,选择一种适合你的(一般来说,推荐配置二,这个配置方式可以适用任何电脑环境)。

配置一:

配置二:

配置好之后,我们就可以打开一个工程进行编译了。进入File->Open nRF Connect SDK Project

在NCS中,项目是通过CMakeLists.txt表示的,如下:

而开发板一般是在如下目录的:

选择开发板,就是选择芯片,除此之外,开发板还规定了芯片的一些外设使能情况,以及一些基本外围电路连接情况,这个跟Keil如下选择Device的操作是异曲同工的,而且NCS这种做法更灵活,功能也更多,扩展性也更好。

如下为hello_world项目的装载图:

下面将对nRF52/nRF9160/nRF53项目,以及客户自定义项目,分别进行阐述,以详细说明如何装载一个项目,编译一个项目,下载一个项目和debug一个项目。

3.1.1 nRF52项目示例

以nrf\applications\nrf_desktop项目为例,开发板选择支持LLPM的gaming mouse:nrf52840gmouse_nrf52840

装载成功后,编译,如下所示:

然后下载,如下所示:

如果要Debug,按如下界面进入:

3.1.2 nRF53项目示例

Nordic第一个支持nRF53的tag是v1.4.99-dev1,v1.5.0将正式支持nRF53的量产开发,如果你需要开发nRF53,请使用v1.4.99以上版本。

由于nRF53包括两个核:app核和network核,app核用来运行应用程序,network核用来运行射频协议栈,所以每次下载的时候需要同时把两个核的hex都下载进去,但是SES只支持一次下载一个核(west可以同时下载两个核,具体请参考3.2)。在使用SES开发nRF53的时候,项目装载/编译/下载/debug跟其他芯片是一样的,但这些操作只是针对一个核(一般来说都是app核),此时如果还需要network核配合的话,那么需要你手动先把network核的image下载进去,这个可以通过nrfjprog或者west来完成。这里要强调一点的是,当你选择一个蓝牙项目,这个项目默认会同时把app核和network核对应的工程都进行编译,然后生成各自的hex文件,然后你通过nrfjprog或者west把network核的image下载进去,通过SES本身的Target->Download菜单把app核的image下载进去。

以nrf\samples\bluetooth\peripheral_uart蓝牙透传项目为例:

编译如下所示:

编译成功后,先下载network核里面的image,我们以west flash来下载。进入目录:nrf\samples\bluetooth\peripheral_uart\build_nrf5340dk_nrf5340_cpuapp\hci_rpmsg,然后在cmd输入:

  1. west flash

即可将image下载到network核里面,如下所示:

如果west flash在你的环境跑不通,那么你可以直接使用nrfjprog来下载network核的image,进入目录:nrf\samples\bluetooth\peripheral_uart\build_nrf5340dk_nrf5340_cpuapp\hci_rpmsg,然后在cmd输入如下命令:

  1. nrfjprog -f NRF53 --coprocessor CP_NETWORK --sectorerase --program merged_CPUNET.hex --verify

然后下载app核image,app核image可以直接通过SES下载,如下:

如需debug,按如下界面进入:

3.1.3 nRF9160项目示例

以nrf\samples\nrf9160\mqtt_simple项目为例:

编译如下所示:

下载如下所示:

如需debug,按如下界面进行:

3.1.4自定义项目装载示例

如果是你的自定义项目或者Zephyr项目或者不使用toolchain自带的SES,此时就不能使用SES自带的快捷方式来装载项目,而需要自己去相应的目录找到项目工程和开发板,比如我们装载Zephyr的blinky例子。先选择项目工程CMakeLists.txt所在的目录,如下:

再选择相应的开发板,如下:

然后项目就装载成功了,如下:

后面编译,下载和调试,和之前一样,就不再赘述。

3.2 命令行方式开发环境搭建

NCS或者Zephyr项目命令行编译的时候,一般使用west命令,west语法可以通过west –help获得,如下:

  1. west help

如果你能看到上面的界面,那么你的环境基本上就没问题了。

一个典型的west命令如下所示:

  1. west build -b nrf52840dk_nrf52840
  2.  
  3. 注:-b指定开发板,这里使用nRF52840开发板

通过百度网盘或者Toolchain Manager安装的SDK,必须进入如下目录:install folder\toolchain,并双击git-cmd.cmd以打开命令行,而不能直接打开CMD,如下所示:

git-cmd.cmd会自动把环境变量设好,否则后续编译会有问题。

下面分别以nRF52/nRF53/nRF9160开发为例,展示相应的编译和下载命令。

3.2.1 nRF52项目示例

下面以编译nrf\applications\nrf_desktop项目为例来讲解。首先进入项目所在的目录:

cd nrf\applications\nrf_desktop

界面将如下所示:

由于已经进入了项目所在的目录,编译的时候就无需再指定项目目录了,由于nrf_desktop支持多个开发板,我们还需要指定用哪一个开发板,如下所示:

  1. west build -b nrf52840gmouse_nrf52840

或者

  1. west build -b nrf52840gmouse_nrf52840 -p
  2.  
  3. 注:-p用来清除build目录缓存

由于上面没有指定build目录,上面的命令会自动生成一个build目录(名字就是build),然后所有的编译文件会自动放在该build目录下。

build目录如下所示:

编译成功后,就可以烧写了,使用如下命令进行烧写:

  1. west flash

或者

  1. west flash erase

3.2.2 nRF53项目示例

以nrf\samples\bluetooth\peripheral_uart为例来讲解。首先进入目录:nrf\samples\bluetooth\peripheral_uart,然后输入如下编译命令:

  1. west build -b nrf5340dk_nrf5340_cpuapp --build-dir build_nrf5340dk_nrf5340_cpuapp -p
  2.  
  3. 注:-b指定开发板,--build-dir指定编译目录,-p清除老的编译目录内容

编译成功后,就可以烧写了,使用如下命令进行烧写:

  1. west flash --build-dir build_nrf5340dk_nrf5340_cpuapp

3.2.3 nRF9160项目示例

以nrf\samples\nrf9160\mqtt_simple为例来讲解。首先进入目录:nrf\samples\nrf9160\mqtt_simple,然后输入如下编译命令:

  1. west build -b nrf9160dk_nrf9160ns --build-dir build_nrf9160dk_nrf9160ns -p
  2.  
  3. 注:-b指定开发板,--build-dir指定编译目录,-p清除老的编译目录内容

编译成功后,就可以烧写了,使用如下命令进行烧写:

  1. west flash --build-dir build_nrf9160dk_nrf9160ns

3.3 NCS SDK版本说明

NCS SDK开发包目录如下所示:

可以看出,NCS SDK包含nrf,zephyr,bootloader,nrfxlib,modules等多个代码库,其中nrf代码库就是NCS SDK的主代码库(manifest),nrf代码库的版本就是NCS SDK的版本,所以要查看NCS SDK当前版本号,进入nrf目录,输入

  1. git branch

如下:

熟悉git的同学都知道,一个SDK如果包含多个代码库,那么每个代码库都有自己的版本,而且代码库版本之间是相互关联的,以NCS SDK为例,当nrf版本切换为v1.4.0时,其他代码库的版本也需要做相应切换,那么对应nrf v1.4.0的Zephyr代码库版本是多少呢?mcuboot代码库版本是多少呢?如果直接使用git工具,那么你需要一个一个记,然后一个一个checkout。在NCS或者Zephyr中,引入了west工具,这样通过管理nrf代码库版本就可以间接管理其他代码库的版本,比如我们现在把NCS SDK版本切换到v1.4.0,指令如下所示:

  1. git checkout v1.4.0

然后通过west update命令,就可以让其他代码库版本自动跟v1.4.0 nrf代码库同步,这样你不需要记住或者管理其他代码库版本,只需管理nrf代码库版本即可。

  1. west update

4. NCS项目的配置或选项

4.1 NCS项目配置介绍(autoconf.h 和devicetree_unfixed.h)

每一个NCS或者Zephyr项目都包含了非常多的配置项或选项,总数有可能达几千项之多,然而绝大多数配置项我们都不需要去管他们,只需要使用默认值即可,所以开发一个简单的NCS应用程序,有可能我们不需做任何配置,就可以跑起来。当你开发复杂的应用程序的时候,你又会体会到NCS配置的灵活性和方便性了,这其实也是NCS一个很大的优势。本章我们先讲如何查看配置项,后面一章我们再以例子来说明如何更改配置项。NCS或者Zephyr里面主要包含两种配置项:Kconfig和DeviceTree,Kconfig主要负责软件的配置,DeviceTree主要负责板子硬件的配置。两者最终都会生成一个.h文件,其中Kconfig生成的头文件为:autoconf.h,而DeviceTree生成的头文件为devicetree_unfixed.h他们都在如下目录:

autoconf.h文件示例如下:

devicetree_unfixed.h文件示例如下:

如果大家开发过nRF5 SDK的话,一定记得里面有个:sdk_config.h,从机理上,sdk_config.h和上面的autoconf.h/devicetree_unfixed.h并没有本质区别,他们都是完成同样的功能。但是很多人都觉得Kconfig和DeviceTree很复杂,其实他们复杂之处在于如何生成这两个头文件,但是头文件本身并不复杂。在nRF5 SDK中,用户可以直接修改sdk_config.h文件,由于sdk_config.h文件太大,所以Nordic使用了CMSIS_Configuration_Wizard来格式化这个头文件,以形成如下的图形化界面方便大家去修改它:

在NCS或者Zephyr里面,由于autoconf.h/devicetree_unfixed.h是由Python脚本自动生成的,所以用户不能直接修改autoconf.h/devicetree_unfixed.h这两个头文件。用户只能去修改生成这两个头文件的输入,以达到间接修改他们的目的。这样做的好处是:更灵活,而且不容易出错(Python会自动帮你找出配置不合理的地方或者语法错误)。可以说,一旦你熟悉了这套配置机制,你就会爱上它。

下面分别对autoconf.h和devicetree_unfixed.h两者的生成过程进行阐述。

4.1.1 Kconfig (Kconfig, prj.conf及autoconf.h)

我们先把生成autoconf.h文件的整体流程图贴出来,后面再对这个图进行解释:

autoconf.h文件是由许许多多的Kconfig文件生成的(注:其实Kconfig来源于Linux系统,NCS或者Zephyr对其进行了继承和定制),基本上每个模块都有自己的Kconfig文件,比如蓝牙controller模块:

使用文本编辑器打开Kconfig,你将会看到它的内容大概如下所示:

除了系统模块可以定义Kconfig文件,你自己的项目模块也可以定义自己的Kconfig文件,如何定义?依葫芦画瓢,仿照例子来即可。记住,在NCS或者Zephyr里面,只要可以用文本编辑器打开的文件,他们的语法都是直接可读的,不需要你另外去学习他们,直接仿照例子,你就可以定制自己的内容。

除了Kconfig,autoconf.h还有一个输入来源:本项目的配置文件。前面说过,Kconfig文件都是模块自带的,模块为每一个选项都设了一个默认值,如果你想修改这个默认值,怎么办?你不需要跑到模块下面直接把Kconfig文件改了(这样不方便,而且也会影响到其他项目工程的运行)。为此NCS或者Zephyr引入了prj.conf文件,prj.conf文件内容大概如下所示:

通过prj.conf,大家就可以更改默认的Kconfig选项了,而且这个更改是永久的,并只适用于本项目。

所有的Kconfig和prj.conf结合,先生成一个.conf文件,最后再生成autoconfig.h。.conf文件在如下目录:

其内容大概如下所示:

可以看出,.config文件格式更接近Kconfig和prj.conf,起到了一个中间桥梁作用。.config和autoconfig.h两者内容是可以一一对应的,因此大部分图形配置系统都是直接调用.config文件来完成图形化配置Kconfig的,后面我们会讲解如何使用SES和menuconfig来图形化配置.config文件。.config是一个临时文件,编译系统默认会以它为基准来生成autoconfig.h,所以一旦你的Kconfig或者prj.conf更新了,记得一定要重新装载项目,以更新.config文件,从而生成新的autoconfig.h文件。

4.1.2 Device Tree( *.dts, *.overlay及devicetree_unfixed.h)

同样我们先把生成devicetree_unfixed.h的整体流程图列出来,后面再对其进行解释:

DeviceTree也是Linux里面的概念,NCS或者Zephyr对其进行了继承和定制。在DeviceTree里面,每一种硬件比如芯片或者板子,都可以使用DeviceTree语言进行描述。DeviceTree使用了树形结构,按照层级关系把板子包含的组件一一描述清楚,每块板子都会定义一个dts文件,比如nrf52840dk_nrf52840开发板,它对应的dts文件是nrf52840dk_nrf52840.dts,其内容如下所示:

可以看到板子dts文件包含了一个nrf52840_qiaa.dtsi,nrf52840_qiaa.dtsi对应的就是nRF52840这颗芯片本身的dts文件。nrf52840 dtsi里面定义的内容,nRF52840dk开发板直接包含进来,然后在此基础上进行定制,比如dtsi里面将UART0配置为关闭,nRF52840dk可以将其改配为使能;另一种需要修改的情况,nRF52840dk增加了一些其他组件,比如LED/Button/外部Flash等,这些设备都可以成为nrf52840dk_nrf52840.dts里面的一个节点。nrf52840dk_nrf52840.dts是一种比较简单的板子,因此一个dts文件就可以将其表达清楚。我们还会碰到一种情况,几块板子大部分特性都是相同的,只有少数组件不一样,这个时候,我们会把相同的地方拎出来组成一个common.dts,然后这几块板子再引用这个common.dts文件,比如目录:zephyr\boards\arm\nrf9160dk_nrf9160,里面包含两块开发板:nrf9160dk_nrf9160ns和nrf9160dk_nrf9160,两块开发板内容基本上是一样的,所以在这里把相同的内容拎出来组成了一个:nrf9160dk_nrf9160_common.dts,然后nrf9160dk_nrf9160ns.dts和nrf9160dk_nrf9160.dts再引用nrf9160dk_nrf9160_common.dts,这样拆分一下,逻辑关系更清晰,将使系统变得更灵活,扩展性更好。

除了<board>.dts,devicetree_unfixed.h还有一个输入来源:本项目的硬件配置文件,即overlay文件。刚才说了,每块板子都有自己的dts文件,里面描述了各个节点的状态,有时候我们会有多个项目共用同一块板子的情况,比如我们的开发板支持很多例子工程,每个例子工程的配置都不一样,有的例子需要打开uart,有的例子需要关闭uart,这种情况怎么办?你不需要跑到开发板定义目录下去更改<board>.dts或者<board>_common.dts,你只需要在本项目下定义一个<board>.overlay文件就可以实现你的目标,<board>.overlay文件内容示例如下所示:

通过<board>.overlay,大家就可以更改板子的默认配置,而且这个更改是永久的,并只适用于本项目

<board>.dts和<board>.overlay结合先生成一个zephyr.dts文件,最后再生成devicetree_unfixed.h。zephyr.dts文件在如下目录:

其内容大概如下所示:

可以看出zephyr.dts就是<board>.dts和<board>.overlay两个文件最终合并的结果,而且zephyr.dts和devicetree_unfixed.h是可以一一对应的,因此大家可以通过查看zephyr.dts来获知自己的硬件配置到底对不对,符不符合预期。

4.1.3 配置程序起始地址和大小

每个应用都需指定其image的ROM起始地址和大小,以及运行时所占RAM的起始地址和大小,这些都需要在工具链中进行配置的,比如Keil,是在如下界面完成相关配置:

在NCS或者Zephyr中,如果是一个单image应用,程序的起始地址和大小是在DeviceTree中配置的,如下:

对于单image应用,最有用的就是上面三个红框标出来的地方,其他配置选项你可以忽略不管,一般来说能改的就一项:storage_partition的起始地址和大小,storage_partition就是分配给用户用来存储数据的区域。

一般来说,无线IoT应用都是要求具有固件升级功能,为了升级固件,BootLoader就必不可少,此时一个应用至少有两个image:BootLoader对应的image,以及app对应的image,对于这种多image应用,程序起始地址和大小配置一般不通过DeviceTree直接来修改,而是交由partition manager(PM)模块来管理,具体请参考7章:开发你的第一个multi-image应用

4.2 图形化查看Kconfig选项

上面说了,大家可以通过.config文件去查看最终的Kconfig配置,然后通过zephyr.dts文件去查看最终的DeviceTree配置。zephyr.dts文件不是很大,因此推荐使用这种方法去查看。但是.config文件有点长,直接查看它有点累,而且容易搞错,为此NCS推出了两个图形化查看工具:SES配置和基于命令行方式的menuconfig或者guiconfig

4.2.1 SES查看

进入Project->Configure nRF Connect SDK Project,如下所示:

由于一个项目的配置项太多,我们一般在右上角搜索配置项名字,找到它,然后查看它的说明。同时我们可以去尝试修改它,修改成功后,点击“Configure”,配置才能生效。通过图形化界面进行修改,我们可以很快找到合适的配置选项,当大家对系统还不是很熟的时候,推荐使用这种方式去试错。这里强调一下,通过这种方式所做的修改是临时的,一旦项目重启或者缓存刷新了,这里的更改就会失效,所以我们一般推荐使用上面的prj.conf去永久修改配置项。

对于multi-image(多image)应用,点击Project->Configure nRF Connect SDK Project,会同时出现所有image的配置菜单,其中“menuconfig”对应的是主应用的配置项(其他menuconfig对应的是子image的配置项,具体请参考7章:开发你的第一个multi-image(多image)应用),如下:

4.2.2 命令行方式查看

命令行方式使用如下命令查看项目配置:

  1. west build -t menuconfig

执行上述命令后,将显示如下界面:

注:上述命令需要先安装windows-curses ,即在cmd中执行如下命令:pip install windows-curses –user,此命令的执行需要联网。如果你的电脑无法联网,建议使用guiconfig查看工程配置,其对应的命令为:

  1. west build -t guiconfig

执行上述命令后显示的界面如下所示:

由于一个项目的配置项太多,我们一般使用Jump to进行搜索,找到它,然后查看它的说明。同时我们可以去尝试修改它,修改成功后,选择“Save”,配置才能生效。通过图形化界面进行修改,我们可以很快找到合适的配置选项,当大家对系统还不是很熟的时候,推荐使用这种方式去试错。这里强调一下,通过这种方式所做的修改是临时的,一旦项目重启或者缓存刷新了,这里的更改就会失效,所以我们一般推荐使用上面的prj.conf去永久修改配置项。

5. NCS SDK中几个比较重要的目录

如前所述,NCS SDK中包括了多个代码库,每个代码库都是相互独立的,而且每个代码库包含的代码都很多,如果一行一行代码读下去,那将是一个无底洞。所以实际开发中,我们都是参考例子,按照例子去做,碰到不懂的API,再去看API说明,循环往复,最终完成自己的开发。

5.1 例子目录

我们先说说例子所在的目录。NCS中商业级的应用程序是放在如下目录:

nrf\applications

如果你的应用跟上面的应用相似,那么推荐使用上面的例子,因为他们基本上属于turn-key级的方案,跟成熟的商业应用差不多,你需要的开发工作量最少。

其次是如下例子目录nrf\samples,这个都是Nordic自己开发的一些例程:

然后就是Zephyr自带的例子zephyr\samples:

大家有时候会觉得NCS或者Zephyr例子还是不够多,比如很多驱动API怎么用,好像没有例子。其实Zephyr所有API的的使用,都可以在zephyr\tests下面找到示例,所以当你找不到例子的时候,不妨在这里找一找:

5.2 API目录

NCS里面这么多API,到底该使用哪些API?API说明又在哪里?一般而言,我们只使用代码库里面的include目录下的API,API说明也在那里

比如nrf代码库的include目录:nrf\include

Zephyr标准API的include目录zephyr\include:

其他代码库也是遵守这个规范的,比如Nordic开发的底层驱动API(与RTOS无关):

modules\hal\nordic\nrfx\drivers\include

注:有些人会问,modules\hal\nordic\nrfx\drivers\include和zephyr\include\drivers两个目录里面的驱动API,我到底该使用哪个呢?zephyr\include\drivers这个是Zephyr标准的驱动API,按照Zephyr标准来定义的,它调用了底层API:modules\hal\nordic\nrfx\drivers\include,modules\hal\nordic\nrfx\drivers\include这里面的API都是Nordic自己实现的,跟平台无关。所以说,一般推荐使用zephyr\include\drivers这里面的API,只有这里面没有或者实现不了的功能(比如将同一个引脚动态分配给UART和SPI,Zephyr标准API就无能为力),这个时候才使用modules\hal\nordic\nrfx\drivers\include这里面的API。

5.3 板子定义目录

通过在cmd输入:

  1. west boards

就可以查看目前Zephyr支持哪些标准板子:

上面这些板子都是在如下目录定义的:zephyr\boards\arm。由于Cortex-M33内核支持secure和non-secure(ns)两种应用,所以每一个Cortex-M33内核都包含两种硬件定义:安全和非安全。比如nrf9160dk,虽然物理上只有一块板子,逻辑上我们把它划分成两块板子:nrf9160dk_nrf9160和nrf9160dk_nrf9160ns,nrf9160dk_nrf9160是跑安全应用,而nrf9160dk_nrf9160ns是跑非安全应用。同样nRF5340dk,虽然物理上只有一块板子,但是它有两个核都可以供用户使用,其中app核既可以跑安全应用又可以跑非安全应用,而网络核只能跑一种应用类型,所以nrf5340dk在逻辑上就划分成三块板子:nrf5340dk_nrf5340_cpuapp(app核,跑安全应用),nrf5340dk_nrf5340_cpuappns(app核,跑非安全应用)以及nrf5340dk_nrf5340_cpunet(network核,跑非安全应用)。

除了这些标准Zephyr板子,NCS还有一些自定义板子,他们在如下目录:nrf\boards\arm

如果你要自定义自己的板子,可以参考上面例子来

6. 开发你的第一个NCS程序

现在我们开始我们第一个NCS程序或者Zephyr程序的开发,在NCS中,有如下两个现成的例子:zephyr\samples\hello_world和zephyr\samples\basic\blinky。hello_world例子就是在串口中打印一串字符串,而blinky例子就是让开发板的LED1一闪一闪,这两个程序直接就可以编译和运行,而且应该所有的Zephyr开发板都可以跑这两个程序。现在我们把这两个程序合成一个程序,即既打印字符串给串口助手,又让开发板LED1一闪一闪,同时我们把字符串打印变成循环打印,并将字符串同时输出到串口助手和RTT viewer。下面我们一步一步给大家演示这个开发过程。

本章所有代码可以到如下百度网盘链接获取,进入“开发你的第一个NCS(Zephyr)应用程序”-> “hello_world”,下载hello_world_ncsv140.rar

6.1 修改hello_world main.c文件

首先,我们以hello_world例子为基础,将这个例子拷到一个其他目录下(任何目录都可(不要有中文和空格等),只要你的环境变量都设好了,所有NCS例子的目录可以随意更改,这个真是非常方便!),这里让大家拷贝到其他目录,只是为了演示NCS编译路径的依赖性做得非常好,没有别的意思。如果你不想拷贝,也没关系,咱们可以直接在原始目录上进行修改,NCS自带git管理系统,非常方便你进行版本管理。我们做如下修改,以循环打印同一条日志:

  1. void main(void)
  2. {
  3. while(1)
  4. {
  5. printk("Hello World! %s\n", CONFIG_BOARD);
  6. k_sleep(K_SECONDS(1));
  7. }
  8. }

6.2 在项目中添加一个新文件blinky.c

然后把blinky代码加到hello_world,这里面就会用到添加一个新文件的技能。我们先把zephyr\samples\basic\blinky\src里面的main.c改名为blinky.c,然后拷贝到hello_world\src目录下。如何把blinky.c添加到项目中来呢?推荐的方法是修改CMakelists.txt文件,通过它加入新的编译文件或者库,或者包含新的目录。我们做如下修改,就可以把blinky.c加进来了:

  1. target_sources(app PRIVATE src/blinky.c)

这种方式不管是NCS项目还是Zephyr项目,都能工作成功,而且是我们首推的方式。至于CMakeLists的语法怎么理解和使用?还是那句话:参考例子,不要专门去学习。除了修改CMakeLists方法外,NCS还引入了一种图形化方法,使用SES来添加文件,如下所示:

这种方式只有nrf\samples这个目录下的例子才支持,zephyr\samples这个目录下的例子默认不支持这种方式。

其实通过SES添加文件,本质上跟修改CMakeLists方法一样,它只不过在CMakeLists文件里面预先放入如下两行标识,这样SES就可以把新添加的文件塞到这两行标识之间:

我们把这两行标识放在我们的hello_world例子里面,这样我们也可以通过SES添加文件了。blinky.c文件添加成功后,相应的CMakelists文件也更新了,如下所示:

6.3 修改blinky.c文件

好了,现在blinky.c文件已经添加成功了,我们再对其进行修改,修改代码如下所示:

  1. void blinky_thread(void)
  2. {
  3. const struct device *dev;
  4. bool led_is_on = true;
  5. int ret;
  6.  
  7. dev = device_get_binding(LED0);
  8. if (dev == NULL) {
  9. return;
  10. }
  11.  
  12. ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
  13. if (ret < 0) {
  14. return;
  15. }
  16.  
  17. while (1) {
  18. gpio_pin_set(dev, PIN, (int)led_is_on);
  19. led_is_on = !led_is_on;
  20. k_msleep(SLEEP_TIME_MS);
  21. }
  22. }
  23.  
  24. K_THREAD_DEFINE(blinky_thread_id, 1024, blinky_thread, NULL, NULL,
  25. NULL, 7, 0, 0);

如上,我们直接把blinky代码变成另外一个线程以达到我们的目的(当然你也可以把blinky代码变成一个模块,以供hello_world的main调用,这里就不演示这种方式了)。

6.4 编译和运行你的第一个hello_world程序

上述修改结束后,我们可以使用任何Zephyr标准板运行这个例子,以nrf52840dk为例,SES工程装载如下所示,然后编译和下载。

或者使用west命令进行编译和下载,命令如下所示:

  1. west build -b nrf52840dk_nrf52840 -d build_nrf52840dk_nrf52840 -p
  2. west flash --build-dir build_nrf52840dk_nrf52840

不管是SES下载完程序还是west下载完程序,可以看到程序正常执行,串口助手在循环打印“Hello World! nrf52840dk_nrf52840”,然后开发板上LED1在一闪一闪。串口截图如下:

我们第一个hello_world程序算是正式大功告成了。

6.5 使用prj.conf和prj_<board>.overlay配置项目

我们现在对hello_world再做一个修改:把log输出到RTT viewer而不是串口助手。传统的nRF5 SDK需要做两件事来达到这个目的:一是修改sdk_config.h文件,二是添加相应文件。但是在NCS SDK里面,你只需在prj.conf里面做如下修改即可达到同样的目的:

  1. CONFIG_USE_SEGGER_RTT=y
  2. CONFIG_RTT_CONSOLE=y
  3. CONFIG_UART_CONSOLE=n

运行后结果如下所示:

按道理说,上面的例子已经把日志输出改为RTT Viewer了,这时去测量nrf52840dk电流,应该很低才对,但实际上此时电流大概为500多微安。这是什么原因呢?我们看一下zephyr.dts文件,如下:

可以看出,uart0和uart1都处于使能状态,从而导致功耗偏高。我们通过overlay文件,将uart0和uart1关闭,修改如下所示:

同时在prj.conf把serial模块关掉(注:serial模块在所有项目默认是打开的),如下

  1. CONFIG_SERIAL=n

实际上,serial模块只使用了uart0模块,所以只需把uart0关掉即可,如下:

  1. &uart0 {
  2. status = "disabled";
  3. };

重新编译和下载,这时我们再去测量nrf52840dk电流,此时电流只有几微安,符合预期。

7. 开发你的第一个multi-image(多image)应用

有时一个应用会包含多个image,最典型的情况有两种:一是BootLoader+app,BootLoader一个image,app一个image,二是spm+app,spm一个image,app一个image(注:spm是Nordic为Cortex-M33非安全应用设计的一个引导程序,像nRF9160/nRF5340这种M33内核,所有非安全应用都会默认使能spm)。在NCS SDK中,编译一个项目,会同时生成多个不同image,这种应用就称为multi-image应用。image应用其生成的hex名为:zephy.hex,multi-image应用其生成的hex名为:merged.hex,merged.hex意味着这个项目会生成多个image,然后将他们合并(merge)成一个hex:merged.hex,因此multi-image应用对用户来说,最终也只有一个image,用户只需下载这个image即可。以nrf\samples\nrf9160\http_application_update为例,这是一个典型的多image应用,它包含3个image:BootLoader image,spm image以及app image,这三个image都是在nrf\samples\nrf9160\http_application_update编译目录下生成的,编译成功后,我们将同时看到三个image的编译目录,如下所示:

7.1 分区管理(Partition Manager)

传统的SDK,如果一个产品包含多个image,那么每个image都会对应一个项目,用户需要同时维护多个项目,而且需要同时维护这几个项目的版本关联关系。NCS中引入了partition manager(PM)模块(注:PM和前面的SPM是两个完全不同的模块,二者之间没有任何联系),由PM完成对多个image的管理,以及存储划分。在PM中,主应用称为parent应用,其他应用称为child应用,通过使能parent应用的某些Kconfig,可以自动装载child应用,然后自动编译child应用,然后生成child应用的hex,并将child应用的hex和parent应用的hex合并在一起生成前文所述的merged.hex,这一切都发生在parent应用的build目录中。

PM是如何工作的呢?PM首先假定有一个app image,也就是我们的parent应用,这个应用对应的hex就是前文所述的zephyr.hex,那么app image放在Flash什么地方呢?这个是由PM动态决定的,PM将根据各个image的相对位置,来决定最终的image排列。一般来说,parent应用是默认应用,它不需要特别去指定自己的位置,而child应用则需要告诉PM自己的位置在哪里,这个是通过child应用目录下面的pm.yml文件来实现的,pm.yml会告诉PM本child应用会放在那里,pm.yml文件内容大概如下所示:

pm.yml是按照相对位置来决定本child应用的位置的,而且里面会用到Kconfig或者DeviceTree的宏定义,所以前面的<board>.dts文件会定义很多image slot,其实也是为了给PM引用的。pm.yml看起来又是一种新格式文件,让人觉得有点不适应,其实还是那句话,你不需要专门去学习这个文件的原理,它的语法和格式都是你直接可以读懂的,多看看例子,自然就明白了。而且一般开发过程中,大家也不需要关心child应用目录下的文件,大家只需关心parent应用目录下的相关PM文件即可。

那么parent应用目录下有哪些PM文件呢?首先就是build根目录下多image最终布局的partitions.yml文件,以nrf\samples\nrf9160\http_application_update为例,其partitions.yml文件如下所示:

partitions.yml文件是由PM模块自动生成的,用户不能直接修改。

然后就是pm.config 和pm_config.h文件,这两个文件一一对应,pm.config和partitions.yml放在同一个目录下,其内容大概如下所示:

而pm_config.h是C语言代码直接引用的文件,它在build\zephyr\include\generated目录下,以nrf\samples\nrf9160\http_application_update为例,其pm_config.h文件如下所示:

一般来说,使用PM自动生成的存储layout就可以了,只有一个配置有可能需要改:settings_storage的大小,这个可以通过Kconfig选项CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE直接修改就可以了。

如果需要人为指定某个image的位置,也就是说需要对自动生成的partitions.yml进行修改,怎么办呢?其实很简单,把partitions.yml这个文件拷出来,放在项目的根目录下,然后将其重新命名为:pm_static.yml,然后大家就可以按照自己的需求将里面的值进行修改。这里补充一下PM的另一个工作机理,当PM检测到parent应用目录下面有pm_static.yml文件,它就不会再自动去分配存储空间,而是直接使用这个静态的存储空间layout。

7.2 多image的hello_world程序开发

本节所有代码可以到如下百度网盘链接获取,进入“开发你的第一个NCS(Zephyr)应用程序”-> “hello_world”,下载hello_world_ncsv140.rar

我们现在将第6章的hello_world程序跑在多image环境下。现在我们以nrf9160dk_nrf9160ns为例,重新编译一下前述的hello_world程序,由于nrf9160dk_nrf9160ns为非安全应用,前面也说过,所有Cortex-M33非安全应用都会默认使能spm模块,所以spm image将会自动加载进来并进行编译。

装载成功后,你可以看到build和download的target都变成:merged.hex,而不是以前的zephyr.hex,如下:

而且build目录也会多一个spm的build目录,如下所示:

程序跑起来后,log如下所示:

跟第6章一样,我们再定义一个overlay文件,以将uart0关掉,此时我们去测9160dk的电流,应该只有几微安,但实际上我们测下来还是500多微安,这是为什么呢?因为spm还使能了serial和uart0,这个可以从spm的.config和zephyr.dts文件得到验证,如下:

所以uart0在spm中打开了,然后程序跳到app,uart0还是处于打开状态,从而导致功耗偏高。那么我们怎么可以便捷的关闭spm里面的serial模块和uart0?方法一,大家跑到spm例子里面,然后定义前述的prj.conf和<board>.overlay以关闭serial模块和uart0,但这种方法会影响其他例子;方法二,我们在parent应用中定义spm的prj.conf和<board>.overlay文件,这样spm只在这个parent应用中关闭了serial模块和uart0,对其他应用不产生任何影响。为了将parent应用中的conf和overlay文件传给spm,需要用到NCS编译系统的编译变量,给相应的变量赋值,从而可以将相关文件传递给spm,具体请参考8

我们对CMakelists文件做如下修改:

  1. if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/spm.conf")
  2. set(spm_CONF_FILE
  3. prj.conf
  4. ${CMAKE_CURRENT_LIST_DIR}/spm.conf
  5. )
  6. endif()
  7.  
  8. if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/spm.overlay")
  9. set(spm_DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/spm.overlay")
  10. endif()

通过设置spm_CONF_FILE和spm_DTC_OVERLAY_FILE两个变量,我们将spm.conf和spm.overlay两个文件传给了spm image编译过程,从而达到控制spm image编译配置目的。

spm.conf我们定义如下选项:

CONFIG_SERIAL=n
CONFIG_UART_CONSOLE=n

spm.overlay我们定义如下节点:

  1. &uart0 {
  2. status = "disabled";
  3. };

我们再重新装载hello_world程序,可以看到在spm中,serial和uart0都关闭了,如下:

此时再去测量9160dk电流,就降到几微安了。

上面日志要不打印到串口助手,要不打印到RTT viewer,而且都是堵塞式打印。我们现在将打印改成异步的,而且同时打印到串口助手和RTT viewer。为此我们将logging模块打开,同时设置如下Kconfig:

  1. CONFIG_ASSERT=y
  2. CONFIG_ASSERT_LEVEL=2
  3.  
  4. CONFIG_LOG=y
  5. CONFIG_LOG_DEFAULT_LEVEL=2
  6. CONFIG_LOG_BACKEND_RTT=y
  7. CONFIG_LOG_BACKEND_UART=y
  8. CONFIG_LOG_BACKEND_RTT_MODE_DROP=y
  9. CONFIG_LOG_MODE_OVERFLOW=y
  10. CONFIG_LOG_PRINTK=y
  11. CONFIG_LOG_PRINTK_MAX_STRING_LENGTH=256
  12. CONFIG_LOG_BUFFER_SIZE=4096
  13. CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE=256
  14. CONFIG_LOG_STRDUP_BUF_COUNT=64
  15. CONFIG_LOG_STRDUP_MAX_STRING=64
  16. CONFIG_LOG_BACKEND_SHOW_COLOR=n
  17. CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP=n
  18. CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=1024
  19.  
  20. CONFIG_CONSOLE=y
  21. CONFIG_USE_SEGGER_RTT=y
  22. CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
  23. CONFIG_RTT_CONSOLE=y
  24. CONFIG_UART_CONSOLE=y

只需做上述修改,就可以达到我们前述的目的,非常方便(无需添加文件,无需修改include目录,这就是NCS!)。这次大家可以使用nrf5340dk_nrf5340_cpuapp跑一下试试。

注:上述日志配置直接从nrf_desktop拷贝过来,大家以后也可以参考它来使能自己的log模块。

8. NCS编译系统几个要注意的点

8.1 NCS几个重要的编译系统变量

在NCS或者Zephyr编译系统中,有几个变量非常重要,每个人最好掌握他们,把他们使用好,会让你的编译变得得心应手,这几个变量是:

ZEPHYR_BASE,用来指示你的Zephyr代码库的绝对目录,比如取值:C:\Nordic\NCS\SDK\tag\v1.4.0\zephyr

BOARD,用来指定编译用的板子,比如取值:nrf52840dk_nrf52840

CONF_FILE,用来指定项目的conf文件,如果没有指定,默认用prj.conf,详细说明见8.1

DTC_OVERLAY_FILE,用来指定项目的overlay文件,如果没有指定,默认用<board>.overlay,详细说明见8.2

PM_STATIC_YML_FILE,用来指定parent应用,即app的pm_static文件,如果没有指定,默认用pm_static.yml,详细说明见7.1

CMAKE_BUILD_TYPE,命令行可以通过这个变量传递一个参数给CMakelists.txt文件或者其他build过程。

上述变量不分大小写,所以CONF_FILE和conf_file是一样的,其他类同。因为这些变量是针对每一个image的,所以每一个image都有自己的board,conf_file,dtc_overlay_file等。对于单image应用,这个好理解也好区分;那如果是多image应用,该如何区分每个image的conf_file和dtc_overlay_file呢?这可以通过使用image专用变量来实现。如前所述,conf_file这个变量本身是作用于app image的,实际上你可以把这个变量看成:app_conf_file,只不过默认都是app image,所以就把app_省略了。当你需要在parent应用中去设置child应用的conf_file,你就不能直接使用conf_file这个变量了(因为它是用来设置parent应用本身的conf文件),而需使用childImageName_conf_file,比如上面的hello_world程序,我们使用了spm_conf_file这个变量,用来设置子image spm的conf_file。跟conf_file变量一样,dtc_overlay_file变量使用了同样的规则。NCS中目前主要有如下4个child image:

  • mcuboot。可升级的BootLoader
  • b0。不可升级的BootLoader
  • spm。Cortex-M33非安全应用的引导程序,Nordic自己开发的。
  • tfm。trusted-firmware-m,作用跟spm相似,但是符合PSA标准,由第三方开发。

除了上述child image,在编译nRF5340 app核的时候,我们也可以自动包含如下 network核的child image:

  • b0n。nRF5340 network核的b0程序。
  • hci_rpmsg。nRF5340 network核的蓝牙controller
  • 802154_rpmsg。nRF5340 network核的802.15.4 controller

通过上面的childImage名字加上前面的编译系统变量,就可以通过parent应用去控制child应用的编译过程,大大方便了多image的开发流程。

关于上述编译系统变量的使用,大家可以参考:nrf\applications\nrf_desktop和nrf\applications\asset_tracker。

nrf_desktop虽然只有一个CMakeLists.txt,但实际上这个CMakeLists.txt包含了20多个项目,它是怎么做到的呢?它就是通过board和cmake_build_type这两个变量来实现的。比如要设置某一块板子对应的某一个项目的conf文件,它使用了如下语句:

  1. set(CONF_FILE "configuration/${BOARD}/app_${CMAKE_BUILD_TYPE}.conf")

比如说,$BOARD=nrf52840dk_nrf52840,$CMAKE_BUILD_TYPE=ZDebug_keyboard,那么它对应的conf文件就是如下这个:

asset_tracker的CMakeLists.txt文件定义了子image spm的conf文件,以及定义了一个静态的pm文件,如下所示:

  1. set(spm_CONF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/spm.conf)
  2. set(PM_STATIC_YML_FILE
  3. ${CMAKE_CURRENT_SOURCE_DIR}/configuration/${BOARD}/pm_static.yml
  4. )

大家在写自己的multi image应用的时候,可以多借鉴上面的例子,尤其是nrf_desktop,这是一个非常全面的例子,基本上你要的功能,它都可能有参考

8.2 conf文件命名规则及编译顺序

如前所述,可以通过多种方法指定用户自定义Kconfig文件,除了上面讲的prj.conf,符合如下命名标准的conf文件也可以被系统自动加载进来。

  1. 首先读取CONF_FILE变量,我们可以将多个conf文件都赋给这个变量(每个conf文件之间以分号或者空格隔开),这些配置文件最终会合并成一个。我们可以通过三种方式设置CONF_FILE变量

    • 通过命令行方式传递:-DCONF_FILE=<file1.conf;file2.conf>
    • 在CMakeLists.txt中并且必须在调用find_package(Zephyr)之前(也就是包含boilerplate.cmake之前)
    • 通过CMake变量cache
  2. 否则,系统将使用应用目录下的prj_<build>.conf 和boards/<BOARD>_<build>.conf两者的合并结果
  3. 否则,系统将使用应用目录下的prj_<BOARD>.conf
  4. 否则,系统将使用应用目录下的boards/<BOARD>.conf和prj.conf 的合并结果
  5. 否则,系统将使用应用目录下的prj.conf

记住:如果同一个Kconfig选项或者符号被配置多次,以最后一次配置为准

8.3 overlay文件命名规则及编译顺序

系统也可以有多个overlay文件,overlay文件装载的顺序是:

  1. 首先读取DTC_OVERLAY_FILE变量,我们可以同时将多个overlay文件赋给这个变量(每个overlay文件之间以分号或者空格隔开),这些overlay文件最终合并为一个文件。我们可以通过如下方式设置DTC_OVERLAY_FILE变量

    • 通过命令行方式传递:-DDTC_OVERLAY_FILE="file1.overlay;file2.overlay"
    • 在CMakeLists.txt中并且必须在调用find_package(Zephyr)之前(也就是包含boilerplate.cmake之前)
  2. 否则,系统将使用应用目录下的boards/<BOARD>.overlay
  3. 否则,系统将使用应用目录下的<BOARD>.overlay

开发你的第一个NCS(Zephyr)应用程序的更多相关文章

  1. Delphi for iOS开发指南(3):创建一个FireMonkey iOS应用程序

    http://cache.baiducontent.com/c?m=9d78d513d9d431a94f9d92697d60c015134381132ba1d0020fa48449e3732b4b50 ...

  2. nRF Connect SDK(NCS)/Zephyr固件升级详解 – 重点讲述MCUboot和蓝牙空中升级

    如何在nRF Connect SDK(NCS)中实现蓝牙空中升级?MCUboot和B0两个Bootloader有什么区别?MCUboot升级使用的image格式是怎么样的?什么是SMP协议?CBOR编 ...

  3. 一个简单的flask程序

    初始化 所有Flask程序都必须创建一个程序实例. 程序实例是Flask类的对象,经常使用下述代码创建: from flask import Flask app = Flask(__name__) F ...

  4. Web程序员开发App系列 - 开发我的第一个App,源码下载

    Web程序员开发App系列 Web程序员开发App系列 - 认识HBuilder Web程序员开发App系列 - 申请苹果开发者账号 Web程序员开发App系列 - 调试Android和iOS手机代码 ...

  5. ArcGIS API for JavaScript开发环境搭建及第一个实例demo

    原文:ArcGIS API for JavaScript开发环境搭建及第一个实例demo ESRI公司截止到目前已经发布了最新的ArcGIS Server for JavaScript API v3. ...

  6. [android开发IDE]adt-bundle-windows-x86的一个bug:无法解析.rs文件--------rs_core.rsh file not found

    google的android自带的apps写的是相当牛逼的,将其导入到eclipse中方便我们学习扩展.可惜关于导入的资料太少了,尤其是4.1之后的gallery和camera合二为一了.之前导4.0 ...

  7. Android Wear 开发入门——怎样创建一个手机与可穿戴设备关联的通知(Notification)

    创建通知 为了创建在手机与可穿戴设备中都能展现的通知,能够使用 NotificationCompat.Builder.通过该类创建的通知,系统会处理该通知是否展如今手机或者穿戴设备中. 导入必要的类库 ...

  8. 开发你的第一个BLE应用程序—Blinky

    本文将和大家一起编写我们的第一个BLE应用程序:Blinky(闪灯程序),哪怕你之前没有任何BLE开发经验,也不用担心,只要跟着文中所述步骤,你就可以一步步搭建自己的第一个BLE应用程序.通过这个Bl ...

  9. 【转】Esp8266学习之旅① 搭建开发环境,开始一个“hellow world”串口打印。

    @2019-02-28 [小记] Esp8266学习之旅① 搭建开发环境,开始一个“hellow world”串口打印.

随机推荐

  1. Comparator比较器

    Comparator比较器 简介 为什么写? comparator 是javase中的接口,位于java.util包下,该接口抽象度极高,有必要掌握该接口的使用 大多数文章告诉大家comparator ...

  2. Django 多对多表的三种创建方式

    第一种: class Book(models.Model): name = models.CharField(max_length=32) # 第一种自动创建 authors = models.Man ...

  3. 深度学习基础 Probabilistic Graphical Models | Statistical and Algorithmic Foundations of Deep Learning

    目录 Probabilistic Graphical Models Statistical and Algorithmic Foundations of Deep Learning 01 An ove ...

  4. 基于CefSharp开发(一)开发什么?没想好

    一.创建项目 创建WPF (.Net Core)项目 二.CefSharp引用 程序包管理器控制台引入CefSharp Install-Package CefSharp.Wpf -Version 85 ...

  5. day3(django配置跨域)

    1.跨越原理 1. 首先浏览器安全策略限制js ajax跨域访问服务器 2. 如果服务器返回的头部信息中有当前域: // 允许 http://localhost:8080 这个网站打开的页面中的js访 ...

  6. Python爬虫实战案例:取喜马拉雅音频数据详解

    前言 喜马拉雅是专业的音频分享平台,汇集了有声小说,有声读物,有声书,FM电台,儿童睡前故事,相声小品,鬼故事等数亿条音频,我最喜欢听民间故事和德云社相声集,你呢? 今天带大家爬取喜马拉雅音频数据,一 ...

  7. 关于moviepy打包报错AttributeError: module audio/video.fx.all has no attribute fadein、crop文章的纠错和抄袭

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 老猿前面有篇文章<moviepy应用pyin ...

  8. 关于Python链式赋值的赋值顺序问题

    在<第4.7节 Python特色的序列解包.链式赋值.链式比较>一文中,老猿这样介绍的: 链式赋值是用一行语句将多个变量赋值为同一个值,语法如下: 变量1=变量2=变量n=赋值表达式 该语 ...

  9. js 几种跨域解决方法

    同源策略: JS只能与同一个域中的页面进行通讯,必须是协议.域名.端口都相同,相同域下才能相互通信,这可以被认为是一种通信原则,叫同源策略. 跨域: js跨域是指通过js在不同的域之间进行数据传输或通 ...

  10. MySQL事务(一)认识事务

    简单来说,事务就是要保证一组数据库操作,要么全部完成,要么全部失败. 为什么要有事务 数据库中的数据是共享资源,因此数据库系统通常要支持多个用户的或不同应用程序的访问,会出现并发存取数据的现象. 数据 ...