Cours:TPs Mecatro

De troyesGEII
Révision datée du 2 février 2016 à 15:55 par SergeMoutou (discussion | contributions) ({{Vert|Recherche de la valeur la plus proche du tableau}})
Aller à : navigation, rechercher

Carte d'extension arduino pour les Tps d'info

Comme d'habitude, on se réfère à : Premiers Pas en Informatique Embarquée document de cours (de Simon Landrault et al.)

TP-1 Diode électroluminescente

Cours

Notion de setup()

Notion de loop()

Notion d'entrée/sortie :

  • pinMode
  • digitalWrite et digitalRead

TP : led seule

  • Suivre le document à partir de la page 48

TP Faire clignoter un groupe de LEDs

  • Suivre le document à partir de la page 61.

Notre partie matérielle sera différente : elle sera constituée d'un shield tout fait dont on vous donne la documentation.

Numéro f5 f4 f3 f2 f1 f0 p1 p0
Couleur r o v r o v v r
Arduino Pin 13 12 11 10 9 8 7 6
Port Arduino UNO PB5 PB4 PB3 PB2 PB1 PB0 PD7 PD6
Port Arduino LEONARDO PC7 PD6 PB7 PB6 PB5 PB4 PE6 PD7

TP Réaliser un chenillard

  • Suivre le document de cours à partir de la page 64 jusqu'à la page 65.
  • Pouvez-vous réaliser le même chenillard avec une boucle ?
  • Réaliser des chenillards de votre propre invention. Si vous n'avez pas d'idée, demandez à votre enseignant.

TP Feux de signalisation routière

  • Suivre le document de cours à partir de la page 67 en repérant les modifications de notre shield par rapport aux branchements donnés dans ce cours.

TP-2 Boutons et diodes électroluminescantes

Cours

Détecter la position d'un bouton

Il est naturellement possible de connecter des boutons poussoirs à un PORT et de demander au micro-contrôleur de dire si le bouton est appuyé ou relâché. Sur un robot mobile on peut utiliser ce principe pour détecter des objets (un peu tard d'ailleurs puisqu'il y a eu contact).

Connexions de boutons poussoirs à un micro-controleur

Regardez attentivement le dessin de gauche et en particulier la connexion sur le bit PB4. On sait parfaitement ce qu'il se passe si le bouton est appuyé : on est relié à Vcc on aura donc un un logique en lecture. Mais qu'en est-il si on lâche le bouton poussoir ? Tout dépend de la technologie du PORT. La technologie TTL fournirait un un logique donc aucune différence suivant que le bouton est appuyé ou pas !

Remarques :

  • L'idée qui consiste à croire que puisqu'on est relié à rien, alors on lit systématiquement un zéro logique est parfois une idée fausse !!!
  • Le bit PB4 du schéma de droite ne fonctionne pas forcément comme on l'attend : lâché est un un logique, appuyé est un zéro logique !
  • Le bit PB0 du schéma de droite fonctionne normalement : appuyé = un logique, relâché = zéro logique

Il faut donc utiliser une résistance supplémentaire que l'on appelle résistance pullup (ou Résistance de tirage en français). Soit elle existe à l'intérieur du PORT, soit il faut l'ajouter à l'extérieur du PORT.

Utiliser une pullup interne

L'utilisation d'une résistance de pullup interne permet de simplifier le schéma. Il y a deux façons de réaliser un pullup interne :

  • écriture d'un un logique après passage en entrée
void setup() {
  // put your setup code here, to run once:
  pinMode(11,INPUT);
  digitalWrite(11,HIGH);
}
  • déclaration directe
void setup() {
  // put your setup code here, to run once:
  pinMode(11,INPUT_PULLUP);
  delay(1); // obligatoire !!!!
}

Détecter un changement d'état d'un bouton poussoir

Pourquoi détecter un changement d'état d'un bouton ? Réponse en cours.

Voici la technique à méditer très longuement :

unsigned char etatPresent=0,etatPasse=0;
void setup(){
     .....            // configuration des e/s
}
 
void loop(){
   etatPasse=etatPresent;                                // mémorise l'état précédent (le présent devient le passé)
   etatPresent=digitalRead(??);                          // lecture de la valeur actuelle
   if ( ( etatPresent == ?? ) && ( etatPasse == ?? ) ) {   // si appui alors ....   
         .....
   }
   delay(20); // est-ce obligatoire ???
}

