Cours:TPS 2103 1

De troyesGEII
Révision datée du 28 mars 2015 à 11:13 par Bjacquot (discussion | contributions) (Annulation des modifications 5100 de Bjacquot (discussion))
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

Retour à la liste des Tps

Éléments de correction


Nous avons abordé la programmation des cartes arduino au premier semestre en utilisant des fonctions de haut niveau ce qui, bien que pouvant simplifier certaine tâche, présente un certain nombre de limitation.

L'objectif de ces TPs est de découvrir le fonctionnement du µcontrôleur Atmega328p et d'en explorer les possibilités.

Nous commencerons simplement de façon analogue au premier TP du module M1102 en configurant des e/s (il est sans doute utile de jeter un coup d’œil au lien précédent !).

Ex 1: mise en jambe !

On considère la led f5. La patte correspondante (PB5) sera donc une sortie (commande) pour le µcontrôleur. On configure donc le registre de direction de la façon suivante :

DDRB bit 7 6 5 4 3 2 1 0
Valeur 0 0 1 0 0 0 0 0

On rappelle la connexion des LEDs et couleur avec les PORTs correspondants pour le shield de l'IUT de Troyes :

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

Question.jpg Compléter le programme suivant pour que la led s'allume et s'éteigne pendant 500ms

Remarque : On utilisera astucieusement l'opérateur ^

void setup()
{
   DDRB = 0x?? ;         // configuration des e/s (registre de direction DDRx) sur le port D.
}

void loop()
{
   PORTB ???? ;          // on modifie en conséquence l'état de la sortie
   ....                  // il faudra utiliser la fonction delay !
}
Bluebg.png
Aidesmall.png
À propos de cette image

Version simple pour la led f0


void setup()
{
   DDRB = 0x01 ;         // configuration des e/s (registre de direction DDRx) sur le port D.
}

void loop()
{
   PORTB = 0x00 ;          // mise à zéro de la sortie
   delay(500);
   PORTB = 0x01 ;         // mise à un de la sortie
   delay(500);
}

Ex 2: sans délais ...

Plusieurs leds simultanées

Question.jpg Modifier le programme précédent pour ajouter une deuxième led (f1) s'allumant et s'éteignant au même rythme

Question.jpg Faire en sorte que les 2 leds soient allumées en alternance

Les limites ...

On souhaite avoir 2 rythmes différents pour le clignotement. L'une des leds aura une période de 1s (2x500ms), et l'autre led de 200ms (2x100ms).

On constate aisément que le rapport entre les périodes est de 5, on choisira un "delay" de boucle de 100ms et il suffira de ne changer l'état de la 2ème led que toutes les 5 itérations, suivant le principe suivant :

unsigned char i;
void setup()
{
  ...
  i=0;
}

void loop()
{
  i++;
  if (i==Nb)   // toutes les Nb itérations
  {
    ...
    i=0;
  }
  delay(???); // un seul delay dans le programme
}

Comme le commentaire l'indique le programme ne doit comporter qu'un seul delay !!! Il faut donc bien le choisir !

Question.jpg Écrire le programme répondant au cahier des charges

Question.jpg Essayer ensuite de modifier le programme pour avoir une période de 225ms pour l'une des leds et 190ms pour l'autre.

Une autre possibilité

Plutôt que d'utiliser des "delay", nous allons utiliser une montre (le temps depuis le démarrage du µcontrôleur en fait).

Le principe est donné dans le programme suivant :

unsigned long dateActuelle, dateModification;

void setup()
{
}

void loop()
{
   dateActuelle = millis();
   if ( (dateActuelle - dateModification) > dureeSouhaitee ) // la condition est vraie si le dernier changement est trop vieux
   {
       ...                                                   // on modifie alors les sorties
       dateModification=dateActuelle;                        // et on mémorise l'heure du dernier changement
   }
}

Autre explication : Si on lance la cuisson des pâtes à 20h15 et que celle ci dure 10 minutes il faut les sortir à 20h25 !

Question.jpg Sur cette base, écrire un programme répondant à la question précédente.

Ex 3: bouton et led

Nous considérons pour le moment le bouton A et la led de droite (p0).

Le tableau suivant donne les caractéristiques utiles sur les 4 boutons poussoirs d'entrée pour le shield de l'IUT :

Bouton Position Arduino Pin Port Interruption Résistance de tirage Niveau logique de l'entrée arduino si bouton appuyé
A Bas Gauche 2 PD2 int0 Pull Up + inverseur 1
D Haut Gauche 3 PD3 int1 Pull Up + inverseur 1
B Bas Droite A0 PC0 Pull Down + inverseur 0
C Haut Droite A1 PC1 Pull Down + inverseur 0


Question.jpg Complétez le programme suivant pour que la led s'allume si le bouton est appuyé

void setup()
{
   DDRD = 0x?? ;                 // configuration des e/s (registre de direction DDRx) sur le port D.
}

