*fichier de définition des registres du ATMEGA328P: /usr/lib/avr/include/avr/iom328p.h =====TP 1 Informatique Embarquée CESI===== Dire aux étudiants de se connecter en ge2i / ge2i et d'utiliser le navigateur firefox ===Installation et lancement de wokwi2gtkwave=== En début de chaque séance, taper ALT+F2 puis saisir gnome-terminal . Dans le terminal copier coller le texte suivant et laisser ouvert le terminal: echo commence mkdir -p ~/wokwi cd ~/wokwi rm wokwi2gtkwave.py wget https://raw.githubusercontent.com/bvandepo/wokwi2gtkwave/main/wokwi2gtkwave.py chmod a+x wokwi2gtkwave.py python3 ./wokwi2gtkwave.py echo fini //TODO: gérer de delay 25 par sctutation timer: // delay(25); unsigned long timerTache1delay = millis(); while(millis() - timerTache1delay <= 1000) {} //et comprendre pourquoi il fait SEI url du projet sur Wokwi: https://wokwi.com/arduino/projects/313885536265699904 relever les ip avec ip addr ls installations à faire: j'ai 15 ip de 10.6.12.144 à 158 mais il y a plus de machines ~/Bureau/iutatrier/IUT/update_pc2en$ cat ppi_ER2AU #!/bin/sh for i in $(seq 144 158) do ping -c 2 10.6.12.$i | grep time | grep seq & done s'inspirer de ~/Bureau/iutatrier/IUT/update_pc2en/update_pc2aip.sh sudo apt update il faut taper O plusieurs fois en console sudo apt install chromium-browser https://askubuntu.com/questions/118025/bypass-the-yes-no-prompt-in-apt-get-upgrade sudo DEBIAN_FRONTEND=noninteractive apt update -y sudo apt -y install chromium-browser bizarre sur E10, il n'y a que chromium sudo apt -y install chromium sudo apt -y install gtkwave python3-pip sudo pip3 install watchdog =====Informations avant de commencer!===== {{https://bvdp.inetdoc.net/files/iut/tp_pic/warning.jpeg}} Pour faire le TP, vous devrez bien lire le sujet, ne pas sauter de parties, ne pas inventer de prototypes de fonctions mais bien respecter ce qui est demandé, car les fonctions que vous écrirez devront pouvoir être substituées à d'autres fonctions ayant les mêmes prototypes. Les marges de manœuvre dont vous disposez sont au niveau de l'implémentation de ces fonctions, mais les prototypes font partie du cahier des charges et ne sont pas négociables! Le TP se déroulera sur plusieurs séances, il est de votre responsabilité de conserver les fichiers que vous aurez produits; pour cela, sauver l'URL de votre projet sur Wokwi et également LE CONTENU DU CODE pour éviter toute perte. Vous devrez également conserver les chronogrammes intéressants pour pouvoir les revoir à posteriori et les comparer. =====Prise en main du programme d'exemple===== Vous allez dans un premier temps utiliser un programme existant pour en comprendre le fonctionnement puis vous devrez écrire votre propre programme avec des contraintes différentes. Ouvrir dans une autre fenêtre: https://wokwi.com/arduino/libraries/SevSeg/SevSeg_Counter Puis, en haut à gauche, cliquer sur "Save a copy". Comme pour te TD, vous devriez vous loguer et conserver cette URL ainsi que sauvegarder votre programme à la fin de la séance et vous l'envoyer par mail (car vous travaillez sur un compte local sur le PC). ====Analyse de la schématique et des composants==== Lire la documentation du composant afficheur 7 segments multiplexé à 4 chiffres: https://docs.wokwi.com/parts/wokwi-7segment En lisant le fichier diagram.json, déterminer le type d'afficheur 7 segments utilisé. Relever sur papier les connexions électriques entre l'Arduino (avec la numérotation Arduino) et l'afficheur En plus de la numérotation des broches Arduino, vous devrez également relever la correspondance avec les broches du microcontrôleur ATMEGA328P. Les connecteurs IOL et IOH (avec la numérotation Arduino de 0 à 13) à droite sont respectivement pilotés par les registres **PORTD** et **PORTB** sur le schéma suivant: {{https://bvdp.inetdoc.net/files/cesi/tp1/schemaunoports.png}} Regrouper les broches des différents signaux et dire par quoi elles sont pilotées. Par exemple, les 7 segments sont pilotés par ... Schéma de la carte Arduino UNO R3: https://bvdp.inetdoc.net/files/cesi/Arduino_Uno_Rev3-schematic.pdf ====Utilisation d'une librairie haut niveau pour piloter l'afficheur==== Dans le programme fourni, la valeur à afficher est transmise via **sevseg.setNumber(val, position_point_décimal);** puis **sevseg.refreshDisplay()** doit être appelée régulièrement pour réaliser une étape du balayage Analyser le code fourni et comprendre l'utilisation du timer par scrutation qui a été abordée au dernier TD. Modifier la schématique pour reproduire le schéma suivant: {{https://bvdp.inetdoc.net/files/cesi/tp1/schemawokwitp7seg1.png}} Au besoin, le fichier de schématique est disponible sur: https://bvdp.inetdoc.net/files/cesi/tp1/diagram1.json Adapter le code pour piloter l'affichage avec la valeur 10bits lue sur le Convertisseur Analogique Numérique via l'entrée A0. Ajouter également l'affichage sur la console de Debug de la valeur lue en décimal suivie d'un retour à la ligne. Vérifier en simulation le bon fonctionnement du système pour différentes positions du potentiomètre. Solution: dans setup() Serial.begin(115200); dans loop() unsigned int val=analogRead(A0); sevseg.setNumber(val, 0); Serial.println(val); sevseg.refreshDisplay(); // Must run repeatedly ====Analyse des signaux de commande de l'afficheur==== Ajouter à la schématique le composant "Logic Analyzer (8 channels)" et connecter * les entrées D3..0 de l'analyseur logique aux broches 5..2 de l'Arduino * les entrées D7..4 de l'analyseur logique aux broches 13..10 de l'Arduino {{https://bvdp.inetdoc.net/files/cesi/tp1/schemawokwitp7seg2.png}} Au besoin, le fichier de schématique est disponible sur: https://bvdp.inetdoc.net/files/cesi/tp1/diagram2.json Lancer une simulation puis l’arrêter. Un fichier portant l'extension vcd est téléchargé, le sauver dans le dossier Téléchargements (si l'option est proposée, cocher toujours effectuer cette action). Nous allons automatiser l'affichage des chronogrammes contenus dans ce fichier pour cela, ouvrir un terminal en pressant ALT+F2 puis xterm et touche Entrée. Copiez coller dans ce terminal le texte suivant (il faudra ensuite laisser ce terminal ouvert pendant toute la durée où vous souhaitez pouvoir afficher les chronogrammes): echo commence mkdir -p ~/wokwi cd ~/wokwi rm -f wokwi2gtkwave.py && wget --no-check-certificate https://raw.githubusercontent.com/bvandepo/wokwi2gtkwave/main/wokwi2gtkwave.py python3 ./wokwi2gtkwave.py echo fini ancien fichier: https://bvdp.inetdoc.net/files/cesi/wokwi2gtkwave.py Relancer une simulation puis l’arrêter. Vous devriez voir une fenêtre **gtkwave** s'ouvrir et afficher le chronogramme, sinon appeler l'enseignant. Sur le chronogramme, vous pourrez zoomer à l'aide de la molette de la souris en maintenant la touche CTRL pressée. Les fichiers chronogrammes sont automatiquement déplacés dans un sous dossier ~/wokwi/vcdforgtkwave que vous pourrez renommer à posteriori si vous voulez en conserver certaines versions et éviter qu'elles ne soient écrasées. Interprétez les chronogrammes: * En déduire comment les LED sont câblées dans cet afficheur? * Zoomer sur des instants de changements de segments et regarder combien de digits sont actifs à cet instant. * Combien de LEDs peuvent être allumées simultanément au maximum? * A quelle fréquence l'affichage de la totalité des chiffres est effectué? {{https://bvdp.inetdoc.net/files/cesi/tp1/chrono1.png}} Solution: Les différents segments sont balayés (activé individuellement à l'état bas (cathode)). Pour chaque segment, l'Arduino active le ou les digits qui doivent l'être (activés ensemble à l'état haut (anode)). Tous les segments d'un même afficheur sont pilotés par un même signal DIGIT, donc c'est un afficheur à anodes communes. Au moment du changement de segment, AUCUN digit ne doit être actif! La séquence d'affichage complète balaie les 8 segments. A chaque instant, il y a au plus 4 LEDS allumées. Il y a en tout 8 segments (dont le point décimal), 15.8ms entre deux affichages consécutifs sur un même segment soit à peu près 63Hz. =====Programme avec votre propre librairie===== Le programme d'exemple étant maintenant compris (normalement...), vous allez devoir écrire vous même les fonctions permettant de piloter l'afficheur. ====Numérotation des digits et segments==== Nous utiliserons une numérotation un peu différente de celle de la documentation de l'afficheur 7 segments fournie en: https://docs.wokwi.com/parts/wokwi-7segment ^ numérotation TP ^ rôle ^ affichage sur le chronogramme ^ | digit 0 | chiffre le plus à gauche (miliers) | D0 | | digit 1 | chiffre (centaines) | D1 | | digit 2 | chiffre (dizaines) | D2 | | digit 3 | chiffre le plus à droite (unités) | D3 | | segment 0 à 3 | segment A à D | non visibles| | segment 4 | segment E | D4 | | segment 5 | segment F | D5 | | segment 6 | segment G | D6 | | segment 7 | point décimal DP | D7 | ====Changements par rapport à l'exercice précédent==== La librairie utilisée précédemment est très **générique**, ce qui signifie qu'elle peut être utilisée dans plusieurs configurations différentes en s'adaptant facilement (par exemple à différents type et taille d'afficheurs, à différente familles de microcontrôleurs). La librairie que vous allez écrire sera moins générique mais sera **spécifique et optimisée** pour un microcontrôleur et un afficheur particulier, les deux étant connectés d'une manière fixe. Ceci nous permettra d'illustrer **la commande à bas niveau** en pilotant les broches du microcontrôleur directement via ses registres. Dans cet exercice, pour changer, nous allons utiliser un autre type de cablage de l'afficheur. Pour changer le type d'afficheur du schéma, remplacer dans le fichier diagram.json dans les attributs du composant "wokwi-7segment" "attrs": { "digits": "4", "common": "anode" } par "attrs": { "digits": "4", "common": "cathode" } Avec cet afficheur, vous devrez balayer les différents digits (actifs à l'état bas) et pour chaque digit piloter les segments (actifs à l'état haut) qui doivent s'allumer. VOUS DEVREZ ABSOLUMENT veiller à ce que: - il n'y ait jamais deux digits actifs en même temps, pour cela vous devrez en désactiver un AVANT d'activer le suivant. - les changements de valeur pilotant les segments soient réalisés lorsque AUCUN des digits n'est actif. Solution: D3 actif, puis D3 désactivé, puis changement de D7..4, puis activation de D0 {{https://bvdp.inetdoc.net/files/cesi/tp1/chrono2.png}} ====Programme de départ==== Copier coller ce programme à la place de l'ancien. Il contient la définition des variables globales dont vous aurez besoin: byte numDigits = 4; byte digitPins[] = {2, 3, 4, 5}; byte numSegments = 8; byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13}; byte tabDeco7seg[]={0x3f,0x06,0x5b,0x4F,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; void setup() { Serial.begin(115200); Serial.println("debut"); } void loop() { } ====Initialisations==== Dans un premier temps, nous utiliserons les fonctions Arduino **digitalWrite(...)** et **pinMode(...)** pour configurer/piloter les broches (en utilisant les tableaux définis en variables globales pour identifier les broches). Compléter le programme pour configurer toutes les broches utiles dans la bonne direction et à une valeur inactive. Il est très fortement conseillé d'utiliser des boucles car le traitement à produire pour les différentes broches est quasiment le même. Solution: void setup() { for (int i=0;i ===Vérification de la commandabilité=== Modifier **loop()** temporairement juste pour allumer sur l'afficheur des dizaines le segments F et le point décimal. Solution: void loop() { ... digitalWrite(segmentPins[5],1); digitalWrite(segmentPins[7],1); digitalWrite(digitPins[2],0); } Faire de même cette fois-ci pour allumer les segments A et E de l'afficheur des milliers. Solution: void loop() { ... digitalWrite(segmentPins[0],1); digitalWrite(segmentPins[4],1); digitalWrite(digitPins[0],0); } Une fois que vous avez vérifié en simulation que vous êtes bien capables de piloter les segments, commenter ce code dans **loop()**. ====Fonction de pilotage d'un digit==== Implémenter la fonction **void setDigit(unsigned char nDigitActif,unsigned char nSegsActif)** dont les paramètres sont: * nDigitActif prenant une valeur comprise en 0 et 3 pour indiquer le digit à activer * nSegsActif prenant une valeur sur 8 bits pour indiquer (à l'état haut) les segments (et le point) à allumer. Cette fonction devra successivement (copier coller ces 3 items dans votre code en tant que commentaires, et écrire votre code sous chaque commentaire): - désactiver tous les digits avant de changer les segments - piloter les segments en utilisant les valeurs des bits de nSegsActif - activer le bon digit Tester votre fonction en utilisant ce sous programme **loop()**: void loop() { setDigit(0,0x10); setDigit(1,0x20); setDigit(2,0x40); setDigit(3,0x80); } Solution: {{https://bvdp.inetdoc.net/files/cesi/tp1/chrono3.png}} ///////////////////////////////// void setDigit(unsigned char nDigitActif,unsigned char nSegsActif) { //désactive tous les digits avant de changer les segments for (int i=0;i Vérifier que l'affichage est correct. Faire de même pour cet autre programme: Tester votre fonction en utilisant ce sous programme **loop()**: void loop() { setDigit(0,1); setDigit(1,2); setDigit(2,3); setDigit(3,4); } ====Décodage 7 segments==== Nous avons vu en TD l'utilisation d'une table de transcodage (Look-Ut Table) pour réaliser le décodage 7 segments. Cette table est déclarée dans votre programme dans le tableau **tabDeco7seg**. Adapter **loop()** pour afficher les chiffres présents dans les cases du tableau **byte digits[4];** en réalisant le décodage. Vous aurez pris soin d'initialiser **digit[]** pour que chaque case contienne un chiffre différent à afficher sur le digit correspondant. Vous veillerez aussi à ce qu'une valeur supérieure à 15 dans une case de **digit[]** ne provoque pas une fuite mémoire lors de la recherche de la valeur correspondante dans la table **tabDeco7seg**. Pour l'instant vous ne vous occuperez pas du point décimal. Vous ne devez pas modifier la fonction **void setDigit(unsigned char nDigitActif,unsigned char nSegsActif)**! Solution: void loop() { digits[0]=1; digits[1]=2; digits[2]=3; digits[3]=4; for (int i=0;i ====Gestion du point décimal==== Toutes les valeurs stockées dans le tableau **tabDeco7seg** ont le bit 7 à la valeur 0. Donc votre programme à l'exercice précédent doit avoir éteint tous les points décimaux. Nous allons maintenant utiliser la variable globale **byte dots;** pour indiquer quels points décimaux afficher. Pour cela le bit i de **dots** à 1 indique qu'il faut allumer le point décimal de l'afficheur i. Adapter le programme précédent pour activer correctement les points décimaux. Pour cela, utiliser les opérateurs logiques et de décalage vus en TD. Vous ne devez toujours pas modifier la fonction **void setDigit(unsigned char nDigitActif,unsigned char nSegsActif)**! Solution: void loop() { digits[0]=1; digits[1]=2; digits[2]=3; digits[3]=4; for (int i=0;i>i)&1)<<7); } ====Conversion binaire 16 bits vers BCD 4 digits==== Vous devez maintenant implémenter une fonction **void setNumber(unsigned int val)** qui va remplir les 4 cases du tableau **digits** à partir d'une valeur numérique sur 16 bits non signée passée via le paramètre **val**. Pour cela, vous pourrez utiliser notamment l'opérateur modulo (% en C). Tester votre programme pour afficher la valeur "789.5". Pour cela appelez setNumber(7895); et dots=4; Solution: void setNumber(unsigned int val) { digits[0]=(val/1000)%10; digits[1]=(val/100)%10; digits[2]=(val/10)%10; digits[3]=(val/1)%10; } void loop() { setNumber(7895); dots=4; for (int i=0;i>i)&1)<<7); } ====Gestion du temps==== Observez pour l'exercice précédent sur les chronogrammes la fréquence à laquelle les digits sont balayés et relevez la durée pendant laquelle chacun d'eux est allumé à chaque affichage. Un rapport cyclique différent se traduit par une intensité lumineuse perçue différente. Pour éviter cela, nous allons mettre en place 2 tâches pseudo parallèles: * Une première servant à calculer une valeur à afficher (incrémentée à chaque appel) et à la convertir en BCD. * Une seconde servant à réaliser l'affichage multiplexé (balayage) des différents digits. UN SEUL DIGIT DOIT ÊTRE PILOTÉ A CHAQUE APPEL. La structure du sous programme **loop()** est fournie et vous devez implémenter les 2 taches dans les fonctions correspondantes. /////////////////////////// void tache1(){ } /////////////////////////// void tache2(){ } /////////////////////////// void loop() { unsigned int periodiciteTache1=100; //100ms entre chaque incrémentation de la valeur à afficher unsigned int periodiciteTache2=4; //4ms pour l'affichage de chaque digit static unsigned long timerTache1 = millis(); static unsigned long timerTache2 = millis(); if (millis() - timerTache1 >= periodiciteTache1) { timerTache1 += periodiciteTache1; tache1(); } if (millis() - timerTache2 >= periodiciteTache2) { timerTache2 += periodiciteTache2; tache2(); } } /////////////////////////// } Vérifier à l'aide des chronogrammes que chaque digit est bien piloté pendant approximativement 4ms et donc que l'affichage est rafraîchi à une cadence de 62.5Hz. Vérifier que la valeur affichée sur l'afficheur est bien cohérente avec le temps simulé (affiché en haut à droite de la schématique pendant la simulation). Solution: /////////////////////////// void tache1(){ static int val=0; val++; setNumber(val); } /////////////////////////// void tache2(){ static byte numAfficheur=0; setDigit(numAfficheur,tabDeco7seg[digits[numAfficheur]] | ((dots>>numAfficheur)&1)<<7); numAfficheur=(numAfficheur+1)%numDigits; } {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}}{{https://bvdp.inetdoc.net/files/imageschiffres/chiffre-1.jpg?40%}}} Appeler un enseignant pour valider votre travail. ====Gestion du temps par Interruption Timer==== Vous avez peut être relevé des déviations par rapport à la valeur idéal de 4ms pour l'affichage sur les digits. Nous pouvons aggraver ce phénomène en rendant la tâche 1 bloquante pendant plus longtemps. Pour cela, ajouter à la fin de la tâche 1 un appel à **delay(25);** et relever les chronogrammes. Dans ce cas, on observe que la tâche 2 ne pourra pas être exécutée toutes les 4ms, le processeur restant bloqué dans l'exécution de la tâche 1 pendant plus de 25ms. Nous allons maintenant utiliser un mécanisme appelé **Interruption** pour que la tâche 2 soit appelée lorsque nécessaire, en interrompant la tâche 1 si besoin. Avec les interruptions, il n'est plus nécessaire de venir scruter régulièrement la valeur du timer pour exécuter la tâche si besoin. Au lieu de cela, une **configuration de l'interruption** est effectuée au début du programme afin que la tâche 2 s'exécute à une cadence prédéterminée. Retirer l'appel de la tâche 2 dans **loop()**, et appeler la fonction **void setupTimer2()** une fois dans **setup()**. Ainsi le sous programme **ISR(TIMER2_COMPA_vect)** est appelé automatiquement toutes les 4ms. //Timer 2 : https://www.aranacorp.com/fr/utilisation-des-timers-de-larduino/ // timer2 (8 bits) qui est utilisé par la fonction Tone() et la génération de la PWM sur les broches 3 et 11. void setupTimer2(){ cli(); // disable all interrupts TCCR2A = (1< Lorsque nous utilisons le mécanisme d'interruption, il est nécessaire de prendre des précautions: il faut mettre en place un **verrou** sur les données et périphériques utilisés en commun par les différentes tâches pour éviter par exemple que l'interruption timer affiche des valeurs en cours de réglage par la tâche 1. Pour cela, vous veillerez à ajouter avant toute modification des variables digits[] ou dots par la tache 1, l'instruction suivante pour éviter l'exécution du programme d'interruption timer pendant la modification de variable partagée: cli(); // disable all interrupts Une fois la variable modifiée, insérer l'instruction suivante pour autoriser à nouveau l'interruption timer: sei(); // enable all interrupts Observer à nouveau les chronogrammes et vérifier que les durées d'alternance des digits sont maintenant respectées. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}}{{https://bvdp.inetdoc.net/files/imageschiffres/chiffre-2.jpg?40%}}} Appeler un enseignant pour valider votre travail. =====Pilotage des broches à bas niveau===== Nous avons utilisé jusqu'à maintenant la fonction **digitalWrite(...)** pour piloter individuellement les broches de l'afficheur. Nous allons maintenant mettre en place un pilotage **bas niveau** permettant d'accéder simultanément à plusieurs bits en écrivant une valeur 8 bits dans un registre associé au port de sortie. Nous allons commencer par évaluer le temps nécessaire à la commande de l'afficheur 7 segments par votre fonction **void setDigit(void setDigit(...)**. Pour cela, nous allons utiliser une broche (A3, pilotée par le bit 3 du port C) qui va être mise à 1 pendant l'exécution de la fonction et à 0 le reste du temps. Configurer la broche A3 en sortie et à l'état bas dans **setup()**. Au début de la fonction **void setDigit(...)**, modifier le registre **PORTC** pour mettre son bit 3 à la valeur 1, puis le remettre à la valeur 0 à la fin de la fonction (utiliser les opérateurs & et | ainsi que des masques). Défaire la connexion du point décimal à l'entrée D7 de l'analyseur logique et y connecter A3 de l'Arduino pour pouvoir observer ce signal et relever la durée pendant laquelle le signal est à l'état haut pour quelques digits. Solution: dans setup() pinMode(A3, OUTPUT); digitalWrite(A3, LOW); dans setDigit() PORTC=(PORTC | (1<<3) ); //active la broche A3 pour début de mesure de la durée d'exécution: TIC ... PORTC=(PORTC & ~(1<<3) ); //désactive la broche A3 pour fin de mesure de la durée d'exécution: TOC durée relevée = 70 us ====Implémentation bas niveau==== Nous allons conserver cette implémentation de votre fonction **void setDigit(...)** en la renommant **void setDigitHAL(...)** et vous allez maintenant devoir ré-implémenter une fonction **void setDigit(...)** réalisant le même travail mais sans utiliser la fonction **digitalWrite(...)** de la HAL Arduino. Veillez à ne pas écrire inutilement plusieurs valeurs à la suite dans un registre de PORT, car chaque valeur écrite pilote effectivement la broche. Éventuellement passer par une variable temporaire adéquate, dont la valeur sera finalement affectée au registre de port. Pour réaliser la nouvelle implémentation de **void setDigit(...)**, vous devrez manipuler les différents registres **PORTB**, **PORTC** et **PORTD** pour successivement (copier coller ces items dans votre code en tant que commentaires, et écrire votre code sous chaque commentaire): - activer la broche A3 pour début de mesure de la durée d'exécution: TIC - régler toutes les cathodes inactives - régler les segments (poids faible), en maintenant toutes les cathodes inactives - régler les segments (poids fort) - régler la cathode active - désactiver la broche A3 pour fin de mesure de la durée d'exécution:TOC Pour cela vous devrez utiliser des opérateurs logiques, de décalage, des valeurs constantes ainsi que les paramètres **nDigitActif** et **nSegsActif**. Avant de coder, vous devriez dessiner chaque registre modifié pour chaque action et les valeurs modifiées pour chacun des bits. Une fois la fonction codée et le fonctionnement étant correct, relever sa durée d'exécution sur le chronogramme. La durée relevée par votre professeur est de 2.5 us, ce qui est bien plus rapide que l'implémentation utilisant la HAL Arduino. Solution: //point décimal sur PB5 //segments sur PB4-0 puis PD7-6 //digits sur PD5-2 void setDigit(unsigned char nDigitActif,unsigned char nSegsActif) { PORTC=(PORTC | (1<<3) ); //active la broche A3 pour début de mesure de la durée d'exécution: TIC PORTD=(PORTD | 0x3C ) ; //1: règle toutes les cathodes inactives PORTD=(PORTD & 0x3F) | (((nSegsActif) & 0x3)<<6); //2: règle les segments (poids faible), en maintenant toutes les cathodes inactives PORTB=(PORTB & 0xC0) | (((nSegsActif) >>2)&0x3f); //3: règle les segments (poids fort) PORTD=PORTD & ~((1< =====Organisation des fichiers pour la librairie===== En vous inspirant de la librairie utilisée dans le TD1 [[cesitd1]], créer 2 fichiers: * lib7seg.cpp dans lequel vous déplacerez les constantes et l'implémentation des fonctions. Vous veillerez également à ajouter au début de ce fichier: #include "lib7seg.h". Vous laisserez les fonctions de configuration du timer et la routine d'interruption timer dans le fichier du programme principal! * lib7seg.h dans lequel vous copierez les prototypes des fonctions. Vous veillerez également à ajouter au début de ce fichier: #include Vous déplacerez les initialisations liées à l'afficheur 7 segments de la fonction **setup()** à une fonction **setup7seg()** qui sera elle même appelée par **setup()** et rangée dans la librairie. Vous devrez également ajouter à votre librairie une fonction **void setDots(unsigned char val)** permettant de régler la valeur de la variable **dots** et donc de configurer quels points décimaux doivent être allumés. Vous devrez également ajouter à votre librairie une fonction **void refresh()** permettant de gérer le balayage des digits. Vous y déplacerez le code de la fonction **tache2()**, et vous veillerez à appeler **refresh()** à la place de **tache2()** dans la routine d'interruption timer. Solution: #include "lib7seg.h" ///////////////////////////////// void setup() { setup7seg(); } ///////////////////////////////// void tache1(){ static int val=0; val++; setNumber(val); delay(25); } ///////////////////////////////// void loop() { unsigned int periodiciteTache1=100; //100ms entre chaque incrémentation de la valeur à afficher static unsigned long timerTache1 = millis(); if (millis() - timerTache1 >= periodiciteTache1) { timerTache1 += periodiciteTache1; tache1(); } } ///////////////////////////////// /////////////////////////// //Timer 2 : https://www.aranacorp.com/fr/utilisation-des-timers-de-larduino/ // timer2 (8 bits) qui est utilisé par la fonction Tone() et la génération de la PWM sur les broches 3 et 11. void setupTimer2(){ cli(); // disable all interrupts TCCR2A = (1< #include void setup7seg(); void setNumber(unsigned int val); void setDots(unsigned char val); void refresh(); Pour ce fichier, c'est le gros bazard, il faudra faire le ménage: #include "lib7seg.h" byte numDigits = 4; byte digitPins[] = {2, 3, 4, 5}; byte numSegments = 8; byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13}; byte tabDeco7seg[]={0x3f,0x06,0x5b,0x4F,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; void setupTimer2(); void setup7seg(){ for (int i=0;i>2)&0x3f); //3: règle les segments (poids fort) PORTD=PORTD & ~((1<>2)&0x1f); PORTB=tmpPORTB; PORTD=tmpPORTD; interrupts(); //globally re-enable interrupts //les 63ns de décallage entre l'écriture des 2 registres sont visibles en simulation */ PORTC=(PORTC | (1<<3) ); //active la broche A3 pour début de mesure de la durée d'exécution: TIC //noInterrupts(); //globally disable interrupts //Il faut éviter tout changement de segment pendant qu'au moins une anode est active PORTD=(PORTD |0x3C ) ; //règle toutes les cathodes inactives en premier //delayMicroseconds(100); PORTD=(PORTD & 0x3F) | (((nSegsActif) & 0x3)<<6); //règle les segments en premier, avec aucune anode active //PORTB=(PORTB & 0xE0) | (((~nSegsActif) >>2)&0x1f); //règle les segments en premier //pour gérer le point décimal il faut garder 8 segments //PORTB=(PORTB & 0xC0) | (((~nSegsActif) >>2)&0x1f); //règle les segments en premier //PORTB=(PORTB & 0xC0) | (((~nSegsActif) >>2)&0x3f); //règle les segments en premier et le point byte tmpPORTB=(PORTB & 0xC0) | (((nSegsActif) >>2)&0x3f); //règle les segments en premier et le point //tmpPORTB=(tmpPORTB & 0xDF); //force le point //tmpPORTB=(PORTB & 0xC0); PORTB=tmpPORTB; //delayMicroseconds(100); //PORTD=PORTD | ((1<>5)&1); } ///////////////////////////////// byte dots=0x04; //{0,0,0,0} sur les 4 bits de poids faible; byte digits[4]; void setDots(unsigned char val) { dots=val; } void setNumber(unsigned int val) { //pinMode(A3,OUTPUT); //digitalWrite(A3,HIGH); //mesure de la durée de ce traitement digits[0]=(val/1000)%10; digits[1]=(val/100)%10; digits[2]=(val/10)%10; digits[3]=(val/1)%10; //digitalWrite(A3,LOW); /* for (int i=0;i<4;i++){ Serial.print(i); Serial.print(":"); Serial.print(digits[i]); Serial.println(""); } */ } ///////////////////////////////// void refreshDisplayCathodeParCathode() { static byte numAfficheur=0; //sans les points décimaux //setDigitReg(numAfficheur,tabDeco7seg[digits[numAfficheur]]); //avec les points décimaux setDigit(numAfficheur,tabDeco7seg[digits[numAfficheur]] | ((dots>>numAfficheur)&1)<<7); /* Serial.print("numAfficheur:"); Serial.print(numAfficheur); Serial.print(" , digits[numAfficheur]: "); Serial.print(digits[numAfficheur]); Serial.print(" , "); Serial.print(tabDeco7seg[digits[numAfficheur]] | (((dots>>numAfficheur)&1)<<7),HEX); Serial.println(""); */ numAfficheur=(numAfficheur+1)%numDigits; //delay(50); } /////////////////////////// void refresh(){ static byte numAfficheur=0; setDigit(numAfficheur,tabDeco7seg[digits[numAfficheur]] | ((dots>>numAfficheur)&1)<<7); numAfficheur=(numAfficheur+1)%numDigits; } {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}}{{https://bvdp.inetdoc.net/files/imageschiffres/chiffre-3.jpg?40%}}} Appeler un enseignant pour valider votre travail. =====Application 1==== Nous proposons maintenant d'utiliser la librairie que vous venez de faire pour réaliser une application de monitoring. Connecter 4 potentiomètres aux entrées A0, A1, A2 et A4 de l'Arduino pour reproduire le schéma suivant: {{https://bvdp.inetdoc.net/files/cesi/tp1/schemawokwitp7seg3.png}} Au besoin, le fichier de schématique est disponible sur: https://bvdp.inetdoc.net/files/cesi/tp1/diagram3.json Ecrire un programme permettant de réaliser les 4 taches suivantes en parallèles: - Une première tâche réalise l'acquisition des mesures sur ces 4 entrées en permanence dans une tache, chaque 100ms. Les valeurs lues devront être stockées dans un tableau **unsigned int datain[4]**. - Dans une seconde tâche, exécutée chaque deux secondes, le numéro de l'entrée dont la valeur est affichée sur l'afficheur 7 segments sera changée, de sorte que l'on balaye les 4 entrées en 8 secondes. Cette tache remplie le rôle de choix d'aiguillage. Elle devra également indiquer sur l'afficheur quelle est la valeur en cours d'affichage, cette tache devra régler la valeur de commande des points décimaux (avec la fonction **setDots(...)**) pour allumer le point décimal i lors de l'affichage de la valeur **datain[i]**. - Une troisième tâche, exécutée à la cadence maximale doit utiliser la valeur de l'entrée sélectionnée par la seconde tâche pour qu'elle soit affichée par la routine d'interruption timer de votre librairie. - Une quatrième tâche gérant le balayage des différents digits est appelée par interruption Timer. #include "lib7seg.h" ///////////////////////////////// void setup() { setup7seg(); } ///////////////////////////////// unsigned int datain[4]; ///////////////////////////////// void tache1(){ datain[0]=analogRead(A0); datain[1]=analogRead(A1); datain[2]=analogRead(A2); datain[3]=analogRead(A4); } ///////////////////////////////// unsigned char indice=0; ///////////////////////////////// void tache2b(){ indice=(indice+1)%4; setDots(1<= periodiciteTache1) { timerTache1 += periodiciteTache1; tache1(); } unsigned int periodiciteTache2=2000; //2000ms static unsigned long timerTache2 = millis(); if (millis() - timerTache2 >= periodiciteTache2) { timerTache2 += periodiciteTache2; tache2b(); } tache3(); } ///////////////////////////////// ===Affichage des mesures=== Nous allons maintenant tracer les valeurs lues sur les 4 entrées analogiques dans un **plotter** comme indiqué sur https://docs.wokwi.com/guides/serial-monitor#configuring-the-serial-monitor . Pour cela, dans diagram.json, après la ligne: "editor": "wokwi", ajouter: "serialMonitor": { "display": "plotter", "newline": "lf" }, et dans la tache 1, après la collecte des mesures sur les entrées analogiques, ajouter: for (int i=0;i<4;i++){ Serial.print(datain[i]); Serial.print(" "); } Serial.println(); En lançant la simulation et en jouant sur les potentiomètres, vous devriez voir un tracé similaire à: {{https://bvdp.inetdoc.net/files/cesi/tp1/schemawokwitp7seg4.png}} {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}}{{https://bvdp.inetdoc.net/files/imageschiffres/chiffre-4.jpg?40%}}} Appeler un enseignant pour valider votre travail. =====Application 2==== Nous proposons maintenant d'intégrer la librairie objet permettant d'utiliser l'encodeur incrémental vu dans [[cesitd2]] à ce projet pour afficher la position angulaire et la vitesse de rotation sur l'afficheur 7 segments. Modifier la schématique pour reproduire le schéma suivant: {{https://bvdp.inetdoc.net/files/cesi/tp1/schemawokwitp7seg5.png}} Au besoin, le fichier de schématique est disponible sur: https://bvdp.inetdoc.net/files/cesi/tp1/diagram4.json Sur ce schéma, vous retrouvez l'encodeur incrémental ainsi que l'afficheur 7 segments multiplexé à 4 digits. Une LED connectée à la sortie A5 de l'Arduino fait office de signe, en s'allumant pour indiquer le signe négatif. Un bouton de sélection connecté ) l'entrée A4 est utilisé pour permettre l'affichage soit de la position angulaire soit de la vitesse. L'affichage devra être actualisé cinq fois par seconde. Pour l'affichage de la vitesse, vous pourrez ajouter une fonction d'affichage d'une valeur flottante positive à la librairie d'affichage. Cette fonction devra convertir la valeur flottante en un entier pour occuper au mieux les 4 digits et piloter correctement l'affichage du point. Dans le cas d'une valeur trop grande pour être afficher, il faudra afficher EEEE pour indiquer une erreur. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}}{{https://bvdp.inetdoc.net/files/imageschiffres/chiffre-5.jpg?40%}}} Appeler un enseignant pour valider votre travail. =====Rendre la librairie de pilotage de l'afficheur 7 segments "Orientée Objet"===== Visionner la vidéo de cours présentant les concepts de la Programmation Orientée Objet: https://youtu.be/5Y9P4MIkTIs Polycopié du cours POO: https://bvdp.inetdoc.net/files/iut/cours_POO_intro_complet_2021_numbered.pdf La classe suivante est proposée pour la librairie d'affichage: #include /*! Clib7Seg Classe pour la gestion de l'afficheur 7 segments multiplexé */ class Clib7Seg { public: /*! Clib7Seg() constructeur sans paramètre qui utilise des broches par défaut et une taille d'afficheur fixe */ Clib7Seg(); /*! Clib7Seg(byte numDigitsInit,byte * digitPinsInit, byte numSegmentsInit, byte * segmentPinsInit); constructeur avec des paramètres qui permet de configurer les broches utilisées ainsi que le nombre de digits */ Clib7Seg(byte numDigitsInit,byte * digitPinsInit, byte numSegmentsInit, byte * segmentPinsInit); /*! void setNumber(unsigned int val); demande de mise à jour de la valeur numérique affichée à partir du paramètre entier val. */ void setNumber(unsigned int val); /*! void setDots(unsigned char val); demande de mise à jour de la valeur affichée pour les points. val est une valeur dont le bit 0 pilote le point de digit de gauche, et chacun des bits suivants pilote le point du digit suivant. */ void setDots(unsigned char val); /*! void refresh(); demande de rafaichissement de l'affichage pour effectuer le balayage. Cette méthode doit être appelée suffisament fréquemment pour que le balayage ne soit pas perçu par l'oeil. Elle peut être appelée dans une tache périodique utilisant un timer par scrutation ou dans une routine d'interruption timer matériel val est une valeur dont le bit 0 pilote le point de digit de gauche, et chacun des bits suivants pilote le point du digit suivant. */ void refresh(); /*! setNumberFloatAbs(float floatAbs); IMPLEMENTATION SUR 4 DIGITS SEULEMENT demande de mise à jour de la valeur numérique affichée à partir du paramètre entier floatAbs. C'est la valeur absolue de floatAbs qui est affichée. Le point est positionné automatiquement et en cas de dépassement de la valeur par rapport à la capacité d'affichage, des chiffres E sont affichés. */ void setNumberFloatAbs(float floatAbs); private: //! nombre de digits de l'afficheur byte numDigits; /*! liste des broches utilisées pour les digits pointeur vers un tableau alloué dans le constructeur plutot qu'un tableau, utilise un pointeur pour gérer une taille variable*/ byte *digitPins; //! nombre de segments de l'afficheur (doit être 8) byte numSegments; /*! liste des broches utilisées pour les segments pointeur vers un tableau alloué dans le constructeur plutot qu'un tableau, utilise un pointeur pour gérer une taille variable*/ byte *segmentPins; //! masque de bits pour gérer jusqu'à 8 points byte dots; /*! stockage des chiffre à afficher sur les digits de l'afficheur pointeur vers un tableau alloué dans le constructeur*/ byte *digits; //! méthode de pilotage individuel des digits, appelée par refresh() void setDigitHAL(unsigned char nDigitActif,unsigned char nSegsActif); //! méthode contenant les initialisations communes aux différents constructeurs void init(); }; Adapter votre libraire pour qu'elle implémente cette classe. Le code de configuration du timer et de gestion de l'interruption devront être hors de la classe et le programme d'interruption timer devra juste appeler la méthode **refresh()**. Le plus simple est de mettre tout le code timer dans le fichier du programme principal. Le code de votre librairie utilisant un accès bas niveau aux broches via les registres ne sera pas repris dans cette librairie objet car il ne permet pas la modularité (changer le nombre de digits et les broches). Dans la classe, au lieu d'utiliser des tableaux pour stocker les broches et les digits, les membres déclarés sont des pointeurs. Pour pouvoir les utiliser comme des tableaux, il vous faudra procéder à l'allocation dynamique de mémoire dans les constructeurs. Par exemple, pour allouer le tableau digitPins avec numDigitsInit éléments, il faut faire: numDigits = numDigitsInit; digitPins=new byte[numDigits]; L'instanciation de l'afficheur dans le programme principal se fera par: byte digitPins[] = {2, 3, 4, 5}; byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13}; Clib7Seg aff7seg(4,digitPins,8,segmentPins); //Objet en variable globale car utilisé dans l'interruption {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}}{{https://bvdp.inetdoc.net/files/imageschiffres/chiffre-6.jpg?40%}}} Appeler un enseignant pour valider votre travail. Fichiers de solutions application2 : https://bvdp.inetdoc.net/files/cesi/tp1/solutionapp2/ Projet wokwi objet avec encodeur: https://wokwi.com/arduino/projects/317350715447050817 Projet wokwi objet avec les 4 potars: https://wokwi.com/arduino/projects/316725612965265984 Si vous êtes arrivé jusqu'ici, demander à l'enseignant quoi faire ensuite.