女人被狂躁到高潮视频免费无遮挡,内射人妻骚骚骚,免费人成小说在线观看网站,九九影院午夜理论片少妇,免费av永久免费网址

當前位置:首頁 > 嵌入式 > 《嵌入式技術與智能系統(tǒng)》
[導讀]Zephyr開源項目由Linux基金會維護,是一個針對資源受限的嵌入式設備優(yōu)化的小型、可縮放、多體系結構實時操作系統(tǒng)(RTOS)。近年來,Zephyr RTOS在嵌入式開發(fā)中的采用度逐步增加,支持的開發(fā)板和傳感器不斷增加,其廣泛的設備支持和高度的可擴展性吸引了開發(fā)者的關注。相比FreeRTOS等小型RTOS而言,教育生態(tài)不夠成熟的Zephyr系統(tǒng)規(guī)模更大,結構更復雜,這提高了開發(fā)者入門和精通的門檻。文章對Zephyr硬件抽象層和設備驅(qū)動的架構與實現(xiàn)進行系統(tǒng)性分析,重點闡述了設備驅(qū)動模型和設備樹的作用。為了展示基于Zephyr的嵌入式軟件開發(fā),文章在BBC micro:bit V2開源硬件上構建樣例Zephyr設備驅(qū)動和應用程序,并做解釋和驗證。



1. 引言

Zephyr是由Linux基金會管理的開源實時操作系統(tǒng)(RTOS) [1],其前身為用于數(shù)字信號處理的Virtuoso操作系統(tǒng),后被風河(Wind River)收購,更名為Rocket RTOS。2016年它成為了Linux基金會的項目,更名為Zephyr。

Zephyr得到了多家半導體企業(yè)的支持,包括恩智浦、意法半導體、瑞薩、北歐半導體(Nordic)、英特爾和德州儀器等,并已經(jīng)被應用到了眾多設備中,覆蓋了消費電子、能源、醫(yī)療、工業(yè)、農(nóng)業(yè)等領域[2]。Zephyr的Apache 2.0開源協(xié)議授權讓它在非商用和商用解決方案中都可免費使用[3]。

近年來Zephyr的熱度逐漸上升,在嵌入式開發(fā)中的采用度逐步增加。Eclipse基金會的《2024年物聯(lián)網(wǎng)和嵌入式開發(fā)者調(diào)查報告》表明,在資源受限設備上使用Zephyr的開發(fā)者從2022年的8%增長到了2024年的21%,這已經(jīng)和裸機直接編程的比例相當,也非常接近第二位的FreeRTOS (29%) [4]。

相比FreeRTOS等小型RTOS而言,教育生態(tài)不夠成熟的Zephyr系統(tǒng)規(guī)模更大,結構更復雜,這提高了開發(fā)者入門和精通的門檻。本文對Zephyr硬件抽象層和設備驅(qū)動的架構與實現(xiàn)進行系統(tǒng)性分析,重點闡述了設備驅(qū)動模型和設備樹的作用。為了展示基于Zephyr的嵌入式軟件開發(fā),本文在BBC micro:bit V2開源硬件上構建樣例Zephyr設備驅(qū)動和應用程序,并做解釋和驗證。

2. Zephyr的硬件抽象層和配置概述

Zephyr有著完善的設備驅(qū)動支持,而且高度可配置。作為Linux基金會的項目,它用到了和Linux內(nèi)核類似的工具,特別是設備樹(Device Tree)和Kconfig配置語言。本章將對與開發(fā)息息相關的硬件抽象化和配置進行概述。

2.1. 設備驅(qū)動模型

Zephyr的設備驅(qū)動模型負責初始化系統(tǒng)中所有的驅(qū)動程序,為系統(tǒng)中的所有設備驅(qū)動提供了統(tǒng)一的配置方法[5]。如圖1所示的是設備驅(qū)動模型的概覽。

Zephyr中每一種子系統(tǒng)驅(qū)動(UART、I2C等)都有著泛用類型(Generic Type,非設備特定)的接口,具體的驅(qū)動實現(xiàn)會提供實現(xiàn)這些驅(qū)動接口函數(shù)的指針。在圖1中可以看到,在子系統(tǒng)2中有兩種設備驅(qū)動的實例,但是兩種驅(qū)動都會提供泛用API 1到3的實現(xiàn)。應用程序代碼可以在兼容的設備上直接使用泛用API,具體驅(qū)動的實現(xiàn)代碼會被調(diào)用。如子系統(tǒng)1中所示,同一種驅(qū)動可以在系統(tǒng)中多次實例化,比如多個UART接口。

設備驅(qū)動代碼在初始化時也會為每個設備提供驅(qū)動特定的配置,即圖1中的struct config。在實際代碼中這可能是通過Kconfig配置的參數(shù),比如顯示器的刷新頻率。驅(qū)動代碼還可以為每個驅(qū)動指定一個結構用于存儲相關的數(shù)據(jù)。

Figure 1. An overview of the device driver model (source: zephyrproject.org)

1. 設備驅(qū)動模型概覽(來源:zephyrproject.org)

一個驅(qū)動的泛用接口定義會出現(xiàn)在驅(qū)動的頭文件中,圖2中定義了subsystem子系統(tǒng)的泛用接口subsystem_do_this和subsystem_do_that函數(shù)。圖3中的my_driver驅(qū)動實現(xiàn)了自己的do_this和do_that函數(shù),并將它們的指針填入了驅(qū)動API結構(do_this和do_that成員)。注意應用程序代碼應該直接使用subsystem_do_this/that函數(shù),這兩個函數(shù)會通過DEVICE_API_GET宏進入正確的驅(qū)動接口實現(xiàn),即my_driver_do_this/that函數(shù)。在實際的驅(qū)動中,subsystem會被替代為能夠代表設備的名稱,例如在通用的顯示驅(qū)動接口(include/zephyr/drivers/display.h)中,subsystem被替代為了display。

