Ligne 67 : |
Ligne 67 : |
| {{FImage|WiPy-Hack-Button-01.jpg|640px}} | | {{FImage|WiPy-Hack-Button-01.jpg|640px}} |
| | | |
− | == Programme == | + | == Programme simple == |
| + | === Lire l'état du bouton === |
| + | Pour lire l'état du bouton, vous devez déclarer la broche en entrée et activer la résistance pull-up (voyez l'explication en début d'article) |
| | | |
| <syntaxhighlight lang="python"> | | <syntaxhighlight lang="python"> |
− | # Cet exemple controle la LED heartbeat depuis un bouton branché sur GP8. | + | from machine import Pin |
| + | gp8 = Pin( 'GP8', mode=Pin.IN, pull=Pin.PULL_UP ) |
| + | </syntaxhighlight> |
| + | |
| + | Pour lire la valeur de la broche, vous pouvez utiliser les deux possibilités suivantes: |
| + | <syntaxhighlight lang="python"> |
| + | gp8() |
| + | gp8.value() |
| + | </syntaxhighlight> |
| + | |
| + | Dans les deux cas, la fonction retourne: |
| + | * 0 si la broche est au niveau bas |
| + | * 1 si la broche est au niveau haut |
| + | |
| + | === Attention: logique inversée === |
| + | {{ambox|text=Etant donné que le bouton utilise le montage pull-up (voyez en début de tutoriel) gp8.value() retourne une valeur en logique inversée.}} |
| + | |
| + | La broche GP8 sera: |
| + | * au niveau haut (3.3v) lorsque personne ne presse sur le bouton |
| + | * au niveau bas (0v) lorsque quelqu'un presse le bouton |
| + | |
| + | L'appel {{fname|gp8.value()}} retourne donc un valeur en logique inversée: |
| + | * '''1''': signifie que le bouton N'EST PAS pressé |
| + | * '''0''': signifie que le bouton EST pressé |
| + | |
| + | === Exemple === |
| + | <syntaxhighlight lang="python"> |
| + | # Cet exemple controle la LED heartbeat et la fait clignoter jusqu'à ce que l'on presse |
| + | # sur le bouton raccordé sur GP8. |
| # Le but est d'apprendre a utiliser une entrée du WiPy avec un bouton | | # Le but est d'apprendre a utiliser une entrée du WiPy avec un bouton |
| # | | # |
Ligne 87 : |
Ligne 117 : |
| from machine import Pin | | from machine import Pin |
| import time | | import time |
| + | import wipy |
| + | |
| + | # Desactiver HeartBeat + reconfigurer |
| + | wipy.heartbeat( False ) |
| + | led = Pin( 'GP25', mode=Pin.OUT ) |
| + | led.value( 0 ) |
| + | |
| + | # Declare la broche en entrée |
| + | gp8 = Pin( 'GP8', mode=Pin.IN, pull=Pin.PULL_UP ) |
| + | |
| + | # tant que le bouton n'est pas pressé |
| + | while gp8.value() == 1: |
| + | led.toggle() # changer etat led |
| + | time.sleep_ms( 200 ) # attendre 0.2 sec |
| + | |
| + | # Eteindre la LED |
| + | led.value( 0 ) |
| + | |
| + | # Réactiver heartbeat |
| + | del( led ) |
| + | wipy.heartbeat( True ) |
| + | </syntaxhighlight> |
| + | |
| + | == Interruption et callback == |
| + | Ce qui serait idéal, c'est d'appeler automatiquement une fonction lorsque le bouton est pressé. Une telle fonction est appelée ''fonction callback''. |
| + | |
| + | Nous allons utiliser le principe d'interruption qui permet d'interrompre le programme principal lorsqu'une broche change d'état. |
| + | |
| + | Le processus d'interruption appel alors une fonction (dite ''fonction de rappel'' ou ''fonction callback'') qui effectue la tâche attendue puis rend la main au système. |
| + | |
| + | === Mode d'interruption === |
| + | il y a plusieurs modes d'interruption (comme décrits dans [http://arduino103.blogspot.be/2012/02/les-interruptions-sur-arduino.html cet article Arduino]), les plus utilisés sont: |
| + | |
| + | ==== Les flans montant (dit "'''rising'''") ==== |
| + | L'interruption est déclenchée (voir la flèche bleue) lorsque le signal passe du niveau bas au niveau haut. |
| + | |
| + | [[Fichier:Hack-wipy-button-irq-rising.jpg|480px]] |
| + | |
| + | ==== Les flans descendant (dit "'''falling'''") ==== |
| + | L'interruption est déclenchée (voir la flèche bleue) lorsque le signal passe du niveau bas au niveau haut. |
| + | |
| + | [[Fichier:Hack-wipy-button-irq-falling.jpg|480px]] |
| + | |
| + | === Limitation des interruptions === |
| + | {{ambox-stop|text=Une interruption et l'éxecution de la fonction ''callback'' reste un processus exceptionnel qui prend place (qui s'insère sauvagement) dans l'exécution d'un programme principal. |
| + | |
| + | Il y a donc des limitations et contraintes à l'utilisation d'une fonction d'interruption avec MicroPython}} |
| + | |
| + | # Vous ne pouvez pas utiliser la fonction {{fname|print()}} dans la fonction de callback. Il ne se passera rien. |
| + | # Vous ne pouvez pas faire d'allocation de mémoire dans la fonction d'interruption (cela pourrait changer dans le futur).<br />Toutes les variables et objets doivent exister {{underline|avant}} le premier appel de la fonction callback. |
| + | # ... <small>d'autres contraintes peuvent exister, référez vous à la [https://micropython.org/resources/docs/en/latest/wipy/library/machine.Pin.html documentation de MicroPython]</small> |
| + | |
| + | === Exemple === |
| + | Dans cet exemple nous allons prendre le contrôle de la LED HeartBeat et la faire changer d'état à chaque fois que l'on presse sur le bouton raccorder sur GP8. |
| + | |
| + | <syntaxhighlight lang="python"> |
| + | # Cet exemple utilise les interruption et callback pour changer l'état de |
| + | # la LED heartbeat à chaque fois que le bouton branché sur GP8 est pressé. |
| + | # |
| + | # |
| + | # Ou acheter un WiPy et une carte d'extension |
| + | # http://shop.mchobby.be/product.php?id_product=736 |
| + | # http://shop.mchobby.be/product.php?id_product=737 |
| + | # http://shop.mchobby.be/category.php?id_category=68 |
| + | # |
| + | # Voyez le tutoriel |
| + | # http://wiki.mchobby.be/index.php?title=Hack-wipy-button |
| + | # |
| + | # Auteur: Meurisse D. pour shop.mchoby.be |
| + | # Licence: CC-BY-SA |
| + | # |
| + | from machine import Pin |
| + | import wipy |
| + | |
| + | # Réutilisation de la LED heartbeat |
| + | wipy.heartbeat( False ) |
| + | led = Pin( 'GP25', mode=Pin.OUT ) |
| + | led.value( 0 ) |
| + | |
| + | # Definir la fonction callback pour |
| + | # inverser l'etat de la LED Heartbeat |
| + | # |
| + | def pincb( pin ): |
| + | led.toggle() |
| + | |
| + | # Definir la broche GP8 comme entrée et activer |
| + | # la résistance pull-up |
| + | p8 = Pin( 'GP8', mode=Pin.IN, pull=Pin.PULL_UP ) |
| + | |
| + | # Définir l'interruption sur flanc descendant (falling) pour |
| + | # appeler la fonction callback |
| + | i = p8.irq( trigger=Pin.IRQ_FALLING, handler=pincb ) |
| + | </syntaxhighlight> |
| + | |
| + | Voila, vous pouvez maintenant presser le bouton et voir la LED HeartBeat changer d'état à chaque pression. |
| + | |
| + | Nous avons utilisé l'option {{fname|1=trigger=Pin.IRQ_FALLING}} car l'entrée est configurée en Pull-Up. L'entrée GP8 est donc continuellement au niveau haut... et passe au niveau bas lorsque l'on presse sur le bouton poussoir. Il faut donc détecter le flanc descendant du signal. |
| + | |
| + | Tel que définit ci-avant, il est possible de déclencher la fonction callback directement |
| + | <syntaxhighlight lang="python"> |
| + | # Appel direct de la fonction callback |
| + | i() |
| + | </syntaxhighlight> |
| + | |
| + | Une interruption peut également être désactivée avec {{i.disable()}} et réactivée avec {{i.enable()}}. |
| + | |
| + | === Déparasitage des boutons === |
| + | Vous aurez certainement remarqué que, suivant les cas, la LED heartbeat change d'état une ou plusieurs fois même si vous pressez une seule fois le bouton. |
| + | |
| + | Cela arrive souvent avec les boutons mécaniques où le contact n'est pas franc mais passer pas une phase transitoire avec plusieurs rebond avant le contact définitif. |
| + | |
| + | [[Fichier:Switchbounce.jpg|480px]]<small><br />Exemple de contact parasite lors du relâchement d'un bouton.<br />Le même phénomène se produit lorsque le bouton est pressé.</small> |
| + | |
| + | Ce sont ces rebonds qui déclenchent plusieurs fois l'interruption. |
| + | |
| + | == Déparasitage des boutons == |
| + | Il y a différentes techniques de déparasitages. Si vous utilisez les interruptions & callback, vous aurez besoin d'utiliser une méthode de déparasitage matérielle. |
| + | * [http://wiki.mchobby.be/index.php?title=Entr%C3%A9e_Bouton#D.C3.A9parasitage_des_boutons Déparasitage des boutons pour Arduino] (information utile en tous les cas) |
| + | * [http://www.eng.utah.edu/~cs5780/debouncing.pdf http://www.eng.utah.edu/~cs5780/debouncing.pdf] (''anglais'') - document très intéressant avec explications détaillées et différentes techniques de déparasitage. |
| + | |
| + | === déparasitage logiciel === |
| + | Si vous avez lisez l'état du bouton dans votre programme, vous pourrez utiliser une méthode de déparasitage logiciel comme celle-ci utilisé ci-dessous. |
| + | |
| + | <font color="red">Nous avons créé une classe {{fname|PullUpButton}} qui fait du déparasitage logiciel sur une entrée équipée d'un bouton avec résistance pull-up. Cette classe est stockée dans le fichier '''debounce.py'''</font> |
| + | |
| + | Vous pouvez télécharger le fichier '''debounce.py''' et le placer directement dans le répertoire {{fname|/flash/lib/}} de votre WiPy. |
| + | |
| + | {{download-box|Téléchargez debounce.py|http://df.mchobby.be/wipy/debounce.py}} |
| + | |
| + | [[Fichier:Hack-wipy-button-debounce.jpg|800px]] |
| + | |
| + | Voici comment exploiter la classe {{fname|PullUpButton}}, cet exemple attends que le bouton soit presser 5x pour sortir de la boucle de comptage. |
| + | |
| + | <syntaxhighlight lang="python"> |
| + | from debounce import PullUpButton |
| + | import time |
| + | |
| + | btn = PullUpButton( 'GP8' ) |
| + | |
| + | counter=0 |
| + | while counter < 5: |
| + | print( 'presser le bouton svp' ) |
| + | while not btn.is_pressed(): |
| + | time.sleep_ms( 100 ) |
| + | counter=counter+1 |
| + | print( 'counter = %i' % counter ) |
| + | </syntaxhighlight> |
| + | |
| + | ce qui produit le résultat suivant après '''exactement''' 5 pressions physiques sur le bouton... les rebonds de contact ne viennent plus nous importuner. |
| + | |
| + | <nowiki>presser le bouton svp |
| + | counter = 1 |
| + | presser le bouton svp |
| + | counter = 2 |
| + | presser le bouton svp |
| + | counter = 3 |
| + | presser le bouton svp |
| + | counter = 4 |
| + | presser le bouton svp |
| + | counter = 5</nowiki> |
| + | |
| + | ==== Classe PullUpButton ==== |
| + | Pour les plus curieux, voici l'implémentation de la classe {{fname|PullUpButton}} que vous trouverez dans le fichier {{fname|debounce.py}}. |
| + | |
| + | Vous pouvez clairement y voir une seconde relecture de l'entrée après un délais de 10 millisecondes. C'est là qu'intervient le déparasitage logicielle de l'entrée. |
| + | |
| + | <syntaxhighlight lang="python"> |
| + | from machine import Pin |
| + | import time |
| + | |
| + | class PullUpButton: |
| + | """ Classe pour gérer un bouton pull-up sur une broche X. |
| + | Détecte lorsque la broche passe à 0 """ |
| + | p = None # Pin object |
| + | state = None # Last known state |
| | | |
| + | def __init__( self, button_pin ): |
| + | self.p = Pin( button_pin, Pin.IN, pull=Pin.PULL_UP ) |
| + | self.state = self.p.value() |
| + | |
| + | def is_pressed(self): |
| + | """ vérifie si le bouton est pressé / détecte le changement |
| + | d'état. Ne sera signalé qu'une seule fois! """ |
| + | val = self.p.value() |
| + | result = False |
| + | if val != self.state: |
| + | # relecture dans 10 ms (deboucing) |
| + | time.sleep_ms( 10 ) |
| + | val2 = self.p.value() |
| + | if val == val2: # valeur stable :) |
| + | self.state = val |
| + | result = (val == 0) # Is pressed |
| + | return result |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
| {{HACK-WIPY-TRAILER}} | | {{HACK-WIPY-TRAILER}} |