Arduino 04arduino_goodfields_64.jpgLa gestion basique du temps et le GRAFCET
 
Sommaire

La  gestion basique du temps La gestion basique du temps GRAFCET GRAFCET double  grafcet double grafcet
La page principale ... La page principale ...
   
Vers le début Vers Page 2 Vers sommaire La gestion basique du temps
Donc, il va falloir gérer les temporisations de la LED autrement pour avoir encore du programme qui tourne pendant que la LED reste allumée ou éteinte.

Dans un premier temps, on va créer une variable "tempo_led" qui va être un compteur "de temps".
On va décrémenter ce compteur de 1 à chaque tour de programme ( cette méthode ne donne rien de précis, mais pour démarrer c'est suffisant, on verra plus loin comment faire quelquechose de mieux avec time.h)
...
// vitesse de clignotement
int demiperiode=25000;
// temporisations
int tempo_a;
...
void loop() {

// gestion des temporisations
  if (tempo_a > 0 ) tempo_a--;
...
   
Vers le début Vers Page 2 Vers sommaire GRAFCET

Puis élaborer une structure de programme en "grafcet". Le GRAFCET est une méthode d'analyse et de programmation d'automatismes. Très en vogue dans les années 1980, je suis étonné de l'efficacité de cette méthode que j'applique partout (langage BASIC, C, PHP, scripts VBS, PIC, ARDUINO ...).
Les intérêts de cette méthode sont (pour moi) :
- une possibilité d'appliquer l'adage "un bon dessin vaut mieux qu'un long discours" quand on veut décrire un automatisme, ou un programme.
- une méthode d'analyse très proche à la fois du besoin (ce que l'on veut automatiser) et du programme à écrire, ce qui, dans la pratique, permet de mettre d'accord très rapidement le "client" et le "programmeur".
- une analyse structurée en blocs de petite taille, ainsi, la mise au point, le dépannage, la modification se trouvent facilités.
- un "effet de bord" très intéréssant est l'économie évidente de ressource CPU, qui se trouve à la fois plus réactif, plus disponible, optimisé, et permet de repousser le besoin de gérer des interruption assez loin.
En fait, je prends quelques libertés par rapport à la norme. En particulier, comme les sorties de l'ARDUINO sont "à mémoire" (elles restent dans l'état où on les a mises jusqu'à ce qu'on les change), je me permet de faire dans le test de TRANSITION des actions sur les sorties, des réarmements de tempos etc, alors qu'on ne dvrait que faire des changement d'ETAPE. Ceci vient aussi du fait que le langage C est bien plus puissant que tous les langages d'automates programmables (en tout cas ceux des années 1980/1990, dates de mes dernières expériences dans ce domaine)

Un document fait par un professeur, très bien fait ! Un document fait par un professeur, très bien fait ! Un powerpoint pas mal fait ... Un powerpoint pas mal fait ... (internet) Voici un aperçu rapide de la méthode sur WikiPédia (internet) Voici un aperçu rapide de la méthode sur WikiPédia
(internet) Un cours plus complet (internet) Un cours plus complet

En ce qui nous concerne, on va aller directement à une structure qui va utiliser la commande SWITCH en C :
On rajoute une variable "grafcet_a", dont la valeur va donner le numéro de l'étape en cours.
A la fin du module d'INITIALISATIONS, on arme le grafcet en forcant l'étape 1.
Et on écrit le grafcet à la place du module qui fait clignoter la LED. Je pense avoir suffisamment commenté pour que vous compreniez ...
Voici le listing à jour (tutoriel_4)

/*
  GoodFields tutoriel_4 
  FRONT MONTANT/DESCENDANT
  TEMPORISATION basique
  GRAFCET
*/
 
//**********************************************************
// DECLARATIONS
// entrées-sorties
int led = 13;
int bp = 2;
int bp_etat, bp_memoire;
// vitesse de clignotement
int demiperiode=25000;
// temporisations
int tempo_a;
// GRAFCETS
int grafcet_a;