Figure 2. A sample driver interface definition (source: zephyrproject.org)

2. 樣例驅(qū)動接口定義(來源:zephyrproject.org)

Figure 3. A sample driver implementation (source: zephyrproject.org)

3. 樣例驅(qū)動實現(xiàn)(來源:zephyrproject.org)

在進行具體子系統(tǒng)驅(qū)動的實例化時,驅(qū)動代碼還會提供初始化代碼和初始化的優(yōu)先級。

2.2. 設備樹

設備樹(Device Tree)是用于描述硬件的層級化數(shù)據(jù)結構。設備樹規(guī)范[6]描述了設備樹的概念、用途、結構、設備樹綁定(binding)和設備樹語言。

2.2.1. 設備樹的作用

Zephyr和Linux同樣使用設備樹,Zephyr為了減少運行時的數(shù)據(jù)和代碼,會使用設備樹的數(shù)據(jù)產(chǎn)生C語言頭文件[7]。Zephyr中定義了一整套宏,用于訪問設備樹節(jié)點和取得設備樹節(jié)點的屬性。

Zephyr中設備樹有兩項主要作用:

  • 在設備驅(qū)動模型中描述硬件。

  • 提供硬件的初始配置。

設備樹和Kconfig在Zephyr中都起到了配置語言的作用,設備樹用于描述硬件和啟動時的配置,Kconfig則主要用于配置軟件。

設備樹有兩種輸入文件:設備樹源文件和設備樹綁定[8]。源文件描述了設備樹本身,綁定則用于描述設備樹的內(nèi)容,特別是數(shù)據(jù)類型和結構。Zephyr在構建時使用這兩種文件生成C頭文件,devicetree.h頭文件提供通用的宏訪問設備樹(以“DT_”打頭)。

2.2.2. 設備樹的語法

圖4所示的是一個最小的樣例設備樹源文件[9]:

Figure 4. A minimum device tree file (source: zephyrproject.org)

4. 設備樹最小樣例(來源:zephyrproject.org)

圖中“/”代表根節(jié)點,a-node是根節(jié)點的子節(jié)點,a-sub-node是a-node的子節(jié)點,a-sub-node還有一個label (標簽) subnode_nodelabel。標簽是可選的,在設備樹中每個標簽只能出現(xiàn)一次,代碼可以通過標簽直接訪問節(jié)點。每個節(jié)點都有自己的路徑,和Linux文件路徑相似,例如a-sub-node的全路徑為:/a-node/a-sub-node。

圖5所示的是一個較為貼近實際硬件的設備樹樣例:

Figure 5. A complete device tree example (source: zephyrproject.org)

5. 一個完整的設備樹樣例(來源:zephyrproject.org)

圖5中可以看到節(jié)點名的命名方法為“總線類型或設備名@地址”,這樣的慣例不僅有助于區(qū)分類似的節(jié)點,還能夠幫助快速確定節(jié)點指向的設備和總線類型。地址的慣例根據(jù)設備類型有所不同:

  • 在內(nèi)存中映射的外設:使用寄存器映射的基地址,例如i2c@40003000表示I2C映射的寄存器基地址為0x40003000。

  • I2C外設:使用外設在I2C總線上的地址,例如apds9960的I2C地址為0x39。

  • SPI外設:使用外設的片選線序號,如果沒有則使用0。

  • 內(nèi)存:使用物理內(nèi)存的起始地址,例如memory@2000000表示從0x2000000物理地址開始的RAM。

  • 在內(nèi)存中映射的閃存:和RAM類似使用物理起始地址,例如flash@8000000。

  • 固定的閃存分區(qū):使用分區(qū)的偏移量,例如在flash@8000000設備中可以有一個partitions節(jié)點代表分區(qū)表,其中有partition@0和partition@20000兩個節(jié)點,分別意味著起始地址0x8000000和0x8020000的兩個分區(qū)。

設備樹節(jié)點中每個屬性有一個名稱和一個值,屬性的值可以是字符串、整型數(shù)、布爾值、8位整型數(shù)組、字符串數(shù)組、混合類型數(shù)組、指向節(jié)點的phandle (類似C語言中的指針)、復數(shù)的phandle或是phandle數(shù)組。

設備樹節(jié)點中幾個重要的屬性如下:

  • compatible:表示節(jié)點所代表的硬件設備,本文翻譯為兼容名。兼容名屬性在構建過程中十分重要,驅(qū)動程序通過兼容名的值查找可以適配的硬件。兼容名的值可以是字符串數(shù)組,將數(shù)個驅(qū)動程序從最特定到最泛用進行排列,首個匹配的驅(qū)動程序會被加載。

  • reg:用于設備尋址,其格式為16進制的<地址,長度>。

  • status:用于表示節(jié)點是否啟用。Zephyr支持“okay”和“disabled”,分別表示啟用和禁用。節(jié)點必須啟用,Zephyr的驅(qū)動模型才會應用到節(jié)點上。

除了標簽,設備樹源文件中還可以定義chosen (選擇)和alias (別名)來幫助應用代碼或驅(qū)動尋找特定的節(jié)點,如圖6所示。

Figure 6. Use chosen and aliases nodes in a device tree file (source: zephyrproject.org)

6. 在設備樹中使用chosen和aliases節(jié)點(來源:zephyrproject.org)

圖中/alias和/chosen節(jié)點都不指向?qū)嶋H的硬件設備,它們被用來指定設備樹中的其他節(jié)點:my-uart是/soc/serial@12340000路徑的別名(uart0標簽名),uart0標簽還被選為“zephyr, console”。選擇和別名可以幫助抽象化不同的開發(fā)板,例如閃燈樣例(samples/basic/blinky/src/main.c)中使用led0別稱節(jié)點達到支持多種開發(fā)板的目的,只要開發(fā)板的設備樹文件中有別稱為led0的節(jié)點,樣例即可運行。

