原文网址:https://techfantastic.wordpress.com/2013/11/15/beaglebone-black-device-tree-overlay/
经过一晚上的Google,终于大致明白device tree是怎么用的了,这里简单梳理一下思路。
一、简介
===============================================================device tree是ARM linux 3.7开始使用的系统控制硬件资源的方式,这里说的硬件资源既包括片上的诸如GPIO、PWM、I2C、ADC等资源,也包括外部拓展的如FLASH、LCD等。ARM使用device tree目前还是很新的东西(2012年低诞生的),自带3.8版本Linux系统的Beaglebone Black是第一批使用device tree的ARM设备之一。难怪目前关于它的中文资料还很少。之前的linux如果要配置硬件,需要重新编译内核,但用device tree就不必重新编译内核,甚至不必重启系统就能实现。如此方便的工具让我们来看一看庐山面目!从单片机、STM32过渡到Cortex-A8,之前直接操作寄存器来控制硬件的思路已经不好使了,但是device tree提供了一种很类似直接操作寄存器,但是比它更有条理,更容易理解的方式。首先需要编写一个.dts文件(device tree source),在文件中说明我要设置的硬件和它的各种属性,然后使用dtc命令编译这个.dts文件生成对应的二进制文件.dtb(device tree blob),系统启动时就会加载这个device tree并配置各种硬件资源。实际上Beaglebone Black自带系统中/boot/目录下已经包含了一些编译好的.dtb文件,从文件名来看似乎每个.dtb文件都能配置一款beagleboard.org的开发板,其中有一个叫做am335x-boneblack.dtb的文件,没猜错的话应当负责了Beaglebone black的缺省硬件配置。但因为已经编译成了二进制文件,所以我们无法读取其内容。
那么我们如果想要自己修改某些功能改怎么办呢?我们肯定不能重新编译一个am335x-boneblack.dtb代替原来的文件,那样会疯掉的。不过我们可以使用device tree overlay来动态重定义某些功能。device tree overlay与device tree类似,同样是编写一个.dts文件,编译成.dtbo文件(末尾的o应该代表overlay)。不同的是我们不把它放到/boot/目录中去,它也不必在启动时加载,而可以在需要时随时进行动态加载。另外device tree overlay的.dts文件跟device tree的.dts文件格式还是有一点区别的,下面要介绍的是device tree overlay的.dts。接下来我们上机操作一下。
二、编写.dts文件
===============================================================用ssh连接好Beaglebone black以后,我们先来找找Angstrom系统自带的.dts文件,看看它们长什么样子。用下面的命令搜索一下dts结尾的文件# find / -name *dts
会得到下面这个列表(部分省略)
1 2 3 4 5 6 7 8 9 10 11 | /lib/firmware/cape-bone-dvi-00A0 .dts /lib/firmware/bone_pwm_P8_45-00A0 .dts /lib/firmware/BB-SPI1A1-00A0 .dts /lib/firmware/BB-ADC-00A0 .dts /lib/firmware/BB-I2C1A1-00A0 .dts /lib/firmware/BB-BONE-SERL-01-00A1 .dts /lib/firmware/cape-bone-dvi-00A2 .dts /lib/firmware/bone_pwm_P8_13-00A0 .dts /lib/firmware/cape-bone-hexy-00A0 .dts /lib/firmware/BB-BONE-LCD7-01-00A2 .dts ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /* * Copyright (C) 2013 CircuitCo * * Virtual cape for UART1 on connector pins P9.24 P9.26 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /dts-v1/; /plugin/; / { compatible = "ti,beaglebone" , "ti,beaglebone-black" ; /* identification */ part-number = "BB-UART1" ; version = "00A0" ; /* state the resources this cape uses */ exclusive-use = /* the pin header uses */ "P9.24" , /* uart1_txd */ "P9.26" , /* uart1_rxd */ /* the hardware ip uses */ "uart1" ; fragment@0 { target = <&am33xx_pinmux>; __overlay__ { bb_uart1_pins: pinmux_bb_uart1_pins { pinctrl-single,pins = < 0x184 0x20 /* P9.24 uart1_txd.uart1_txd MODE0 OUTPUT (TX) */ 0x180 0x20 /* P9.26 uart1_rxd.uart1_rxd MODE0 INPUT (RX) */ >; }; }; }; fragment@1 { target = <&uart2>; /* really uart1 */ __overlay__ { status = "okay" ; pinctrl-names = "default" ; pinctrl-0 = <&bb_uart1_pins>; }; }; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | / { fragment@0 { target = <&am33xx_pinmux>; __overlay__ { bb_uart1_pins: pinmux_bb_uart1_pins { pinctrl-single,pins = < 0x184 0x20 /* P9.24 uart1_txd.uart1_txd MODE0 OUTPUT (TX) */ 0x180 0x20 /* P9.26 uart1_rxd.uart1_rxd MODE0 INPUT (RX) */ >; }; }; }; fragment@1 { target = <&uart2>; __overlay__ { status = "okay" ; pinctrl-names = "default" ; pinctrl-0 = <&bb_uart1_pins>; }; }; }; |
具体来说,am33xx_pinmux可以定义芯片功能复用引脚的具体功能,它使用了pinctrl-single,pins这个驱动,其中第一项0x184代表要修改的引脚,第二项0x20代表要修改成哪个功能(pinctrl-single的具体用法见)。这里把P9.24和P9.26两个引脚定义成了uart1的TX和RX。uart2这个target则使能了uart1(这个uart2实际上对应的是硬件的uart1)。
如果把树形结构什么的都忽略掉,就会发现其实它实现了我之前用寄存器干的事:定义引脚功能,然后使能串口。
那么当我想自己写device tree的时候,一上来就会遇到一个问题:我怎么知道我想控制的对象target名字是什么?我怎么知道它有哪些属性?取值范围是什么?答案在上。(不过有一些在这里似乎找不到,回头解决)
了解了dts文件的基本框架,我们再把之前丢掉的细节拿回来说明一下。(这些细节有些是非常重要的,实际使用中一定不要随意丢掉!)
首先这两行说明了dts的版本号,声明了这个文件的内容是一个plugin 1 2 | /dts-v1/; /plugin/; |
1 | compatible = "ti,beaglebone" , "ti,beaglebone-black" ; |
1 2 3 | /* identification */ part-number = "BB-UART1" ; version = "00A0" ; |
1 2 3 4 5 6 7 | /* state the resources this cape uses */ exclusive-use = /* the pin header uses */ "P9.24" , /* uart1_txd */ "P9.26" , /* uart1_rxd */ /* the hardware ip uses */ "uart1" ; |
三、编译.dts文件
===============================================================写好.dts文件以后需要用dtc编译器编译一下,生成.dtbo文件才能使用。假设我们写好了一个名为ADAFRUIT-SPI0-00A0.dts的文件,编译指令如下# dtc -O dtb -o ADAFRUIT-SPI0-00A0.dtbo -b 0 -@ ADAFRUIT-SPI0-00A0.dts
然后就会生成ADAFRUIT-SPI0-00A0.dtbo文件。下面解释一下各个参数
-O dtb 声明输出格式为dtb文件-o 输出文件名-b 设置启动CPU-@ (我不太清楚这项是干嘛的,似乎是overlay专有的一项)注意文件的命名,一定是“程序名-版本号.dtbo(.dts)”的形式。编译完成以后,一定要把.dtbo文件放到/lib/firmware/目录下才能使用# cp ADAFRUIT-SPI0-00A0.dtbo /lib/firmware
四、overlay的使用 (Exporting and Unexporting an Overlay,加载和卸载)
===============================================================所有已经加载的overlay列表都在/sys/devices/bone_capemgr.*/slots这个文件中。(bone_capemgr.*中的*号实际是一个数字,但是每次系统启动时这个数字可能会变化,所以我们用通配符*代替。)我们打开这个文件看一看# cat /sys/devices/bone_capemgr.*/slots 1 2 3 4 5 6 | 0: 54:PF--- 1: 55:PF--- 2: 56:PF--- 3: 57:PF--- 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI |
# echo BB-UART1 > /sys/devices/bone_capemgr.*/slots
然后我们再打开slots文件看看有什么变化
1 2 3 4 5 6 7 | 0: 54:PF--- 1: 55:PF--- 2: 56:PF--- 3: 57:PF--- 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI 6: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART1 |
外设使用完毕以后,如何卸载呢?一种方法是重启系统,另一种是
# echo -6 > /sys/devices/bone_capemgr.*/slots
但是在最近的Angstrom系统中,用这种方法会导致kernel panic,然后ssh会断开,所以现在还是用重启系统的方法吧。相信今后这个问题应该很快会解决的。
注:adafruit的这篇令我受益匪浅,在此表示一下感谢。