首页 > 编程知识 正文

uboot设备树和内核设备树,设备树编写

时间:2023-05-04 11:42:07 阅读:27504 作者:4984

公众号欢迎关注本人微信公众号。 公众号分享更多的嵌入式知识和资料,分享个人学习嵌入式的心得。 请大家一起来玩。

一个语句谈到要理解linux设备树的提示:本文介绍了如何使用Rockchip平台在linux下应用设备树。 在其他平台上,虽然有些写法略有不同,但基本上不影响我们的学习。 百通,接下来开始设备树之旅吧。

文章的目录公众号表示,理解linux设备树的序言1、为什么要引用设备树的使用。 2、使用设备树给我们带来了那些好处吗? 另一方面,设备树1、设备树文件2、rk3399-tve1030g.dts3、内核内设备树文件3、总结2、设备树的节点1、设备树的i2c的使用方法总结3、设备树的使用方法总结3 3 .设备树中断的指定方法

前言为什么要引用设备树来使用? 新功能出现时,应该有疑问为什么要使用设备树。 使用设备树给我们带来了那些好处吗? 不使用设备树有不好的地方吗? 让我们逐一回答。

1、为什么要引用使用机器树? 其实,在较早的另一个平台上已经引用了设备树,只是在ARM平台上的引用延迟了,所以很多没有在ARM平台上部署设备树的早期开发人员都很熟悉这样的文件夹mach-xxx 在未浏览设备树之前,kernelarcharm目录下有许多与芯片和开发板相关的文件。 例如,三星芯片s5pv210 a在这个芯片上开发产品,在上面添加文件,b公司也用这个芯片在上面开发产品,在上面添加文件,世界上就有无数的公司在开发无数的产品,在这个目录下不久就会变得非常庞大于是,这时,linux的创始人linux的父亲Linus Torvalds看到这种情况时发了邮件。 thiswholearmthingisaf * ckingpainintheass。 通过这句话,ARM Linux社区现在部署设备树。

kernelarcharm目录中的平台相关文件

2、使用设备树给我们带来了那些好处吗? 设备树是一种编程语言,用于描述芯片与外围设备之间的连接关系,实际上类似于c语言的语法。 主板具有主芯片CPU,如Rochchip的33xx系列,根据CPU的不同,有很多外围控制器,如I2C、SPI、MIPI控制器、DDR控制器、NIC控制器等众所周知,寄存器地址因芯片和控制器而异,但Rochchip和三星芯片相对于I2C控制器的寄存器配置地址一定不同。 实际上,不同的制造商不仅芯片寄存器不同,通过一个制造商不同系列芯片的寄存器地址也可能不同。 即使是同一个芯片,如果这个芯片有两个I2C控制器,我们可能只使用I2C1。 其他控制器需要关闭以节省电力。 这是设备树用来说明与芯片相关的东西。 板上不仅有主控芯片,还有很多外围设备。 例如,一家a公司生产了使用Rochchip相同芯片的平板电脑。 但是这两个平台在硬件上有区别。 例如,a公司使用一个陀螺仪硬件连接到I2C1,而b公司连接到I2C2。 假设linux系统支持两个陀螺仪的驱动,但连接到针脚的I2C控制器不同,所以是否需要修改代码才能支持这两种平板电脑呢? 参照设备树时,实际上不使用,修改设备树即可。 因为设备树描述了这些硬件设备的连接,所以系统启动了,例如,如果a公司的平板陀螺仪安装在I2C1下,则只需在设备树中描述陀螺仪在I2C1下

下图是设备树的树结构

另一方面,设备树1、设备树文件的RK平台的设备树路径: kernelarcharm 64bootdtsrockchip

编译设备树期间生成的中间文件:rk3399-tve1030g.dtb.d.dtc.tmp

开发板设备树文件: rk3399-tve1030g.dts

给定的设备树文件是内核使用的skddp文件: rk3399-tve1030g.dtb

2、查看rk3399-tve1030g.dts下的rk3399-tve1030g.dts,很多东西都被我删除了。 为了简化,请大家通俗易懂