Zephyr中每個支持的開發(fā)板都有自己的主設備樹文件,micro:bit V2的文件位于路徑boards/bbc/microbit_v2/bbc_microbit_v2.dts,其中可以看到GPIO按鈕、LED顯示矩陣、I2C總線和I2C總線上的傳感器等硬件。應用也可以提供專門針對開發(fā)板的設備樹覆蓋文件,路徑為“<應用或模塊路徑>/boards/<開發(fā)板名>.overlay”。覆蓋文件中可以增加新的選擇/別名節(jié)點,也可以配合新的設備樹綁定文件(見下節(jié))增加節(jié)點。

2.2.3. 設備樹綁定

設備樹自身的結構相對自由,需要有設備樹綁定才能夠正確、完整地描述硬件[10]。設備樹綁定中包含對設備樹節(jié)點格式和內(nèi)容的要求。Zephyr使用YAML文件存儲設備樹綁定。

圖7所示的是一個樣例綁定文件[11]:

Figure 7. A sample device tree binding file (source: Martin Lampacher’s code on GitHub)

7. 一個樣例設備樹綁定文件(來源:Martin Lampacher在GitHub上的代碼)

圖7中可以看到3個重要的鍵值[12]:

  • description (描述):描述綁定文件適配的硬件的字符串。

  • compatible (兼容名):和設備樹中的兼容名對應,一個綁定文件的兼容名如果和一個設備樹節(jié)點一致,則該設備樹節(jié)點的格式應當符合綁定文件的內(nèi)容。

  • properties (屬性):描述了符合綁定的節(jié)點中的屬性與格式。

圖8所示的是設備樹節(jié)點符合圖7中的定義:

Figure 8. A device tree node that is compatible with the binding (source: Martin Lampacher’s code on GitHub)

8. 符合綁定文件的設備樹節(jié)點(來源:Martin Lampacher在GitHub上的代碼)

圖8中可以看到:

  • 節(jié)點的兼容名和綁定的一致。

  • 每一個屬性都有按照綁定中type的類型賦值。

Zephyr中默認包括的綁定文件位于dts/bindings子目錄下,按照類型進行分類,以兼容名的名稱進行命名。

除非向Zephyr中添加新的硬件支持,一般開發(fā)中不添加新的綁定文件。需要時應用可以增加新的綁定文件(<應用或模塊路徑>/dts/bindings/<兼容名>.yaml),并在設備樹覆蓋文件中添加符合綁定定義的節(jié)點。

2.2.4. 在程序中訪問設備樹節(jié)點和屬性

從C/C++應用代碼中可以用多種方式訪問設備樹節(jié)點。

Figure 9. Methods to access a device tree node (source: zephyrproject.org)

9. 訪問設備樹節(jié)點的方法(來源:zephyrproject.org)

圖9為例,多種宏都可以得到i2c@40002000節(jié)點(注意:將所有不是字母數(shù)字的字符替換為下劃線):

  • DT_PATH(soc, i2c_40002000):將全路徑以逗號隔開,省略所有“/”。

  • DT_NODELABEL(i2c1):使用標簽名。

  • DT_ALIAS(sensor_controller):使用別名。

  • DT_INST(x, vnd_soc_i2c):尋找第x個兼容名為“vnd,soc-i2c”的節(jié)點。在本例中因為只有一個節(jié)點,x應為0。在多“vnd, soc-i2c”節(jié)點的情況下,x和設備樹中節(jié)點的對應關系不能保證。

對于chosen節(jié)點(圖9中不包括),使用DT_CHOSEN指定節(jié)點,例如針對圖6中的設備樹可以使用“DT_CHOSEN(zephyr_console)”。

注意:上述宏不能用于變量,只能用于宏定義。

DT_NODE_HAS_PROP宏可以用于檢測節(jié)點是否有特定屬性,例如 “DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), clock_frequency)”的值為1。訪問節(jié)點的屬性時使用DT_PROP宏,例如“DT_PROP(DT_PATH(soc, i2c_40002000), clock_frequency)”的值為100000。DT_PROP的值可以用于變量初始化或是靜態(tài)定義。

Zephyr定義了眾多與設備樹相關的宏,在官方文檔中有分類總結。在開發(fā)中請根據(jù)需要查閱文檔,并參考Zephyr豐富的開發(fā)板/傳感器樣例庫。

2.3. Kconfig配置工具

Kconfig是在構建時配置Zephyr內(nèi)核和子系統(tǒng)的主要方式,Kconfig也是Linux內(nèi)核的配置系統(tǒng)。Zephyr中的Kconfig配置選項按照文件夾的層級結構分布,從Zephyr代碼庫根目錄的Kconfig.zephyr文件開始。根Kconfig文件用包含(include)語句包括了子系統(tǒng)(例如內(nèi)核、驅(qū)動和代碼庫)的Kconfig文件,子系統(tǒng)還可以進一步深入定義更深層的Kconfig結構和選項。

開發(fā)板和應用可以指定需要啟用的配置。BBC micro:bit V2板的默認選項位于文件boards/bbc/microbit_v2/bbc_microbit_v2_defconfig中,包括系統(tǒng)時鐘、串口和控制臺等選項。每個應用中的prj.conf則包含了應用所需的選項。

與Linux類似,在Zephyr中可以通過命令行界面進行Kconfig選項配置[13]。針對應用構建后產(chǎn)生的build文件夾運行命令“west build --build-dir ./build -t menuconfig”即可進入命令行界面(見圖10)。

Figure 10. Kconfig menuconfig interface

10. Kconfig配置命令行界面

