Go Down

Topic: [Resolu] Programmation de base ... (Read 2280 times) previous topic - next topic

barbudor

je ne comprend ni ta question, ni ton code (qui n'est pas très bien indenté d'ailleurs).

Tu rentre dans le if si bp1 est true, ce qui ne peut arriver que si bouton vaut 1 une fois.
Ensuite, puisque tu as commenté bp1=false, tu devrais rentrer dans le if pour toujours.
Sauf que si tu appuie sur le bouton 2, alors tu devrais rentrer dans le if mais jamais exécuter le contenu des 2 boucles, jusqu'à ce que tu relâche le bouton 2 (en fait tu rentre dans la boucle mais tu en sort tout de suite).

Si c'est çà que tu veux faire, c'est franchement moche comme codage. Peu clair et peu lisible.
Si tu ne veux pas rentrer dans la boucle, n'y rentre pas, test bouton==2 avant. De toute façon, bouton ne va pas changer de valeur tant que tu ne sort pas de là pour retourner dans appui().
Si ce n'est pas çà que tu veux faire, alors çà prouve que tu n'arrive même pas à relire ton propre code.
;)


zeric

Merci barbu d'être le seul à bien vouloir m'aider.
Pour mon problème, ça prouve que je débute en codage et que j'ai d'énormes progrès à faire, merci de me le rappeler !

Quote
c'est franchement moche comme codage. Peu clair et peu lisible.

Relire ce que je viens juste d'écrire au-dessus ! Et si tu peux m'aider à faire mieux, montre moi, je suis preneur.

//bp1=false, mis en commentaire exprès pour passer dans la boucle constamment et bien voir le problème.

Quote
Sauf que si tu appuie sur le bouton 2, alors tu devrais rentrer dans le if mais jamais exécuter le contenu des 2 boucles, jusqu'à ce que tu relâche le bouton 2 (en fait tu rentre dans la boucle mais tu en sort tout de suite).

Ben mince alors, c'est justement ce que je veux..., et si tu l'essais, tu verras que ce n'est pas le cas, ou alors j'avais bu quelques whiskys avant de faire mes essais  :smiley-red:

Bon, pour une explication claire et concise, en littéral :
-J'appuie sur un bouton "n" du keypad, ça me déclenche une animation avec des "for" qui doit tourner constamment jusqu'à une autre intervention.
-Si j'appuie sur un autre bouton "p" du keypad, je dois arrêter l'animation de suite (et pas seulement à la fin des 2 boucles comme ça fonctionne actuellement, malgré les "break").
(je rajoute pour être bien compris que, bien sûr, je fais en sorte que bp1 ne reste pas actif tout le temps)

:~ :~

barbudor

Il y a plusieurs façon d'aborder le problème.

1ere approche : méthode débutant
Puisque le test du boutton se fait dans la fonction appui(), si tu souhaites qu'un appui sur le bouton sorte de la boucle, il faut donc appeller appui() dans la boucle afin de retester et mettre à jour la variable bouton
Exemple (simplifié à une seule boucle et avec une indentation correcte):
Code: [Select]
if (bp1 == true) {
    lcd.setCursor(1,1);
    lcd.print("En cours");
    for (byte i=10; i<15; i++) {
        appui();
if (bouton == 2) break;
lcd.setCursor(i,1);
lcd.print("*");
delay(200);
     }
}


L'inconvénient de cette approche c'est que si elle marche encore bien alors que ton code reste simple (tu ne gère que 2 choses en même temps : le bouton et le LCD), elle va devenir infernale dès que tu va vouloir ajouter plus de tâches.
Notamment, le fait d'utiliser une boucle avec un delay(200) monopolise le CPU dans cette boucle.
Imagine que maintenant il faille rajouter :
- gérer la liaison série,
- contrôler un moteur,
...

Il faut donc découper chaque tâche en modules élémentaires non bloquants qui vont pouvoir être appelés dans loop() sans empêcher loop() de tourner en continue pour passer la main successivement à chaque tache.

En gros, tu devrait pouvoir écrire :
Code: [Select]
loop()
{
  gere_bouton();
  gere_lcd();
}


Dans chaque tâche, tu ne dois pas rester bloqué. Tu rentre dans la fonction, tu regarde s'il y a quelque chose a faire. Si oui, tu le fais, si non tu ressort immédiatement. Jamais tu n'attends!
Il faut donc définir chaque tâche comme une machine d'états qui va passer d'un état à un autre en fonction d'évènements. Si l'évènement ne se produit pas, la fonction retourne pour passer la main à une autre tâche.

Par exemple, essayons de définir la tâche qui va gérer le bouton.
Je choisit le fonctionnement suivant :
La tache à 2 états :
1) Aucun bouton n'a été pressé
2) Un bouton à été pressé
Les évènements :
Ax) Un bouton x est pressé
B) Le bouton a été acquitté par une autre tache
Le déroulement:
- Si dans l'état 1 et évènement Ax alors je passe dans l'état 2, signalant à l'extérieur qu'un bouton est pressé
- Si dans l'état 2 et évènement Ax (même bouton), je ne change rien (le but est qu'un appui long ou plusieurs appuis sur le même bouton ne sont pas pris en compte plusieurs fois)
- Si dans l'état 2 et évènement Ay (autre bouton), je change la signalisation du bouton
Une tâche extérieure peut remettre l'état 1) pour confirmer qu'elle a pris en compte le bouton et qu'un nouvel appui sera un nouvel évènement.

