Si vous avez un Beaglebone Black (BBB) et que vous souhaitez y connecter vos propres appareils (pas des capes), vous avez peut-être déjà entendu parler de l'arborescence des appareils. Dans mon cas, je voulais connecter un appareil RTC au bus I2C sur le BBB. Il y a beaucoup d'informations dispersées sur le Web et cet article est censé être un résumé de ce que j'ai trouvé comme également un guide pour le faire.
Je vais donc donner un exemple complet d'activation du bus I2C sur le BBB ainsi que de brancher une puce DS1308 RTC en utilisant les pilotes de périphériques inclus dans le noyau. Cela semble intéressant?
Alors lisez la suite et veuillez laisser des commentaires si quelque chose n'est pas clair. Si vous êtes un peu pressé, vous pouvez également saisir le code de superposition de l'arborescence des périphériques sur Github et vous envoler.
J'utilise ArchLinux ARM sur mon BBB principalement parce que Arch Linux est génial et je suis peut-être trop stupide pour utiliser des distributions debianoid. Voici un screenfetch du système ..
Comme vous pouvez le remarquer, la version du noyau est déjà au-dessus de ce truc 3.x. Ce que vous ne pouvez pas voir dans la capture d'écran, c'est que le noyau prend en charge les superpositions d'arborescence de périphériques à l'aide de l'utilitaire Capemgr .
Je vais le faire rapidement, vous pouvez trouver des connaissances plus approfondies ici , ici , ici et ici . L'arborescence des périphériques est une structure décrivant le matériel sous-jacent de votre plate-forme. Il est fortement utilisé dans les appareils embarqués, car les SOC et tout ça n'ont pas de bus comme PCI où les appareils peuvent être découverts. Ils doivent être définis statiquement et sont attachés à un "bus de plate-forme" pour donner une poignée aux pilotes de périphériques livrés avec le noyau.
Avant l'introduction de l'arborescence des périphériques sous Linux, tout ce travail devait être fait avec des fichiers d'en-tête C spécifiques et des implémentations personnalisées qui devaient ensuite être fusionnées dans le noyau principal. Ainsi, étant une tâche exhaustive imaginable, il en est arrivé au célèbre Linus Torvalds rant . Ici vous allez avec encore plus arrière-plan de l'arborescence des périphériques .
Pour décrire l'arborescence des périphériques, nous utilisons des fichiers .dts
(Source de l'arborescence des périphériques), qui sont lisibles par l'homme et compilés par le compilateur d'arborescence des périphériques (dtc
) dans des blobs d'arborescence des périphériques (.dtb
), Un format binaire. Lorsque le système démarre, le chargeur de démarrage (par exemple -boot ) remet ce blob au noyau. Le noyau l'analyse et crée tous les périphériques comme indiqué par l'arborescence des périphériques.
Si vous ne me croyez pas, utilisez le compilateur d'arborescence des périphériques pour atteindre l'arborescence des périphériques que votre BBB utilise actuellement.
Si vous ne l'avez pas encore installé, procurez-vous le package approprié.
pacman -Sy dtc-overlay
dtc -f -I fs /proc/device-tree | less
Ce canal vers le pager less
est recommandé en raison du grand nombre de résultats générés par cette commande. Le résultat devrait ressembler à ceci ..
Toutes les parties de votre arborescence de périphériques peuvent également être examinées dans la source du noyau, mais comme il existe également un mécanisme d'inclusion, les informations sont réparties entre plusieurs fichiers dans
<kernel-source>/Arch/arm/boot/dts/..
Certains fichiers pertinents sont:
am335x-bone-common.dtsi
am335x-boneblack.dts
am33xx.dtsi
Remarque: Les fichiers
.dtsi
Sont équivalents aux fichiers.h
En C ou C++ car ils sont inclus (donc le "i" à la fin) par les fichiers.dts
Ils décrivent tous des périphériques liés au processeur, des périphériques communs sur la plate-forme Beaglebone ou des périphériques adaptés uniquement au Beaglebone Black.
Bonne question, je vois que tu es toujours avec moi. Comme je l'ai déjà dit, le blob de l'arborescence des périphériques est analysé au démarrage du noyau. Ainsi, lorsque votre système est opérationnel, toute la magie est déjà terminée. Sur une plate-forme comme le BBB avec tout un tas de cartes d'extension ( Capes ) cela vous obligerait à recompiler l'arborescence des périphériques à chaque fois que vous optez pour une autre cape à utiliser.
Par conséquent, vous avez le mécanisme de superposition qui vous permet d'ajouter ou de modifier des appareils dans votre arborescence d'appareils AT RUNTIME! Amazing.
Remarque: pour pouvoir compiler les superpositions de l'arborescence des périphériques, assurez-vous d'installer le package approprié comme ci-dessus (
dtc-overlay
)
Je vais vous donner un exemple. Comme le BBB n'a pas d'horloge en temps réel (rtc) qui serait utile pour générer des horodatages pour les mesures, etc., nous allons corriger cela.
Nous allons utiliser une puce d'horloge en temps réel ds1307 (en fait, j'ai une ds1308 rtc mais le pilote est compatible) et communiquer avec elle via le bus I2C1 sur le BBB. Par défaut, ce bus est désactivé sur le BBB comme vous pouvez le voir dans les sources de l'arborescence des périphériques.
Les informations importantes contenues dans cet extrait sont les suivantes:
Nous allons maintenant créer une superposition pour configurer les broches GPIO pour le bus i2c1, activer ce bus et ensuite nous ajouterons le bus i2c1 du périphérique rtc afin que le pilote approprié soit automatiquement chargé et que le périphérique rtc soit créé dans /dev
.
Les broches GPIO sur les en-têtes P8 et P9 sur le BBB ont plusieurs fonctionnalités qui sont muxed ensemble et nous devons donc ajuster les paramètres pinmux pour les utiliser pour la communication I2C. Comme vous pouvez le voir dans ce tablea pour le bus I2C1, nous devrons utiliser les broches d'en-tête 17 et 18 en mode mux 2. Pour obtenir plus d'informations sur la gestion GPIO sur le look BBB ici .
/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 */
À première vue, la syntaxe de superposition semble assez étrange, mais elle est essentiellement constituée de soi-disant fragments qui ciblent un nœud de périphérique déjà existant et modifient ce nœud (et ses enfants).
Dans ce cas, nous ciblons le nœud de périphérique am33xx_pinmux
Défini dans l'arborescence des périphériques du processeur (am33xx.dtsi
). Dans ce nœud, nous ajoutons un nouveau nœud enfant appelé pinmux_i2c1_pins qui n'existait pas auparavant (jetez un œil à am335x-bone-common.dtsi
Pour vérifier) et le libellé i2c1_pins.
La partie suivante est un peu plus complexe et si vous êtes intéressé, lisez this . Chaque broche GPIO est configurée par un registre unique avec plusieurs bits pour contrôler son comportement et tous les registres sont contrôlés par le pilote pinctrl-single
. Pour définir une broche spécifique, utilisez simplement son décalage d'adresse par rapport à l'adresse de base (vous le trouverez dans le tableau d'en-tête P9 ci-dessus) et sa configuration de broche comme deuxième paramètre.
J'ai emprunté cet aperçu à Derek Molloy pour expliquer le mode pin. Étant donné que 0x72
Équivaut à 01110010b
, Les deux broches sont configurées comme entrées avec une résistance de rappel activée et un contrôle d'orientation actif en mode multiplexeur 2.
Et le mode multiplexeur 2 pour ces broches signifie que la broche 17 sur l'en-tête P9 est la ligne d'horloge SCL et la broche 18 sur l'en-tête P9 est la ligne de données SDA.
C'est tout à fait correct, nous allons donc étendre notre superposition comme suit.
/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 */
Dans le code ci-dessus, nous avons ajouté un nouveau fragment qui cible le nœud de périphérique i2c1 et lui dit d'utiliser notre configuration de broches précédemment définie. Nous définissons une fréquence d'horloge I2C de 100 kHz et activons l'appareil.
De plus, l'horloge rtc a été ajoutée en tant qu'enfant au nœud i2c1. Les informations importantes pour le noyau sont la déclaration compatible, nommant le pilote à utiliser (ds1307
) Et l'adresse des périphériques sur le bus I2C (0x68
). L'adresse I2C du rtc peut être obtenue à partir de la fiche technique.
Au début, la source de l'arborescence des périphériques doit être compilée. Utilisez le compilateur dtc avec l'appel suivant.
dtc -O dtb -o <filename>-00A0.dtbo -b 0 -@ <filename>.dts
Mise en garde! Le nom de fichier doit être une concaténation du nom que vous désirez plus la balise de version comme vu ci-dessus (-00A0) sinon vous aurez du mal.
Le fichier .dtbo
Résultant doit être copié dans /lib/firmware
Et je n'ai vraiment aucune idée d'où vient la convention de dénomination "-00A0" mais il y a d'autres fichiers dans le répertoire du firmware qui l'utilisent également.
À partir de maintenant, vous pouvez charger votre superposition dynamiquement en utilisant Capemgr. Pour ce faire, déplacez-vous dans /sys/devices/platform/bone_capemgr/
Puis exécutez ..
echo <filename> > slots
Capemgr recherchera alors votre fichier .dtbo
Dans le répertoire du firmware et le chargera si possible. En consultant le fichier des emplacements, vous pouvez voir si la procédure a réussi. Ça devrait ressembler a quelque chose comme ca..
Examinez l'arborescence des périphériques utilisée par le Beaglebone.
dtc -f -I fs /proc/device-tree | less
Vous trouverez toutes les entrées de la superposition.
De plus, il devrait y avoir un nouveau périphérique I2C (/dev/i2c-1
) Et un nouveau périphérique rtc (/dev/rtc1
) Dans votre système de fichiers.
Pour voir vos bus i2c, installez le package i2c-tools
Et utilisez ..
i2cdetect -r 1
La sortie devrait être quelque chose comme ça ..
Comme vous pouvez le voir, l'adresse 0x68 est occupée par un périphérique.
Pour interroger votre utilisation rtc ..
hwclock -r -f /dev/rtc1
Non, il y a une autre option que vous avez, en chargeant les superpositions de l'arborescence des périphériques au démarrage. IMPRESSIONNANT!
Pour ce faire, ouvrez /boot/uEnv.txt
Et ajoutez bone_capemgr.enable_partno=<filename>
À l'instruction optargs
. Voilà à quoi ça ressemble sur mon BBB
optargs=coherent_pool=1M bone_capemgr.enable_partno=bbb-i2c1
De manière confuse, le nom de fichier est utilisé dans optargs et non dans la balise part-number
Définie dans la superposition de l'arborescence des périphériques.
Vous pouvez obtenir mon exemple de code à côté d'un Makefile utile sur github si vous le souhaitez.
Désolé pour le long post.
Ces informations sont très utiles et précieuses. J'ai écrit un pilote de noyau i2c que je peux charger dynamiquement pour parler à une puce personnalisée à l'adresse 0x77. J'ai réussi à communiquer avec la puce dans le passé en instanciant manuellement le périphérique comme suit: echo act2_chip 0x77>/sys/bus/i2c/devices/i2c-1/new_device. Une fois le périphérique instancié, je peux le voir à l'aide des outils i2cdetect et mon pilote de noyau chargeable peut communiquer avec la puce.
Maintenant, j'essaie d'instancier l'appareil en utilisant la méthode de l'arborescence des appareils. Donc, suivant votre exemple, j'ai changé certains paramètres dans votre fichier dtsi comme ci-dessous:
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>;
};
J'ai connecté la puce aux broches 17 et 18 pour scl et sdk. Voici la sortie dmesg que j'obtiens après echo> slots:
Mais lors de l'insertion du pilote dans le noyau, je vois la fonction de sonde appelée. cela signifie que le conducteur est en mesure de voir l'appareil autant que je pense.
Et lorsque j'essaie d'écrire dans le pilote du noyau, j'obtiens le message suivant: omap_i2c 4802a000.i2c: le contrôleur a expiré