//**********************************************************
// INITIALISATIONS
void setup() {                
// initialize the digital pins
  pinMode(led, OUTPUT);     
  pinMode(bp, INPUT);     
// ligne série (pour traces)
  Serial.begin(4800);  // ou 9600 selon votre port COM
// on arme le grafcet
  grafcet_a = 1;
}
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {

// gestion des TEMPORISATIONS
  if (tempo_a > 0 ) tempo_a--;
  
//--------------------------------
// GRAFCET A
switch (grafcet_a) {

  case 1:
  if ( tempo_a == 0 ) {        // si la tempo est terminée,
    digitalWrite(led, HIGH);   // on allume la LED
    tempo_a = demiperiode;             // on arme la tempo
    grafcet_a++;               // et on passe à l'étape suivante
  }
  break;      // cette instruction vous fait sauter à la fin du "switch"
  // sinon, on continue, et cela fait perdre du temps, et il peut arriver que d'autre "case" fonctionnent ! 

  case 2:
  if ( tempo_a == 0 ) {
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    tempo_a = demiperiode;             // on arme la tempo
    grafcet_a=1;               // on reboucle
  }    
  break;

// si grafcet_a n'a pas une valeur correcte, on passe ici
  default: 
      grafcet_a = 0; // on arrête
  break;
}      // fin du "switch"
// FIN GRAFCET A
//--------------------------------
// Bouton-poussoir
  bp_etat = !digitalRead(bp);
  if (!bp_memoire && bp_etat){
    Serial.print ("BOUTON appuy\xE9 grafcet_a=");
    Serial.print (grafcet_a);
    Serial.print (" tempo_a=");
    Serial.println (tempo_a);
  }
  if (bp_memoire && !bp_etat) {
    Serial.print ("BOUTON lach\xE9 grafcet_a=");
    Serial.print (grafcet_a);
    Serial.print (" tempo_a=");
    Serial.println (tempo_a);
  }
// mise en mémoire pour le prochain tour
  bp_memoire = bp_etat;
//--------------------------------

}
//**********************************************************

Vous remarquerez que j'ai poussé la valeur de : int demiperiode=25000;, et que malgré cela, la led cligonte assez vite !
et modifié les prints du BOUTON POUSSOIR pour avoir des traces intéréssantes ... On constate alors que les événements du BP arrivent de facon aléatoires par rapport au fonctionnement du grafcet et de la temporisations.
On vient de réaliser en quelque sorte 2 programmes totalement indépendants, asynchrones l'un par rapport à l'autre !!!
En schéma Grafcet on peut écrire :


GRAFCET LED (Départ)
 
  
1 attente fin extinction
 
si tempo_a terminée 
allumer la LED
resetter la tempo_a
passer à l'étape suivante
 
  
2 attente fin allumage
 
si tempo_a terminée 
éteindre la LED
resetter la tempo_a
retourner à l'étape 1
 
  

GRAFCET LED (default)
 
  
default ERREURS :Arrêter le grafcet
 

   
Vers le début Vers Page 2 Vers sommaire double grafcet
Si vous n'êtes pas convaincus, ajoutez une seconde LED sur le port 12 et essayez ceci :

/*
  GoodFields tutoriel_5
  FRONT MONTANT/DESCENDANT
  TEMPORISATION basique
  GRAFCET
*/
 
//**********************************************************
// DECLARATIONS
// entrées-sorties
int led_a = 13;
int led_b = 12;
int bp = 2;
int bp_etat, bp_memoire;
// vitesse de clignotement
int tempo_a_reset=10000;
int tempo_b_reset=7000;
// temporisations
int tempo_a,tempo_b;
// GRAFCETS
int grafcet_a,grafcet_b;

