Yocto-I2C et TSL2591

Yocto-I2C et TSL2591

On nous a récemment demandé comment interfacer un capteur de lumière ambiante TSL2591 d'AMS avec le Yocto-I2C. C'est une question intéressante pour deux raisons: le TSL2591 est considérablement plus sensible en basse lumière que le Yocto-Light-V2 ou le Yocto-Light-V3 et surtout son protocole I2C n'est pas des plus simple à implémenter. C'est l'occasion de tester les limites du système de jobs autonomes du Yocto-I2C.



Cet article suppose que vous ayez quelques notions sur le fonctionnement des interfaces I2C, Si ce n'était pas le cas, on vous recommande de lire le chapitre approprié dans la documentation du Yocto-I2C.

Le TSL2591, version Adafruit
Le TSL2591, version Adafruit


Le TSL2591 qu'on a à disposition est déjà soudé sur un petit PCB commercialisé par Adafruit tout ce qu'on a à faire c'est le connecter au Yocto-I2C.

Connexion au Yocto-I2C
Connexion au Yocto-I2C


La même chose en vrai
La même chose en vrai


Ça, c'était la partie facile, il faut maintenant configurer le Yocto-I2C pour qu'il puisse communiquer avec le TSL2591, et pour cela il faut commencer par étudier attentivement le datasheet du TSL2591.

Configuration

En lisant ce document, on note que:

  • Le capteur fonctionne entre 2.7V et 3.6V. On pourra donc l'alimenter en 3.3V sans risque et on n'aura pas besoin de l'alimentation auxiliaire (aka powerOutput) du Yocto-I2C.
  • Le protocole I2C du capteur peut monter jusqu'à 400kHz, mais on peut parfaitement se contenter d'une vitesse standard de 100KHz, en électronique, aller plus vite que nécessaire est souvent contre-productif.
  • Le datasheet ne précise pas si le capteur supporte la condition restart, dans le doute on va se contenter d'utiliser des stop/start classiques
  • Les autres paramètres peuvent rester à des valeurs standard

Configuration du Yocto-I2C dans le VirtualHub
Configuration du Yocto-I2C dans le VirtualHub



Ecriture du job de communication I2C

Il s'agit maintenant d'écrire un job autonome qui va automatiquement interroger le capteur, et pour cela il faut se plonger encore plus profondément dans l'étude du datasheet. On y apprend que:

  • L'adresse sur 7 bits du capteur est 0x29, Le premier byte de chaque transfert I2C sera donc 0x29 * 2 = 0x52 pour l'écriture et 0x29 * 2 + 1= 0x53 pour la lecture.
  • Les adresses des registres I2C sont stockées dans les 5 bits de poids faible du registre de commande, les trois autres bits restant la plupart du temps à 0b101. La commande pour accéder au registre 0 est donc 0b10100000 = 0xA0, la commande pour accéder au registre 1 est 0xA1 et ainsi de suite...
  • L'adressage incrémental est supporté, si vous faites deux opérations de lecture de suite sans toucher au registre de commande, vous obtiendrez les valeurs de deux registres contigus.
  • A l'allumage, le capteur ne fait pas de mesures, pour enclencher les mesures il faut mettre à 1 les bits 0 et 1 du registre 0, les autres peuvent être laissés à zéro.
  • Par défaut, les mesures se font à 10 Hz càd un résultat toutes les 100ms.
  • Le résultat des mesures est en counts , pas en lux.
  • Le résultat pour la mesure de lumière blanche (CH0) est stocké dans les registres 0x14 (LSB) et 0x15 (MSB), codé sur 16 bits, autrement dit sur un WORD en little endian.
  • A 10Hz, la valeur de CH0 peut varier entre 0 et 37888, et non pas 65535 comme on pourrait s'y attendre.
  • Il y a quatre réglages de gain, stockés dans les bits 4 et 5 du registre 1, ces réglages correspondent aux facteurs x1, x25 , x428 et x9876.


Les différentes commande I2c dont on va avoir besoin sont donc:

  • {S}52A003{P} pour mettre en marche le capteur
  • {S}52A1{P}{S}53xx{N}{P} pour lire le gain
  • {S}52B4{P}{S}53xx{A}xx{N}{P} pour lire la valeur du canal 0
  • {S}52A100{P} pour mettre le gain à 0
  • {S}52A110{P} pour mettre le gain à 1
  • {S}52A120{P} pour mettre le gain à 2
  • {S}52A130{P} pour mettre le gain à 3

Pour gérer tout ça on va avoir besoin de d'une variable d'état $state et d'une variable $gain pour se rappeler de la valeur de gain courante. On propose d'écrire un job dont la structure serait la suivante.


Structure du job pour interroger le TSL2591
Structure du job pour interroger le TSL2591



