Arduino 04
La gestion basique du temps et le GRAFCET
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--;
...
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)
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 :
 |
si tempo_a terminée | |
allumer la LED resetter la tempo_a passer à l'étape suivante |
| |
 |
si tempo_a terminée | |
éteindre la LED resetter la tempo_a retourner à l'étape 1 |
| |
default |
ERREURS :Arrêter le 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 B (Départ) (les 2 grafcets se déroulent indépendemment) |
| |
 |
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 |
| |
 |
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".
Dernière mise à jour : 09:48:27 12/10/2017