Le nouveau BeagleBone Black est compatible avec son prédécesseur blanc en ce qui concerne les ports d’entrées-sorties, ce qui lui permet d’hériter de ses fameuses « capes » (les cartes d’extension que l’on peut empiler afin d’ajouter de nouvelles fonctionnalités). Pour commencer à explorer le BeagleBone Black, je me suis intéressé à ses entrées analogiques, comme @HuguesSert me l’avait suggéré sur Twitter.
Le BeagleBone Black propose 7 entrées analogiques dans l’intervalle de tension [0, +1.8V] initialement conçues pour supporter un écran tactile d’où le « TSC » (Touchscreen) que l’on trouvera dans le nom du driver, mais on peut parfaitement les utiliser pour d’autres applications. Ces canaux se trouvent sur le connecteur P9 (celui situé du côté de l’alimentation +5V). Les entrées A0 à A6 se situent respectivement sur les broches 39, 40, 37, 38, 33, 36 et 35 de P9, la masse analogique étant sur la broche 34 et sur la broche 32 on peut trouver une tension de référence à +1.8V.
Attention à ne surtout pas dépasser la tension de référence, sur une entrée analogique. Je ne me suis pas risqué à vérifier s’il y a des protections efficaces, mais le System Reference Manual du BeagleBone Black précise cette limite explicitement (p.27).
Broches d’alimentation en 1.8V
La méthode la plus simple pour fournir une valeur ne dépassant jamais 1,8V est encore de fournir en entrée un signal obtenu par division de cette tension de référence. Nous pouvons vérifier qu’elle est bien présente entre les broches 34 (GND_ADC) et 32 (V_ADC).
Entrées analogiques
Sur le précédent BeagleBone (je n’en ai plus d’exemplaire sous la main, je n’ai pas vérifié ceci), il suffisait pour lire l’état d’une des entrées analogiques de consulter : /sys/devices/platform/tsc/ain<numero>
ou /sys/devices/platform/omap/tsc/ain<numero>
sur les noyaux récents. Ceci ne fonctionne plus sur le nouveau BeagleBone Black.
Avec ce dernier, nous sommes encouragés à utiliser un nouveau sous-système du kernel : IIO
(Industrial I/O Subsystem) qui est dédié aux convertisseurs analogiques->numériques (ADC, en entrée) et dans une moindre mesure aux convertisseurs numériques->analogiques (DAC, en sortie).
Nous allons devoir activer la gestion des IIO en utilisant le module Cape Manager du kernel.
Démarrons un BeagleBone Black sur la distribution Angström incluse dans la carte eMMC, puis interrogeons le module Cape Manager pour voir quelles capes il a détectées.
root@beaglebone:~# ls /sys/devices/ 44e10800.pinmux ARMv7 Cortex-A8 bone_capemgr.8 breakpoint fixedregulator.9 ocp.2 platform soc.0 software system tracepoint virtual root@beaglebone:~#
root@beaglebone:~# cat /sys/devices/bone_capemgr.8/slots 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 root@beaglebone:~#
Pour le moment, seules deux pseudo-capes sont présentes, pour implémenter le support du connecteur HDMI et de la mémoire flash eMMC. Les supports de capes (nommés Device Tree Fragments) se trouvent dans le répertoire firmware/cape
du kernel après application des patches pour BeagleBone (je reviendrai sur ce sujet dans un prochain article). En voici la liste pour le noyau 3.8.13.
[kernel]$ ls firmware/capes/ am33xx_pwm-00A0.dts bone_pwm_P9_16-00A0.dts BB-BONE-AUDI-01-00A0.dts bone_pwm_P9_21-00A0.dts BB-BONE-CAM3-01-00A2.dts bone_pwm_P9_22-00A0.dts BB-BONE-eMMC1-01-00A0.dts bone_pwm_P9_28-00A0.dts BB-BONE-GPEVT-00A0.dts bone_pwm_P9_29-00A0.dts BB-BONE-LCD4-01-00A0.dts bone_pwm_P9_31-00A0.dts BB-BONE-LCD4-01-00A1.dts bone_pwm_P9_42-00A0.dts BB-BONE-LCD7-01-00A2.dts cape-bone-2g-emmc1.dts BB-BONE-LCD7-01-00A3.dts cape-bone-adafruit-lcd-00A0.dts BB-BONE-LCD7-01-00A4.dts cape-bone-adafruit-rtc-00A0.dts BB-BONELT-BT-00A0.dts cape-boneblack-hdmi-00A0.dts BB-BONE-PRU-01-00A0.dts cape-bone-dvi-00A0.dts BB-BONE-PRU-02-00A0.dts cape-bone-dvi-00A1.dts BB-BONE-PWMT-00A0.dts cape-bone-dvi-00A2.dts BB-BONE-RS232-00A0.dts cape-bone-exptest-00A0.dts BB-BONE-RST-00A0.dts cape-bone-geiger-00A0.dts BB-BONE-RST2-00A0.dts cape-bone-hexy-00A0.dts BB-BONE-SERL-01-00A1.dts cape-bone-iio-00A0.dts bone_pwm_P8_13-00A0.dts cape-bone-lcd3-00A0.dts bone_pwm_P8_19-00A0.dts cape-bone-lcd3-00A2.dts bone_pwm_P8_34-00A0.dts cape-bone-mrf24j40-00A0.dts bone_pwm_P8_36-00A0.dts cape-bone-nixie-00A0.dts bone_pwm_P8_45-00A0.dts cape-bone-pinmux-test-00A0.dts bone_pwm_P8_46-00A0.dts cape-bone-tester-00A0.dts bone_pwm_P9_14-00A0.dts cape-bone-weather-00A0.dts [kernel]$
Celle qui va nous intéresser pour le moment est cape-bone-iio
. Demandons au Cape Manager d’en assurer le support.
root@beaglebone:~# echo cape-bone-iio > /sys/devices/bone_capemgr.8/slots root@beaglebone:~#
Vérifions à nouveau les capes gérées.
root@beaglebone:~# cat /sys/devices/bone_capemgr.8/slots 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,cape-bone-iio root@beaglebone:~#
Un nouveau répertoire helper.11
(le driver s’appelle TSC AM335x IIO Helper) est alors apparu dans /sys/devices/ocp.2/
. Voyons son contenu.
root@beaglebone:~# ls -l /sys/devices/ocp.2/helper.11/ total 0 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN0 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN1 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN2 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN3 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN4 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN5 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN6 -r--r--r-- 1 root root 4096 Jan 1 01:35 AIN7 lrwxrwxrwx 1 root root 0 Jan 1 01:35 driver -> ../../../bus/platform/drivers/bone-iio-helper -r--r--r-- 1 root root 4096 Jan 1 01:35 modalias drwxr-xr-x 2 root root 0 Jan 1 01:35 power lrwxrwxrwx 1 root root 0 Jan 1 00:40 subsystem -> ../../../bus/platform -rw-r--r-- 1 root root 4096 Jan 1 00:40 uevent root@beaglebone:~#
Les fichiers AIN0
à AIN6
représentent les entrées analogiques que nous allons tester. L’entrée AIN7
est utilisée me semble-t-il pour la régulation de la tension d’alimentation.
Relions la broche 36 (entrée analogique 5) à la broche 34 (GND_ADC) et demandons le contenu du fichier AIN5
.
root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 340 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 0 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 0 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 0 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 0 root@beaglebone:~#
La première valeur (340) était un peu surprenante. Maintenant, relions la broche 36 à la broche 32 (VDD ADC).
root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 0 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 1799 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 1799 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 1799 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 1798 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 1799 root@beaglebone:~#
Plusieurs remarques s’imposent :
- La valeur fournie par le fichier AIN est donc la tension mesurée en millivolts. Le convertisseur ADC du processeur AM3559 offre une résolution de 12 bits, mais le driver Linux se contente donc de 11 bits (intervalle [0-2047]) à cause de la limitation à +1.8V.
- Visiblement, à chaque lecture du fichier AIN, le driver nous renvoie la dernière valeur qu’il avait déjà mesurée puis relance une nouvelle acquisition. La première valeur lue de chaque série n’est donc pas valide. C’est important d’en tenir compte dans les applications qui échantillonnent un signal.
Je branche maintenant sur la broche AIN5 (36) le point-milieu d’un potentiomètre dont les deux extrémités sont reliées aux broches 32 (VDD_ADC) et 34 (GND_ADC). Je place le potentiomètre à peu près à mi-course.
root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 1799 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 791 root@beaglebone:~# cat /sys/devices/ocp.2/helper.11/AIN5 791 root@beaglebone:~#
Nous avons confirmation que la première valeur est fausse (c’est l’acquisition faite juste après la dernière lecture de l’expérience précédente).
On peut voir varier dynamiquement la valeur d’entrée analogique alors que je tourne le potentiomètre alternativement d’une butée à l’autre.
root@beaglebone:~# while true; do cat /sys/devices/ocp.2/helper.11/AIN5 ; done [...] 0 0 0 10 87 280 561 987 1460 1728 1796 1799 1798 [...] 1799 1798 1778 1728 1578 1331 967 621 359 221 116 61 13 0 0 [...] (Contrôle-C) root@beaglebone:~#
La boucle du shell n’étant pas très rapide, nous voyons peu de valeurs durant la phase intermédiaire entre les deux butées. La documentation du processeur AM3359 du BeagleBone Black précise qu’il peut réaliser 200.000 échantillonnages par seconde. Vérifions ceci avec un petit programme C qui va lire en boucle l’entrée AIN indiquée.
read-ain.c: #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BUFFER_SIZE 128 #define HELPER_NAME "cape-bone-iio\n" int main(int argc, char * argv[]) { int num; int fd; char buffer[BUFFER_SIZE]; int i; int nb_samples; int * values; int n; if ((argc < 3) || (sscanf(argv[1], "%d", & num) != 1) || (sscanf(argv[2], "%d", & nb_samples) != 1)) { fprintf(stderr, "usage: %s analog-input-number nb-samples\n", argv[0]); exit(EXIT_FAILURE); } if ((num < 0) || (num > 6)) { fprintf(stderr, "wrong analog input number: %d\n", num); exit(EXIT_FAILURE); } values = calloc(nb_samples, sizeof(int)); if (values == NULL) { perror(argv[0]); exit(EXIT_FAILURE); } strcpy(buffer, "/sys/devices/bone_capemgr.8/slots"); fd = open(buffer, O_WRONLY); if (fd < 0) { perror(buffer); exit(EXIT_FAILURE); } write(fd, HELPER_NAME, strlen(HELPER_NAME)); close(fd); snprintf(buffer, BUFFER_SIZE, "/sys/devices/ocp.2/helper.11/AIN%d", num); fd = open(buffer, O_RDONLY); if (fd < 0) { perror(buffer); exit(EXIT_FAILURE); } for (i = 0; i < nb_samples; i ++) { lseek(fd, 0, SEEK_SET); n = read(fd, buffer, BUFFER_SIZE); if (n < 0) { perror("read"); exit(EXIT_FAILURE); } buffer[n] = '\0'; if (sscanf(buffer, "%d", & (values[i])) != 1) { fprintf(stderr, "unreadable value: %s\n", buffer); exit(EXIT_FAILURE); } // usleep(1000); } for (i = 0; i < nb_samples; i ++) { fprintf(stdout, "%d\n", values[i]); } close (fd); return EXIT_SUCCESS; }
Ce programme lit en boucle l’entrée analogique indiquée en premier argument et stocke les valeurs dans un tableau dont la dimension est fournie en second argument. Puis il affiche le résultat. Compilons-le, et exécutons-le avec peu d’itérations pour commencer.
root@beaglebone:~# gcc read-ain.c -o read-ain -Wall root@beaglebone:~# time ./read-ain 5 32 root@beaglebone:~# ./read-ain 5 32 669 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 1555 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 -1270740598 root@beaglebone:~#
Nous nous apercevons alors que le driver nous renvoie des valeurs erronées si nous lisons l’entrée analogique trop vite. En effet, il ne vérifie pas (ce qui est probablement une erreur) si la lecture précédente est terminée avant d’en relancer une nouvelle. Un patch est apparemment en cours d’intégration pour le driver de l’ADC AM335x, mais en attendant, le plus simple est d’ajouter une petite attente après chaque lecture bien que ce ne soit qu’un palliatif inélégant. Empiriquement, j’ai vu qu’un sommeil de 900 à 1000 microsecondes semble suffire (c’est la ligne en commentaire dans l’exemple ci-dessus).
Conclusion
Les entrées analogiques accessibles par l’intermédiaire du port P9 sont initialement prévues pour la gestion d’un écran tactile. En l’état actuel, le driver ne permet pas d’obtenir directement depuis l’espace utilisateur des valeurs d’acquisition à une fréquence supérieure à 1kHz environ, ce qui est plutôt faible en traitement du signal.
Ces entrées sont donc bien adaptées pour participer à l’IHM d’un système embarqué (en lisant la valeur d’un potentiomètre par exemple), mais pas pour des véritables acquisitions de données analogiques. Nous pouvons espérer que l’évolution du driver pour le convertisseur A/N améliorera cette situation.
Sources :
Sitara AM335x Datasheet
Merci Christophe
Je viens de recevoir mon BBB et tes articles me semblent très intéressants.
Je vais expérimenter tout ça ce week-end.
Assez tentante en effet cette carte… Le seul manque que je lui trouve en fait, c’est côté alim: Via USB c’est bien, mais l’ethernet POE lui aurait ouvert des horizons plus larges encore.
Il existe des adaptateurs pseudo POE : http://www.adafruit.com/products/435
Et il devrait être possible de réaliser un « CAPE » pour ça.
Malheureusement les CAPE coûtent plus cher que le BBB lui même.
Il y a un vrai adaptateur PoE pour RPI: http://www.megaleecher.net/Raspberry_Pi_POE
55£ sur eBay…
Christophe, j’ai fais les manipulation que tu décris, (enfin la première partie) et j’ai 2 remarques:
A un moment tu indique : »Maintenant, relions la broche 36 à la broche 34 (VDD ADC). »
Il s’agit bien sur de la broche 32 et non 34.
Chez moi j’ai bone_capemgr.9 et non bone_capemgr.8, helper.14 au lieu de helper.11
(uname -a indique:
Linux beaglebone 3.8.13 #1 SMP Fri May 31 12:58:04 CEST 2013 armv7l GNU/Linux)
Si les répertoires changent de nom comme ça, ça ne va pas être pratique pour porter les softs !
Oui, merci de m’avoir signalé l’erreur sur le numéro de broche, je corrige le texte de l’article en conséquence.
En ce qui concerne le nombre qui suit le nom du répertoire
bone_capemgr
(8 ou 9) et celui du répertoirehelper
(11 ou 14), j’ai vu qu’il y a en effet des variantes, mais je n’ai pas l’explication.Au niveau des scripts shell on peut utiliser
Puis
Mais je conviens que ce n’est pas très élégant. Je vais essayer de trouver la signification de ces numéros.
Merci Christophe
Il semble qu’on puisse lire la valeur 12bit dans le repertoire:
/sys/bus/iio/devices/iio\:device0/
Exemple:
cat /sys/bus/iio/devices/iio\:device0/in_voltage5_raw