Cours:XR207 tp digicode : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
m ({{Bleu|Codez !!}})
 
(67 révisions intermédiaires par 2 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
[[Cours:TPs_2103|{{Rouge|<big>'''Retour à la liste des Tps'''</big>}}]]
+
<big>{{Rouge|******************************************}}</big>
 +
<big>{{Rouge|*** Apporter la malette d'informatique ***}}</big>
 +
<big>{{Rouge|******************************************}}</big>
 +
 
 +
[[Cours:CoursM2103|{{Rouge|<big>'''Fiche résumé'''</big>}}]]
 +
 
 +
[[Cours:TPs_2103|{{Bleu|<big>'''Retour à la liste des Tps'''</big>}}]]
  
 
[[Cours:TPS_2103_tp3_corrige|{{Vert|<big>'''Éléments de correction'''</big>}}]]
 
[[Cours:TPS_2103_tp3_corrige|{{Vert|<big>'''Éléments de correction'''</big>}}]]
  
[[Fichier:ArduinoPinout.png|600px|droite]]
+
[[Fichier:Arduino-nano-pinout.png|600px|droite]]
  
 
Vous allez développer un système de digicode architecturé autour d'un atmega328p (le µcontrôleur présent sur les cartes arduino).
 
Vous allez développer un système de digicode architecturé autour d'un atmega328p (le µcontrôleur présent sur les cartes arduino).
Ligne 9 : Ligne 15 :
 
<big>'''Pour ce TP, nous utilisons une carte arduino {{Rouge|sans le shieldinfo}}'''</big>
 
<big>'''Pour ce TP, nous utilisons une carte arduino {{Rouge|sans le shieldinfo}}'''</big>
  
<big>'''Nous utiliserons les fonctions arduino dans ce TP, en essayant de les éviter au maximum afin d'écrire un code le plus réutilisable possible.</big>
 
  
 +
=objectifs=
 +
*numération binaire (affichage avec des leds de la valeur d'un entier signé ou non )
 +
*e/s + pullup
 +
*utilisation de fonctions
 +
*déclaration de fonctions
 +
*algorithme simple et implémentation
 +
 +
=Matériel=
  
={{Rouge|Informations lumineuses}}=
+
Nous utiliserons :
 +
*1 carte arduino NANO
 +
*1 plaque à essais
 +
*8 leds pour afficher une valeur
 +
*1 résistance de 330Ω par led
 +
*1 clavier matriciel
 +
*fils M-M
 +
*fils M-F
  
Le digicode sera équipé de 2 voyants lumineux (rouge et vert).
+
=Informations lumineuses=
  
On utilisera des résistances de 330Ω, et vous êtes libre de choisir un montage à anodes ou cathodes communes.
+
==Câblage==
  
{{Todo|Câbler sur une plaque à essais les leds sur les pin PC0 et PC1}}
+
{{Todo|Câbler sur une plaque à essais les 8 leds :}}
 +
*utiliser des résistances de 330Ω pour régler l'intensité lumineuse
 +
*PD0 à PD7
 +
*mettre les leds dans "l'ordre" des broches
  
{{Question|Écrire une fonction pour éteindre ou allumer une led dont le prototype sera le suivant :}}
+
==Utilisation des 8 leds==
  
<source lang=c>
+
Nous utiliserons dans nos programmes [[Cours:CoursM2103#Fixed_width_integer_types|les types de variable dont la taille est explicitement donnée]]
void setEtatLed(unsigned char numLed,unsigned char val);
 
</source>
 
  
{{Aide|Astuce}}
+
Le {{Rouge|débogage}} d'un programme est fondamental, il existe différentes façons de le faire. Ici nous nous contenterons {{Rouge|d'utiliser des leds}} pour afficher des informations sur l'exécution du programme.
  
 +
Principe :
  
Vous pourrez vous inspirer du programme suivant permettant de faire clignoter la led "13" de la carte Arduino Uno.
 
 
<source lang=c>
 
<source lang=c>
#define led PB5
+
#include <avr/io.h>
 +
#include <util/delay.h>
  
void setup()
+
void clignote()
 
{
 
{
  DDRB |= 1 << led;
+
    PORTD=0;
 
+
    for (uint8_t i=0;i<5;i++)
 +
    {
 +
        PORTD ^= 0xFF;
 +
        _delay_ms(50);
 +
    }
 
}
 
}
  
void loop()
+
int main()
 
{
 
{
  PORTB |= 1 << led;
+
    // config des sorties
  delay(100);
+
    DDRD = 0xFF; // les 8  broches des leds en sortie
  PORTB &= ~(1 << led);
+
 
  delay(100);
+
    // qques variables
 +
    uint8_t val1 = 0;
 +
    int8_t val2 = -1;
 +
    uint8_t val3 = 255;
 +
    uint16_t val4 = 256;
 +
 
 +
    while(1)
 +
    {
 +
        PORTD = val1;
 +
        _delay_ms(2000);
 +
        clignote();
 +
        PORTD = val2;
 +
        _delay_ms(2000);
 +
        clignote();
 +
        PORTD = val3;
 +
        _delay_ms(2000);
 +
        clignote();
 +
        PORTD = val4;
 +
        _delay_ms(2000);
 +
        for (int i=0;i<10;i++) clignote();
 +
    }
 
}
 
}
 
</source>
 
</source>
{{finAide}}
 
  
 +
{{Question|Indiquer l'affichage réalisé par chacune des lignes}} PORTD=valx
 +
 +
Les nombres négatifs sont représentés pas la méthodes des compléments à deux : https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_deux ]
  
={{Rouge|Decodage d'un clavier}}=
+
=Decodage d'un clavier=
  
=={{Bleu|Câblage}}==
+
==Câblage==
  
Le tableau suivant résume la disposition physique du clavier avec la position physique sur le connecteur des lignes (L1 à L4) et colonnes (C1 à C3) en vue de dessus :
+
Le tableau suivant résume la disposition physique du clavier avec la position physique sur le connecteur des lignes (L0 à L3) et colonnes (C0 à C2) en vue de dessus :
 
{|align="center" border="1" cellpadding="20" cellspacing="0"
 
{|align="center" border="1" cellpadding="20" cellspacing="0"
 
|-
 
|-
Ligne 63 : Ligne 111 :
 
{|align="center" border="1" cellpadding="3" cellspacing="0"
 
{|align="center" border="1" cellpadding="3" cellspacing="0"
 
|-
 
|-
| B0 || B1 || B2 || B3 || D4 || D5 || D6
+
| PC2 || PC3 || PC4 || PC5 || PB0 || PB1 || PB2
 
|-
 
|-
| L1 || L2 || L3 || L4 || C1 || C2 || C3
+
| L0 || L1 || L2 || L3 || C0 || C1 || C2
 
|}
 
|}
 
|-
 
|-
Ligne 74 : Ligne 122 :
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! L/C !! C1 !! C2 !! C3
+
! L/C !! C0 !! C1 !! C2
 
|-
 
|-
! L1 || 1 || 2 || 3
+
! L0 || 1 || 2 || 3
 
|-
 
|-
! L2 || 4 || 5 || 6
+
! L1 || 4 || 5 || 6
 
|-
 
|-
! L3 || 7 || 8 || 9
+
! L2 || 7 || 8 || 9
 
|-
 
|-
! L4 || * || 0 || #
+
! L3 || * || 0 || #
 
|}
 
|}
 +
||
 +
[[Fichier:ClavierMatricielPrincipe.png|200px]]
 
|}
 
|}
  
{{Todo|Câbler sur une plaque à essais le clavier 12 touches que l'on vous donne. Les {{Rouge|colonnes}} seront reliées sur le port {{Rouge|D}} et les {{Vert|lignes}} sur le port {{Vert|B}} (cf indications ci dessus)}}
+
{{Todo|Câbler sur une plaque à essais le clavier 12 touches que l'on vous donne. Les {{Rouge|colonnes}} seront reliées sur le port {{Rouge|B}} et les {{Vert|lignes}} sur le port {{Vert|C}} (cf indications ci dessus)}}
  
 
'''Remarque :'''  On ne câblera aucune résistance de tirage, on utilisera les résistances internes au µcontrôleur.
 
'''Remarque :'''  On ne câblera aucune résistance de tirage, on utilisera les résistances internes au µcontrôleur.
  
=={{Bleu|Indice de ligne}}==
+
{{Aide|pullUp}}
 +
 
 +
 
 +
2 étapes sont nécessaires pour activer une résistance de pullUp sur une broche :
 +
*Configurer la broche en entrée
 +
**pour une seule broche : DDRB &=~ (1<<PB1);
 +
**pour un groupe        : DDRB &=~ ((1<<PB1)|(1<<PB2)|(1<<PB3) ...);
 +
*Activer ensuite la/les résistance(s) de pullUp
 +
**pour une seule broche : PORTB |= (1<<PB1);
 +
**pour un groupe        : PORTB |= (1<<PB1)|(1<<PB2)|(1<<PB3) ...;
 +
{{finAide}}
 +
 
 +
=={{Bleu|Principe de décodage}}==
 +
 
 +
En utilisant un clavier matriciel, chaque touche n'est pas associé à une broche du µcontrôleur. Dans le cas présent, on passe de :
 +
*12 entrées nécessaires si chaque touche était connectée directement
 +
*7 broches nécessaires avec le clavier matriciel
  
{{Question|Ecrire et tester le programme lecture_ligne() qui renvoie le numéro de ligne sur laquelle un bouton est appuyé}}
+
L'intérêt évident est donc une {{Rouge|réduction du nombre de broches}} nécessaires à l'utilisation du clavier.
*Servez-vous bien évidemment du travail en TD !
 
*Rappelons qu'il faut écrire un {{Rouge|'1'}} sur une {{Rouge|entrée}}, pour activer la résistance de {{Rouge|pull-up}} associée
 
*La validation se fera en utilisant la {{Rouge|liaison série}}, à l'aide [http://arduino.cc/en/Reference/Serial des fonctions arduino].
 
  
<source lang=c>
+
L'inconvénient qui en résulte est la {{Rouge|complexité relative du code}} pour trouver quelle touche est appuyée.
char lecture_ligne()
+
 
 +
Le principe de décodage est {{Rouge|symétrique}} entre lignes et colonnes. Détaillons donc le fonctionnement pour trouver sur quelle ligne une touche est appuyée :
 +
 
 +
*On configure les broches {{Rouge|colonnes en sortie}}.
 +
*On configure ces sorties à {{Rouge|l'état 0}}
 +
*Les broches {{Rouge|lignes sont en entrée}}
 +
*l'état des lignes dépend de l'appui sur un bouton :
 +
**si {{Rouge|aucun bouton}} appuyé, les résistances de tirage donnent {{Rouge|un état 1}} sur les lignes
 +
**si on appui sur un bouton de {{Rouge|la ligne0}} (key 1/2/3), alors l'entrée ligne PC2 passe à {{Rouge|l'état 0}}.
 +
**la valeur change suivant la ligne sur laquelle un bouton est appuyé
 +
*il suffit donc d'observer les 4 bits PC5..PC2 (que l'on note ici etat), et ainsi :
 +
**si PC5..PC2 = 1111 , pas de touche
 +
**si PC5..PC2 = 1110 , au moins une touche parmi 1/2/3
 +
**si PC5..PC2 = 1101 , au moins une touche parmi 4/5/6
 +
**...
 +
 
 +
==Indice de ligne==
 +
 
 +
{{Question|Compléter et vérifier le programme lecture_ligne() qui renvoie le numéro de ligne sur laquelle un bouton est appuyé}}
 +
*La validation se fera en utilisant des leds connectées sur le port C :
 +
**On utilisera les 8 leds du PORTD pour afficher le résultat
 +
**par ex : PORTD = lecture_ligne();
 +
 
 +
<source lang=cpp>
 +
int8_t lecture_ligne()
 
{
 
{
   char ch;
+
   int8_t etatEntrees;
   DDRD ?= .... ;    // commençons par lister les sorties sur le port D
+
   DDRB  |= ( (1<<PB2) | ( (1<<PB1) | ( (1<<PB0) ) ;    // commençons par lister les sorties sur le port B
   DDRB ?= .... ;    // puis les entrées sur le port B
+
   DDRC  &=~ ( (1<<PC5) | (1<<PC4) |(1<<PC3) |(1<<PC2) )  ;    // puis les entrées sur le port C
   PORTD?=..... ;    // on place les sortie à l'état 0
+
   PORTB &=~ ( (1<<PB2) | ( (1<<PB1) | ( (1<<PB0) ) ;    // on place les sortie à l'état 0
   PORTB?= .... ;    // on active les résistances de pull-up sur les entrées
+
   PORTC |= ( (1<<PC5) | (1<<PC4) |(1<<PC3) |(1<<PC2) ) ;    // on active les résistances de pull-up sur les entrées
  
   _delay_ms(1);        // un délais est nécessaire pour l'activation des pull-ups
+
   _delay_ms(1);        // un délai est nécessaire pour l'activation des pull-ups
  
   ch = PINB & 0x??; // on récupère ensuite l'état des entrées
+
   etatEntrees = PINC & 0b00111100; // on récupère ensuite l'état des entrées en cachant les bits non utiles
   switch (ch)
+
   switch (etatEntrees)
 
   {
 
   {
     case : return 0; // aucune touche
+
        // PPPPPPPP
     case : return 1; // L1
+
        // CCCCCCCC
 +
        // ..5432..
 +
     case 0b00111000 : return 0; // L0 (appui sur l'une des touches 1/2/3 => {{Rouge|entrée PC2 à 0}} )
 +
     case 0b00110100 : return 1; // L1 => {{Rouge|entrée PC3 à 0}}
 
     case  : return 2; // L2
 
     case  : return 2; // L2
 
     case  : return 3; // L3
 
     case  : return 3; // L3
    case  : return 4; // L4
+
     // si autre cas, pas de touches deux touches ou autre
     // si autre cas, deux touches ou autre
 
 
     default : return -1;
 
     default : return -1;
 
   }
 
   }
Ligne 128 : Ligne 217 :
  
  
=={{Bleu|Touche appuyée}}==
+
==Touche appuyée==
 
Nous souhaitons maintenant écrire une fonction qui renverra une valeur tel que décrit ci dessous :
 
Nous souhaitons maintenant écrire une fonction qui renverra une valeur tel que décrit ci dessous :
  
Ligne 134 : Ligne 223 :
 
|-
 
|-
 
| <source lang=c>
 
| <source lang=c>
char touches[LIGNES][COLONNES] = { // tableau de char à 2 dimensions
+
const int8_t touches[4][3] = { // tableau de int8_t à 2 dimensions
  {1,,},
+
                              { 1, 2, 3},
  {...},
+
                              { 4, 5, 6},
  {...},
+
                              { 7, 8, 9},
  {,,11}
+
                              {10, 0,11}
};
+
                            };
  
char getTouche();
+
int8_t getTouche();
 
</source>
 
</source>
 
|| La valeur retournée sera :
 
|| La valeur retournée sera :
Ligne 150 : Ligne 239 :
 
|}
 
|}
  
{{Question|Écrire cette fonction en vous servant bien évidemment des 2 fonctions précédentes et pourquoi pas du tableau "touches" complété.}}
+
{{Question|Écrire cette fonction en vous servant bien évidemment des 2 fonctions précédentes et pourquoi pas du tableau "touches" :}}
 +
 
 +
*c'est un tableau de 4 lignes et 3 colonnes
 +
*la valeur dans la case [0,0] correspond à la valeur de la touche pour ligne=0 et colonne=0  => 1
 +
*la valeur dans la case [1,2] correspond à la valeur de la touche pour ligne=1 et colonne=2  => 6
  
 
<source lang=c>
 
<source lang=c>
char getTouche()
+
int8_t getTouche()
 
{
 
{
 
   ....
 
   ....
Ligne 159 : Ligne 252 :
 
</source>
 
</source>
  
={{Rouge|Assemblons le tout}}=
+
=réalisation d'un digicode=
  
Nous allons utiliser les 2 fonctions {{Rouge|getTouche()}} et {{Rouge|setEtatLed()}} pour réaliser un programme de digicode.
+
Nous allons utiliser la fonction {{Rouge|getTouche()}} pour réaliser un programme de digicode.
 
 
=={{Bleu|Cahier des charges}}==
 
  
 +
==contraintes==
 
Commençons pour définir les contraintes :
 
Commençons pour définir les contraintes :
  
*On appuie sur la touche ON (touche '*') => la led rouge s'allume
+
*On appuie sur la touche '*' pour démarrer la saisie du code => la led rouge s'allume brièvement
 
*L'utilisateur doit alors saisir le code sur 4 chiffres
 
*L'utilisateur doit alors saisir le code sur 4 chiffres
*On valide le code par la touche OK (touche '#')
+
*On valide le code par la touche '#'
*Si le code est bon la led verte s'allume pendant 10s, sinon la led rouge clignote pendant 10s
+
*Si le code est bon une led verte s'allume pendant 10s, sinon une led rouge clignote pendant 10s
 
 
=={{Bleu|Codez !!}}==
 
 
 
Nous commençons par décrire le programme grâce à [http://fr.wikipedia.org/wiki/Organigramme_de_programmation l'algorigramme suivant :]
 
 
 
*initialiser i
 
*éteindre les leds
 
*si la touche apuyé est '*'
 
**{{Rouge|répéter}}
 
***attendre plus de touches appuyées ( à l'aide d'un while(getTouche....); )
 
***{{Bleu|répéter}}
 
****lire clavier => touche    ( touche = getTouche(); !!! )
 
****{{Bleu|tant que touche<0}}
 
***si touche '*'
 
****réinitialiser i
 
***sinon
 
****sauvegarder touche dans un tableau
 
****incrémenter i
 
***{{Rouge|tant que i<4}}
 
**attendre plus de touches appuyés
 
**attendre appui sur touche
 
**si touche '#'
 
***vérifier le code
 
***si code valide
 
****allumer led verte pendant 10s
 
***sinon
 
****faire clignoter led rouge pendant 10s
 
 
 
 
 
Remarque : Utiliser un tableau pour mémoriser le code, ex :
 
 
 
<source lang=c> const unsigned char code[4]={1,2,3,4};</source>
 
 
 
{{Question|Écrire le programme correspondant}}
 
 
 
=={{Bleu|Améliorations}}==
 
 
 
{{Todo|Il serait intéressant de proposer une procédure de changement du code.}}
 
 
 
 
 
{{Todo|Il est également intéressant de régler une durée maximum (4s) de saisie du code, en utilisant un timer.}}
 
 
 
={{Rouge|Buzzer}}=
 
 
 
=={{Bleu|Objectif}}==
 
Nous souhaitons utiliser le buzzer afin d'avoir une information sonore sur la touche du clavier appuyée.
 
 
 
Pour ce faire, chaque touche sera associée à une note de musique tel qu'indiqué dans le tableau suivant :
 
 
 
{| class="wikitable"
 
|-
 
! Touche || 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9 || * || #
 
|-
 
! Note || Do || Do# || Ré || Ré# || Mi || Fa || Fa# || Sol || Sol# || La || La# || Si
 
|-
 
! Fréquence (Hz) || 261,63 || 277,18 || 293,66 || 311,13 || 329,63 || 349,23 || 369,99 || 392 || 415,3 || 440 || 466,16 || 493,88
 
|-
 
! Période (µs)  || 3822  || 3608  || 3405  || 3214  || 3037  || 2863  || 2703  || 2551|| 2408  || 2273|| 2145  || 2025
 
|}
 
 
 
 
 
=={{Bleu|Programmons}}==
 
 
 
{{Question|Vous utiliserez le programme réalisé au tp précédent, et notamment la fonction {{Rouge|playnote(note,duree)}} pour jouer la note associée à chaque appui d'une touche}}
 
 
 
 
 
 
 
 
 
 
 
={{Rouge|Complément au [[Cours:TPS_2103_tp2|TP précédent]] : Mesure de temps d'exécution}}=
 
 
 
Nous allons dans cette partie mesurer le temps d'exécution de certaines fonctions Arduino, et se rendre compte de l'une de leur principale limite.
 
 
 
Le temps écoulé sera mesuré à l'aide du {{Rouge|Timer 2}}.
 
 
 
Le principe est le suivant :
 
*Initialiser le timer
 
*Répéter plusieurs fois l'instruction souhaitée
 
*Observer la valeur du timer
 
*Afficher cette valeur
 
 
 
=={{Bleu|Le timer 2 et ses registres (sans interruption)}}==
 
[[Fichier:AVR_Timer2.png|thumb|500px|Documentation simple du Timer 2 (8 bits)]]
 
Voici ci-contre, avec les conventions schématiques habituelles, le schéma de fonctionnement du timer 2.
 
 
 
On distingue ainsi le bit b0 du registre '''TIFR2''' appelé '''TOV2''' qui permet de détecter un débordement du timer. Pour plus de précisions, référez-vous au td sur le sujet !
 
  
Les habituels bits de configuration de la division se trouvent dans le registre '''TCCR2B''' et fonctionnent exactement comme pour les autres timers.
 
  
Le registre '''ASSR''' sert à choisir la source de l'horloge du timer 2. Pour nous, sauf mention contraire, ce sera toujours le quartz. Ce registre doit être configuré dans ce mode de fonctionnement par défaut.
+
==codez !==
  
=={{Bleu|Configuration des entrées/sorties}}==
+
{{Question|Ecrire un programme répondant au cahier des charges}}
  
Nous souhaitons ici évaluer le temps mis par la fonction [http://arduino.cc/en/Reference/pinMode {{Rouge|pinMode()}}].
+
*vous pouvez vous servir d'un LLM
 +
**https://claude.ai/new
 +
**[https://gemini.google.com/app?utm_source=deepmind.google&utm_medium=referral&utm_campaign=gdm&utm_content= gemini]
 +
*il suffit de mémoriser les 6 dernières touches appuyées
 +
**déclarer un tableau
 +
**faire un décalage du tableau
 +
**rentrer la nouvelle valeur
 +
**comparer le tableau par rapport au code
  
L'affichage du résultat sera tout simplement transmis sur la liaison série en utilisant les fonctions suivantes :
+
==Améliorations==
*Serial.begin(debit)
 
*Serial.print("valeur = ")
 
*Serial.println(valeur,DEC)
 
  
{{Question|Compléter le programme suivant en choisissant les bonnes valeurs pour évaluer le temps d'exécution de pinMode()}}
+
{{Question|Ajouter une procédure de changement du code.}}
<source lang=c>
 
void setup()
 
{
 
  // Variables éventuelles
 
  
  // Initialisation du port série
 
  Serial.begin(9600);
 
 
  // Configuration du timer 2 : Attention, chaque bit doit être configuré à '0' ou '1'
 
  TCCR2A ??? (1<<WGM20); // mettre à 0
 
  TCCR2A ??? (1<<WGM21); // mettre à 0
 
 
  TCCR2B ??? (1<<WGM22); // mettre à 0
 
// choix du pré-diviseur :
 
  TCCR2B ??? (1<<CS22);
 
  TCCR2B ??? (1<<CS21);
 
  TCCR2B ??? (1<<CS20);
 
 
  // Initialisation du Timer : acquittement et valeur de départ
 
  TIFR2|=1<<TOV2;
 
 
}
 
 
void loop() {
 
  TCNT2=0; // timer2 mis à zéro
 
 
//********** debut du calcul dont on veut mesurer la durée
 
  for (i=0;i<100;i++) // Le nombre d'itérations peut/doit être adapté !
 
  {
 
    // Fonction à évaluer : il est intéressant de répéter la fonction plusieurs fois
 
    pinMode(6,OUTPUT);
 
  }
 
//********* fin du calcul dont on veut mesurer la durée
 
 
 
  // Récupérer la valeur du timer et l'afficher seulement si le timer n'a pas débordé !!!
 
 
  // envoyer dans liaison série
 
 
  delay(500);
 
}
 
</source>
 
 
 
'''Attention ! ''' Ne prenez pas ce que vous donne le timer pour argent comptant. Cette valeur doit avoir certaines propriétés :
 
* ne pas être accompagnée d'un débordement de '''TCNT2'''. Vous seriez donc très inspiré d'écrire la valeur du flag '''TOV2''' avec la liaison série en plus de la valeur de temps
 
* augmenter lorsqu'on augmente le nombre de boucles exécutées sur l'instruction testée (autre manière de dire les choses, on double le temps si on double le nombre de boucles).
 
 
Ce travail nécessite dons un peu de soin et beaucoup d'essais pour trouver la bonne valeur de la division dans le pré-diviseur à choisir. Il peut être utile de mettre les tests dans loop() pour prendre son temps pour ouvrir la liaison série.
 
 
 
{{Question|Comparer en déclarant une sortie directement en configurant le registre DDRx}}
 
 
=={{Bleu|Valeur de sortie}}==
 
 
Si la fonction pinMode() est d'importance, sa durée d'exécution ne l'est pas forcément puisque la plupart du temps elle n'est exécutée qu'une seule fois.
 
 
Allons donc un peu plus loin dans la manipulation des ports en modifiant l'état d'une sortie (pin 6 arduino ou PD6).
 
 
{{Question|Évaluer la durée d'exécution du code suivant :}}
 
 
<source lang=c>
 
//void setup()
 
//{
 
  pinMode(6,OUTPUT);
 
  for (char i=0;i<10;i++)
 
  {
 
    digitalWrite(6,0);
 
    digitalWrite(6,1);
 
  }
 
 
 
//}
 
 
//void loop()
 
//{
 
//}
 
</source>
 
  
{{Question|Écrire le même code en utilisant directement les registres ad-hoc et comparer les vitesses d'exécution.}}
+
{{Question|Faire en sorte que le temps maximum de saisie du code soit de 4s}}

Version actuelle datée du 4 février 2026 à 14:53

******************************************
*** Apporter la malette d'informatique ***
******************************************

Fiche résumé

Retour à la liste des Tps

Éléments de correction

Arduino-nano-pinout.png

Vous allez développer un système de digicode architecturé autour d'un atmega328p (le µcontrôleur présent sur les cartes arduino).

Pour ce TP, nous utilisons une carte arduino sans le shieldinfo


objectifs

  • numération binaire (affichage avec des leds de la valeur d'un entier signé ou non )
  • e/s + pullup
  • utilisation de fonctions
  • déclaration de fonctions
  • algorithme simple et implémentation

Matériel

Nous utiliserons :

  • 1 carte arduino NANO
  • 1 plaque à essais
  • 8 leds pour afficher une valeur
  • 1 résistance de 330Ω par led
  • 1 clavier matriciel
  • fils M-M
  • fils M-F

Informations lumineuses

Câblage

Todo.jpg Câbler sur une plaque à essais les 8 leds :

  • utiliser des résistances de 330Ω pour régler l'intensité lumineuse
  • PD0 à PD7
  • mettre les leds dans "l'ordre" des broches

Utilisation des 8 leds

Nous utiliserons dans nos programmes les types de variable dont la taille est explicitement donnée

Le débogage d'un programme est fondamental, il existe différentes façons de le faire. Ici nous nous contenterons d'utiliser des leds pour afficher des informations sur l'exécution du programme.

Principe :
#include <avr/io.h>
#include <util/delay.h>

void clignote()
{
    PORTD=0;
    for (uint8_t i=0;i<5;i++)
    {
         PORTD ^= 0xFF;
         _delay_ms(50);
    }
}

int main()
{
    // config des sorties
    DDRD = 0xFF; // les 8  broches des leds en sortie

    // qques variables
    uint8_t val1 = 0;
    int8_t val2 = -1;
    uint8_t val3 = 255;
    uint16_t val4 = 256;

    while(1)
    {
        PORTD = val1;
        _delay_ms(2000);
        clignote();
        PORTD = val2;
        _delay_ms(2000);
        clignote();
        PORTD = val3;
        _delay_ms(2000);
        clignote();
        PORTD = val4;
        _delay_ms(2000);
        for (int i=0;i<10;i++) clignote();
    }
}

Question.jpg Indiquer l'affichage réalisé par chacune des lignes PORTD=valx

Les nombres négatifs sont représentés pas la méthodes des compléments à deux : https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_deux ]

Decodage d'un clavier

Câblage

Le tableau suivant résume la disposition physique du clavier avec la position physique sur le connecteur des lignes (L0 à L3) et colonnes (C0 à C2) en vue de dessus :

PC2 PC3 PC4 PC5 PB0 PB1 PB2
L0 L1 L2 L3 C0 C1 C2

ArduinoClavier.png

L/C C0 C1 C2
L0 1 2 3
L1 4 5 6
L2 7 8 9
L3 * 0 #

ClavierMatricielPrincipe.png

Todo.jpg Câbler sur une plaque à essais le clavier 12 touches que l'on vous donne. Les colonnes seront reliées sur le port B et les lignes sur le port C (cf indications ci dessus)

Remarque : On ne câblera aucune résistance de tirage, on utilisera les résistances internes au µcontrôleur.

Bluebg.png
Aidesmall.png
À propos de cette image

pullUp


2 étapes sont nécessaires pour activer une résistance de pullUp sur une broche :

  • Configurer la broche en entrée
    • pour une seule broche : DDRB &=~ (1<<PB1);
    • pour un groupe  : DDRB &=~ ((1<<PB1)|(1<<PB2)|(1<<PB3) ...);
  • Activer ensuite la/les résistance(s) de pullUp
    • pour une seule broche : PORTB |= (1<<PB1);
    • pour un groupe  : PORTB |= (1<<PB1)|(1<<PB2)|(1<<PB3) ...;

Principe de décodage

En utilisant un clavier matriciel, chaque touche n'est pas associé à une broche du µcontrôleur. Dans le cas présent, on passe de :

  • 12 entrées nécessaires si chaque touche était connectée directement
  • 7 broches nécessaires avec le clavier matriciel

L'intérêt évident est donc une réduction du nombre de broches nécessaires à l'utilisation du clavier.

L'inconvénient qui en résulte est la complexité relative du code pour trouver quelle touche est appuyée.

Le principe de décodage est symétrique entre lignes et colonnes. Détaillons donc le fonctionnement pour trouver sur quelle ligne une touche est appuyée :

  • On configure les broches colonnes en sortie.
  • On configure ces sorties à l'état 0
  • Les broches lignes sont en entrée
  • l'état des lignes dépend de l'appui sur un bouton :
    • si aucun bouton appuyé, les résistances de tirage donnent un état 1 sur les lignes
    • si on appui sur un bouton de la ligne0 (key 1/2/3), alors l'entrée ligne PC2 passe à l'état 0.
    • la valeur change suivant la ligne sur laquelle un bouton est appuyé
  • il suffit donc d'observer les 4 bits PC5..PC2 (que l'on note ici etat), et ainsi :
    • si PC5..PC2 = 1111 , pas de touche
    • si PC5..PC2 = 1110 , au moins une touche parmi 1/2/3
    • si PC5..PC2 = 1101 , au moins une touche parmi 4/5/6
    • ...

Indice de ligne

Question.jpg Compléter et vérifier le programme lecture_ligne() qui renvoie le numéro de ligne sur laquelle un bouton est appuyé

  • La validation se fera en utilisant des leds connectées sur le port C :
    • On utilisera les 8 leds du PORTD pour afficher le résultat
    • par ex : PORTD = lecture_ligne();
int8_t lecture_ligne()
{
  int8_t etatEntrees;
  DDRB  |= ( (1<<PB2) | ( (1<<PB1) | ( (1<<PB0) ) ;    // commençons par lister les sorties sur le port B
  DDRC  &=~ ( (1<<PC5) | (1<<PC4) |(1<<PC3) |(1<<PC2) )  ;    // puis les entrées sur le port C
  PORTB &=~ ( (1<<PB2) | ( (1<<PB1) | ( (1<<PB0) ) ;    // on place les sortie à l'état 0
  PORTC |= ( (1<<PC5) | (1<<PC4) |(1<<PC3) |(1<<PC2) ) ;    // on active les résistances de pull-up sur les entrées

  _delay_ms(1);        // un délai est nécessaire pour l'activation des pull-ups

  etatEntrees = PINC & 0b00111100; // on récupère ensuite l'état des entrées en cachant les bits non utiles
  switch (etatEntrees)
  {
         // PPPPPPPP
         // CCCCCCCC
         // ..5432..
     case 0b00111000 : return 0; // L0 (appui sur l'une des touches 1/2/3 => {{Rouge|entrée PC2 à 0}} )
     case 0b00110100 : return 1; // L1 => {{Rouge|entrée PC3 à 0}} 
     case  : return 2; // L2
     case  : return 3; // L3
     // si autre cas, pas de touches deux touches ou autre
     default : return -1;
  }
}

Indice de colonne

Question.jpg De la même façon, écrire et tester la fonction lecture_colonne() qui permettra d'obtenir le numéro de colonne sur laquelle un bouton est appuyé.


Touche appuyée

Nous souhaitons maintenant écrire une fonction qui renverra une valeur tel que décrit ci dessous :

const int8_t touches[4][3] = { // tableau de int8_t à 2 dimensions
                               { 1, 2, 3},
                               { 4, 5, 6},
                               { 7, 8, 9},
                               {10, 0,11}
                             };

int8_t getTouche();
La valeur retournée sera :
  • -1 si pas de touches (ou plusieurs) appuyées
  • la valeur correspondant à la touche pour les chiffres
  • 11 pour le #
  • 10 pour l' *

Question.jpg Écrire cette fonction en vous servant bien évidemment des 2 fonctions précédentes et pourquoi pas du tableau "touches" :

  • c'est un tableau de 4 lignes et 3 colonnes
  • la valeur dans la case [0,0] correspond à la valeur de la touche pour ligne=0 et colonne=0 => 1
  • la valeur dans la case [1,2] correspond à la valeur de la touche pour ligne=1 et colonne=2 => 6
int8_t getTouche()
{
   ....
}

réalisation d'un digicode

Nous allons utiliser la fonction getTouche() pour réaliser un programme de digicode.

contraintes

Commençons pour définir les contraintes :

  • On appuie sur la touche '*' pour démarrer la saisie du code => la led rouge s'allume brièvement
  • L'utilisateur doit alors saisir le code sur 4 chiffres
  • On valide le code par la touche '#'
  • Si le code est bon une led verte s'allume pendant 10s, sinon une led rouge clignote pendant 10s


codez !

Question.jpg Ecrire un programme répondant au cahier des charges

  • vous pouvez vous servir d'un LLM
  • il suffit de mémoriser les 6 dernières touches appuyées
    • déclarer un tableau
    • faire un décalage du tableau
    • rentrer la nouvelle valeur
    • comparer le tableau par rapport au code

Améliorations

Question.jpg Ajouter une procédure de changement du code.


Question.jpg Faire en sorte que le temps maximum de saisie du code soit de 4s