在界面中可以通過方向鍵和ESC/空格鍵進行導航,在選項上通過空格鍵進行選擇。修改選項后D鍵保存最小配置到文件,也就是當前界面中定義的Kconfig選項和Zephyr定義的開發(fā)板默認選項的區(qū)別。圖11所示的是micro:bit V2 LED矩陣顯示樣例的輸出結果(第3.1節(jié)會使用這一樣例):

Figure 11. Kconfig minimum config output

11. Kconfig最小配置輸出

對比前面提到的開發(fā)板默認Kconfig選項和應用添加的選項(samples/boards/bbc/microbit/display/prj.conf),可以看到只有“CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS”選項是上述兩個文件中沒有包括的,這是因為北歐半導體的HAL層自動定義了這一選項(modules/hal_nordic/nrfx/nrfx_kconfig.h)。除了這樣的例外情況,一般在命令行界面中選中了新的選項,用最小選項輸出就可以幫助確定新的選項名,之后就可以將其加入到prj.conf文件中,從而在編譯過程中包括這一選項。

Kconfig選項除了用于開啟子系統(tǒng)功能之外,也用于配置驅(qū)動、應用代碼,以及下一章將要講解的日志系統(tǒng)。在代碼中可以用“CONFIG_”宏取得配置值,構建用的CMakeLists.txt文件也可以用“CONFIG_”讀取配置值。

3. 基于Zephyr的嵌入式應用開發(fā)

本章中,我們將結合樣例在Zephyr上實踐嵌入式應用開發(fā),幫助理解上一章中的理論。

3.1. 環(huán)境配置和運行第一個程序

首先,跟隨Zephyr項目入門指南完成環(huán)境配置、Zephyr和Zephyr SDK的安裝。總體來說,Zephyr在Linux中的安裝和配置步驟最為簡潔,推薦在Ubuntu Linux上進行Zephyr的實驗和開發(fā)。本章提及的命令和環(huán)境細節(jié)均以在Ubuntu 24.04版本上使用Zephyr 4.0.99開發(fā)版本為準,運行時使用BBC micro:bit V2開發(fā)板(見圖12)。

Figure 12. micro:bit V2 board (source: microbit.org [14])

12. micro:bit V2板(來源:microbit.org [14])

Zephyr的樣例庫中包括眾多開發(fā)板和傳感器的樣例,不過指南中提到的閃燈樣例(Blinky,路徑samples/basic/blinky)并不能直接套用在micro:bit V2上。此處我們采用micro:bit V2的LED矩陣顯示樣例(路徑samples/boards/bbc/microbit/display)。連接開發(fā)板到Ubuntu系統(tǒng)上,運行圖13中的命令進行編譯和燒錄。命令中的“-p”選項意味著進行全新編譯,當對工程進行重復編譯時使用“-p auto”選項允許west工具只對更改的部分進行重新編譯,這適合在開發(fā)迭代時節(jié)約時間。

Figure 13. Commands to compile and flash the sample onto the micro:bit V2 board

13. 針對micro:bit V2板編譯和燒錄的命令

成功后開發(fā)板會自動啟動Zephyr,開發(fā)板背后(見圖12,有BBC micro:bit v2字樣的面為正面) 5乘5的LED矩陣會顯示數(shù)字倒計時9到0,然后是LED的逐行逐列“行軍”,最后開始持續(xù)滾動顯示“Hello Zephyr!”的字樣。

該實例展示了較為復雜的單組件運作,從主函數(shù)(samples/boards/bbc/microbit/display/src/main.c)可以看到樣例通過一個針對micro:bit板專用的中間層(drivers/display/mb_display.c)對泛用的顯示驅(qū)動(頭文件zephyr/drivers/display.h)進行擴充,實現(xiàn)了大多數(shù)的功能,例如初始化、打印數(shù)字或字母,以及按照0/1矩陣點亮LED等。

3.2. 閃燈樣例和設備樹問題

上一節(jié)提到,Zephyr的閃燈樣例在micro:bit V2上不能運行,本節(jié)讓我們了解其背后的理由和如何修復與設備樹相關的問題。

運行圖14所示的命令嘗試編譯閃燈樣例:

Figure 14. Commands to compile the blinky sample

14. 編譯閃燈樣例的命令

運行的結果是圖15所示的編譯錯誤:

Figure 15. Compile error of the blinky sample

15. 閃燈樣例的編譯錯誤

第2.2.4節(jié)中提到,Zephyr提供一整套設備樹宏,本例中GPIO代碼使用的DT_ALIAS宏不能完全展開。Zephyr中設備樹宏錯誤的原因一般都與編譯錯誤中提到的頭文件無關,而是設備樹有格式/內(nèi)容的錯誤,或者訪問設備樹的方式有誤。幾種常見的錯誤如下:

  • 混淆了選擇、別名、標簽名和節(jié)點名,或者輸入了錯誤的字符串(例如沒有將非字母數(shù)字的字符轉(zhuǎn)換為下劃線)。

  • 在硬件特定的宏中(例如圖15的GPIO_DT_SPEC_GET需要指向一個GPIO phandle節(jié)點)使用了不同硬件的節(jié)點。

  • 設備樹的節(jié)點和綁定的格式要求不一致,導致節(jié)點未能生成正確的頭文件,因此應用或者驅(qū)動中的宏無法展開。注意:這和簡單的設備樹語法錯誤不同,語法問題在編譯設備樹時就會導致編譯失敗,內(nèi)容的問題則可能導致在應用代碼中無法使用特定屬性或宏。

  • 使用了錯誤的宏組合或者宏的參數(shù)錯誤,特別是For-Each循環(huán)宏和硬件特定的宏。

打開micro:bit V2的設備樹文件(boards/bbc/microbit_v2/bbc_microbit_v2.dts),可以看到aliases節(jié)點下沒有l(wèi)ed0,缺少led0別名導致了編譯的失敗[15]。

