Linux源碼的arch/powerpc/boot/dts/目錄下存放了很多dts文件,可以作為參考文件。另外dtc編譯器在內核源碼2.6.25版本之後已經被包含進去。在2.6.26版本之後,生成blob的簡單規則已經加入makefile,如下命令:
$ make ARCH=powerpc canyonlands.dtb
也可以根據自己的硬體修改好dts文件後,用下面類似命令生成dtb文件。
$ dtc -f -I dts -O dtb -R 8 -S 0x3000 test.dts > mpc836x_mds.dtb
$ mkimage -A ppc -O Linux -T flat_dt -C none -a 0x300000 -e 0 -d mpc836x_mds.dtb mpc836x_mds.dtu
『貳』 如何使用dtc編譯設備樹 devicetree
DTS (device tree source)
.dts文件是一種ASCII 文本格式的Device
Tree描述,此文本格式非常人性化,適合人類的閱讀習慣。基本上,在ARM
Linux在,一個.dts文件對應一個ARM的machine,一般放置在內核的arch/arm/boot/dts/目錄。由於一個SoC可能對應多個machine(一個SoC可以對應多個產品和電路板),勢必這些.dts文件需包含許多共同的部分,Linux內核為了簡化,把SoC公用的部分或者多個machine共同的部分一般提煉為.dtsi,類似於C語言的頭文件。其他的machine對應的.dts就include這個.dtsi。譬如,對於VEXPRESS而言,vexpress-v2m.dtsi就被vexpress-v2p-ca9.dts所引用,
vexpress-v2p-ca9.dts有如下一行:
/include/
"vexpress-v2m.dtsi"
當然,和C語言的頭文件類似,.dtsi也可以include其他的.dtsi,譬如幾乎所有的ARM
SoC的.dtsi都引用了skeleton.dtsi。
.dts(或者其include的.dtsi)基本元素即為前文所述的結點和屬性:
[plain] view
plainprint?
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
a-byte-data-property = [0x01 0x23 0x34 0x56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 {
};
};
};
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
a-byte-data-property = [0x01 0x23 0x34 0x56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 {
};
};
};
上述.dts文件並沒有什麼真實的用途,但它基本表徵了一個Device
Tree源文件的結構:
1個root結點"/";
root結點下面含一系列子結點,本例中為"node1" 和
"node2";
結點"node1"下又含有一系列子結點,本例中為"child-node1" 和
"child-node2";
各結點都有一系列屬性。這些屬性可能為空,如"
an-empty-property";可能為字元串,如"a-string-property";可能為字元串數組,如"a-string-list-property";可能為Cells(由u32整數組成),如"second-child-property",可能為二進制數,如"a-byte-data-property"。
下面以一個最簡單的machine為例來看如何寫一個.dts文件。假設此machine的配置如下:
1個雙核ARM
Cortex-A9 32位處理器;
ARM的local bus上的內存映射區域分布了2個串口(分別位於0x101F1000 和
0x101F2000)、GPIO控制器(位於0x101F3000)、SPI控制器(位於0x10170000)、中斷控制器(位於0x10140000)和一個external
bus橋;
External bus橋上又連接了SMC SMC91111
Ethernet(位於0x10100000)、I2C控制器(位於0x10160000)、64MB NOR
Flash(位於0x30000000);
External bus橋上連接的I2C控制器所對應的I2C匯流排上又連接了Maxim
DS1338實時鍾(I2C地址為0x58)。
其對應的.dts文件為:
[plain] view
plainprint?
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flashethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
上述.dts文件中,root結點"/"的compatible 屬性compatible =
"acme,coyotes-revenge";定義了系統的名稱,它的組織形式為:<manufacturer>,<model>。Linux內核透過root結點"/"的compatible
屬性即可判斷它啟動的是什麼machine。
在.dts文件的每個設備,都有一個compatible
屬性,compatible屬性用戶驅動和設備的綁定。compatible
屬性是一個字元串的列表,列表中的第一個字元串表徵了結點代表的確切設備,形式為"<manufacturer>,<model>",其後的字元串表徵可兼容的其他設備。可以說前面的是特指,後面的則涵蓋更廣的范圍。如在arch/arm/boot/dts/vexpress-v2m.dtsi中的Flash結點:
[plain] view
plainprint?
flash@0,00000000 {
compatible = "arm,vexpress-flash", "cfi-flash";
reg = <0 0x00000000 0x04000000>,
<1 0x00000000 0x04000000>;
bank-width = <4>;
};
flash@0,00000000 {
compatible = "arm,vexpress-flash", "cfi-flash";
reg = <0 0x00000000 0x04000000>,
<1 0x00000000 0x04000000>;
bank-width = <4>;
};
compatible屬性的第2個字元串"cfi-flash"明顯比第1個字元串"arm,vexpress-flash"涵蓋的范圍更廣。
再比如,Freescale
MPC8349 SoC含一個串口設備,它實現了國家半導體(National Semiconctor)的ns16550
寄存器介面。則MPC8349串口設備的compatible屬性為compatible = "fsl,mpc8349-uart",
"ns16550"。其中,fsl,mpc8349-uart指代了確切的設備, ns16550代表該設備與National Semiconctor
的16550
UART保持了寄存器兼容。
接下來root結點"/"的cpus子結點下面又包含2個cpu子結點,描述了此machine上的2個CPU,並且二者的compatible
屬性為"arm,cortex-a9"。
注意cpus和cpus的2個cpu子結點的命名,它們遵循的組織形式為:<name>[@<unit-address>],<>中的內容是必選項,[]中的則為可選項。name是一個ASCII字元串,用於描述結點對應的設備類型,如3com
Ethernet適配器對應的結點name宜為ethernet,而不是3com509。如果一個結點描述的設備有地址,則應該給出@unit-address。多個相同類型設備結點的name可以一樣,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000與serial@101f2000這樣的同名結點。設備的unit-address地址也經常在其對應結點的reg屬性中給出。ePAPR標准給出了結點命名的規范。
『叄』 嵌入式Linux系統移植和裁剪_10_環境搭建和測試(九)
這段時間新冠肺炎疫情又波及了好幾個省,下個星期又要在家辦公了,哪都不能去了。不過話說回來,在城裡面本來也就沒什麼好玩的,也沒什麼地方好去的。
接著上一次的內容,要從網路載入內核和設備樹,以及掛載網路根文件系統物枝毀,需要在UBoot裡面設置 bootcmd 和 bootargs 兩個環境變數如下:
set bootcmd 'tftp 41000000 uImage; tftp 42000000 exynos4412-fs4412.dtb; bootm 41000000 - 42000000'
set bootargs 'console=ttySAC2,115200 root=/dev/nfs rw nfsroot=192.168.1.8:/samba/nfs/rootfs,proto=tcp clk_ignore_unused /init=/linuxrc ip=192.168.1.100:192.168.1.8:192.168.1.1:255.255.255.0'
這兩個參數需要根據自己的實際情況加以調整,主要是nfs和ip的一些設置。
bootcmd 是內核啟動命令,其表示使用tftp協議,從TFTP文件夾下載入uImage到41000000的內存地址。然後從TFTP文件夾下載入exynos4412-fs4412.dtb到42000000的內存地址,然後執行bootm命令,從41000000的地址啟動內核。
bootargs 是內核啟動的參數,比如設置控制台的串口參數,指定根文件系統(這里我們使用nfs從網路上掛載根文件系統,注意協議用tcp,如果用udp的話,很可能會因為丟包而導致掛載失敗)
最後需要saveenv保存設置。
就緒之後,板子重新上電,不要回車打斷UBoot,讓其引導Linux內核啟動,結果內核啟動失敗。
後來在群里問了老師,他們發了內核鏡像和設備樹過來,讓我用他們編譯的試試,我一試,還真能用。本來我還懷疑是不是根文件系統的問題,後來老師讓我看看設備樹,說是要注釋掉下面這一段。搭頌
但是我一看,本來就是注釋掉的啊。。。然後就無解了,我以為可能還是根文件系統的問題,群里問老師也沒有再回,罩備就擱置了兩天,這也算是中間的一個小插曲吧。然後我剛剛又在群里問,另一個老師說這一段現在不需要注釋了,就是要加進來。然後我就把注釋去掉了,重新編譯後,果然就可以啟動了。
可以看到根文件系統已經掛載成功了,那這樣我們的環境搭建部分就測試OK了,我們得到了一份可以在4412板子上運行的Linux內核源碼,UBoot源碼和根文件系統。
所以呢,感覺培訓機構的資料管理挺亂的,各個老師之間的信息不同步,資料也不同步,視頻可能是很老的,配套資料也過時了,你不問還不知道,這可能也是他們需要建立大量的群來答疑的原因吧,總之,體驗不佳。當然他們覺得這些步驟你不需要做啊,直接用現成的虛擬機鏡像就可以了嘛,編譯工具鏈,各種依賴庫,環境變數那些都給你搞好了,幹嘛要自己搞。他們這種想法就只適合那些初學驅動的人,確實,如果你只想學習驅動,那確實用他們准備好的虛擬機鏡像就可以了。但是如果我要換一個晶元,環境還得自己搭,那就不好使了,或者說我自己已經有虛擬機了,再搞一個鏡像其實顯得很累贅。
好了,今天就到這里了,後面就開始UBoot的移植學習。
『肆』 單獨編譯內核和設備樹
source /opt/fsl-imx-xwayland/4.19-warrior/environment-setup-aarch64-poky-linux
export ARCH=arm64
make -j 16
生成的Image 和dtb在下面的路徑
~/imx-yocto-bsp/build-imx8mmevk/tmp/work/imx8mmevk-poky-linux/linux-imx/4.19.35-r0/git/arch/arm64/boot
『伍』 uboot和dts有什麼區別
U-Boot是一種開源的引導載入程序,用於在啟動時載入操作系統或其他應用程序。它是在開發板的啟動階段運行的,負責初始局森旦化硬體,載入內核映像,以及在啟動時執行其他必要的初始化步驟。U-Boot通常作為固件交付給硬體廠商,然後由廠商進行適當的配置和編譯。
DTS(設備樹源文件)是Linux內核中的一種數據結構,用於描述硬體設備和平台的信息。它提供了一個結構化的方式來描述硬體和設備的連接,以及如何訪問它們。DTS文件通常由開發人員編寫,描述硬體板子的設備樹,然後在編譯內核時,通過設備春衫樹編譯工具生成二進制設桐擾備樹(DTB)文件。內核啟動時會讀取DTB文件,並在初始化硬體時使用它。
因此,U-Boot和DTS都是用於嵌入式系統中的組件,但它們的作用不同。U-Boot負責引導載入程序,初始化硬體和載入內核映像,而DTS則描述硬體設備和平台的信息。
『陸』 嵌入式新手學寄存器還是庫函數
如果有相關嵌入式開發基礎昌行,可以從寄蔽肢存器開始學。如果是第一次接觸單片機,建議從庫函數開始學,這樣上手快,等熟練後分析庫函數,就可以學習各寄存器了。這還用問嗎?當然是寄存器了。
哦,不對,當然是庫函數了。
下面是我以前寫了一篇匯總文章,可以看一下;
【STM32系列匯總】博主的STM32實戰快速進階之路(持續更新)_GREYWALL-CSDN博客
先說一下目前ST推出的CubeMX;
STM32 基於 CubeMX配置GPIO點亮LED燈(超級詳細+圖文並茂)
嘗試使用Cube進行一些開發學習,這里對此做一個梗概,先有一個全面的了解。
Cube全家桶
曾幾何時,ST剛推出CubeMX的時候,自動生成的外設初始化代碼都會有這樣那樣的錯誤,而如今,隨著軟體的迭代升級,Cube生態也越來越完善,並且使用也越來越方便,ST推出的STM32Cube Ecosystem提供了免費整套的開發軟體工具和嵌入式軟體包,其中可以從晶元的外設配置,程序開發,程序下載以及系統監視一系列的功能。如下圖所示;
STM32 CubeMX 幾乎STM32的設備都可以使用這個工具初始化配置代碼,這個是基於Java開發的圖形化界面配置軟體,可以為Cortex-M自動生成需要配置的C代碼耐並嘩,還可以為支持Linux系統的Cortex-A內核生成設備樹。
STM32 CubeIDE 這是一款ST推出的免費的集成開發環境,基於Eclipse和GNU C/C++ 等開源工具鏈,可以編譯調試代碼,並且官方也將STM32 CubeMX集成到IDE中,這樣整個軟體的配置和開發變得渾然一體,便捷非常。
STM32CubeProgrammer 軟體編程工具,支持(JTAG,SWD,UART,USB DFU,I2C,SPI,CAN)等方式對設備和外部存儲器進行讀寫。
STM32CubeMonitor 系列工具。強大的監視工具可幫助開發人員實時微調其應用程序的行為和性能,這個目前還沒有嘗試,暫且先相信官方的自誇吧。
所以使用以上的ST Cube全家桶進行STM32的軟體開發,還是有不少坑要在實踐過程中慢慢爬,整體的一個流程基本如下;
第一步:使用 CubeMX初始化相應的晶元外設功能等等;
第二步:使用CubeIDE進行代碼編輯調試等操作,當然,如果你喜歡也可以使用IAR,Keil,或者VSCode,這個完全出自個人喜好和習慣,但是據說CubeIDE里集成了CubeMX,這個難道不香嗎? 加上如果想使用 gcc編譯器,那也省去了很多配置的麻煩;
第三步:使用CubeProgrammer進行程序燒錄,主要是支持的協議多啊,還可以吧,感覺是吹的挺厲害的;
第四步:以後可能會出更多的功能插件,目前感覺不是十分必要。
CubeMX
STM32 CubeMX的安裝,十分簡單,不過下載需要提交郵箱,基本根據系統提示就可以完成,另外,STM32 CubeMX目前自動生成的代碼支持官方的HAL庫和LL庫,像以前筆者常用的標准外設庫就已經被ST拋棄了,最新的STM32F7已經不支持標准外設庫了。 同時,CubeMX初始化生成C代碼項目,最終的工程可以符合IAR,Keil MDK和GCC,所以這里相對來說比較方便,不用對著手冊擼寄存器了,也不用對著官方標准外設庫demo進行移植,真的是哪裡不會點哪裡。
HAL庫的封裝相對來說好一點,但是代碼讀起來相對比較冗雜,通用性和移植性好,符合軟體工程的設計思想,那麼會犧牲一點效率了。
LL庫會再底層一點,這幾個的選擇還是看個人喜好和團隊的需要了。
CubeIDE
再官網下載CubeIDE這個軟體並安裝,假設你已經成功安裝並打開了軟體,會看到;
這時候CubeIDE已經集成了CubeMX了,新建STM32工程,step by step即可快速開始了。
CubeProg
整體看了一下 STM32 CubeProg 的介紹,其實有點還是可以的,首先這個軟體支持多平台如:Windows, Linux, macOS等等,Java 進行開發的有點,這一系列差不多都是用Java開發的吧,另外可能對st-link的支持比較好,如果使用jlink的話,用個openocd也無妨,但是畢竟是套裝,下面簡單羅列一下;
支持擦除,讀寫Flash等等操作;
支持Motorola S19, HEX, ELF 和 二進制格式;
ST-LINK的固件升級;
多平台:Windows, Linux, macOS,其實就看好這一點;
結語
對於Cube進行了簡單的學習和介紹,目前還有不少坑要爬,建議看一下HAL的封裝,有必要LL也可以看看,基本上還是符合CMSIS那套標准。另外關於開發環境,如果需要使用CubeIDE進行開發,它已經集成了CubeMAX,則無需另外下載了,直接一站式服務搞定,如果只需要生成初始化代碼,那麼CubeMAX還是有必要單獨裝一下的。至於寄存器,開發起來比較費時間。
手動碼字,嚴重影響了我拔刀的速度,如果你覺得我走心,文章對您有所幫助,點贊,支持一波吧!
歡迎關注@極客小麥
編輯於 2021-04-09著作權歸作者所有
贊同 25
『柒』 如何在linux-3.x內核編譯設備樹
可以讓設備樹文件和內核一起編譯,單獨編譯的化,可以參考下面的文檔:
http://blog.csdn.net/woshigaoyuan/article/details/13996277
『捌』 openwrtgpio中斷dts配置
第一段:在OpenWrt中,配置GPIO中斷需要在設備桐畢樹中指定相應的GPIO引腳。這可以通過編輯設備樹文件(.dts)含清來完成。在設備樹中,需要指定GPIO的編號、中斷類型和中斷觸發方式。
第二段:首先,需要找到設備樹中的GPIO節點,通常位於「/soc/gpio@XXX」路徑下。在節點中,需要添加「interrupts」屬性來定義中斷類型和中斷觸發方式。例如,以下代碼表示使用邊緣觸發方式的下降沿中斷:
interrupts = <0 15 2>;
第三段:其中,第一個數字0表示中斷類型,0代表使用IRQ號,1代表使用GPIO編號。第二個數字15是中斷號,可以在設備樹文件中找到。第三個數字2表示邊緣觸發方式,0表示低電平觸發,1表示高電平觸發,2表示下降沿觸發,3表示上升沿觸發。
第四段:完成設備樹文件的編輯後,需要重新編譯設備樹並更新內核。然後,就可以在應用程序中使用GPIO中斷了。談輪前例如,可以使用GPIO庫的gpio_request()函數來請求GPIO資源,然後使用gpio_irq_request()函數注冊中斷處理函數,最後使用gpio_irq_enable()函數使能中斷。在中斷處理函數中,可以根據需要執行相應的操作,比如讀取GPIO狀態、更新數據等。