Linux的设备树将每个外设都视为一个节点,整体架构很明确清晰,也很便于设备管理。
学习视频地址:【正点原子】STM32MP157开发板
1. 设备树结构
2. 文件关系
DTC:编译器
DTS:设备树源文件
DTSI:设备树头文件
DTB:设备树编译所得文件
编译设备树
需进入到Linux内核源码目录
make dtbs # 编译所有设备树
make xxx.dtb # 编译某个设备树
3. 设备树语法
设备节点
命名格式
node-name@unit-address # unit-address表示设备地址或寄存器首地址
label: node-name@unit-address # label可以便于访问节点
数据形式
compatible = "arm,cortex-a7"; # 字符串
reg = <0 0x123456 100> # 32位无符号整数
compatible = "st,stm32mp157d-atk", "st,stm32mp157"; # 字符串列表
标准属性
compatible:兼容性属性,用于将设备和驱动进行绑定,格式如下,前者为厂商,后者为驱动名字
compatible = "manufacturer,model"
model:描述开发板的名字或者设备模块信息
model = "STMicroelectronics STM32MP157C-DK2 Discovery Board";
status:状态属性
#address-cells 和#size-cells:长度属性
这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。
reg:寄存器属性
reg = <address1 length1 address2 length2 address3 length3……>
ranges:地址映射/转换表
属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字
矩阵,ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成:
child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址 所占用的字长。
parent-bus-address:父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物 理地址所占用的字长。
length:子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。
特殊节点
aliases:定义别名
aliases {
serial0 = &uart4;
};
chosen: uboot向Linux内核传递数据,如以下这样子定义属性之后,chosen中会存在bootargs和stdout-path两个属性
chosen {
stdout-path = "serial0:115200n8";
};
向节点追加信息
以在i2c节点下添加fxls8471芯片节点为例,我们需要在.dts文件中添加相应的内容,这样子不会影响其他引用了i2c所在的.dtsi文件的设备树
&i2c1 {
/* 要追加或修改的内容 */
};
#例
&i2c1 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c1_pins_b>;
pinctrl-1 = <&i2c1_pins_sleep_b>;
status = "okay";
clock-frequency = <100000>;
fxls8471@1e {
compatible = "fsl,fxls8471";
reg = <0x1e>;
position = <0>;
interrupt-parent = <&gpioh>;
interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
};
};
4. 设备树在代码文件中的使用
其实就是读取各个节点的信息,并把它们放入到代码变量中,从而实现各个功能配置。
dtsled.nd = of_find_node_by_path("/stm32mp1_led");
if(dtsled.nd == NULL) {
printk("stm32mp1_led node nost find!\r\n");
return -EINVAL;
} else {
printk("stm32mp1_lcd node find!\r\n");
}
/* 2、获取compatible属性内容 */
proper = of_find_property(dtsled.nd, "compatible", NULL);
if(proper == NULL) {
printk("compatible property find failed\r\n");
} else {
printk("compatible = %s\r\n", (char*)proper->value);
}
/* 3、获取status属性内容 */
ret = of_property_read_string(dtsled.nd, "status", &str);
if(ret < 0){
printk("status read failed!\r\n");
} else {
printk("status = %s\r\n",str);
}
/* 4、获取reg属性内容 */
ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 12);
if(ret < 0) {
printk("reg property read failed!\r\n");
} else {
u8 i = 0;
printk("reg data:\r\n");
for(i = 0; i < 12; i++)
printk("%#X ", regdata[i]);
printk("\r\n");
}