micro:bit V2的LED矩陣由十個GPIO輸出控制,個別改變一個控制引腳(pin)并不能點亮LED。紅色的電源指示燈和黃色的USB指示燈也并沒有連接到GPIO上,因此只是依靠開發(fā)板本身,我們并不能通過擴展設備樹簡單地修改好閃燈樣例。不過,micro:bit V2可以外接LED,將外接LED的GPIO添加到設備樹中就可以修復閃燈樣例。

添加設備樹覆蓋文件samples/basic/blinky/boards/bbc_microbit_v2.overlay修復編譯錯誤[16],見圖16

Figure 16. The device tree overlay file to fix the compilation error

16. 修復編譯錯誤的設備樹覆蓋文件

可以看到文件增加了一個兼容名為gpio-leds的節(jié)點leds,然后為含有GPIO信息的led_0子節(jié)點增加別名led0。gpio-leds的驅(qū)動(drivers/led/led_gpio.c)提供了開關和設定亮度的接口,不過在閃燈樣例中,代碼(samples/basic/blinky/src/main.c)只是通過GPIO_DT_SPEC_GET宏從設備樹取得了GPIO引腳的信息,然后直接使用gpio_pin_toggle_dt切換GPIO輸出狀態(tài)。

對比主設備樹文件的edge_connector (邊緣連接器)節(jié)點和開發(fā)板的引腳圖[17]可以看到,圖16中gpio0接入點引腳4對應P2引腳(開發(fā)板下側(cè)標記2的金手指),運行時如果有連接外接LED,閃燈樣例就能夠運行。

類似的設備樹覆蓋文件方法,只要正確地修改GPIO接入點和引腳號,也可以讓沒有l(wèi)ed0別名的開發(fā)板支持閃燈樣例。

3.3. 樣例應用和詳解

本節(jié)將使用基于官方樣例[18]改編的樣例應用。除了主程序代碼還包括:

  • 一個簡單的自定義代碼庫(accel):從3-軸加速度傳感器取得加速度數(shù)值,該庫可以通過Kconfig啟用或禁用。

  • 一個簡單的自定義LED矩陣驅(qū)動層(ledmatrix):不使用Zephyr的顯示驅(qū)動,手動通過GPIO點亮單個行或列的LED,該驅(qū)動層可以通過Kconfig啟用/禁用和配置。

  • 設備樹覆蓋文件:用于輔助自定義代碼庫和LED矩陣驅(qū)動層,并展示簡單的設備樹功能。

驅(qū)動、代碼庫和主函數(shù)各自配置了日志模塊,可以通過Kconfig配置日志級別。

3.3.1. 3-軸加速度傳感器的代碼調(diào)用

從主設備樹文件上可以看到,micro:bit V2上內(nèi)建了ST的lsm303agr 3-軸加速度傳感器(見圖17)。在樣例應用中,custom-module/lib/accel/accel.c源代碼和custom-module/include/app/lib/accel.h頭文件將尋找傳感器設備和從傳感器設備取得3-軸加速度值的功能包裝到了一個簡單的自定義庫accel中。

Figure 17. micro:bit V2 device tree file snippet (source: Zephyr on GitHub)

17. micro:bit V2設備樹文件片段(來源:Zephyr GitHub代碼庫)

accel庫代碼中,尋找傳感器設備的get_accel_device函數(shù)通過別名accel尋找設備樹中的加速度傳感器設備,這一別名在micro:bit V2的主設備樹文件中并不存在(其中只有accel0),而是由樣例應用設備樹覆蓋文件(app/boards/bbc_microbit_v2.overlay)提供的。其中增加了accel別名,指向標簽為lsm303agr_accel的節(jié)點。

設備樹覆蓋文件能在開發(fā)板的主設備樹文件上進行增添和修改,它的幾項用途如下[19]:

  • 增加別名(本例的accel)或者選擇。

  • 覆寫已有節(jié)點的屬性值,例如更改串口的數(shù)據(jù)速率。

  • 刪除節(jié)點的一個屬性。

  • 增加子節(jié)點,例如總線上新的子設備。

回到accel.c代碼中,get_accel_values函數(shù)用于獲取3-軸加速度值,其中sensor_sample_fetch和sensor_channel_get函數(shù)調(diào)用完成了樣本刷新和取樣本值的功能。了解它們是如何針對特定的傳感器完成代碼調(diào)用的,能夠幫助我們更加深入地理解Zephyr的設備驅(qū)動模型(第2.1節(jié))。

sensor_sample_fetch和sensor_channel_get函數(shù)均為泛用傳感器驅(qū)動API,從Zephyr代碼庫頭文件include/zephyr/drivers/sensor.h可以看到兩個函數(shù)會分別調(diào)用設備驅(qū)動API sample_fetch和channel_get函數(shù)。設備樹中設備的兼容名決定了適配的驅(qū)動程序。在設備樹文件中,傳感器的兼容名有兩個:“st,lis2dh”和“st,lsm303agr-accel”。驅(qū)動的適配順序是先查找第一個兼容名,在Zephyr代碼中搜索st_lis2dh (非字母數(shù)字的字符替代為下劃線),可以找到drivers/sensor/st/lis2dh/lis2dh.c文件包含定義驅(qū)動的語句“#define DT_DRV_COMPAT st_lis2dh”。圖18所示的是該驅(qū)動的驅(qū)動API結構定義:

Figure 18. lis2dh device driver API definition (source: Zephyr on GitHub)

18. lis2dh設備驅(qū)動的API定義(來源:Zephyr GitHub代碼庫)

可以看到該驅(qū)動將lis2dh_sample_fetch和list2dh_channel_get函數(shù)的指針指定為設備sample_fetch和channel_get API的實現(xiàn)。lis2dh驅(qū)動支持I2C和SPI總線,在主設備樹文件中可以看到,micro:bit V2中的傳感器是在i2c總線上的。圖19所示的是lis2dh驅(qū)動的部分初始化代碼:

Figure 19. lis2dh device driver initialization code (source: Zephyr on GitHub)

19. lis2dh設備驅(qū)動初始化代碼(來源:Zephyr GitHub代碼庫)

代碼通過DT_INST_FOREACH_STATUS_OKAY宏,對每一個狀態(tài)為okay的兼容設備擴展LIS2DH_DEFINE宏,后者會通過DT_INST_ON_BUS判斷設備是否在spi總線上,如果是,就進一步擴展LIS2DH_DEFINE_SPI初始化驅(qū)動,否則會擴展LIS2DH_DEFINE_I2C宏(micro:bit V2的情況)。那么,設備樹是如何讓DT_INST_ON_BUS能夠進行判定的呢?

micro:bit V2設備樹中傳感器所在的i2c節(jié)點兼容名為“nordic,nrf-twim”,從其綁定文件dts/bindings/i2c/nordic,nrf-twim.yaml中可以看到,文件包含(include)了nordic,nrf-twi-common.yaml (同文件夾下),然后該文件又進一步包含了i2c-controller.yaml,在這一文件中終于看到了“bus: i2c”的信息。也就是說,從設備樹綁定可以得知傳感器從屬于使用i2c總線的控制器。

由于lis2dh驅(qū)動能夠被正確地配置,系統(tǒng)不會查找兼容“st,lsm303agr-accel”的驅(qū)動。在運行時,accel代碼庫中的sensor_sample_fetch和sensor_channel_get函數(shù)會調(diào)用st_lis2dh驅(qū)動的函數(shù)。

在Zephyr的在線文檔中,通過兼容名可以找到設備樹綁定的參考頁面,例如本例中的驅(qū)動文檔標題為“st,lis2dh (on i2c bus)”。

3.3.2. 設備樹綁定和自定義驅(qū)動

在樣例應用中,自定義的ledmatrix驅(qū)動(custom-module/drivers/ledmatrix/ledmatrix.c)使用GPIO在LED矩陣上實現(xiàn)了簡單點亮矩陣邊緣一排或一行5枚LED的功能。在前一節(jié)中提到,驅(qū)動需要匹配到設備樹的設備節(jié)點上。本例中我們創(chuàng)建了自定義的“custom-ledmatrix”兼容名和其綁定,以及l(fā)edmatrix驅(qū)動實現(xiàn)。

圖20圖21所示的分別是custom-ledmatrix設備樹綁定文件(custom-module/dts/bindings/custom-ledmatrix.yaml)和micro:bit V2設備樹覆蓋文件中的對應節(jié)點:

Figure 20. The device tree binding file for custom-ledmatrix

20. custom-ledmatrix設備樹綁定文件

Figure 21. The custom-ledmatrix device tree node

21. cutstom-ledmatrix設備樹節(jié)點

圖20中可以看到,custom-ledmatrix綁定中有兩個GPIO引腳phandle數(shù)組,分別代表LED矩陣的行GPIO引腳(推挽)和列GPIO引腳(開漏) [20]。在圖21中,注意到GPIO接入點、引腳號和邏輯電平模式與開發(fā)板主設備樹文件中“l(fā)ed_matrix”節(jié)點(兼容名“nordic,nrf-led-matrix”)是一致的[21]。樣例中我們使用GPIO在不使用動態(tài)刷新的情況下進行亮、滅燈,所以不需要其他的屬性。

需要特別注意的是,為了表示phandle每個說明符(specifier)成員的長度(例如GPIO除了接入點之外需要提供兩個數(shù)據(jù)成員),在綁定中一般應提供名稱為“#*-cells”的屬性。不過由于GPIO類phandle十分常見,只要屬性的命名以“-gpios”結尾,如本例中的led-row-gpios和led-col-gpios,就不需要提供這一屬性。關于“#*-cells”屬性的細節(jié)詳見官方文檔。

圖21中還可以看到設定設備狀態(tài)就緒的語句(status為“okay”),節(jié)點能夠使用該屬性是因為綁定文件包含了base.yaml。上一節(jié)中提到,標記設備就緒對于驅(qū)動的初始化是必須的,例如圖19中用到的DT_INST_FOREACH_STATUS_OKAY宏。

自定義驅(qū)動的頭文件定義見custom-module/include/app/drivers/ledmatrix.h,可以看到驅(qū)動API由5個函數(shù)組成(見ledmatrix_driver_api結構定義),分別負責點亮LED矩陣最邊緣的行或是列(共4個API)和關閉LED顯示(第5個API)。在驅(qū)動的實現(xiàn)(custom-module/drivers/ledmatrix/ledmatrix.c)中,這5個函數(shù)會被實現(xiàn)(見driver_api結構) [22]。現(xiàn)在讀者應該能夠理解ledmatrix驅(qū)動的基本結構。最后,圖22所示的是驅(qū)動的初始化宏:

Figure 22. The initialization of the ledmatrix driver

22. ledmatrix驅(qū)動的初始化

LEDMATRIX_DEFINE中使用GPIO_DT_SPEC_GET_BY_IDX配合DT_INST_FOREACH_PROP_ELEM_SEP,從設備樹循環(huán)提取GPIO引腳phandle數(shù)組中的成員,從而靜態(tài)組成gpio_dt_spec數(shù)組[23],用于在設備驅(qū)動配置結構(見圖23)中存儲行和列GPIO引腳屬性[24]。

Figure 23. The configuration structure of the ledmatrix driver

23. ledmatrix驅(qū)動的配置結構

和上一節(jié)提到的傳感器驅(qū)動類似,DT_INST_FOREACH_STATUS_OKAY針對每個狀態(tài)為就緒的、兼容名為“custom-ledmatrix”的設備進行驅(qū)動初始化。

