导航:首页 > 源码编译 > 嵌入式设备树编译工具

嵌入式设备树编译工具

发布时间:2023-05-10 13:37:40

‘壹’ 编译linux内核设备树文件使用什么命令

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状态、更新数据等。

阅读全文

与嵌入式设备树编译工具相关的资料

热点内容
找漫画看应该下载什么app 浏览:178
如何在vps上搭建自己的代理服务器 浏览:742
nginxphp端口 浏览:403
内脏pdf 浏览:150
怎么看云服务器架构 浏览:85
我的世界国际服为什么登不进服务器 浏览:996
微盟程序员老婆 浏览:930
intellij创建java 浏览:110
java连接odbc 浏览:38
启动修复无法修复电脑命令提示符 浏览:359
手机编程是什么 浏览:98
山东移动程序员 浏览:163
苏州java程序员培训学校 浏览:477
单片机液晶驱动 浏览:855
魔拆app里能拆到什么 浏览:131
新预算法的立法理念 浏览:144
wdcpphp的路径 浏览:134
单片机p0口电阻 浏览:926
浏览器中调短信文件夹 浏览:594
五菱宏光空调压缩机 浏览:69