Ce qui se traduit par les tâches suivantes, qui seront exécutées en fonction de l'état de la variable $state:

  • Tâche "init", périodique une fois:

    • assert ! isset($state)
    • writeLine {S}52A003{P}
    • expect 29:{A}{A}{A}
    • writeLine {S}52A1{P}{S}53xx{N}{P}
    • expect 29:{A}{A} 29:{A}($gain:BYTE)
    • compute $gain=($gain>>4)&3
    • compute $state=1
    • log Init Done

  • Tâche "read", périodique toutes les 100ms:
    • assert isset($state)
    • assert $state=1
    • writeLine {S}52B4{P}{S}53xx{A}xx{N}{P}
    • expect 29:{A}{A} 29:{A}($CH0:WORDL)
    • compute $1 = ($gain==0 ? $CH0*9.876 : ($gain==1 ? $CH0*0.428 : ($gain==2 ? $CH0*0.025 : $CH0*0.001 )))
    • compute $state = ( $CH0<50 && $gain<3 ? 2 + $gain +1 : $state)
    • compute $state = ( $CH0>37000 && $gain>0 ? 2+$gain-1 : $state)

  • Tâche "setGainTo0", périodique toutes les 100ms:
    • assert isset($state)
    • assert $state==2
    • writeLine {S}52A100{P}
    • expect 29:{A}{A}{A}
    • compute $gain=0
    • log gain set to 0
    • wait 200
    • compute $state=1

  • Tâche "setGainTo1", périodique toutes les 100ms:
    • assert isset($state)
    • assert $state==3
    • writeLine {S}52A110{P}
    • expect 29:{A}{A}{A}
    • compute $gain=1
    • log gain set to 1
    • wait 200
    • compute $state=1

  • Tâche "setGainTo2", périodique toutes les 100ms:
    • assert isset($state)
    • assert $state==4
    • writeLine {S}52A120{P}
    • expect 29:{A}{A}{A}
    • compute $gain=2
    • log gain set to 2
    • wait 200
    • compute $state=1

  • Tâche "setGainTo3", périodique toutes les 100ms:
    • assert isset($state)
    • assert $state==5
    • writeLine {S}52A130{P}
    • expect 29:{A}{A}{A}
    • compute $gain=3
    • log gain set to 3
    • wait 200
    • compute $state=1


Le cœur du job sont les trois dernières lignes de la tâche "read": on a lu le registre CH0 qu'on multiple par le facteur correspondant au gain courant, divisé par 1000 pour éviter un overflow, et on affecte le résultat au genericSensor N°1 du Yocto-I2C. Si la valeur du canal CH0 est plus petite que 50 ou plus grande que 37000, on décide de corriger le gain en calculant une nouvelle valeur de $state ce qui va déclencher l'exécution de la tâche setGainToX correspondante. Notez qu'après avoir changé le gain on attend 200ms, on a en effet remarqué que changer le gain fausse la valeur de mesure courante. Vous remarquerez qu'il y a une tâche setGainToX par valeur de gain parce que les jobs du Yocto-I2C ne permettent pas l'utilisation d'une variable dans les transferts I2C.

Vous pouvez télécharger le job tout fait ici. Après l'avoir uploadé sur le système de fichiers de votre Yocto-I2C, vous pouvez le démarrer directement depuis l'interface du VirtualHub et si vous voulez que le job démarre automatiquement à l'allumage du module, il vous suffit de modifier en conséquence le réglage "Startup job" de la configuration du Yocto-I2C.

Count vs Lux

Avec ce job, on se retrouve avec la valeur mesurée stockée dans le genericSensor N°1 du Yocto-I2C. Malheureusement cette valeur est en count et non pas en lux. Le datasheet du TSL2591 se contente de suggérer d'utiliser une "formule empirique" pour faire la conversion. En comparant avec un luxmètre calibré, on a remarqué que pour des mesures faites à 10hz, il suffit de diviser le nombre de count par deux pour obtenir des valeurs semblable à celles du luxmètre. Plutôt que de hard-coder cette division dans le protocole, on a préféré la stocker dans le mapping du GenericSensor1.

Conversion count=> lux dans le mapping du sensor
Conversion count=> lux dans le mapping du sensor



L'intérêt de faire la conversion à cet endroit, c'est qu'elle est facile à modifier par la suite. En effet les valeur min/max de "ADC count value" décrites à la figure 9 du datasheet nous laisse penser qu'il peut y avoir des différences importantes d'un capteur à l'autre.

Conclusion

Interfacer un capteur I2C n'est pas toujours chose facile, mais par contre l'utilisation du Yocto-I2C permet, une fois le job idoine écrit, de lire les valeurs d'un capteur I2C aussi facilement que si c'était un capteur Yoctopuce natif. La preuve: voici des mesures du TSL2591 affichées directement dans Yocto-Visualisation.

Valeurs du TSL2591 dans Yocto-Visualization
Valeurs du TSL2591 dans Yocto-Visualization


Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.