Cours:TDs 2103 : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
m (Question 1)
m (Question 1)
Ligne 223 : Ligne 223 :
 
|}
 
|}
 
Ce tableau sera du type :
 
Ce tableau sera du type :
unsigned char tab[]={,...};
+
unsigned char tab[]={0xD7,...};
  
Essayer de comprendre la première valeur avant de continuer.
+
Essayer de comprendre la première valeur avant de continuer : pourquoi 0xD7 fourni à afficheLeds affiche un zéro ?

Version du 13 octobre 2014 à 10:38

Nous allons mettre dans cette série de TDs un ensemble de travaux réalisés en groupes de TD mais en salle informatique.

Éléments de correction

Initiation au timer 0

Notre objectif est de reprendre un exercice de TD et de le réaliser de manière plus complète en salle d'informatique.

Étant donné que l'exercice présenté plus loin utilise la liaison série que nous n'avons pas appris à utiliser directement en C, vous utiliserez le langage Arduino pour réaliser l'exercice correspondant. La manipulation de PORTs se fera par contre directement en C.

Rappel de la documentation du timer 0

La documentation officielle du timer 0 fait 10 pages. Nous utilisons quant à nous une série de dessins résumant le fonctionnement de certaines parties.

Voici un premier dessin :

Documentation du timer 0 de l'ATMega328

Rappel de la documentation du shield utilisé

Puisque nous allons utiliser les LEDs pour afficher une valeur binaire sur 8 bits, voici la documentation correspondante :

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

Seule la ligne correspondant à l'Arduino UNO nous intéresse dans la suite.

Exercice

Question 1

Le site : convert base vous propose un algorithme de division par 10 que voici :

unsigned int A;
unsigned int Q; /* the quotient */
        Q = ((A >> 1) + A) >> 1; /* Q = A*0.11 */
        Q = ((Q >> 4) + Q)     ; /* Q = A*0.110011 */
        Q = ((Q >> 8) + Q) >> 3; /* Q = A*0.00011001100110011 */
        /* either Q = A/10 or Q+1 = A/10 for all A < 534,890 */

Sans chercher à comprendre l'algorithme de division, on vous demande de le transformer en une fonction de prototype :

unsigned int div10(unsigned int A);

Indication

unsigned int div10(unsigned int A){ 
// a compléter ICI : ceci a été réalisé en TD en salle !!!
}

Ne pas chercher à réaliser un test pour le moment mais réaliser une compilation pour retirer les fautes de syntaxe.

Question 2

Les LEDs du shield maison sont couplées à un arduino UNO. Écrire un sous-programme capable d'afficher un nombre sur 8 bits sur les LEDs. Réaliser un programme de test à l'aide d'un compteur par exemple et/ou mieux qui utilise la division par 10 et sor sur les LEDs.

Indication

Comme l'indique le commentaire ci-dessous, le sous-programme que vous réaliserez ne devra en aucun cas changer d'autres bits que ceux que l'on utilise pour l'affichage des LEDs !!!

//************************************
// Ne modifie que les bits concernés
// pour les deux PORTs concernés
//************************************
void afficheLeds(unsigned char ch){
  // A compléter ici. On n'utilisera que des décalages 
  //et des masques pour réaliser cette fonction
}

Question 3

Écrire un programme complet qui mesure le temps d'exécution du sous programme de division par 10 avec le timer 0, puis modifier le programme pour qu'il puisse comparer avec une division par 10 normale. On pourra utiliser un front montant sur le bouton A pour choisir le type de division réalisé.

Indications

La mise au point peut être longue pour choisir correctement le nombre de boucles de calculs nécessaires pour avoir une affichage correct sur 8 bits... ainsi que la valeur du pré-scaler.

Nous trouvons sur la page de documentation le tableau suivant :

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

Un programme gérant la détection de front ressemblera à :

char etatPresent=0,etatPasse=0;
unsigned char etatSortie=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 ....
   {
         .....
   }
}

Question 4

Modifier le programme de la question 3 pour qu'au lieu d'afficher sur des LEDs, l'affichage se fasse par la liaison série. On prendra soin d'afficher en même temps le type d'algorithme utilisé en même temps que la durée. On pourra améliorer l'affichage en affichant la durée en ms. Ceci sera fait dans un premier temps en utilisant directement le timer 0, puis dans un deuxième temps en utilisant la primitive Millis.

Indication

  • Vous ne pouvez pas utiliser la primitive delay pour ne pas saturer la liaison série car toute manipulation du timer 0 l'emêchera de fonctionner normalement.
  • Serial est la documentation sur la liaison série
  • Millis donne les millisecondes écoulées depuis le début du programme

La primitive Millis utilise le timer 0 de manière transparente. Mais vous ne pourrez pas l'utiliser si vous utilisez déjà le timer 0.

Question 5 : le mode de scrutation du flag