TP

Suivre le document de cours à partir de la page 71 jusqu'à la page 80. Vous utiliserez au choix un montage avec plaque à essais ou le shield de l'IUT.

Pour ceux qui sont rapides, réalisez trois chenillard différents qui peuvent être appelés avec un bouton poussoir :

  • j'appuie sur le bouton poussoir pour passer au chenillard 1
  • j'appuie sur le bouton poussoir pour passer au chenillard 2
  • j'appuie sur le bouton poussoir pour passer au chenillard 3
  • un nouvel appui repasse au chenillard 1 ...

Correction partielle

unsigned char etatPresent=0,etatPasse=0;
unsigned char menu=0,tempo=0;
unsigned char leds;
void setup(){
  char i;
  for (i=6;i<=13;i++) pinMode(i,OUTPUT);    // Déclaration des 6 so
  pinMode(2,INPUT);
}

void allumeLeds(unsigned char motif){
  char i;
  for (i=0;i<8;i++) {
    digitalWrite(i+6,(motif>>i) & 1);
  }
}

void loop(){
   etatPasse=etatPresent;                                // mémorise l'état précédent (le présent devient le passé)
   etatPresent=digitalRead(2);                          // lecture de la valeur actuelle
   if ( ( etatPresent == 0 ) && ( etatPasse == 1 ) ) {   // si appui alors ....   
         menu++;
         if (menu==3) menu = 0;
   }
   tempo++;
   if (tempo == 50) {
     tempo = 0;
     switch(menu) {
       case 1 : leds = leds << 1;if (leds==0) leds = 1;break;
       case 2 : leds = leds >> 1;if (leds==0) leds = 128;break;
       }
   }
   allumeLeds(leds);
   delay(20); // est-ce obligatoire ? OUI
}

TP-3 Afficheurs sept segments

Cours

  • Opérateurs de décalage : retour sur la gestion du temps de la correction du TP précédent
  • Sous-programme
  • affichage 7 segments
    • affichage sur 1 seul digit
    • affichage sur deux digits

TD : Plus loin sur la gestion du temps

Soit le programme :

void loop() {
  tempo++; // déclaré en global comme unsigned char
  if ((tempo & 0x01)==0x01) { // détection changement sur b0
    // faire chose1 ici
  }
  if ((tempo & 0x03)==0x02) { // détection changement sur b1
    // faire chose2 ici
  }
  delay(2);
}
  • Tous les combien de temps chose1 et chose2 sont réalisés ?
  • Comment modifier ce programme pour détecter un front sur un interrupteur environ toutes les 20 ms ?

TP

Introduction

Suivre le document de cours à partir de la page 81 jusqu'à 85. La partie décodeur 4 bits vers 7 segments ne sera donc pas traitée en pratique mais vous pouvez la lire pour information.

Voici la documentation du shield que l'on utilisera : Carte d'extension Arduino. Le programme de la page 85 devra donc être adapté à notre shield.

Plus loin avec affichage 7 segments et boutons

Commencez par aller chercher le sous-programme "void affiche7sgt(unsigned char v)" dans la section un premier caractère de cette page. Elle ne fonctionne correctement que si l'on a pris les tableaux qui lui sont associés.

On vous demande maintenant de réaliser le cahier des charges suivant :

  • Un compteur automatique ou manuel
    • un bouton pour le choix (automatique ou manuel)
    • un bouton manuel qui sert à incrémenter le compteur

Une détection de changement d'état devra être utilisée pour les deux boutons.

Utilisation de plusieurs afficheurs

Vous pouvez lire la partie cours du document à partir de la page 91. Mais le travail à réaliser est trouvé ici : dans l'exercice 3.

TP-4

Retour à la liste des Tps

Éléments de correction


Nous avons exploré dans les Tps précédents les e/s binaires. Ici nous nous intéresserons aux entrées analogiques et utiliserons la fonction analogRead() permettant de lire l'état d'une des entrées analogiques.


La carte d'extension que nous utilisons possèdent plusieurs capteurs délivrant des signaux analogiques variant au maximum entre 0V et 5V.


