開發你的第一個NCS(Zephyr)應用程式

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進行闡述的,尤其是這篇://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平台)。下載鏈接為//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下載鏈接為://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輸入:

west flash

即可將image下載到network核裡面,如下所示:

  

如果west flash在你的環境跑不通,那麼你可以直接使用nrfjprog來下載network核的image,進入目錄:nrf\samples\bluetooth\peripheral_uart\build_nrf5340dk_nrf5340_cpuapp\hci_rpmsg,然後在cmd輸入如下命令:

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獲得,如下: 

west –help

 

如果你能看到上面的介面,那麼你的環境基本上就沒問題了。

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

west build -b nrf52840dk_nrf52840

註:-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支援多個開發板,我們還需要指定用哪一個開發板,如下所示:

west build -b nrf52840gmouse_nrf52840

或者

west build -b nrf52840gmouse_nrf52840 -p

註:-p用來清除build目錄快取

由於上面沒有指定build目錄,上面的命令會自動生成一個build目錄(名字就是build),然後所有的編譯文件會自動放在該build目錄下。

  

build目錄如下所示:

 

 

 

  

編譯成功後,就可以燒寫了,使用如下命令進行燒寫:

west flash

或者

west flash –erase

 

 

 

 

3.2.2 nRF53項目示例

以nrf\samples\bluetooth\peripheral_uart為例來講解。首先進入目錄:nrf\samples\bluetooth\peripheral_uart,然後輸入如下編譯命令:

west build -b nrf5340dk_nrf5340_cpuapp --build-dir build_nrf5340dk_nrf5340_cpuapp -p

註:-b指定開發板,--build-dir指定編譯目錄,-p清除老的編譯目錄內容

 

編譯成功後,就可以燒寫了,使用如下命令進行燒寫:

west flash --build-dir build_nrf5340dk_nrf5340_cpuapp

  

3.2.3 nRF9160項目示例

以nrf\samples\nrf9160\mqtt_simple為例來講解。首先進入目錄:nrf\samples\nrf9160\mqtt_simple,然後輸入如下編譯命令:

west build -b nrf9160dk_nrf9160ns --build-dir build_nrf9160dk_nrf9160ns -p

註:-b指定開發板,--build-dir指定編譯目錄,-p清除老的編譯目錄內容

 

編譯成功後,就可以燒寫了,使用如下命令進行燒寫:

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目錄,輸入

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,指令如下所示:

git checkout v1.4.0

然後通過west update命令,就可以讓其他程式碼庫版本自動跟v1.4.0 nrf程式碼庫同步,這樣你不需要記住或者管理其他程式碼庫版本,只需管理nrf程式碼庫版本即可。

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 命令行方式查看

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

west build -t menuconfig

執行上述命令後,將顯示如下介面:

  

註:上述命令需要先安裝windows-curses ,即在cmd中執行如下命令:pip install windows-curses –user,此命令的執行需要聯網。如果你的電腦無法聯網,建議使用guiconfig查看工程配置,其對應的命令為:

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輸入:

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管理系統,非常方便你進行版本管理。我們做如下修改,以循環列印同一條日誌:

  

void main(void)
{
    while(1)
    {
        printk("Hello World! %s\n", CONFIG_BOARD);
        k_sleep(K_SECONDS(1));
    }
}

 

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加進來了:

  

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文件已經添加成功了,我們再對其進行修改,修改程式碼如下所示:

  

void blinky_thread(void)
{
    const struct device *dev;
    bool led_is_on = true;
    int ret;

    dev = device_get_binding(LED0);
    if (dev == NULL) {
        return;
    }

    ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
    if (ret < 0) {
        return;
    }

    while (1) {
        gpio_pin_set(dev, PIN, (int)led_is_on);
        led_is_on = !led_is_on;
        k_msleep(SLEEP_TIME_MS);
    }
}

K_THREAD_DEFINE(blinky_thread_id, 1024, blinky_thread, NULL, NULL,
        NULL, 7, 0, 0);

如上,我們直接把blinky程式碼變成另外一個執行緒以達到我們的目的(當然你也可以把blinky程式碼變成一個模組,以供hello_world的main調用,這裡就不演示這種方式了)。

6.4 編譯和運行你的第一個hello_world程式

上述修改結束後,我們可以使用任何Zephyr標準板運行這個例子,以nrf52840dk為例,SES工程裝載如下所示,然後編譯和下載。

  

或者使用west命令進行編譯和下載,命令如下所示:

west build -b nrf52840dk_nrf52840 -d build_nrf52840dk_nrf52840 -p
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裡面做如下修改即可達到同樣的目的:

  

CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

運行後結果如下所示:

  

按道理說,上面的例子已經把日誌輸出改為RTT Viewer了,這時去測量nrf52840dk電流,應該很低才對,但實際上此時電流大概為500多微安。這是什麼原因呢?我們看一下zephyr.dts文件,如下:

  

可以看出,uart0和uart1都處於使能狀態,從而導致功耗偏高。我們通過overlay文件,將uart0和uart1關閉,修改如下所示:

  

同時在prj.conf把serial模組關掉(註:serial模組在所有項目默認是打開的),如下

CONFIG_SERIAL=n

實際上,serial模組只使用了uart0模組,所以只需把uart0關掉即可,如下: 

&uart0 {
    status = "disabled";    
};

重新編譯和下載,這時我們再去測量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文件做如下修改:

  

if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/spm.conf")
  set(spm_CONF_FILE
    prj.conf
    ${CMAKE_CURRENT_LIST_DIR}/spm.conf
  )
endif()

if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/spm.overlay")
  set(spm_DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/spm.overlay")
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我們定義如下節點:

&uart0 {
    status = "disabled";    
};

我們再重新裝載hello_world程式,可以看到在spm中,serial和uart0都關閉了,如下:

  

此時再去測量9160dk電流,就降到幾微安了。

上面日誌要不列印到串口助手,要不列印到RTT viewer,而且都是堵塞式列印。我們現在將列印改成非同步的,而且同時列印到串口助手和RTT viewer。為此我們將logging模組打開,同時設置如下Kconfig:

  

CONFIG_ASSERT=y
CONFIG_ASSERT_LEVEL=2

CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=2
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_UART=y
CONFIG_LOG_BACKEND_RTT_MODE_DROP=y
CONFIG_LOG_MODE_OVERFLOW=y
CONFIG_LOG_PRINTK=y
CONFIG_LOG_PRINTK_MAX_STRING_LENGTH=256
CONFIG_LOG_BUFFER_SIZE=4096
CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE=256
CONFIG_LOG_STRDUP_BUF_COUNT=64
CONFIG_LOG_STRDUP_MAX_STRING=64
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP=n
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=1024

CONFIG_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
CONFIG_RTT_CONSOLE=y
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文件,它使用了如下語句:

  

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文件,如下所示:

 

 

 

 

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

大家在寫自己的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