Зачем мне это читать?
Если у вас есть Beaglebone Black (BBB), и вы хотите подключить к нему свои устройства (а не накидки), вы, возможно, уже слышали о дереве устройств. В моем случае я хотел подключить устройство RTC к шине I2C на BBB. Существует много информации, разбросанной по всему Интернету, и эта статья предназначена для краткого изложения того, что я нашел, а также руководства, чтобы сделать это.
Итак, я приведу полный пример активации шины I2C на BBB, а также подключения чипа DS1308 RTC с использованием драйверов устройств, входящих в состав ядра. Звучит интересно?
Затем читайте дальше и, пожалуйста, оставляйте комментарии, если что-то неясно. Если вы немного торопитесь, вы также можете просто захватить код наложения дерева устройств на Github и улететь.
Прежде всего.
Я использую ArchLinux ARM на моем BBB, главным образом потому, что Arch Linux поражает, и я, возможно, слишком глуп, чтобы использовать дистрибутивы debianoid. Здесь screenfetch системы.
Как вы могли заметить, версия ядра уже выше того 3.x. То, что вы не видите в screenfetch, состоит в том, что ядро поддерживает наложения дерева устройств с помощью утилиты Capemgr.
Что такое дерево устройств?
Я просто сделаю это быстро, вы сможете найти более глубокие знания здесь, здесь, здесь и здесь. Дерево устройств - это структура, описывающая базовое оборудование на вашей платформе. Он сильно используется во встроенных устройствах, поскольку SOC и прочее не имеют таких шин, как PCI, где можно обнаружить устройства. Они должны быть определены статически и привязаны к "платформенной шине", чтобы дать ручку драйверам устройств, поставляемым с ядром.
До того, как дерево устройств было представлено в Linux, вся эта работа должна была быть выполнена с помощью определенных файлов заголовков C и пользовательских реализаций, которые затем должны были быть объединены в ядро mainline. Таким образом, являясь мыслимой исчерпывающей задачей, он пришел к известному произнесению Линуса Торвальдса. Здесь вы также можете найти еще дерево дерева устройств.
Да, хорошо, но как это работает?
Чтобы описать дерево устройств, мы используем файлы .dts
(файл дерева устройств), которые читаются человеком и скомпилируются компилятором дерева устройств (dtc
) в кадры устройства (.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
эквивалентны файлам.h
в C или С++ потому что они включаются (поэтому "i" в конце) на.dts
Файлы
Все они описывают устройства, относящиеся к процессору, общие устройства на платформе Beaglebone или устройства, которые подходят только для Beaglebone Black.
Вы упомянули оверлеи, что это?
Хороший вопрос, я вижу, ты все еще со мной. Как я уже говорил, блок blob дерева устройств анализируется при загрузке ядра. Поэтому, когда ваша система работает и работает, все волшебство уже закончено. На платформе, подобной BBB, со множеством плат расширения (Capes) это потребует от вас перекомпилировать дерево устройств каждый раз, когда вы идете на другой мыс.
Поэтому у вас есть механизм наложения, который позволяет добавлять или изменять устройства в дереве устройств. RUNTIME! Удивительно.
Примечание. Чтобы иметь возможность компилировать наложения дерева устройств, обязательно установите соответствующий пакет, как указано выше (
dtc-overlay
)
И как я буду использовать все это?
Я приведу вам пример. Поскольку у BBB нет часов реального времени (rtc), которые были бы полезны для создания временных меток для измерений и т.д., Мы исправим это.
Мы будем использовать ds1307 чип часов реального времени (на самом деле у меня есть ds1308 rtc, но драйвер совместим) и обмениваться данными с ним по шине I2C1 на BBB. По умолчанию эта шина отключена на BBB, как вы можете видеть из источников дерева устройств.
Важная информация в этом фрагменте:
- a node с именем 'i2c1' определяется
- он определяется как совместимый с драйвером omap4-i2c
- устройству присваивается адрес памяти (0x4802a000) и соответствующий диапазон адресов (0x1000) в соответствии с процессорами справочное руководство (страница 181)
- статус устройств отключен.
Теперь мы создадим оверлей для настройки выводов GPIO для шины i2c1, активируем эту шину, а затем добавим шину i2c1 rtc-устройства, чтобы автоматически загрузился соответствующий драйвер и было создано rtc-устройство в /dev
.
Штыри GPIO на заголовках P8 и P9 на BBB имеют несколько функций, которые объединены вместе, и поэтому мы должны настроить параметры pinmux, чтобы использовать их для связи I2C. Как вы можете видеть в этой таблице для шины I2C1, мы должны будем использовать штырьки заголовка 17 и 18 в режиме мультиплексирования 2. Чтобы получить больше информации об обработке GPIO на BBB смотрите здесь.
/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";
[email protected] {
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, что только что произошло?
На первый взгляд синтаксис наложения выглядит довольно странно, но в основном он сделан из так называемых фрагментов, которые нацелены на уже существующее устройство node и модифицирует это node (и это дети).
В этом случае мы нацеливаем на устройство am33xx_pinmux
node, которое определено в дереве устройств процессоров (am33xx.dtsi
). Внутри этого node мы добавляем новый дочерний элемент node, называемый pinmux_i2c1_pins, который раньше не существовал (просмотрите am335x-bone-common.dtsi
для проверки) и метку i2c1_pins.
Следующая часть немного сложнее, и если вам интересно читать this. Каждый вывод GPIO настраивается одним регистром с несколькими битами для управления его поведением, а все регистры управляются драйвером pinctrl-single
. Чтобы установить конкретный вывод, просто используйте его смещение адреса от базового адреса (вы найдете это в таблице заголовков P9 выше), и он установит конфигурацию как второй параметр.
Я рассмотрел этот обзор из Дерек Моллой, чтобы объяснить режим вывода. Так как 0x72
эквивалентен 01110010b
, мы имеем оба вывода, сконфигурированные как входы с включенным резистором подтягивания и активным выключением в режиме мультиплексирования.
И режим мультиплексирования 2 для этих контактов означает, что вывод 17 на заголовке P9 является строкой SCL линии синхронизации, а вывод 18 на заголовке P9 является линией передачи данных 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";
[email protected] {
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 */
>;
};
};
};
[email protected] {
target = <&i2c1>;
__overlay__ {
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status = "okay";
rtc: [email protected] { /* 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 node, и попросите его использовать нашу ранее определенную конфигурацию контактов. Мы устанавливаем тактовую частоту I2C 100 кГц и активируем устройство.
Кроме того, часы rtc были добавлены в качестве дочернего элемента в i2c1 node. Важной информацией для ядра является совместимый оператор, называя драйвер для использования (ds1307
) и адрес устройства на шине I2C (0x68
). Адрес rtc I2C может быть получен из таблицы данных.
И как мне получить этот код в ядре?
Сначала необходимо скомпилировать источник дерева устройства. Используйте компилятор dtc со следующим вызовом.
dtc -O dtb -o <filename>-00A0.dtbo -b 0 [email protected] <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=<filename>
в оператор optargs
. Что это выглядит на моем BBB
optargs=coherent_pool=1M bone_capemgr.enable_partno=bbb-i2c1
Смутно, имя файла используется в optargs, а не в теге part-number
, определенном в наложении дерева устройств.
Вы можете получить мой пример кода в сторону полезного Makefile на github, если хотите.
Извините за длинный пост.