void loop()
{
   if ( (PIND & 0x?? ) != 0 )    // on observe l'état de l'entrée
         PORTD = 0x??;          // on modifie en conséquence l'état de la sortie
   else
         PORTD = 0x??;
}

Pour le moment nos programmes sont petits et il est donc possible de les lire complètement pour imaginer les actions qu'il réalise. Mais que se passe-t-il lorsque la taille des programmes augmente (pour atteindre 700 lignes par exemple). Il faut alors mettre en place des techniques appropriées. Ces techniques sont appelées debuggage (le verbe francisé associé est déboguer) et nous en présentons une toute simple maintenant.

Bluebg.png
Aidesmall.png
À propos de cette image

Debuggage


Il faut apprendre à debugger un programme. Le plus simple est de se servir de la liaison série :

void setup()
{
   Serial.begin(9600);
   DDRD = 0x?? ;                 // configuration des e/s (registre de direction DDRx) sur le port D.
}

void loop()
{
   unsigned char res;
   res = PIND & 0x??;
   Serial.print("res = ");
   Serial.println(res,BIN);
   if ( (res) != 0 )
   {
         Serial.println("le test est vrai.");
         PORTD = 0x??;          // on modifie en conséquence l'état de la sortie
   }
   else
   {
         Serial.println("le test est faux.");
         PORTD = 0x??;
   }
   delay(200);                  // indispensable pour ne pas avoir un affichage trop rapide !
}

Question.jpg On peut simplifier le programme précédent en utilisant l'opérateur décalage : il suffit de "recopier" l'état du bouton poussoir (PD2) sur la led (PD6) en décalant de 4 bits (PD6-PD2) vers la gauche

void setup()
{
  DDRD = 0x?? ; // configuration des e/s (registre de direction DDRx) sur le port D.
}

void loop()
{
  PORTD = (PIND&0x??)<<4;
}

Ex 4: Changement d'état

Passé et présent

On souhaite maintenant changer l'état de la led (p0) à chaque appui sur le bouton poussoir A (PD2).

On rappelle comme vu en module M1103 que l'une des méthodes repose sur la notion de présent et passé.

Question.jpg Le code suivant (à compléter) répond à ce cahier des charges

char etatPresent=0,etatPasse=0;
unsigned char etatSortie=0;
void setup()
{
   DDRD= 0x??;            // configuration des e/s
}

void loop()
{
   etatPasse=etatPresent;
   etatPresent = (PIND & ...)>>2;                        // 2 valeurs possibles pour etatPresent : 0 ou 1
   if ((etatPresent==1)&&(etatPasse==0))   PORTD^=0x??;  // modifier la sortie associée à la led p0
}

ou mieux ...

Nous allons utiliser une interruption, dont vous devez comprendre le fonctionnement !

Todo.jpg Commencer par comprendre et exécuter le code donné en exemple

Question.jpg Modifier le programme pour que la led(p0) ne change d'état qu'à l'appui sur le bouton poussoir, et non à chaque changement d'état

Bluebg.png
Aidesmall.png
À propos de cette image

EICRA


Il suffit pour cela de choisir le bon mode de déclenchement dans le registre EICRA

Et pour 2 leds ??

Question.jpg Adapter le programme précédent en ajoutant le bouton D (INT1) et une led au choix avec un fonctionnement identique (chaque bouton commande 1 led)

Autre possibilité

Bien évidemment 2 interruptions peuvent modifier la même variable/registre :

Question.jpg On souhaite que l'appui sur le bouton A allume la led p0 et l'appui sur le bouton D l'éteigne. Écrire le programme.

Ex 5: Drapeaux

Les drapeaux sont très utilisés avec les fonctions d'interruption. Le principe est de positionner une variable à une valeur particulière au sein de l'interruption et de faire un test sur cette valeur dans le programme principal, exemple :

volatile unsigned char chg = 0; // les variables partagées entre interruption et programme principale doivent être de type volatile

ISR(INT0_vect)    // programme d'interruption : le programme principal est interrompu,
{                 // l'interruption exécutée et ensuite le programme principal continu normalement son exécution
   chg = 1;       // on positionne le drapeau
}

void setup()
{
  DDRB=0x0F;      // configuration de PB0 en sortie
  cli();          // arrêt des interruptions
  EICRA=0x01;     // mode de déclenchement de l'interruption
  EIMSK=0x01;     // choix des interruptions actives
  sei();          // autorisation des interruptions
}

void loop()
{
  if (chg==1)
  {
    PORTB^=0x01;
    chg=0;
  }
}

Premier usage

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

  • Ne déclencher l'interruption que sur un appui du bouton A
  • Incrémenter le flag à chaque interruption
  • Changer l'état de la led tous les 5 appuis

Clignotement

Question.jpg Écrire le programme répondant au cahier des charges suivant :

  • Faire clignoter une led
  • Chaque appui sur A double la fréquence de clignotement
  • Chaque appui sur D divise par 2 la fréquence de clignotement

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)