通過ledmatrix驅(qū)動層,樣例應用的主函數(shù)就可以很容易地直接進行LED行或是列的點亮操作。配合加速度傳感器的數(shù)據(jù),樣例應用實現(xiàn)了根據(jù)重力方向點亮LED矩陣對應邊緣行或列的效果。

3.3.3. 日志系統(tǒng)

Zephyr提供了日志系統(tǒng)的支持,應用代碼、驅(qū)動、代碼庫可以注冊各自的日志模塊,并通過Kconfig配置模塊的日志級別。日志的可能級別從低到高分別為:DBG (調(diào)試)、INF (信息)、WRN (警告)和ERR (錯誤)。代碼中通過調(diào)用LOG_X (X為級別)宏就可以使用與printk類似的語法寫日志。以樣例應用中的accel代碼庫為例,custom-module/lib/accel/accel.c中包含了zephyr/logging/log.h頭文件,然后使用LOG_MODULE_REGISTER宏定義了日志模塊accel,其日志級別為CONFIG_ACCELLIB_LOG_LEVEL。

在Kconfig中,CONFIG_LOG配置用于在全局啟用日志,然后通過添加CONFIG_<模塊>_LOG_LEVEL_X (X為級別)配置設定個別模塊的級別。本例中應用的配置文件app/prj.conf通過CONFIG_LOG=y在全局開啟了日志功能,然后通過CONFIG_ACCELLIB_LOG_LEVEL_INF=y選項將accel模塊的日志級別定義為INF(信息)級別。ledmatrix驅(qū)動和應用主代碼各自也有日志模塊的配置。

使用日志系統(tǒng)相比使用printk更加可配置,例如只有調(diào)試時才需要的日志可以通過默認日志級別進行過濾,發(fā)布應用時也可以很容易地禁用日志輸出。

4. 運行和調(diào)試Zephyr應用

4.1. 運行樣例應用

編譯和部署樣例應用的命令如圖24所示:

Figure 24. Commands to download and deploy the example application

24. 下載和部署樣例應用的命令

樣例應用開始運行時,將開發(fā)板平放于臺面上,此時LED矩陣不會點亮,如果將開發(fā)板拿起,一側(cè)垂直朝向地面時,檢測到重力一側(cè)的一排或一列5個LED會點亮。例如,當開發(fā)板垂直于臺面正面并面向讀者時,LED矩陣最下一行會點亮(見圖25)。

Figure 25. Illumination of the bottom row LEDs when the board is upright

25. 開發(fā)板垂直擺放時,最下一排的LED點亮

4.2. Zephyr應用的調(diào)試

在Zephyr應用開發(fā)中,最簡單的調(diào)試方法就是輸出日志。micro:bit V2運行樣例應用時會將日志輸出到串口,可以通過任何串口工具連接串口,例如使用minicom的命令:“minicom -D /dev/ttyACM0 -b 115200”。

樣例應用的默認日志級別為INF,編譯時可以通過包括debug.conf的選項將日志級別降低為DBG,程序就會輸出傳感器數(shù)據(jù)和GPIO操作細節(jié),命令為:“west build -b bbc_microbit_v2 app -p --extra-conf debug.conf”。

Zephyr支持在micro:bit V2上使用GDB進行遠程調(diào)試,應用編譯和燒錄(“west build”和“west flash”)后,運行“west debug”就會啟動GDB。GDB簡單的用法例如:設置斷點(“b main.c:<行數(shù)>”或“b <函數(shù)名>”)、逐行執(zhí)行(n)、繼續(xù)執(zhí)行(c)和打印變量(“p <變量名>”)?!皐est debug”命令還可以指定GDB以外的調(diào)試接口[25],例如jlink和openocd。

5. 結語

Zephyr在系統(tǒng)設計上借鑒了Linux等大型開源軟件的設計理念,引入了Linux和桌面系統(tǒng)開發(fā)者熟悉的概念和開發(fā)過程,但相對常見的RTOS,復雜度增加了數(shù)個級別。通過將硬件進行抽象化,以及提供幫助簡化開發(fā)過程的工具和框架(例如west工具和twister測試框架),Zephyr希望能吸引不同領域的開發(fā)者和企業(yè)用戶。但是,開發(fā)和調(diào)試難度的上升也讓不少開發(fā)者望而卻步,特別是熟悉面向硬件直接編程或是使用小型RTOS的嵌入式開發(fā)者。

希望在閱讀本文后,讀者對在Zephyr上進行嵌入式軟件開發(fā)有了初步的了解。本文中的實例并不涉及過于具體的硬件細節(jié)或是復雜的應用需求,Zephyr的官方文檔、實例,以及北歐半導體等硬件廠商的樣例項目都十分有參考價值。雖然官方文檔的中文化有所欠缺,但國內(nèi)開發(fā)者在各類平臺上發(fā)布的學習筆記一直在增加,線上討論也十分熱烈。

Zephyr近年來勁頭強勢,硬件廠商、開發(fā)者和開源社區(qū)的熱情正盛,項目的開發(fā)活躍程度遠超其他RTOS。期待Zephyr項目在未來能夠簡化復雜的系統(tǒng)架構,改善學習難度高和代碼調(diào)試困難等問題,并覆蓋更多的硬件和應用,成為一個全方位的主流物聯(lián)網(wǎng)操作系統(tǒng)。




參考文獻