Nous devons savoir à ce niveau, que tout débordement du timer0 (passage de 0xFF à 0x00) entraîne le positionnement du flag TOV0, bit b0 du registre TIFR. Vous pouvez donc utiliser ce flag pour déterminer si vous avez eu débordement du timer0, ou, en d’autres termes, si le temps programmé est écoulé. Cette méthode à l’inconvénient de vous faire perdre du temps inutilement dans une boucle d'attente.

Petit exemple :

  while ((TIFR & 0x01) == 0); //attente passive

Réaliser un chenillard sur les 4 LEDs en utilisant le sous programme de la question 2 et le timer 0 (à régler correctement pour un chenillard visible).

Indications

On donne le programme suivant concernant le timer 0 :

 int main(void){
 // initialisation du timer   division par 8 
         TCCR0 = 0x02; // prescaler 8 , entrée sur quartz 
         TCNT0 = 0x00; //   tmr0  : début du comptage dans 2 cycles 
 // bit RB0 du PORTB en sortie
         DDRB |= 0x01; //RB0 as output
         while(1) {
                 TIFR |= 0x01; // clr TOV0 with 1 : obligatoire !!!
                 while ((TIFR & (0x01<<TOV0)) == 0);
                 // ce qui est fait ici est fait tous les 256 comptages de TCNT0
                 PORTB ^= 0x01; // on bascule avec ou exclusif
                 // TIFR &= ~(1 << TOV0); // reset the overflow flag 
         }
         return 0;
 }

Pouvez-vous donner la fréquence d'oscillation du bit b0 du PORTB avec quelques explications ? Modifiez-le pour le transformer avec un setup() et un loop() et une fréquence visible à l’œil si votre quartz est à 16 MHz et que votre oeil ne peut distinguer que les fréquences inférieures à 25 Hz (2 à 5 Hz serait très bien pour ce chenillard).

Timer 0 en interruption

Rappel sur la documentation de l'interruption du timer 0

La mise à zéro du bit TOV0 semble être complètement automatique dans les ATMega dès qu'il y a une interruption.

Documentation de l'interruption du timer 0 de l'ATMega8

Pour comprendre cette figure, il suffit de se rappeler qu'un front montant dans l'ellipse rouge réalisera cette-interruption. En C cette interruption est désignée par "TIMER0_OVF_vect". Si vous avez compris que ce n'est pas le logiciel qui positionnera le bit TOV0 mais le matériel, alors vous déduisez que pour réaliser une interruption il suffit de

  • mettre à 1 le bit TOIE0 du registre TIMSK pour l'ATMega8. Pour l'ATMega328 ce registre s'appelle TIMSK0.
  • mettre à 1 le bit I du registre SREG. Ceci se réalise par l'instruction "sei();" en C et "interrupts();" avec l'Arduino.

Réalisation d'un afficheur sept segments sur deux digits

Nous allons réaliser à l'aide d'une interruption la commutation des deux digits d'un affichage sept segments.

Utilisation du travail déjà fait

Pour ne pas réinventer la roue, nous allons utiliser le sous programme que l'on a mis au point pour les LEDs :

//************************************
// Ne modifie que les bits concernés
// pour les deux PORTs concernés
// Version pour UNO
//************************************
void afficheLeds(unsigned char ch){
  unsigned char ch_partie;
  ch_partie = (ch << 6) & 0xC0;
  PORTD &= ~0xC0; // effacement de PD7 et PD6
  PORTD |= ch_partie; // seuls les 1 seront écrits
  ch_partie = (ch >> 2) & 0x3F;
  PORTB &= ~0x3F; // effacement PB5... PB0
  PORTB |= ch_partie; // seuls les 1 seront écrits
}

Nous allons garder ce sous-programme.

Connexion du shield utilisé aux sept segments

Les 2 afficheurs ne peuvent pas être utilisés simultanément. L'état de la sortie mux (arduino port 4 ou PD4) permet de sélectionner l'un ou l'autre. En allumant successivement l'un puis l'autre rapidement, on a l'illusion qu'ils sont tous 2 allumés. Les segments des afficheurs sont câblés de façon analogue comme décrit ci dessous :

Segment pt g f e d c b a
Arduino Pin 11 9 10 8 7 6 12 13
Port UNO PB3 PB1 PB2 PB0 PD7 PD6 PB4 PB5

Voici sous forme schématique la documentation correspondante :

Documentation du Shield avec carte UNO

Exercice

Question 1

Réaliser un tableau permettant un transcodage suivant le principe :

index 0 affichage du 0
index 1 affichage du 1
index n affichage du n
index 15 affichage du F

Ce tableau sera du type : unsigned char tab[]={0xD7,...};

Essayer de comprendre la première valeur avant de continuer : pourquoi 0xD7 fourni à afficheLeds affiche un zéro ?