如果您有一个Beaglebone Black(BBB),并且想要将自己的设备连接到它(不是capes),那么您可能已经听说过设备树.在我的情况下,我想将RTC设备连接到BBB上的I2C总线.网络上散布着大量信息,本文旨在概述我发现的内容,并指导您完成此操作.
因此,我将给出一个在BBB上激活I2C总线的完整示例,以及使用内核中包含的设备驱动程序连接DS1308 RTC芯片.听起来不错?
然后阅读,如果有什么不清楚,请留下评论.如果您有点匆忙,您也可以在Github上抓住设备树重叠代码并飞走.
第一件事
我在我的BBB上使用ArchLinux ARM,主要是因为Arch Linux很棒,我可能太笨了,不能使用debianoid发行版.
这是系统的screenfetch
你可能会注意到内核版本已经超过了3.x的东西.在screenfetch中看不到的是内核支持使用Capemgr实用程序的设备树覆盖.
什么是设备树?
我会做的很快,你可以找到更深入的知识here,here,here和here.
设备树是描述您平台上底层硬件的结构.它在嵌入式设备中大量使用,因为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
由于该命令生成了大量的输出,所以推荐使用传呼机的管道.结果应该看起来像这样..
您的设备树的所有部分也可以在内核源中进行调查,但是由于还有一个包含机制,信息会在几个文件之间分割
<kernel-source>/arch/arm/boot/dts/..
一些相关文件是:
> am335x-bone-common.dtsi
> am335x-boneblack.dts
> am33xx.dtsi
Note: The
.dtsi
files are equivalent to.h
files in C or C++
because they get included (therefore the ‘i’ at the end) by.dts
files
它们都描述与处理器相关的设备,Beaglebone平台上的常见设备或仅适用于Beaglebone Black的设备.
你提到了叠加层,那是什么?
好的问题,我看到你还在我身边.如前所述,内核启动时,设备树blob将被解析.所以当你的系统启动并运行时,整个魔法已经结束了.在像BBB这样的平台上,有一大堆扩展板(Capes),这将需要您每次去另一个斗篷使用时重新编译设备树.
因此,您可以使用覆盖机制,允许您在设备树中添加或修改设备AT RUNTIME!惊人.
Note: to be able to compile device tree overlays make sure to install the appropriate package like above (
dtc-overlay
)
我该如何使用这一切?
我会给你一个例子.由于BBB没有实时时钟(rtc),这对于生成测量等的时间戳是有用的,我们将要解决这个问题.
我们将使用ds1307实时时钟芯片(事实上,我有一个ds1308 rtc,但驱动程序是兼容的),并通过BBB上的I2C1总线进行通信.默认情况下,BBB上的总线被禁用,从设备树源可以看到.
该片段中的重要信息是:
>定义了一个名为’i2c1’的节点
>它被定义为与omap4-i2c驱动程序兼容
>根据处理器reference manual(第181页),设备将分配一个内存映射地址(0x4802a000)和一个适当的地址范围(0x1000)
>设备状态被禁用
现在我们将创建一个覆盖图来配置i2c1总线的GPIO引脚,激活该总线,之后我们将添加rtc-i2c1总线,以便自动加载相应的驱动程序,并在/ dev中创建rtc-device .
BBB上的P8和P9接头上的GPIO引脚具有多种功能,它们被复合在一起,因此我们必须调整引脚设置以将其用于I2C通信.从this table可以看到,对于I2C1总线,我们必须在多路复用器模式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 */
OMG刚刚发生什么?
乍一看,重叠语法看起来很奇怪,但它基本上由所谓的片段组成,目标是已经存在的设备节点并修改该节点(而且是子节点).
在这种情况下,我们定位处理器设备树(am33xx.dtsi)中定义的am33xx_pinmux设备节点.在该节点中,我们添加一个新的子节点,名为pinmux_i2c1_pins,之前不存在(看看am335x-bone-common.dtsi以验证)和标签i2c1_pins.
下一部分更复杂一些,如果您有兴趣,请阅读this.每个GPIO引脚都由一个具有几个位的单个寄存器配置,以控制其行为,所有寄存器由pinctrl单个驱动程序控制.要设置一个特定的引脚只是使用它的地址偏移从基地址(你会发现在上面的P9标题表),它的引脚配置作为第二个参数..
我从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,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设备节点的新片段,并告诉它使用我们以前定义的引脚配置.我们设置一个100kHz的I2C时钟频率并激活该设备.
此外,rtc时钟作为一个小孩添加到i2c1节点.内核的重要信息是兼容语句,命名驱动程序(ds1307)和I2C总线上的器件地址(0x68). rtc的I2C地址可以从数据表中获取.
那我该如何把这个代码放到内核中?
起初设备树源必须被编译.使用带有以下调用的dtc编译器
dtc -O dtb -o <filename>-00A0.dtbo -b 0 -@ <filename>.dts
Caution! The filename must be a concatenation of the name you desire plus the version tag as seen above (-00A0) otherwise you’ll have a hard time.
所得到的.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工具并使用..
i2cdetect -r 1
输出应该是这样的东西..
您可以看到地址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中使用,而不是在设备树覆盖中定义的部件号标签.
如果你喜欢,你可以在github将代码放在一个有用的Makefile上.
对不起,长发
解决方法
在设备实例化之后,我可以看到它使用i2cdetect工具,我的可加载内核驱动程序可以与芯片进行通信.
现在我试图使用设备树方法实例化设备.所以跟随你,我改变了你的dtsi文件中的一些参数,如下所示:
片段@ 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>; };
我将芯片连接到引脚17和18,用于scl和sdk.这是echo中得到的dmesg输出>插槽:
但是当将驱动程序插入内核时,我看到被调用的探测功能.这意味着司机能够尽可能地看到设备.
当我尝试写入内核驱动程序时,我会收到以下消息:omap_i2c 4802a000.i2c:控制器超时