On peut écrire comme cela :
Code: [Select]
enum { ATTENTE_BOUTON, BOUTON_APPUYE } etat_bouton = ATTENTE_BOUTON;
int bouton = 0;
void gere_bouton( void )
{
  switch( etat_bouton )
  {
  case ATTENTE_BOUTON:
    int b = appui(); // modier appui() pour retourner une valeur plutôt que modifier la var. globale
    if ( b > 0 )
    {
      bouton = b;
      etat_bouton = BOUTON_APPUYE;
    }
    break;
  case BOUTON_APPUYE:
    int b = appui(); // modier appui() pour retourner une valeur plutôt que modifier la var. globale
    if ( b > 0 )
    {
      bouton = b;
    }
    break;
  }
}

// fonction pour une autre tâche afin de savoir si un bouton a été pressé, si oui récupérer sa valeur et acquitter.
int lit_bouton( void )
{
  int b;
  switch( etat_bouton )
  {
  case ATTENTE_BOUTON:
    // pas de bouton pressé depuis la dernière fois, je retourne 0
    b = 0;
    break;
  case BOUTON_APPUYE:
    // je prend la valeur du bouton pour la renvoyer
    b = bouton;
    // et je remet l'automate en attente de bouton
    bouton = 0;
    etat_bouton = ATTENTE_BOUTON;
    break;
  }
  return b;
}


Dans la tache gere_xxxx() tu appelle lit_bouton() pour savoir si un bouton a été appellé depuis la dernière fois.

Ok, d'accord, c'est un marteau pilon pour écraser une mouche.
Mais c'est plus facile d'introduire le concept sur un exemple simple pour le comprendre.
En pratique même moi je n'écrirait pas quelque chose d'aussi compliqué pour cela.
Le code suivant est aussi efficace mais reste propre:

Code: [Select]
int bouton = 0;
void gere_bouton( void )
{
    int b = appui(); // modier appui() pour retourner une valeur plutôt que modifier la var. globale
    // Le test est important. Je ne remet pas a zéro bouton si un nouvel appui n'a pas eu lieu.
    if ( b > 0 )
      bouton = b;
}

// fonction pour une autre tâche afin de savoir si un bouton a été pressé, si oui récupérer sa valeur et acquitter.
int lit_bouton( void )
{
  int b = bouton;
  bouton = 0;
  return b;
}


Maintenant vient le plus gros morceau : l'automate de gestion du LCD.

Prend un papier et un crayon, et essaye de découper la tâche du LCD en un certains nombre d'états.
Quels sont les évènements à considérer :
- bouton appuyé
- temps écoulé

Savoir qu'un bouton a été pressé, c'est la fonction lit_bouton().
Savoir qu'un temps a été écoulé.
Pour le temps, il faut bannir la fonction delay() sauf quand tu as des temps très court et critiques à gérer de manière atomique (atomique signifie qu'on n'a pas le droit de découper, sans interruption).
Je te propose le code suivant :
Code: [Select]
unsigned long timer_debut;
void timer_start()
{
  timer_debut = millis();
}
bool timer_ecoule( unsigned long duree_ms )
{
  if ( (millis() - timer_debut) > duree_ms )
    return true;
  return false;
}


Conseil : tu n'est pas forcement obligé de considérer des états distincts pour les différentes étapes de la boucle d'animation. Tu peut avoir un état ANIMATION_1 et un compteur. Peut être te faudra t'il 2 états pa animation ...... ;)

A ton crayon, relevé des copies dans quelques heures :D

zeric

Un grand merci  XD

Quote
De toute façon, bouton ne va pas changer de valeur tant que tu ne sort pas de là pour retourner dans appui().

Tu m'avais pourtant donné la solution juste avant :smiley-roll: , comme quoi le dire c'est bien, l'écrire c'est mieux (enfin pour moi!)
L'appel de "appui()" dans les boucles juste avant le test est la solution.

Pour les "delay" je le fait déjà, toute ma partie critique (loop) ne comporte aucun "delay" (la partie mise ici dans le loop l'est uniquement
pour le débogage et est en fait dans une fonction [sous-programme] qui ne tourne qu'au début de mon appli -un certain temps-, mais jamais pendant).
Mon loop est très très court et ne comporte quasiment que des appels à des fonctions (n'ayant aucun "delay", même de 1ms), je gère du calcul pour rafraîchissement
d'affichage au centième, il faut donc qu'il tourne en dessous de la milliseconde...
Sur ce coup, j'étais passé complètement à côté du rafraîchissement des variables dans une boucle, je le ferais plus, promis :smiley-mr-green:

Par contre, ton approche pour le retour de valeurs au lieu de jouer avec les var globales, je m'y mets tout juste, ça va m'aider, j'apprends, j'apprends...
Et aussi aider ceux qui le liront (cf. le titre ;)), surtout que c'est pour du matos standard, donc directement utilisable...


Go Up