【问题标题】:How to add i2c devices on the Beaglebone Black using device tree overlays?如何使用设备树覆盖在 Beaglebone Black 上添加 i2c 设备?
【发布时间】:2015-11-05 15:57:44
【问题描述】:

我为什么要读这篇文章?

如果您有 Beaglebone Black (BBB),并且想要将自己的设备连接到它(而不是 cape),您可能已经听说过设备树。就我而言,我想将 RTC 设备连接到 BBB 上的 I2C 总线。网络上散布着大量信息,本文旨在总结我发现的内容,同时也是完成它的指南。

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

然后继续阅读,如果有任何不清楚的地方,请离开 cmets。如果您有点着急,您也可以在Github 上获取设备树覆盖代码并飞走。

首要任务。

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

您可能会注意到内核版本已经高于 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 的管道,因为该命令会生成大量输出。结果应该是这样的..

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

中的多个文件中拆分
 <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) 的平台上,这将需要您在每次使用另一个 cape 时重新编译设备树。

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

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

我将如何使用这一切?

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

我们将使用ds1307 实时时钟芯片(实际上我有一个ds1308 rtc,但驱动程序是兼容的)并通过BBB 上的I2C1 总线与其通信。默认情况下,BBB 上的总线是禁用的,正如您从设备树源中看到的那样。

那个sn-p里面的重要信息是:

  • 定义了一个名为“i2c1”的节点
  • 定义为兼容omap4-i2c驱动
  • 根据处理器reference manual(第 181 页)为设备分配一个内存映射地址 (0x4802a000) 和适当的地址范围 (0x1000)
  • 设备状态为禁用

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

BBB 上 P8 和 P9 接头上的 GPIO 引脚具有多种功能,它们混合在一起,因此我们必须调整 pinmux 设置以将它们用于 I2C 通信。正如您在 I2C1 总线的 this table 中看到的那样,我们必须在多路复用器模式 2 中使用接头引脚 17 和 18。要获取有关 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.dtsi) 中定义的am33xx_pinmux 设备节点为目标。在该节点中,我们添加了一个名为 pinmux_i2c1_pins 的新子节点,该子节点之前不存在(查看am335x-bone-common.dtsi 进行验证)和标签 i2c1_pins。

下一部分有点复杂,如果您有兴趣阅读this。每个 GPIO 引脚都由一个具有多个位的寄存器配置以控制其行为,并且所有寄存器都由 pinctrl-single 驱动程序控制。要设置特定引脚,只需使用它与基地址的地址偏移量(您将在上面的 P9 头表中找到)并将其引脚配置作为第二个参数..

我从Derek Molloy借用了这个概述来解释pin模式。由于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地址可以从datasheet中获取。

我如何将该代码放入内核?

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

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

小心!文件名必须是你想要的名字加上上面看到的版本标签 (-00A0) 的串联,否则你会很难。

应将生成的 .dtbo 文件复制到 /lib/firmware 中,我真的不知道“-00A0”命名约定的来源,但固件目录中的其他文件也使用它。

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

echo <filename> > slots

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

检查 Beaglebone 使用的设备树。

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

您会从叠加层中找到所有条目..

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

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

i2cdetect -r 1

输出应该是这样的..

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

查询您的 rtc 使用..

hwclock -r -f /dev/rtc1

但这还不是全部,不是吗?

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

为此打开/boot/uEnv.txt 并将bone_capemgr.enable_partno=&lt;filename&gt; 添加到optargs 语句中。这就是它在我的 BBB 上的样子

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

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

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

抱歉,帖子太长了。

【问题讨论】:

  • 这是一个超级棒的解释!您能否发布更多有关如何连接 GPIO 设备的详细信息。假设需要 5 个 gpios?此外,如果您必须为您的设备编写绑定,比如 rtc,它会是什么样子?以及如何以及在何处插入并构建/测试它?我知道你也很清楚这个问题的答案!!非常期待您的回复!
  • 简而言之,如果您必须使用设备树而不是覆盖来运行驱动程序
  • 如果您不想使用覆盖机制,您需要从内核源代码中获取设备树源代码(形成内核源代码目录,您可以在./arch/arm/boot/dts 中找到它)并添加您的自定义配置。那是容易的部分。之后,您需要为您的板重新编译设备树并替换旧的设备树二进制 blob,该二进制 blob 通常附加到内核 uimage 或 zimage 文件中。可能最好的方法是为您的主板重新编译内核。这样做时,您也可以将驱动程序嵌入内核中。问候
  • @Raulp 关于你的 GPIO 请求看看这个post,也许你可以提取一些有用的信息。
  • 感谢它对我有用!我在 am335x-boneblack.dts 中添加了一个节点,其中包含我正在使用的所需 gpios:my-gpio = ;其中 gpio1 是 gpio 控制器 1 , 16 是 gpio1 bank 上的引脚 => 实际 gpio 引脚是 32*1+16=> 48 ,“1”是 ACTIVE_LOW 配置。我也可以使用宏来表示低电平有效/高的。我使用以下方法提取了 gpio: ret = of_get_named_gpio(child, "my-gpio", 0);

标签: beagleboneblack device-tree


【解决方案1】:

这是非常有用且有价值的信息。我编写了一个 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>;
      };

我在 scl 和 sda 的引脚 17 和 18 处连接了芯片。这是我在 echo > slots 之后得到的 dmesg 输出:

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

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

【讨论】:

  • 您能否确认特定引脚没有被任何其他驱动程序占用?似乎 I2C 驱动程序的引脚请求失败。为此,请在加载驱动程序之前查看位于“/sys/kernel/debug/pinctrl/..”中的文件。我现在手头没有 beaglebone 来告诉你确切的路径,但是有一些文件可以告诉你哪个驱动程序占用了哪些引脚。之后您需要卸载该驱动程序并重试
  • 好的,我找到了路径。它是/sys/kernel/debug/pinctrl/44e10800.pinmux。有几个文件,您应该查看pimux-pinspins 以获取有关引脚状态和占用引脚的驱动程序的信息。
  • 引脚不被其他任何东西使用。我尝试在这里复制输出,但它被版主删除了。一个问题:由于您正在注册的设备在内核中已经有一个驱动程序,我猜是 rtc-ds1307.c,我是否需要使用静态编译到其中的驱动程序来编译内核,或者使用 insmod 执行工作吗?另外查看您提供的 git 代码,您将两个 udev 规则复制到 /etc/udev/rules.d/。这样做的目的是什么。我没有这样做。引脚 17 和 18 上是否有上拉电阻?
  • 不,您不需要将其编译到内核中。将驱动程序加载为内核模块很好。 I2C 文件夹中的 udev 规则只是告诉系统将生成的 i2c-device 的权限设置为0666。所以你不需要是 root 或使用 sudo 来访问 i2c 总线。 rtc 的 udev 规则几乎相同。访问权限已更改,但此外还有一个名为 rtc 的设备文件默认链接到 rtc0(即 CPU 中的 rtc 时钟),我只是将链接弯曲到新创建的 rtc1 文件,即i2c-rtc
  • 默认情况下,所有 GPIO 引脚都应使用上拉电阻作为保护措施,但您可以在内核模块代码中更改它。您可以使用内核函数来操作引脚配置(尽管我体验过它有时无法按预期工作),或者您直接访问引脚配置寄存器并操作相应的位。我建议查看Derek Molloys page 以更深入地了解 GPIO 编程。问候
猜你喜欢
  • 2013-08-25
  • 2019-06-01
  • 2016-07-05
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 2018-11-29
  • 2015-07-04
  • 1970-01-01
相关资源
最近更新 更多