/* *版权(c ) 2018 fuzhourockchipelectronicsco . ltd * * spdx -许可证标识符: ) GPL-2.0ormit )/dttt # includedt-skddpdings/PWM/PWM.h//设备树

#include "rk3399.dtsi" #include "rk3399-android.dtsi"#include "rk3399-opp.dtsi"#include "rk3399-vop-clk-set.dtsi"/ { /* 这个大括号里面表示一个根节点,也是最大的节点,其他的小节点是它的组成成分,什么叫根节点?设备树设备树就是把设备比喻成一棵树,比如一个平板就是一棵树,树上面开枝散叶有很多子节点比如i2c节点,spi节点,下面rk_headset就是我们定义的一个节点 */compatible = "rockchip,rk3399-tve1030g", "rockchip,rk3399";/* 根节点的属性成员,表示这个设备树支持那个板子,比如这个设备树优先支持"rockchip,rk3399-tve1030g"这款板子,其次支持是使用RK3399芯片的板子,内核启动的时候会去匹配这个字符串,匹配上了代表内核能匹配这个设备树*//*rk_headset 根节点下面的一个子节点*/rk_headset {compatible = "rockchip_headset"; /*compatible属性:"rockchip_headset" 后面这串字符需要和驱动匹配,驱动的probe函数才能被执行*/headset_gpio = <&gpio4 RK_PD4 GPIO_ACTIVE_LOW>;/*headset_gpio: 可以理解为一个变量,用来告诉驱动,使用那个GPIO,headset_gpio 可以修改为别的名字,但是对应的驱动也要修改为对应的名字,因为驱动是通过这个名字获取headset_gpio里面的值*/pinctrl-names = "default";pinctrl-0 = <&hp_det>;/*pinctrl-names和pinctrl-0 这个是pinctrl的相关配置,先不管,等说明pinctrl的时候在看*/io-channels = <&saradc 2>;/*io-channels 和headset_gpi0 是一样的,也是可以理解为一个变量,驱动会引用里面的变量*/};}; 3、在内核里面查看设备树文件

在内核里面查看设备树文件,设备树的路径是:/sys/firmware/devicetree/base

由上面的图片我们看到在base文件夹就是设备树的根节点,在根节点下又有很多子节点,比如我们可以看到上面例子中的子节点rk_headset,还有别的节点i2c@ff110000/ 、i2c@ff120000/每个节点i2c节点代表芯片的一个i2c控制器。我们还可以进入到子节点rk_headset的目录查看子节点由什么组成。

我们进入子节点rk_headset可以看里面有组成成员:compatible 、headset_gpio 、io-channels、name、 pinctrl-0、 pinctrl-names和上面我们rk3399-tve1030g.dts里面的rk_headset是一样的。

3、总结

我们初步的看了设备树的组成部分,设备树是由一个根节点组成,根节点下面有许多子节点,子节点下面还可以有子节点的子节点,以此类推形成一个树状的结构。


二、设备树的节点 1.设备树之i2c怎么使用

我们上面说了设备树有个根节点,根节点下面有多个子节点其中I2C是其中的一个子节点,我们知道i2c总线下面可以挂载多个设备,比如下图i2c总线下挂载了触摸屏设备和多个陀螺仪设备,他们通过不同的i2c地址去识别不同的设备,既然i2c下挂载有多个子节点,那我们怎么去描述i2c总线下挂载的数据呢?那就是i2c节点下可以在建立一个子节点。

下面是设备树截取i2c节点的一段代码。

