如何使用设备树覆盖在Beaglebone Black上添加i2c设备?

时间:2015-11-05 15:57:45

标签: beagleboneblack device-tree

我为什么要读这个?

如果您有Beaglebone Black(BBB)并且想要将自己的设备连接到它(而不是斗篷),您可能已经听说过设备树。在我的情况下,我想将RTC设备连接到BBB上的I2C总线。网络上散布着大量信息,本文旨在概述我发现的内容,以及完成任务的指南。

因此,我将给出一个在BBB上激活I2C总线以及使用内核中包含的设备驱动程序连接DS1308 RTC芯片的完整示例。听起来不错?

然后继续阅读,如果有任何不明确的地方请留下评论。如果您有点匆忙,也可以抓住Github上的设备树覆盖代码并飞走。

首先要做的事情。

我在我的BBB上使用ArchLinux ARM主要是因为Arch Linux非常棒而且使用debianoid发行版我可能太愚蠢了。 这是系统的screenfetch ..

ArchLinux screenfetch on BBB

您可能会注意到内核版本已经高于3.x的内容。您在screenfetch中看不到的是内核使用Capemgr实用程序支持设备树覆盖。

什么是设备树?

我只是快速完成,您可以找到更深入的知识herehereherehere。 设备树是描述平台上底层硬件的结构。它在嵌入式设备中大量使用,因为SOC和其他东西没有像PCI这样可以发现设备的总线。它们必须静态定义,并连接到平台总线"给出内核附带的设备驱动程序的句柄。

在将设备树引入Linux之前,所有这些工作都必须使用特定的C头文件和自定义实现来完成,然后所有这些都必须合并到主线内核中。因此,这是一个可以想象的详尽的任务,它来到了着名的Linus Torvalds rant。在这里,您还可以使用device tree background

是的很好,但它是如何工作的?

要描述设备树,我们使用.dts(设备树源)文件,这些文件是人类可读的,并由设备树编译器(dtc)编译到设备树blob中( .dtb),二进制格式。当系统引导引导加载程序(例如u-boot)时,将该blob移交给内核。内核解析它并创建设备树给出的所有设备。

如果您不相信我,请使用设备树编译器加入BBB正在使用的设备树中。

如果您还没有安装它,请获取相应的包..

pacman -Sy dtc-overlay
dtc -f -I fs /proc/device-tree | less

由于该命令产生了大量输出,因此建议使用该分流器less的管道。结果应该看起来像这样......

enter image description here

设备树的所有部分也可以在内核源代码中进行调查,但由于还有一个包含机制,因此信息在几个文件中分开

 <kernel-source>/arch/arm/boot/dts/.. 

一些相关文件是:

  • am335x-bone-common.dtsi
  • am335x-boneblack.dts
  • am33xx.dtsi
  