//**********************************************************
// INITIALISATIONS
void setup() {                
// initialize the digital pins
  pinMode(led_a, OUTPUT);     
  pinMode(led_b, OUTPUT);     
  pinMode(bp, INPUT);     
// ligne série (pour traces)
  Serial.begin(4800);  // ou 9600 selon votre port COM
// on arme les grafcets
  grafcet_a = 1;
  grafcet_b = 1;
}
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {

// gestion des TEMPORISATIONS
  if (tempo_a > 0 ) tempo_a--;
  if (tempo_b > 0 ) tempo_b--;
  
//--------------------------------
// GRAFCET A
switch (grafcet_a) {

  case 1:
  if ( tempo_a == 0 ) {        // si la tempo est terminée,
    digitalWrite(led_a, HIGH);   // on allume la LED
    tempo_a = tempo_a_reset;             // on arme la tempo
    grafcet_a++;               // et on passe à l'étape suivante
  }
  break;      // cette instruction vous fait sauter à la fin du "switch"
  // sinon, on continue, et cela fait perdre du temps, et il peut arriver que d'autre "case" fonctionnent ! 

  case 2:
  if ( tempo_a == 0 ) {
    digitalWrite(led_a, LOW);    // turn the LED off by making the voltage LOW
    tempo_a = tempo_a_reset;             // on arme la tempo
    grafcet_a=1;               // on reboucle
  }    
  break;

// si grafcet_a n'a pas une valeur correcte, on passe ici
  default: 
      grafcet_a = 0; // on arrête
  break;
}      // fin du "switch"
// FIN GRAFCET A
//--------------------------------
// GRAFCET B
switch (grafcet_b) {

  case 1:
  if ( tempo_b == 0 ) {        // si la tempo est terminée,
    digitalWrite(led_b, HIGH);   // on allume la LED
    tempo_b = tempo_b_reset;             // on arme la tempo
    grafcet_b++;               // et on passe à l'étape suivante
  }
  break;      // cette instruction vous fait sauter à la fin du "switch"
  // sinon, on continue, et cela fait perdre du temps, et il peut arriver que d'autre "case" fonctionnent ! 

  case 2:
  if ( tempo_b == 0 ) {
    digitalWrite(led_b, LOW);    // turn the LED off by making the voltage LOW
    tempo_b = tempo_b_reset;             // on arme la tempo
    grafcet_b=1;               // on reboucle
  }    
  break;

// si grafcet_a n'a pas une valeur correcte, on passe ici
  default: 
      grafcet_b = 0; // on arrête
  break;
}      // fin du "switch"
// FIN GRAFCET B
//--------------------------------
// Bouton-poussoir
  bp_etat = !digitalRead(bp);
  if (!bp_memoire && bp_etat){
    Serial.print ("BOUTON appuy\xE9 grafcet_a=");
    Serial.print (grafcet_a);
    Serial.print (" tempo_a=");
    Serial.print (tempo_a);
    Serial.print (" grafcet_b=");
    Serial.print (grafcet_b);
    Serial.print (" tempo_b=");
    Serial.println (tempo_b);
  }
  if (bp_memoire && !bp_etat) {
    Serial.println ("BOUTON lach\xE9");
  }
// pour ralentir l'ARDUINO
  if (bp_etat){ delay(1); }
  
// mise en mémoire pour le prochain tour
  bp_memoire = bp_etat;
//--------------------------------

}
//**********************************************************
GRAFCET LED A( Départ)
 
GRAFCET LED B (Départ)
(les 2 grafcets se déroulent indépendemment)
 
     
1 ALLUMER la LED A
 
1 ALLUMER la LED B
 
si tempo_a terminée  si tempo_b terminée 
Allumer la ledB
réarmer la tempo A
 
Allumer la led B
réarmer la tempo B
 
     
2 ETEINDRE la LED A
 
1 ETEINDRE la LED B
 
si tempo_a terminée  si tempo_b terminée 
Eteindre la led A
réarmer la tempo
Aller en 1
 
Eteindre la led B
réarmer la tempo
Aller en 1
 
     

default ERREURS :Arrêter le grafcet A
 
default ERREURS :Arrêter le grafcet B
 

Chacun des grafcet s'occupe de sa LED. Ils tournent de façon volontairement décalés.
Lorsque l'on appuie sur le BP, on introduit un delay de 1ms (à la fin du programme), qui ralentit considérablement le processeur, et modifie donc nos temporisations "basiques".
La page principale ... La page principale ... la suite ... la suite ...

Dernière mise à jour : 09:48:27 12/10/2017