&i2c5 { //表示引用i2c5这个节点,i2c5这个节点会在根节点下面定义status = "okay"; //使能enable i2c子节点,如果在前面status = "disable",后面会覆盖掉前面的赋值i2c-scl-rising-time-ns = <150>; //i2c-scl-rising-time-ns这个值是变量上面也说了,不同的平台不一样,可能三星的平台就不是这样定义的,可能别的平台根本就不用配置这个,这个是给i2c控制器使用的,我们可以不用管i2c-scl-falling-time-ns = <30>;clock-frequency = <100000>;//这个也是一个变量,表示设置i2c总线速率配置为100k,不同平台也不一样,可以 /* 触摸屏gslx680 节点*/gslx680: gslx680@40 {compatible = "gslX680_tve"; /*和代码匹配,匹配之后调用驱动代码的probe函数注册驱动*/reg = <0x40>; /* i2c 读写地址,注意是7位地址*/max-x = <1200>; /* max-x 变量值,驱动里面会使用 */max-y = <1920>; /* max-y 变量值,驱动里面会使用 */status = "okay"; /* 使能这个触摸屏gslx680 节点*/};};

触摸屏代码节选,代码路径:drivers/input/touchscreen/gslx680.c

static const struct i2c_device_id gsl_ts_id[] = {{"gslX680_tve", 0}, //和设备树里面的gslx680节点的 compatible = "gslX680_tve";进行匹配{}};static struct i2c_driver gsl_ts_driver = {.driver = { .name = GSLX680_I2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(gsl_ts_ids), },#if 0 //ndef CONFIG_HAS_EARLYSUSPEND.suspend = gsl_ts_suspend,.resume = gsl_ts_resume,#endif.probe = gsl_ts_probe, //匹配上后这个probe函数会被调用,然后初始化触摸屏驱动.remove = gsl_ts_remove,.id_table = gsl_ts_id,}; 总结

i2c下面的子节点会在内核起来的时候进行解析,最后会转化为一个struct i2c_client结构体,然后注册到一个链表里面,最后struct i2c_client会和触摸屏驱动里面的struct i2c_driver进行匹配,当这两个匹配上的时候,会调用驱动里面的probe函数,进行驱动的下一步注册。


3.设备树之spi怎么使用

在芯片中spi节点和i2c节点差不多,所以暂时不进行解析。


3.设备树之pinctrl怎么使用 1、pinctrl是用来干什么的?

在设备树里面一定会有pinctrl,在说明白什么是pinctrl之前我们先说一下芯片的Pin脚功能,学过单片机的都知道,芯片的Pin脚一般有两个功能,1、可以当成GPIO进行使用。2、可以当初特殊功能的引脚使用,比如这个Pin脚可以配置为I2c功能,那么这个Pin的寄存器就需要配置为I2c功能,其实内部是通过一个MUX电路进行切换。

既然是配置寄存器那么谁更熟悉如何配置寄存器?当然是芯片的原厂工程师,如果我们自己配置要怎么办?是不是要查询数据手册,然后设置对应的标志位才能切换到i2c功能?这样做是不是效率太低了?专业的事情就让专业的人干吧,我们只要使用就好了,所以pinctrl的作用就是用来管理一组Pin脚的功能,比如这组pin脚想配置为i2c功能,我们在i2c节点里面写出来就好了,内核起来的时候会解析你的配置,然后把引脚配置成i2c的功能。

代码已经被精简,只截取i2c部分拿出来进行说明

/*pinctrl 这个节点是根节点下的一个普通节点*/pinctrl: pinctrl { /* compatible和上面的功能一样主要是用来和芯片原厂的驱动进行匹配,匹配之后调用对于的芯片设置Pinctrl的驱动*/compatible = "rockchip,rk3399-pinctrl";rockchip,grf = <&grf>;rockchip,pmu = <&pmugrf>;#address-cells = <2>;#size-cells = <2>;ranges;i2c6 { /*Pin脚要设置为i2c6功能的配置 */i2c6_xfer: i2c6-xfer {rockchip,pins =<2 10 RK_FUNC_2 &pcfg_pull_none>,<2 9 RK_FUNC_2 &pcfg_pull_none>;};};};

上面是pinctrl的要将pin脚配置为i2c的功能的配置,那么既然配置好了谁要进行使用呢?当然就是i2c节点拿过来进行使用,我们查看i2c6的节点代码

i2c6: i2c@ff150000 {compatible = "rockchip,rk3399-i2c";reg = <0x0 0xff150000 0x0 0x1000>;clocks = <&cru SCLK_I2C6>, <&cru PCLK_I2C6>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH 0>;/*pinctrl-names = "default"是什么意思呢,内核去解析这个节点的的时候会调用下面图片里面的函数pinctrl_select_state,将pinctrl-0 = <&i2c6_xfer>;这个组Pin脚设置为i2c6_xfer配置*/pinctrl-names = "default"; pinctrl-0 = <&i2c6_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};

代码配置kerneldriversbasepinctrl.c

//有些pinctrl-names可能有两种状态,一种是"default",分别为对应的配置为pinctrl-0,"sleep"对应的配置为pinctrl-1,系统休眠的时候会调用pinctrl_select_state这个函数将这组pin脚设置为休眠配置,为了起到省电效果 pinctrl-names = "default" "sleep"; pinctrl-0 = <&i2c6_xfer>;pinctrl-1 = <&i2c6_sleep>
3.设备树之怎么指定中断

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。