« Kidule Ascenseur » : différence entre les versions

Contenu supprimé Contenu ajouté
Marc0NICOLE (discussion | contributions)
Aucun résumé des modifications
Marc0NICOLE (discussion | contributions)
Aucun résumé des modifications
Ligne 12 :
 
[[Catégorie: Programmation C / Pinguino sur Kidules]]
 
 
 
Fait partie de [[:Catégorie:Programmation C / Pinguino sur Kidules | programmation C / Pinguino sur Kidules]]
[[Fichier:Kidules et Ascenseur.jpg|right]]__TOC__
{{clr}}
= But du cours =
Dans le cadre du festival de robotique 2011 de l'[http://festivalrobotique.epfl.ch/ EPFL], Il y a un Atelier de [[:Catégorie:Programmation C / Pinguino sur Kidules | programmation C / Pinguino sur Kidules]]
Cet article sera le support de cours de cet atelier. Il sera maintenu en vie et amélioré au fur et à mesure, et pourrait être une base de départ pour tout enseignant voulant faire des cours de programmation, ainsi qu'un support pour tous les enfants qui ont envie de poursuivre. Pour ceux qui crochent, le matériel peut être acheté chez [http://www.didel.com Didel].
 
== Conventions d'écriture ==
{{Définition|titre=Définition simplifiée|contenu=
Lorsque l'on voit une boîte de définition comme celle-ci, il s'agit dans ce cours d'une définition simplifiée d'un concept.
 
Pour être simple, cette définition n'est pas encore complète. Cela veut dire que ce qui est écrit fonctionne, mais que cela ne donne pas toutes les possibilités du langage C. En principe, à la fin de cette définition, il y a un lien vers un site qui donne des informations plus complètes, mais aussi inutilement complexes pour l'exercice en cours.}}
 
{{Principe|contenu=
Ce sont des principes importants à retenir.
}}
 
Lors d'un exercice, il y a solution qui est donnée. Pour avoir besoin de réfléchir un peu, la solution est cachée et il faut cliquer sur afficher pour la voir.
 
{{Solution|contenu=
Et voila le résultat caché :
<source lang="C">
// avec un bout de programme de solution
</source>
}}
 