注意:.dtsi文件等同于C或C ++中的.h文件   因为它们被.dts包括在内(因此最后的#i;#)   文件

它们都描述了与处理器相关的设备,Beaglebone平台上的常用设备或仅适用于Beaglebone Black的设备。

您提到了叠加层,那是什么?

好问题,我看到你还和我在一起。正如我之前所说,内核启动时会解析设备树blob。因此,当您的系统启动并运行时,整个魔法已经结束。在像BBB这样的平台上有一大堆扩展板(Capes),这需要你每次去另一个斗篷使用时重新编译设备树。

因此,您有覆盖机制,允许您在运行时在设备树中添加或修改设备!惊人。

  

注意:为了能够编译设备树覆盖,请确保安装适当的包,如上所述(dtc-overlay

我将如何使用这一切?

我给你举个例子。由于BBB没有实时时钟(rtc),这对于生成测量等时间戳很有用,我们将解决这个问题。

我们将使用ds1307实时时钟芯片(实际上我有ds1308 rtc但驱动程序兼容)并通过BBB上的I2C1总线与之通信。默认情况下,从设备树源中可以看到BBB上的总线已禁用..

i2c1 node in am33xx.dtsi

该摘录中的重要信息是:

  • 名为&#39; i2c1&#39;的节点已定义
  • 它被定义为与omap4-i2c驱动程序兼容
  • 根据处理器reference manual(第181页)为设备分配内存映射地址(0x4802a000)和适当的地址范围(0x1000)
  • 设备状态已停用

现在我们将创建一个覆盖层来配置i2c1总线的GPIO引脚,激活该总线,然后我们将添加rtc-device i2c1总线,以便自动加载相应的驱动程序和rtc -device在/dev

中创建

BBB上的P8和P9接头上的GPIO引脚具有多个功能,这些功能被复用在一起,因此我们必须调整pinmux设置以将其用于I2C通信。正如您在this table中看到的I2C1总线,我们必须在多路复用模式下使用标头引脚17和18. 2.要获得有关BBB上GPIO处理的更多信息,请here

/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 */
刚刚发生了什么?

乍一看,叠加语法看起来很奇怪,但它基本上由所谓的片段组成,这些片段以现有设备节点为目标并修改该节点(以及它的子节点)。

在这种情况下,我们定位在处理器设备树(am33xx_pinmux)中定义的am33xx.dtsi设备节点。在该节点内,我们添加了一个名为pinmux_i2c1_pins的新子节点,之前不存在(查看am335x-bone-common.dtsi验证)和标签i2c1_pins。

下一部分有点复杂,如果您有兴趣请阅读this。每个GPIO引脚都由一个寄存器配置,有几个位来控制它的行为,所有寄存器都由pinctrl-single驱动程序控制。要设置一个特定的引脚,只需使用它从基地址开始的地址偏移(你会在上面的P9标题表中找到它),它的引脚配置作为第二个参数。

BBB gpio settings

我从Derek Molloy借用了这个概述来解释引脚模式。由于0x72相当于01110010b,因此我们将两个引脚配置为输入,并在多路复用模式2下启用上拉电阻和有源压摆控制。

这些引脚的多路复用模式2表示接头P9上的引脚17是时钟线SCL,接头P9上的引脚18是数据线SDA。

但我们还是要启用I2C1?

这绝对是正确的,所以让我们扩展我们的叠加层如下..

/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 */

在上面的代码中,我们添加了一个针对i2c1设备节点的新片段,并告诉它使用我们之前定义的引脚配置。我们将I2C时钟频率设置为100kHz并激活器件。

此外,rtc时钟作为子节点添加到i2c1节点。内核的重要信息是兼容语句,命名要使用的驱动程序(ds1307)和I2C总线上的设备地址(0x68)。 rtc的I2C地址可以从数据表中获得。

我如何将代码放入内核?

首先必须编译设备树源。使用dtc编译器进行以下调用..

dtc -O dtb -o <filename>-00A0.dtbo -b 0 -@ <filename>.dts
  

小心!文件名必须是您想要的名称和上面显示的版本标签(-00A0)的串联,否则您将会遇到困难。

生成的.dtbo文件应该复制到/lib/firmware,我真的不知道&#34; -00A0&#34;命名约定来自但固件目录中还有其他文件也使用它。

从现在开始,您可以使用Capemgr动态加载叠加层。为此,请进入/sys/devices/platform/bone_capemgr/,然后执行..

echo <filename> > slots

然后,Capemgr将在固件目录中查找您的.dtbo文件,并在可能的情况下加载它。通过查看插槽文件,您可以查看该过程是否成功。看起来应该是这样的......

enter image description here

检查Beaglebone使用的设备树。

dtc -f -I fs /proc/device-tree | less

您将找到叠加层中的所有条目..

enter image description here

enter image description here

此外,文件系统中应该有一个新的I2C设备(/dev/i2c-1)和一个新的rtc设备(/dev/rtc1)。

要查看您的i2c总线,请安装包i2c-tools并使用..

i2cdetect -r 1

输出应该是这样的......

enter image description here

如您所见,地址0x68被设备占用。

查询你的rtc使用..

hwclock -r -f /dev/rtc1

但那不是全部,不是吗?

不,还有一个选项,在启动时加载设备树覆盖。 太棒了!

要执行此操作,请打开/boot/uEnv.txt并将bone_capemgr.enable_partno=<filename>添加到optargs语句中。这就是我在BBB上的样子

optargs=coherent_pool=1M bone_capemgr.enable_partno=bbb-i2c1

令人困惑的是,文件名用于optargs而不是设备树覆盖中定义的part-number标记。

如果您愿意,可以将我的示例代码放在github上的有用Makefile旁边。

很抱歉很长的帖子。

1 个答案:

答案 0 :(得分:2)

这是非常有用且有价值的信息。我写了一个i2c内核驱动程序,我可以动态加载它与地址0x77的自定义芯片通信。通过如下手动实例化设备,我已经成功地与芯片通信:echo act2_chip 0x77&gt; / SYS /总线/ I2C /装置/ I2C-1 / new_device。 设备实例化后,我可以使用i2cdetect工具看到它,我的可加载内核驱动程序可以与芯片通信。

现在我尝试使用设备树方法实例化设备。因此,在您的指导下,我更改了您的dtsi文件中的一些参数,如下所示:

片段@ 1 {     target =&lt;&amp; i2c1&gt ;;

__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>;
  };

我在针脚17和18连接了芯片,用于scl和sdk。这是我在echo&gt;之后得到的dmesg输出。插槽: image

但是在将驱动程序插入内核时,我看到调用了探测函数。这意味着司机能够在我想到的时候看到设备。

当我尝试写入内核驱动程序时,我收到以下消息: omap_i2c 4802a000.i2c:控制器超时