Ili*_*psi 18 device-tree beagleboneblack
如果您有Beaglebone Black(BBB)并且想要将自己的设备连接到它(不是斗篷),您可能已经听说过设备树.在我的情况下,我想将RTC设备连接到BBB上的I2C总线.网络上散布着大量的信息,本文旨在概述我发现的内容以及完成任务的指南.
因此,我将给出一个在BBB上激活I2C总线的完整示例,以及使用内核中包含的设备驱动程序连接DS1308 RTC芯片.听起来不错?
请继续阅读,如果有任何不明确的地方请留下评论.如果您有点匆忙,您也可以在Github上抓取设备树覆盖代码并飞走.
我在我的BBB上使用ArchLinux ARM主要是因为Arch Linux非常棒而且我使用debianoid发行版可能太愚蠢了.这是系统的屏幕截图 ..
你可能会注意到内核版本已经超过了3.x的东西.您在screenfetch中看不到的是内核使用Capemgr实用程序支持设备树覆盖.
我会尽快做到,你可以在这里,这里,这里和这里找到更深层次的知识.设备树是描述平台上底层硬件的结构.它在嵌入式设备中被大量使用,因为SOC和其他东西没有像PCI这样可以发现设备的总线.它们必须静态定义并附加到"平台总线",以便为内核附带的设备驱动程序提供句柄.
在将设备树引入Linux之前,所有这些工作都必须使用特定的C头文件和自定义实现来完成,然后所有这些都必须合并到主线内核中.因此,这是一个可以想象的详尽的任务,它来到了着名的Linus Torvalds咆哮.在这里,您还可以使用更多设备树背景.
描述我们正在使用的.dts设备树(设备树源)文件,这些文件是人类可读的,并由设备树编译器(dtc)编译成设备树blobs(.dtb),二进制格式.当系统引导引导加载程序(例如u-boot)时,将该blob移交给内核.内核解析它并创建设备树给出的所有设备.
如果您不相信我,请使用设备树编译器进入BBB正在使用的设备树.
如果您尚未安装,请获取相应的包装..
pacman -Sy dtc-overlay
dtc -f -I fs /proc/device-tree | less
Run Code Online (Sandbox Code Playgroud)
less由于该命令生成了大量输出,因此建议使用该寻呼机的管道.结果应该看起来像这样......
设备树的所有部分也可以在内核源代码中进行调查,但由于还有一个包含机制,因此信息在几个文件中分开.
<kernel-source>/arch/arm/boot/dts/..
Run Code Online (Sandbox Code Playgroud)
一些相关文件是:
am335x-bone-common.dtsiam335x-boneblack.dtsam33xx.dtsi注:该
.dtsi文件是等同于.h在C文件或C++,因为他们得到包括(因此"我"末)的.dts文件
它们都描述了与处理器相关的设备,Beaglebone平台上的常用设备或仅适用于Beaglebone Black的设备.
好问题,我看到你还在和我在一起.正如我之前所说,内核启动时会解析设备树blob.因此,当您的系统启动并运行时,整个魔法已经结束.在像BBB这样的平台上有一大堆扩展板(Capes),这需要你每次去另一个斗篷使用时重新编译设备树.
因此,您具有覆盖机制,允许您在运行时在设备树中添加或修改设备!惊人.
注意:为了能够编译设备树覆盖,请确保安装适当的包,如上所述(
dtc-overlay)
我给你举个例子.由于BBB没有实时时钟(rtc),这对于生成测量等时间戳很有用,我们将解决这个问题.
我们将使用ds1307实时时钟芯片(实际上我有一个ds1308 rtc,但驱动程序兼容)并通过BBB上的I2C1总线与之通信.默认情况下,从设备树源中可以看到BBB上的总线已禁用.
该代码段中的重要信息是:
现在我们将创建一个覆盖层来配置i2c1总线的GPIO引脚,激活该总线,然后我们将添加rtc-device i2c1总线,以便自动加载相应的驱动程序并创建rtc-device /dev.
BBB上的P8和P9接头上的GPIO引脚具有多个功能,这些功能被复用在一起,因此我们必须调整pinmux设置以将其用于I2C通信.正如您在此表中可以看到的I2C1总线,我们必须在多路复用模式2中使用标头引脚17和18.要获得有关BBB上GPIO处理的更多信息,请查看此处.
/dts-v1/;
/plugin/;
/{ /* this is our device tree overlay root node */
compatible = "ti,beaglebone", "ti,beaglebone-black";
part-number = "BBB-I2C1"; // you can choose any name here but it should be memorable
version = "00A0";
fragment@0 {
target = <&am33xx_pinmux>; // this is a link to an already defined node in the device tree, so that node is overlayed with our modification
__overlay__ {
i2c1_pins: pinmux_i2c1_pins {
pinctrl-single,pins = <
0x158 0x72 /* spi0_d1.i2c1_sda */
0x15C 0x72 /* spi0_cs0.i2c1_sdl */
>;
};
};
};
}; /* root node end */
Run Code Online (Sandbox Code Playgroud)
乍一看,覆盖语法看起来很奇怪,但它基本上由所谓的片段组成,这些片段以现有设备节点为目标并修改该节点(以及它的子节点).
在这种情况下,我们将目标am33xx_pinmux定位在处理器设备树(am33xx.dtsi)中定义的设备节点.在该节点内,我们添加了一个名为pinmux_i2c1_pins的新子节点,该节点之前不存在(请查看am335x-bone-common.dtsi验证)和标签i2c1_pins.
下一部分有点复杂,如果您有兴趣阅读本文.每个GPIO引脚由一个寄存器配置,有几个位来控制它的行为,所有寄存器都由pinctrl-single驱动程序控制.要设置一个特定的引脚,只需使用它与基地址的地址偏移量(你会在上面的P9头表中找到它),并将它的引脚配置作为第二个参数.
我从Derek Molloy借用了这个概述来解释引脚模式.由于0x72相当于01110010b我们将两个引脚配置为具有使能上拉电阻的输入和多路复用模式下的有源压摆控制2.
并且这些引脚的多路复用模式2意味着标题P9上的引脚17是时钟线SCL,而标题P9上的引脚18是数据线SDA.
这是绝对正确的,所以让我们扩展我们的叠加如下..
/dts-v1/;
/plugin/;
/{ /* this is our device tree overlay root node */
compatible = "ti,beaglebone", "ti,beaglebone-black";
part-number = "BBB-I2C1"; // you can choose any name here but it should be memorable
version = "00A0";
fragment@0 {
target = <&am33xx_pinmux>; // this is a link to an already defined node in the device tree, so that node is overlayed with our modification
__overlay__ {
i2c1_pins: pinmux_i2c1_pins {
pinctrl-single,pins = <
0x158 0x72 /* spi0_d1.i2c1_sda */
0x15C 0x72 /* spi0_cs0.i2c1_sdl */
>;
};
};
};
fragment@1 {
target = <&i2c1>;
__overlay__ {
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status = "okay";
rtc: rtc@68 { /* the real time clock defined as child of the i2c1 bus */
compatible = "dallas,ds1307";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x68>;
};
};
};
}; /* root node end */
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,我们添加了一个针对i2c1设备节点的新片段,并告诉它使用我们之前定义的引脚配置.我们将I2C时钟频率设置为100kHz并激活器件.
此外,rtc时钟作为子节点添加到i2c1节点.内核的重要信息是兼容语句,命令驱动程序使用(ds1307)和I2C总线上的设备地址(0x68).rtc的I2C地址可以从数据表中获得.
首先必须编译设备树源.使用dtc编译器进行以下调用..
dtc -O dtb -o <filename>-00A0.dtbo -b 0 -@ <filename>.dts
Run Code Online (Sandbox Code Playgroud)
警告!文件名必须是您想要的名称和上面显示的版本标签(-00A0)的串联,否则您将很难.
.dtbo应该复制生成的文件/lib/firmware,我真的不知道"-00A0"命名约定来自哪里,但固件目录中还有其他文件也使用它.
从现在开始,您可以使用Capemgr动态加载叠加层.为此,请进入/sys/devices/platform/bone_capemgr/然后执行..
echo <filename> > slots
Run Code Online (Sandbox Code Playgroud)
然后,Capemgr将.dtbo在固件目录中查找您的文件,并在可能的情况下加载它.通过查看插槽文件,您可以查看该过程是否成功.它应该看起来像这样......
检查Beaglebone使用的设备树.
dtc -f -I fs /proc/device-tree | less
Run Code Online (Sandbox Code Playgroud)
你会发现叠加层中的所有条目..
此外,文件系统中应该有一个新的I2C设备(/dev/i2c-1)和一个新的rtc设备(/dev/rtc1).
要查看您的i2c总线,请安装包i2c-tools并使用..
i2cdetect -r 1
Run Code Online (Sandbox Code Playgroud)
输出应该是这样的..
如您所见,地址0x68被设备占用.
要查询你的rtc使用..
hwclock -r -f /dev/rtc1
Run Code Online (Sandbox Code Playgroud)
不,还有一个选项,在启动时加载设备树覆盖.真棒!
要这样做,请打开/boot/uEnv.txt并添加bone_capemgr.enable_partno=<filename>到optargs语句中.这就是我在BBB上的样子
optargs=coherent_pool=1M bone_capemgr.enable_partno=bbb-i2c1
Run Code Online (Sandbox Code Playgroud)
令人困惑的是,文件名用于optargs而不是part-number设备树覆盖中定义的标记.
如果你愿意的话,你可以把我的示例代码放在github上有用的Makefile旁边.
很抱歉很长的帖子.
小智 2
这是非常有用且有价值的信息。我编写了一个 i2c 内核驱动程序,可以动态加载该驱动程序以与地址 0x77 处的自定义芯片通信。我过去通过手动实例化设备成功地与芯片通信,如下所示:echo act2_chip 0x77 > /sys/bus/i2c/devices/i2c-1/new_device。设备实例化后,我可以使用 i2cdetect 工具查看它,并且我的可加载内核驱动程序可以与芯片通信。
现在我尝试使用设备树方法实例化设备。因此,按照您的指导,我更改了 dtsi 文件中的一些参数,如下所示:
fragment@1 {
target = <&i2c1>;
__overlay__ {
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status = "okay";
act2_chip: act2_chip@77 { /* the real time clock defined as child of the i2c1 bus */
compatible = "xx,act2_chip";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x77>;
};
Run Code Online (Sandbox Code Playgroud)
我将芯片连接到 scl 和 sda 的引脚 17 和 18。这是 echo > slot 之后得到的 dmesg 输出:

但是,当将驱动程序插入内核时,我看到探针函数被调用。这意味着驱动程序能够按照我的想法看到该设备。
当我尝试写入内核驱动程序时,我收到以下消息: omap_i2c 4802a000.i2c:控制器超时
| 归档时间: |
|
| 查看次数: |
15112 次 |
| 最近记录: |