[1] Zephyr Project (2025) About the Zephyr Project.
[2] Eclipse Foundation (2024) 2024 IoT & Embedded Developer Survey Report.
[3] 王洪波. 嵌入式虛擬化技術與應用: ACRN開源項目實踐[M]. 北京: 機械工業(yè)出版社, 2023.
[4] Zephyr Project (2024) Zephyr Project Overview.
[5] Zephyr Project (2025) Device Driver Model.
[6] The Devicetree Organization (2023) Devicetree Specification, Release v0.4.
[7] Zephyr Project (2025) Devicetree.
[8] Zephyr Project (2025) Scope and Purpose.
[9] Zephyr Project (2025) Syntax and Structure.
[10] Zephyr Project (2025) Devicetree Bindings.
[11] Lampacher, M. (2025) Practical Zephyr Git Repository.
[12] Eliasz, A. (2024) Zephyr RTOS Embedded C Programming. Apress Berkeley.
[13] Lampacher, M. (2024) Practical Zephyr-Kconfig (Part 2).
[14] The Micro:Bit Organization (2025) Meet the New BBC Micro:Bit.
[15] Gammell, C. (2024) Zephyr for Hardware Engineers: GPIO.
[16] Valens, C. (2024) Getting Started with the Zephyr RTOS. Elektor, 2, 98-105.
[17] The Micro:Bit Organization (2025) Micro:Bit Pins.
[18] Zephyr Project (2025) Example Application Git Repository.
[19] Zephyr Project (2025) Devicetree HOWTOs.
[20] Zephyr Project (2021) Zephyr and the BBC Microbit V2 Tutorial Part 1: GPIO.
[21] Lampacher, M. (2024) Practical Zephyr-Devicetree Semantics (Part 4).
[22] Szczys, M. (2024) How to Write a Zephyr Device Driver with a Custom API.
[23] Nordic Semiconductor (2025) GPIO Generic API.
[24] Lampacher, M. (2024) Practical Zephyr-Devicetree Practice (Part 5).
Zephyr Project (2025) Building, Flashing and Debugging.

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

在工業(yè)物聯(lián)網(wǎng)設備部署中,Modbus通信故障是導致系統(tǒng)停機的首要原因之一。據(jù)統(tǒng)計,超過60%的現(xiàn)場問題源于通信配置錯誤或數(shù)據(jù)解析異常。本文從嵌入式系統(tǒng)開發(fā)視角,系統(tǒng)闡述Modbus通信調(diào)試的方法論,結合實際案例解析如何高...

關鍵字: 嵌入式系統(tǒng) Modbus通信

在嵌入式系統(tǒng)開發(fā)中,看門狗(Watchdog Timer, WDT)是保障系統(tǒng)可靠性的核心組件,其初始化時機的選擇直接影響系統(tǒng)抗干擾能力和穩(wěn)定性。本文從硬件架構、軟件流程、安全規(guī)范三個維度,系統(tǒng)分析看門狗初始化的最佳實踐...

關鍵字: 單片機 看門狗 嵌入式系統(tǒng)

人工智能(AI)和機器學習(ML)是使系統(tǒng)能夠從數(shù)據(jù)中學習、進行推理并隨著時間的推移提高性能的關鍵技術。這些技術通常用于大型數(shù)據(jù)中心和功能強大的GPU,但在微控制器(MCU)等資源受限的器件上部署這些技術的需求也在不斷增...

關鍵字: 嵌入式系統(tǒng) 人工智能 機器學習

在資源受限的嵌入式系統(tǒng)中,代碼執(zhí)行效率和內(nèi)存占用始終是開發(fā)者需要權衡的核心問題。內(nèi)聯(lián)函數(shù)(inline functions)和宏(macros)作為兩種常見的代碼展開技術,在性能、可維護性和安全性方面表現(xiàn)出顯著差異。本文...

關鍵字: 內(nèi)聯(lián)函數(shù) 嵌入式系統(tǒng)

在嵌入式系統(tǒng)和服務器開發(fā)中,日志系統(tǒng)是故障排查和運行監(jiān)控的核心組件。本文基于Linux環(huán)境實現(xiàn)一個輕量級C語言日志庫,支持DEBUG/INFO/WARN/ERROR四級日志分級,并實現(xiàn)按大小滾動的文件輪轉(zhuǎn)機制。該設計在某...

關鍵字: C語言 嵌入式系統(tǒng)

在嵌入式系統(tǒng)和高可靠性軟件開發(fā)中,靜態(tài)代碼分析已成為預防缺陷的關鍵手段。PC-Lint(現(xiàn)更名為Gimpel Lint)作為行業(yè)領先的C/C++靜態(tài)分析工具,能夠檢測出編譯器難以發(fā)現(xiàn)的隱式錯誤和編碼規(guī)范違規(guī)。本文通過實戰(zhàn)...

關鍵字: PC-Lint 軟件開發(fā) 靜態(tài)代碼

在嵌入式系統(tǒng)和底層驅(qū)動開發(fā)中,C語言因其高效性和可控性成為主流選擇,但缺乏原生單元測試支持成為開發(fā)痛點。本文提出一種基于宏定義和測試用例管理的輕量級單元測試框架方案,通過自定義斷言宏和測試注冊機制,實現(xiàn)無需外部依賴的嵌入...

關鍵字: C語言 嵌入式系統(tǒng) 驅(qū)動開發(fā)

在嵌入式系統(tǒng)開發(fā)中,實時操作系統(tǒng)(RTOS)的任務調(diào)度算法直接影響系統(tǒng)的響應速度和資源利用率。時間片輪轉(zhuǎn)(Round-Robin, RR)作為一種經(jīng)典的公平調(diào)度算法,通過為每個任務分配固定時間片實現(xiàn)多任務并發(fā)執(zhí)行。本文將...

關鍵字: 實時操作系統(tǒng) RTOS C語言

在嵌入式系統(tǒng)與驅(qū)動開發(fā)中,內(nèi)存映射I/O(Memory-Mapped I/O, MMIO)是一種將硬件寄存器映射到處理器地址空間的技術,允許開發(fā)者通過指針直接讀寫寄存器,實現(xiàn)高效、低延遲的硬件控制。本文通過C語言實戰(zhàn)案例...

關鍵字: 內(nèi)存映射 I/O操作 嵌入式系統(tǒng)
關閉