Arduino 1
| Introduction |
Ces pages n'ont pas l'intention de supplanter les sites officiels de l'ARDUINO mais seulement d'introduire quelques notions de programmation que je considère "de base" pour écrire des programmes propres, dépannables, efficaces, rapides, enfin "bien" quoi.
Voici le sommaire, cliquez pour y aller ! :
Sommaire
On va commencer par charger le bon vieux programme BLINK, qui consiste à faire clignoter la led L13 de l'ARDUINO (Uno). Si vous avez un ARDUINO "pas Uno", la led peut avoir une autre adresse ... (tutoriel_1)
La page officielle pour débuter (au cas où vous ne seriez pas déjà passé par là ...)
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
This example code is in the public domain.
*/
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Bon , normalement, ce programme fait clignoter la led L13 (sur la carte) à la vitesse de 1 battement toutes les 2 secondes. On dit une période de 2 secondes, ou une demi-période de 1 seconde.
Je vous propose de modifier ce programme pour rendre la vitesse de clignotement modifiable :
juste après "int led = 13;", ajoutez une variable "demiperiode", initialisée à 50, et remplacer la valeur de "delay" par cette variable :
int demiperiode = 100;
...
delay(demiperiode); // wait for a while
Normalement, la led va cligonter très vite (10 fois par seconde). C'est volontaire, pour la suite ...
Etape suivante :
On va avoir besoin d'un bouton-poussoir. J'utilise alors le shield prototype, avec le BP "JC1" câblé sur "D2".
J'ai câblé aussi la grosse LED JC3 sur le port 13, pour mieux voir ...
On va également utiliser la liaison série pour débugger, en particulier pour voir si le BP est appuyé. On va donc utiliser le moniteur série.
Voici donc un nouveau programme, ou plutôt un évolution du précédent (tutoriel_2) :
/*
GoodFields tutoriel_2
*/
//**********************************************************
// DECLARATIONS
// entrées-sorties
int led = 13;
int bp = 2;
// vitesse de clignotement
int demiperiode=100;
//**********************************************************
// 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 paramètre de port COM
}
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {
// LED clignotante
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(demiperiode); // wait for a while
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(demiperiode); // wait for a while
// Bouton-poussoir
if (!digitalRead(bp)) Serial.println ("BOUTON appuyé");
if (digitalRead(bp)) Serial.println ("BOUTON laché");
}
//**********************************************************
Notez que l'on teste la valeur inverse de l'entrée, car le montage du BP est "tiré" au +5 Volts, donc à l'état 1.
Vous allez obtenir ceci sur le moniteur série :
...
BOUTON appuyé
BOUTON appuyé
BOUTON appuyé
BOUTON appuyé
BOUTON laché
BOUTON laché
BOUTON laché
...
ACCENTS : problème !
Les accents passent mal ! OUI ! Il suffit de remplcare dans votre texte du "print" par ceci :
é : \xE9
è : \xE8
Voir en annexe liste plus exhaustive.
Et avec défilement à la vitesse de clignotement, soit ici 5 fois par seconde. Il y en a trop ! Un simple message au changement d'état suffirait ! On introduit alors la notion de mémoire, de front montant et descendant :
On va créer une variable "bp_etat", car on va utiliser cet état plusieurs fois, et on a intérêt à lire l'entrée une seule fois, sinon, elle risque de changer en cours de déroulement programme.
Egalement "bp_memoire" qui va être chargée de mémoriser l'état précédent du BP.
Ensuite, on va tester le fait que le BP est à 1, et la mémoire à 0 pour détecter un changement d'état (et/ou l'inverse).
Voici le programme (tutoriel_3) :
/*
GoodFields tutoriel_3
*/
//**********************************************************
// DECLARATIONS
// entrées-sorties
int led = 13;
int bp = 2;
int bp_etat, bp_memoire;
// vitesse de clignotement
int demiperiode=100;
//**********************************************************
// 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
}
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {
// LED clignotante
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(demiperiode); // wait for a while
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(demiperiode); // wait for a while
// Bouton-poussoir
bp_etat = !digitalRead(bp);
if (!bp_memoire && bp_etat) Serial.println ("BOUTON appuy\xE9");
if (bp_memoire && !bp_etat) Serial.println ("BOUTON lach\xE9");
// mise en mémoire pour le prochain tour
bp_memoire = bp_etat;
}
//**********************************************************
Vous allez d'abord voir que ca marche, bien, mais qu'en y regardant de plus près c'est pas très "réactif". Je veux dire que le message n'apparaît pas exactement au moment ou vous manipuler le BP.
Changez la valeur de demiperiode, pour la mettre à 1 par exemple : c'est bien mieux pour le BP ! On a même parfois de détection de "rebonds" (contact pas franc). La led clignote si vite qu'on la dirait à moitié allumée ...
Changez la valeur de demiperiode, pour la mettre à 2500 par exemple : la led va être très lente, mais pour le BP c'est la catastrophe ! Pourquoi ?
Parce que pendant la fonction "delay", votre ARDUINO, ne fait rien ! Et en particulier la partie de programme qui s'occupe du BP ne marche plus ! Donc, le BP n'est scruté que toutes les 5 secondes.
Sachant que votre ARDUINO peut tourner très vite, c'est franchement dommage de le laisser se reposer !!!
CONCLUSION (que j'espère provisoire) : ce programme est mal écrit !
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 ...) :
Voici un aprecu rapide de la méthode sur WikiPédia
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 !!!
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;
//--------------------------------
}
//**********************************************************
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".
Passez à la page suivante pour étudier la gestion du temps plus "réel" ...
Dernière mise à jour : 07:50:08 11/09/2013