Le titre de cet article est un peu compliqué pour les non-initiés… J’ai bien peur que mes explications ne le rendent pas beaucoup plus simple! En fait je viens apporter ma petite contribution dans le développement de l’interface SVXLinkCard de Juan F8ASB et Christian F5UII en créant une procédure de conversion A/N via SPI en TCL, mais ce bout de code peut être réutilisé dans tout projet similaire.
La problématique
La carte SVXLinkCard contient un convertisseur analogique/numérique Microchip MCP3204 raccordé sur le port SPI du Raspberry. Le problème, c’est que SVXLink est écrit en TCL, et qu’il n’existe pas de commande ni en TCL, ni même en ligne de commande Shell, pour piloter le SPI. Dans le système actuel, un script Python lit le convertisseur et retourne la valeur dans un fichier de manière cyclique. Ce n’est pas optimal car ça entraîne des accès disque à chaque conversion, et l’utilisation de plusieurs langages complique un peu la maintenance du projet.
Choix d’une solution
Comme toujours, il y a plusieurs solutions pour contourner le problème. J’ai trouvé entre autres l’outil spi-tools de Christophe Blaess qui fournit des outils en ligne de commande pour accéder à ce port. Un article dans la revue Open Silicium n°19 en fait d’ailleurs la présentation. En cherchant un peu plus loin, j’ai trouvé et retenu tclspi de Karl Lehenbauer, qui permet de piloter le SPI directement depuis TCL, et donc sans passer par la ligne de commande. Je l’ai donc installé selon les instructions sur la page github, que je vais traduire ici.
Accès au SPI en TCL: installation du pilote
Tout d’abord, récupérer les sources sur github:
git clone https://github.com/lehenbauer/tclspi.git
Ensuite, compiler le pilote après avoir installé les paquets nécessaires
sudo apt-get install tcl8.5-dev libi2c-dev autoconf cd tclspi autoconf ./configure --with-tcl=/usr/lib/tcl8.5 make sudo make install
C’est fait. L’accès au SPI en TCL est maintenant possible depuis un script en ajoutant en début de fichier:
package require spi
Un exemple de code est donné sur la page github du projet. Il ne fonctionne pas car je n’ai pas accès au port SPI. Après l’ajout de l’utilisateur “pi” au groupe “spi”, tout fonctionne:
sudo usermod -a -G spi pi
Je m’inspire de ce code pour ma procédure ADC. Je dois formater la commande binaire que j’envoie au convertisseur ADC, et récupérer la trame qu’il m’envoie en retour. J’utilise pour ça les commandes binary format et binary scan.
Le convertisseur MCP3204
C’est un convertisseur 12 bits doté de 4 entrées analogiques. Je l’utilise ici pour des mesures asymétriques (par rapport à la masse) et la tension de référence est l’alimentation, soit 3.3V. La documentation donne le format de la communication série. Il faut envoyer un bit de start suivi d’un bit à 1 pour indiquer une conversion par rapport à la masse. Les trois bits suivants correspondent au numéro de l’entrée à convertir, de 0 à 7. L’état des bits suivants n’a pas d’importance, mais il faut en envoyer suffisamment pour récupérer le résultat. Le minimum nécessaire est de 5 bits de commande, 2 bits pendant lesquels le convertisseur échantillonne et démarre la conversion, et enfin 12 bits de données. Pour plus de facilité, j’envoie 4 octets, ce qui me permettra de récupérer en retour un entier sur 32 bits.
La valeur de retour est tronquée aux 12 bits de données, puis décalée par une division. Enfin la valeur est mise à l’échelle en fonction des valeurs de résistance du pont diviseur d’entrée.
Code source du programme :
# Conversion analogique/numérique asymétrique avec # le circuit intégré Microchip MCP3204 ou MCP3208 en SPI. # L'accès au SPI depuis TCL est permis grâce à l'utilisation du # pilote écrit par Karl Lehenbauer. # usage: tension = [conversion canal R1 R2] # tension: tension mesurée en Volt # canal: entrée du convertisseur, 0 à 3 (MCP3204) 0 à 7 (MCP3208) # R1, R2: pont diviseur résistif sur l'entrée. La plage de tension sur # une entrée est de 0 à 3.3V. Pour mesurer des tensions supérieures, # on insère un pont diviseur. Si R1=9 kOhm et R2=1 kOhm, la tension # est divisée par (R1 + R2)/R1 = 10. En cas de mesure directe sans # pont diviseur, R1 = 0 et R2 = 1 package require spi proc conversion {canal R1 R2} { set spi [spi #auto /dev/spidev0.0] # paramètes de l'interface SPI $spi read_mode 0 $spi write_mode 0 $spi write_bits_word 8 $spi read_bits_word 8 $spi write_maxspeed 500000 $spi read_maxspeed 500000 # commande binaire pour le convertisseur: bit de start, bit "Single Ended", canal 0 set adcCommand [expr 0b11000000 + $canal * 0x08] # Conversion entier vers chaîne binaire set transmitString [binary format c $adcCommand] # Ajout de 3 caractères 0 (pour envoyer 4 en tout), réception de 4 caractères set receiveString [$spi transfer "$transmitString\x00\x00\x00" 50] # affichage de la réception en binaire (pour test) #binary scan $receiveString B* affBin #puts $affBin # Conversion binaire vers entier binary scan $receiveString I convert #puts $convert # extraction de la valeur par "et binaire" et décalage à l'aide d'une division set receivedValue [expr ($convert & 0b00000001111111111110000000000000) / 0x2000] #puts $receivedValue $spi delete set voltage [expr $receivedValue * (3.3/4096) * ($R1 + $R2)/$R2] return $voltage } # Exemple d'utilisation: entrée 2, résistance 10k en série, 1.5k en parallèle #puts [conversion 2 10 1.5]