Vous utiliserez la liaison série afin de vérifier le bon fonctionnement de votre programme


Ex 1: Découverte

Todo.jpg Recopier le programme donné en exemple et constater son fonctionnement

Question.jpg Ajouter un message indiquant le type de capteur. (Ex d'affichage souhaité : Température = 500)

Question.jpg Modifier le programme précédent pour afficher successivement la valeur des 3 capteurs


Remarque : Vous pourrez astucieusement vous servir d'un tableau comme :

char  pinListe[3]={A2,A3,A4};
char * typeMesure[3]={"PCR = ","LDR = ","CTN = "};

Ex 2: Extremums

Nous ne nous intéresserons dans cette partie qu'au capteur de luminosité. L'objectif est d'afficher la valeur mesurée en pourcentage de la valeur maximum d'éclairement mesurée. Il s'agit donc de réussir à trouver les valeurs min et max de l'éclairement.

Si la valeur actuellement mesurée est

  • la valeur min, on affichera 0%.
  • la valeur max, on affichera 100%.

Question.jpg Réaliser le programme correspondant

Ex 3: Fonction %

Une valeur en pourcentage ne dépend que des valeurs minimum, maximum et de la mesure. On peut donc écrire le prototype de la foncion comme suit :

// 0 < val,min,max < 1024 (car conversion sur 10 bits)
// par contre on retourne unsigned char car entre 0 et 100
unsigned char valPourcent(int val, int min, int max);

Question.jpg Modifier votre programme pour utiliser la fonction valPourcent(), que vous implémenterez.

Ex 4: Affichage

Plutôt que d'afficher la valeur des capteurs sur la liaison série, on souhaite faire un affichage directement sur la carte. Vous avez le choix entre l'utilisation des leds ou des afficheurs 7 segements.

Question.jpg En utilisant les Tps précédents (7segts,chenillard) réaliser cet affichage.

Ex 5: Etalonnage du photo-coupleur

La relation entre la valeur donnée par un capteur et la valeur que l'on cherche à mesurer n'est pas toujours linéaire. Faire une étude à partir du programme simple ci-dessous de la linéarité ou non de votre capteur.

Pour une surface donnée (qui restera la même) comme une main ou une feuille de papier, on vous demande de réaliser un programme qui transforme la valeur donnée par la conversion en une valeur en millimètre qui nous intéresse beaucoup plus en général.

void setup()
{
  Serial.begin(9600);
}
 
void loop()
{
  Serial.print("Photocoupleur : ");
  Serial.println(analogRead(A2),DEC);
  delay(500);  
}

Question.jpg Pour une surface donnée (qui reste la même toujours) comme une main ou une feuille de papier, on vous demande de réaliser un programme qui transforme la valeur donnée par la conversion en une valeur en millimètre. Montrer à l'enseignant votre résultat.

TP-5 : Mesure d'une résistance

Éléments de correction


MesureR.png

Principe .... le pont diviseur !?

Soit le schéma ci contre, vous allez dans cette partie écrire un programme permettant de trouver la valeur de la résistance inconnue.

Vous constatez qu'il s'agit d'un simple montage appelé communément "pont diviseur".

On notera Vm la tension de mesure, soit la tension aux bornes de la résistance (Rm) à mesurer.

Question.jpg Donner l'expression de la tension Vm en fonction de R20, Vcc et Rm. Refaire le calcul pour le cas particulier qui nous intéresse : la figure ci-dessous.

Question.jpg Donner ensuite l'expression de la valeur numérique CAN donnée par le convertisseur (tension de référence 5v) en fonction de la résiqtance Rm. Cette formule sera inversée plus loin.

Mesure de la résistance

Diviseur.png

Soit le schéma ci contre,


Les points suivants sont primordiaux :

  • ->Le signal Ve correspond à la patte arduino 5
  • ->Le connecteur "cpt" permet de brancher le composant à identifier
  • ->La tension aux bornes du composant est mesurée sur l'entrée analogique A5




Présence de la résistance ?

  • allumer led rouge si pas de résistance
  • allumer led vert sinon

Question.jpg Réalisez un programme simple permettant de se conformer au cahier des charges : détection de la présence d'une résistance.

Correspondance mesure, valeur de résistance

Comme déjà indiqué dans ce TP, la valeur numérique mesurée est un nombre entre 0 et 1023 (valeur sur 10 bits). 0 correspond à une tension de 0V et 1023 correspond à une tension de 5V.

Il vous est donc possible de modifier la formule du pont diviseur pour prendre en compte ces valeurs sur 10 bits. On notera désormais CAN la valeur retournée par le convertisseur analogique numérique.

Question.jpg Donner l'expression de la valeur de CAN (avec donc CAN entre 0 et 1023) en fonction de R1, 1023 et Rm. En déduire la valeur de l'expression de Rm en fonction des autres paramètres.

Question.jpg Réaliser un programme qui lit la valeur analogique correspondant à la résistance à mesurer, la convertit en une valeur en Ohms et envoie cette valeur sur la liaison série.

Valeur normalisée

On désire maintenant améliorer le programme précédent pour qu'il soit capable de retrouver quelques valeurs normalisées et donc le code des couleurs correspondant. Pour simplifier on va d'abord se contenter des valeurs de l'intervalle [1000,10k] (bornes comprises).

Valeurs de la série E12 associée

On vous demande de chercher (sur internet) les 12 valeurs de la série E12. Vous allez ensuite précalculer les valeurs CAN pour cette série et les ranger dans un tableau de 13 cases (puisque 10k fait partie de ce qui est demandé).

unsigned int valeursE12[]={.... a completer....} // 1023.Rm/(Rm+R)

Question.jpg Compléter le tableau ci-dessus.

Recherche de la valeur la plus proche du tableau

Étant donné que vos résistances ne sont pas forcément très précises, une lecture d'une valeur sur le convertisseur ne donnera pas forcément une valeur exacte du tableau précalculé. Il vous faut donc écrire un programme qui trouve dans le tableau la valeur la plus proche de votre lecture du convertisseur.

Question.jpg Écrire un programme dans lequel le tableau précédent est présent. Puis après lecture du convertisseur analogique numérique, vous allez parcourir le tableau à la recherche de la valeur précalculée la plus proche et une fois trouvée, noter le numéro de la case dans laquelle elle se trouve et envoyez-le par la liaison série pour un test.


Indication : Ne vous faites pas piéger, la valeur la plus proche doit être calculée avec une distance, c'est à dire une valeur absolue autrement vous aurez des problèmes. En effet, une distance négative -23 serait plus petite qu'une distance positive +2 !!!!!

unsigned int valeursE12[13]={... A COMPLETER.....}; // 1023.Rm/(Rm+R)

void setup() {
  // a compléter
}

void loop() {
  unsigned int can,distanceMINI=1024,distance;
  unsigned char i,numeroCase;
  // lecture de la tension sur la patte "analogPin"
  can = analogRead(A5);
  // recherche du numero de case dans le tableau
  for (i=0;i<13;i++) {
    distance = can - valeurs[i];
    if (distance <0) distance = -distance; // ici valeur absolue
    if (distance < distanceMINI) {
       distanceMINI = distance;
       numeroCase = i;
    } // if
  } // for
  // sortie sur la liaison série à compléter

  // attente d'une seconde
  delay(1000);

Question.jpg Il serait opportun de transformer ce que vous venez de faire en laissant une fonction vous retourner la case du tableau la plus proche de la valeur que vous fournissez.

Indication : le prototype de votre fonction devrait être :

unsigned char rechercheCase(int valeurs[],int canValue);

mais si vous êtes débutant, contentez-vous de :

unsigned char rechercheCase(int canValue);

en allant piocher dans le tableau valeursE12[] qui est en variable globale.

Affichage du code des couleurs(Version avec le shield)

Pour éviter les nombreuses conversions du code des couleurs, on vous demande de transformer le calcul en code des couleurs.

Question.jpg Vous allez maintenant compléter le programme précédent pour qu'il affiche le code des couleurs de la résistance présente à l'aide de la liaison série.

Plus loin encore pour les plus rapides

Comment étendre le travail réaliser à une gamme plus grande de résistance ? Une technique consiste à ranger les seules 12 valeurs normalisées (plutôt que les valeurs précalculées du CAN) dans le tableau. On calculera ensuite au fur et à mesure les valeurs attendues et l'on cherchera la résistance qui donne la valeur la plus proche. Les valeurs du tableau seront multipliées par 1000, puis 10000 puis 100000 pour balayer les multiplicateurs (Orange, Jaune et Vert).

Indication : La seule grande différence avec l'exercice précédent est donc que le tableau nous sert à faire les calcules au fur et à mesure avec les multiplicateurs qui augmentent. Une fois trouvé le minimum, il vous faut noter la valeur de la résistance et le coefficient multiplicateur. Les valeurs du "switch" ne seront plus les même mais le principe peut rester. Trouver la couleur multiplicative peut elle aussi se faire à l'aide d'un autre "switch".

Question.jpg Réaliser le programme correspondant et le tester.

ATTENTION ! Le calcul 1023*valeurE12 ne peut pas se faire dans une variable de type int car cela peut dépasser le 32567 maximum de ce type de variable.

TP-6 Boutons Leds mémorisation et temps

Cours

  • Autre manière pour gérer le temps sans bloquer le programme : utiliser millis

Voici une façon de faire :

// définition d'une durée de 2,4s
#define temps_ARRET 2400
void loop(){
  unsigned long time = millis();
  static char rapport_cyclique,avant;
  if ((time>1000)&&(time<1200)){
    rapport_cyclique = 100;avant=FORW;
  }    
  if ((time>1200)&&(time<2200)) {
    rapport_cyclique = 0;avant=FORW;
  }
  if ((time>2200)&&(time<2400)) {
    rapport_cyclique = 100;avant=BACK;
  }

//****** c'est ici qu'on arrete tout  
  if (time > temps_ARRET){ // au bout de 20s
    rapport_cyclique = 0;avant=1;
  }
  Motor_Control(avant,rapport_cyclique,avant,rapport_cyclique); 
}

TP-7 GRAFCETs

Ex 6: POUR ALLER PLUS LOIN : lien avec l'automatisme

Les GRAFCETs abordés en automatisme (Module M2102) peuvent être transformés en équations de récurrences puis en programmes.

Équation de récurrences

La méthode simple consiste à écrire pour chacune des étapes les conditions d'activations (notées Échec d'analyse (L’exécutable <code>texvc</code> est introuvable. Lisez math/README pour le configurer.): AC_i ) et les conditions de désactivations (notées Échec d'analyse (L’exécutable <code>texvc</code> est introuvable. Lisez math/README pour le configurer.): D_i ).

Définition

La condition d'activation Échec d'analyse (L’exécutable <code>texvc</code> est introuvable. Lisez math/README pour le configurer.): AC_i s'obtient en se posant la question : comment activer l'étape i si elle n'est pas active ?

La condition de désactivation s'obtient quant à elle en se posant la question : quelles sont les conditions nécessaires pour que le jeton quitte l'étape i s'il est dedans ?

Transformation en équations de récurrences

Avec ces conditions on va pouvoir former les équations de récurrences :

  • Pour la ou les étapes initiales : Échec d'analyse (L’exécutable <code>texvc</code> est introuvable. Lisez math/README pour le configurer.): x_i^+=AC_i + \overline{D_i} \cdot x_i + Init
  • Pour les étapes non initiales : Échec d'analyse (L’exécutable <code>texvc</code> est introuvable. Lisez math/README pour le configurer.): x_i^+=(AC_i + \overline{D_i} \cdot x_i) \cdot \overline{Init}

L'entrée "Init" sera abandonnée dans toute cette section !

L'indice i parcourant toutes les étapes, il y a autant d'équations de récurrences que d'étapes. En terme matériel, cela signifie que l'on utilisera un bit mémoire par étape. Pour nous on utilisera une variable (de type char) par étape, ce qui est un énorme gaspillage ! mais simplifie la programmation.

Bien sûr, un codage des états (abordé ICI) permet de faire des économies de ce côté là mais rappelez-vous qu'à ce point on a que des étapes et pas encore des états.

Grafcet d'exemple

Nous allons partir d'un GRAFCET assez général pour réaliser un exemple complet.

Prenez un peu de temps pour relire l'équation de AC1 et celle de D3 qui prennent en compte le parallélisme. C'est le seul type de situation qui diffère du Logique séquentielle : Description par graphe d'état.

Trouvez l'erreur dans l'équation de récurrence x4+.

Transformation en programme

Un programme gérant un GRAFCET devra comporter trois parties :

  • une partie pour gérer (et éventuellement calculer) les entrées (temporisées ou pas)
  • une partie pour gérer les équations de récurrences
  • une partie pour gérer les actions

Le tout est dans une boucle infinie

Exemple correspondant au GRAFCET ci-dessus

Les LEDs du shield IUT Troyes sont couplés à l'arduino MEGA2560. Une lecture de son schéma fait apparaître la correspondance :

Numéro f5 f4 f3 f2 f1 f0 p1 p0
Couleur r o v r o v v r
Arduino Pin 13 12 11 10 9 8 7 6
Port UNO PB5 PB4 PB3 PB2 PB1 PB0 PD7 PD6
Port MEGA2560 PB7 PB6 PB5 PB4 PH6 PH5 PH4 PH3
Etape de GRAFCET 1 2 3 4 5 - - -

Le code donné ci-dessous est pour la carte MEGA2560. Il devra être adapté par vos soins pour la carte UNO.

Voici le code correspondant :

//*********** Programme pour MEGA2560 ********************
char x1; // étape 1
void setup(){
  //******* sorties
  DDRB |= 0xF0;//f5 f4 f2 f1
  DDRH |= 0x40;// f0
  //******* entrees
  DDRE &= 0xCF; // 2 and 3
  DDRF &= 0xFC; //A0 et A1 
  //******* initialisation du GRAFCET
  x1 = 1;
}
void loop(){
  char e1,e2,e3,e4;
  static char x2,x3,x4,x5;
  char xf1,xf2,xf3,xf4,xf5; // f pour futur remplace le +
  //******* calcul des entrées
  if (PINE & 0x20) e1 = 1; else e1 = 0; // 2:Bas gauche
  if (PINE & 0x10) e2 = 1; else e2 = 0; // 3:Haut gauche
  if (PINF & 0x02) e3 = 0; else e3 = 1; // A0:bas droite
  if (PINF & 0x01) e4 = 0; else e4 = 1; // A1:Haut droite
  //******* calcul des equations de récurrence
  xf1 = x3 & x5 & e4 | x1 & !e1;
  xf2 = x1 & e1 | x2 & !e2;  
  xf3 = x2 & e2 | x3 & !(e4 & x5);
  xf4 = x1 & e1 | x4 & ! e3;
  xf5 = x4 & e3 | x5 & !(e4 & x3);
  // mise à jour du present avec le futur
  x1 = xf1;
  x2 = xf2;
  x3 = xf3;
  x4 = xf4;
  x5 = xf5;
  //******* calcul des sorties (pour MEGA2560)
  if (x1) PORTB |= (1<<PB7); else PORTB &= ~(1<<PB7);
  if (x2) PORTB |= (1<<PB6); else PORTB &= ~(1<<PB6);
  if (x3) PORTB |= (1<<PB5); else PORTB &= ~(1<<PB5);
  if (x4) PORTB |= (1<<PB4); else PORTB &= ~(1<<PB4);
  if (x5) PORTH |= (1<<PH6); else PORTH &= ~(1<<PH6);
  delay(500);
}

La partie calcul des entrées sera mieux comprise avec les informations suivantes :

Bouton Position Arduino Pin Port MEGA2560 Résistance de tirage Niveau logique si bouton appuyé
A Bas Gauche 2 PE4 Pull Up + inverseur 1
D Haut Gauche 3 PE5 Pull Up + inverseur 1
B Bas Droite A0 PF0 Pull Down + inverseur 0
C Haut Droite A1 PF1 Pull Down + inverseur 0

La documentation correspondante pour votre carte UNO est disponible ICI.

Travail à faire

Question.jpg Modifier le programme précédent pour :

  • l'adapter à votre carte UNO. Pourquoi x1 est une variable globale ?
  • que l'arrivée dans l'étape 5 fasse clignoter la LED correspondante
  • que l'arrivée dans l'étape 3 fasse clignoter la LED correspondante à une autre fréquence
  • que la transition e1 soit sur front montant (sans utiliser d'interruption)
  • que la transition e1 soit sur front montant (en utilisant une interruption)