= Matériel =
Le matériel de cet atelier est composés de Kidules[[http://www.didel.com/KidulesPub.pdf]] produits par Didel[[http://www.didel.com]]
 
== Les Kidules ==
[[Fichier:Kidules Ascenseur et dé.jpg]]
 
On trouve la documentation complète ici pour le Kidule 2550[[http://www.didel.com/kits/Kidule2550.pdf]] et pour le kidule ascenseur [[http://www.didel.com/kits/KiduleAsc.pdf]]
{{clr}}
=== Le schema ===
 
[[Fichier:Schema Kidule Pic 2550 et Ascenseur et De.png]]
 
Le processeur est un PIC 18F2550 dont on trouve le databook ici [[http://ww1.microchip.com/downloads/en/DeviceDoc/39632e.pdf]]
{{clr}}
 
{{:PIC Entrées Sorties}}
 
= Déclaration dans le software pour ce Hardware =
Sur le Kidule PIC 2550, 3 ports sont utilisables PORTB de 8 bits à droite et PORTA (5 bits) ainsi de PORTC (3 bits) à gauche.
 
On trouve ci après les déclarations nécessaires pour adresser correctement le hardware:
Dans ce fichier, on fait deux choses:
# on donne des noms intelligents aux bits d'entrées / sorties, qui correspondent aux deux Kidules
# on a une fonction '''setupHard()''' qui permet d'initialiser correctement les registres TRISx pour indiquer si il s'agit d'entrées ou de sorties
# définir des mots parlant pour '''Allume''', '''Eteint''' ou '''Presse''' plutôt que d'avoir à tout le temps réflechir si il faut un 1 ou un 0 dans ce cas
 
== HardwareAsc.h==
<source lang="c">
#define pic18f2550 1
// ATTENTION: sur le 18F2550, il ne faut pas écrire dans PORTx des bits, mais dans LATx, car
// si on fait plusieurs instructions bit de suite (Read-Modifiy-Write) on a
// des résultats difficiles à comprendre
// cf http://www.microchip.com/forums/tm.aspx?m=421256
// http://www.microchip.com/forums/tm.aspx?m=110258
 
//déclarations pour le Kidule PIC2550
#define Buzzer LATCbits.LATC7
//déclarations pour le module Ascenseur monté à droite
#define Bouton2eme PORTBbits.RB0
#define Bouton1er PORTBbits.RB1
#define BoutonRez PORTBbits.RB2
#define Switch2eme PORTBbits.RB3
#define Switch1er PORTBbits.RB4
#define SwitchRez PORTBbits.RB5
#define Descendre LATBbits.LATB6
#define Monter LATBbits.LATB7
//déclarations pour le module Dé monté à Gauche
#define Led0 LATAbits.LATA0
#define Led1 LATAbits.LATA1
#define Led2 LATAbits.LATA2
#define Led5 LATAbits.LATA3
#define Led4 LATAbits.LATA5
#define Led3 LATCbits.LATC0
#define Led6 LATCbits.LATC1
#define Poussoir PORTCbits.RC2
//déclaration pour l'usage propre à ce programme
#define LedRez Led0
#define Led1er Led1
#define Led2eme Led2
//déclaration pour rendre plus explicite le code vu que les modules sont en logique actif à 0
#define Presse 0
#define Allume 0
#define Eteint 1
// Mise en route du PIC: configuration des PINs en entrées ou sorties
void setup()
{
PORTA = 0x00;
ADCON1= 0x0f; // désactive les entrées anlogiques
CMCON = 0x07;
TRISA = 0b11000000;
PORTA = 0b00111111;
TRISC = 0b00111100;
PORTC = 0b00000011;
TRISB = 0b00111111;
PORTB = 0b00000000;
}
 
 
</source>
 
Le mieux, c'est de copier ce bout de programme dans votre programme, pour ne pas avoir à tout déclarer à la main. Pour l'atelier, vous trouverez un fichier '''hardwareAsc.h''' déjà présent dans le répertoire.
 
= Test de tout le matériel =
Avant de commencer de faire un vrai programme pour l'ascenseur, il est bon de faire un petit programme de test qui vérifie que toutes les déclarations que l'on a fait sont correcte et correspondent bien au hardware que l'on a.
 
Pour tester le Hardware, voici un petit programme simple qui fait les choses suivantes:
* allume successivement les Leds 0 à 7 du Kidule Dé.
* puis si on presse sur le poussoir du Kidule dé, allume toutes les LEDs (qui se feront par la suite éteindre par le premier point)
* si on presse sur le Bouton du 1{{er}} sur le Kidule ascenseur, on entre dans un mode où le bouton du 2eme fait monter et le bouton du Rez fait descendre. Les LEDs 0 à 2 du Kidule Dé reflettent l'état des switchs de l'ascenseur
 
<source lang="c">
#include "hardwareAsc.h"
void setup ()
{
setupHard();
}
 
 
void wait()
{
long i;
for (i=0;i<500000;i++) {
}
}
 
void TestHard()
{
Led0=Allume;
wait();
Led0=Eteint;
Led1=Allume;
wait();
Led1=Eteint;
Led2=Allume;
wait();
Led2=Eteint;
Led3=Allume;
wait();
Led3=Eteint;
Led4=Allume;
wait();
Led4=Eteint;
Led5=Allume;
wait();
Led5=Eteint;
Led6=Allume;
wait();
Led6=Eteint;
if (Poussoir==Presse){
Led0=Allume;
Led1=Allume;
Led2=Allume;
Led3=Allume;
Led4=Allume;
Led5=Allume;
Led6=Allume;
wait();
}
if (Bouton1er==Presse){
while (TRUE) {
if (Bouton2eme==Presse) Monter=1; else Monter=0;
if (BoutonRez ==Presse) Descendre=1; else Descendre=0;
if (Switch2eme==Presse) Led5=Allume; else Led5=Eteint;
if (Switch1er==Presse) Led4=Allume; else Led4=Eteint;
if (SwitchRez==Presse) Led3=Allume; else Led3=Eteint;
}
}
}
 
void loop ()
{
TestHard();
}
 
</source>
 
{{Définition|titre=for|contenu=
l'instruction for permet de faire une boucle (c'est-à-dire répéter un bloc instructions) un certain nombre de fois
<source lang="C">
for (i=0;i<500000;i++) {
/* instructions à répéter */
}
</source>
après la '''(''', on met
*l'instruction a faire avant la boucle: ici '''i=0''' c'est-à-dire mettre la variable i à zero
*la condition qui dit aussi longtemps qu'il faut continuer la boucle (ici si i est plus petit que 500000)
*ce qu'il faut faire à la fin de la boucle: en général, ajouter 1 à la variable i (qui se note i++)
}}
Le but ici est simplement de perdre du temps pour qu'on ait le temps de voir les LED s'allumer une à une; donc on ne fait rien entre les { }
 
{{Définition|titre=while|contenu=
l'instruction while est aussi une boucle qui effectue un bloc d'instruction tant que la condition entre parenthèse est vraie
<source lang="C">
while(condition) {
/* instructions à répéter */
}
</source>
}}
dans notre cas, la condition est le mot TRUE (=vrai en anglais); cette condition est toujours vraie et on s'arrêtera jamais de faire ces dernières instructions
 
== Exercice pratique ==
# Lancez le programme et laisser une fois clignoter toutes les LED.
# Pressez sur le Poussoir du kidule Dé jusqu'à ce que toutes les LED s'allument.
# Ensuite Pressez sur le bouton du 1{{er}} jusqu'à ce que que plus aucune LED du Kidule Dé ne soit allumées.
# Jouez en pressant le le bouton 2eme et Bouton 1{{er}}. Qu'est ce qu'il se passe si on presse sur un Bouton puis sur les deux?
{{Solution|contenu=
Si on essaye toutes les solutions, on observe le comportement suivant
 
 
Ce qui est important est de bien comprendre la différence entre '''Moteur Bloqué''' et '''Moteur en roue libre''':
*'''Moteur Bloqué''': Si on met les sorties Monter et Descendre à 0, le chip de contrôle du moteur court-circuite le moteur. Or un moteur c'est aussi une dynamo: si on le fait tourner, il fabrique de l'électricité. Si on le court-circuite, cela lui mange son énergie et il s'arrête très vite.
*'''Moteur en roue libre''': Si on met les sorties Monter et Descendre à 1, le chip de contrôle du moteur "déconnecte" les fils du moteur. Là aussi il produit de l'électricité, mais comme elle n'est pas utilisée, cela ne lui mange pas d'énergie et il peut continuer sur son élan.
 
Pour l'ascenseur, on vaudra donc mieux le freiner lorsqu'on arrive à un étage. Sinon on dépassera l'étage.
}}
 
= Programme Ascenseur =
C'est le moment de s'attaquer au programme proprement dit pour faire bouger notre ascenseur.
 
Mais avant de programmer, il faut réfléchir à ce que l'on veut faire. Première chose à remarquer, c'est que l'on devra faire plusieurs choses à la fois: lire des boutons, actionner le moteur, allumer et éteindre les Leds et se rappeler ce que l'ascenseur doit faire (monter au 1{{er}} étage par exemple).
 
Lorsque l'on a ce genre de problème (ce qui est très fréquent dans une machine ou dans un robot), la solution est souvent de dessiner un '''graphe d'états'''. Un graphe d'état est un dessin où on met une bulle par état possible de la machine. par exemple ici '''AuRez''' pour dire que l'on est au rez à attendre, ou encore '''Monte2eme''' pour dire que l'on va au 2eme.
 
[[Fichier:Kidule Ascenseur Graphe Etat Sans Transisitons.png]]
 
Puis on trace avec des flèches tout ce qui est possible de faire en termes de changement d'état et à côté de la flèche on marque sous quelle condition on va changer d'état. Par exemple, on peut passer de '''AuRez''' à '''Monter1er''' si on a '''Bouton1er==Presse'''.
 
== Exercice ==
Essaie de tracer toutes les transitions qui sont possibles dans le cas de notre ascenseur. Pour cet exercice, on va imaginer que notre ascenseur est très simple:
* On ne peut lui donner qu'un ordre à la fois.
{{Solution|contenu=
[[Fichier:Kidule Ascenseur Graphe Etat.png]] Par simplification, on n'a pas écrit à chaque transition ==Presse
}}
== Traduction d'un graphe d'états en C ==
Pour traduire un graphe d'états en C, il nous faut:
# déclarer tous les états possibles
# déclarer variable qui contient l'état
# avoir une instruction qui nous permet de faire un bout de programme différent pour chaque état
 
Pour le 1{{er}} point, il existe en C une instruction pour déclarer un type énuméré (qui énumère toutes les possibilités d'une variable de ce type)
{{Définition|titre=enum|contenu=
enum permet de déclarer une énumération, et pour chaque énumération de lui attribuer la valeur qui sera utilisée dans le microprocesseur (optionnel). Dans notre cas, on peut écrire cela de la manière suivante:
<source lang="C">
enum {Au2eme =0b00000100,
Au1er =0b00000010,
AuRez =0b00000001,
Monte2eme =0b00000111,
Monte1er =0b00000011,
Descend1er=0b00000110,
DescendRez=0b00000000}
</source>
}}
 
pour le 2eme point, comme on vient de déclarer ce nouveau type, on peut juste après déclarer la variable Etat et lui donner sa valeur initiale.
 
<source lang="C">
Etat = DescendRez;
</source>
 
pour le 3eme point, il existe une instruction un peu compliquée, mais qui fait exactement ce qu'il nous faut:
 
{{Définition|titre=switch(...) case:|contenu=
l'instruction '''switch''' permet de faire un bout de code ou un autre suivant la valeur d'une variable. On l'utilise de la manière suivantes:
<source lang="C">
switch (variable)
{
case valeur:
instruction;
instruction;
break;
case valeur:
instruction;
break;
default:
instruction;
}
</source>
 
Pour chaque valeur qui nous intéresse, on met un '''case''' suivit de la valeur puis d'un :
les instructions qui suivent ne seront exécutée si la variable du '''switch''' == la valeur. A la fin de ces instruction, on doit écrire un '''break;''' qui indique que l'on peut sauter à la fin de l'} du switch
}}
 
Pour traduire notre graphe d'états en code, nous allons écrire un état par '''case''' du '''switch'''. Dans la première partie de l'état, on va mettre ce que l'on doit faire dans cet état comme par exemple '''Monter=...''', '''Descendre=...''', allumer des leds etc... Puis nous allons regarder si il a a une transition possible avec un '''if(...) { Etat = ''nouvel_etat''}'''.
De cette manière, à chaque fois que la fonction GererAscenseur() est appelée, on va effectuer les actions de cet état, puis regarder si la prochaine fois on doit exécuter un autre état.
 
== Exercice ==
 
Il faut maintenant coder notre programme. Pour pas trop se fatiguer, On peut copier le squelette de programme ci dessous, et on remplacera tous les /**/ par quelque chose d'utile
 
<source lang="C">
#include "hardwareAsc.h"
 
void setup ()
{
setupHard();
}
 
enum {Au2eme =0b00000100,
Au1er =0b00000010,
AuRez =0b00000001,
Monte2eme =0b00000111,
Monte1er =0b00000011,
Descend1er=0b00000110,
DescendRez=0b00000000} Etat = DescendRez;
 
void AfficherEtat()
{
if (Etat & 0b00000001) Led3=Allume; else Led3=Eteint;
if (Etat & 0b00000010) Led4=Allume; else Led4=Eteint;
if (Etat & 0b00000100) Led5=Allume; else Led5=Eteint;
}
 
void GererAscenseur()
{
switch (Etat)
{
case Au2eme:
Monter = 0;
Descendre = 0;
Led2eme = Eteint; //si on est au 2eme, on éteint la LED qui indique qu'on veut aller au 2eme
if (Bouton1er==Presse) {
Etat = Descend1er;
}
else if (BoutonRez==Presse) {
Etat = DescendRez;
}
break;
case Au1er:
Monter = /**/;
Descendre = /**/;
Led1er = Eteint;
if (Bouton2eme==Presse) {
Etat = /**/;
}
else if (/**/==Presse) {
Etat = DescendRez;
}
break;
case AuRez:
Monter = /**/;
Descendre = /**/;
LedRez = /**/;
if (Bouton2eme==Presse) {
Etat = /**/;
}
else if (Bouton1er==Presse) {
Etat = /**/;
}
break;
case Monte2eme:
Monter = /**/;
Descendre = /**/;
Led2eme = Allume;
if (Switch2eme==Presse) {
Etat = /**/;
}
break;
case Monte1er:
Monter = /**/;
Descendre = /**/;
Led1er = Allume;
if (/**/==Presse) {
Etat = Au1er;
}
break;
case Descend1er:
Monter = 0;
Descendre = 1;
Led1er = Allume;
if (Switch1er==Presse) {
Etat = Au1er;
}
break;
case DescendRez:
Monter = 0;
Descendre = 1;
LedRez = Allume;
if (SwitchRez==Presse) {
Etat = AuRez;
}
break;
default:
Led6=Allume; // pour montrer que l'on a un gros problème
}
}
void loop ()
{
GererAscenseur();
AfficherEtat();
}
 
</source>
 
{{Solution|contenu=
<source lang="C">
#include "hardwareAsc.h"
 
void setup ()
{
setupHard();
}
 
enum {Au2eme =0b00000100,
Au1er =0b00000010,
AuRez =0b00000001,
Monte2eme =0b00000111,
Monte1er =0b00000011,
Descend1er=0b00000110,
DescendRez=0b00000000} Etat = DescendRez;
 
void AfficherEtat()
{
if (Etat & 0b00000001) Led3=Allume; else Led3=Eteint;
if (Etat & 0b00000010) Led4=Allume; else Led4=Eteint;
if (Etat & 0b00000100) Led5=Allume; else Led5=Eteint;
}
 
void GererAscenseur()
{
switch (Etat)
{
case Au2eme:
Monter = 0;
Descendre = 0;
Led2eme = Eteint;
if (Bouton1er==Presse) {
Etat = Descend1er;
}
else if (BoutonRez==Presse) {
Etat = DescendRez;
}
break;
case Au1er:
Monter = 0;
Descendre = 0;
Led1er = Eteint;
if (Bouton2eme==Presse) {
Etat = Monte2eme;
}
else if (BoutonRez==Presse) {
Etat = DescendRez;
}
break;
case AuRez:
Monter = 0;
Descendre = 0;
LedRez = Eteint;
if (Bouton2eme==Presse) {
Etat = Monte2eme;
}
else if (Bouton1er==Presse) {
Etat = Monte1er;
}
break;
case Monte2eme:
Monter = 1;
Descendre = 0;
Led2eme = Allume;
if (Switch2eme==Presse) {
Etat = Au2eme;
}
break;
case Monte1er:
Monter = 1;
Descendre = 0;
Led1er = Allume;
if (Switch1er==Presse) {
Etat = Au1er;
}
break;
case Descend1er:
Monter = 0;
Descendre = 1;
Led1er = Allume;
if (Switch1er==Presse) {
Etat = Au1er;
}
break;
case DescendRez:
Monter = 0;
Descendre = 1;
LedRez = Allume;
if (SwitchRez==Presse) {
Etat = AuRez;
}
break;
default:
Led6=Allume;
}
}
void loop ()
{
GererAscenseur();
AfficherEtat();
}
 
</source>
}}
 
= Notes=