Contacts
Enseignant: Jérôme Collin, responsable (local M-4013, poste 5060)
Support technique supplémentaire: Laurent Tremblay (local M-4011, poste 7181)
Chargés de laboratoire: Section 1: Kais Fallouh (Lundi AM)
Romain Lebbadi-Breteau (Mercredi PM)
Section 2: Tristan Rioux (Mardi PM)
Gaëtan Florio (Jeudi AM)
Section 3: Luciano Garin-Iriarte (Lundi PM)
Ely-Cheikh Abass (Jeudi PM)
Section 4: Dorine Dantrun (Mardi AM)
Meriam Ben Rabia (Vendredi AM)
Section 5: Amélie Simard (Mercredi AM)
Abdul-Wahab Chaarani (Vendredi PM)
Section 6: Marc-Antoine Manningham (Mardi soir)
Laurent Bourgon (Jeudi soir)

INF1900

Projet initial de système embarqué

Travail pratique No. 9

Trajectoire préprogrammée



Objectif: Faire effectuer automatiquement des déplacements au robot

Durée: Une semaine exactement

Travail préparatoire: travail pratique no. 5 sur le RS-232 et la mémoire externe

Documents à remettre:

Le travail pratique est matière à une entrevue de laboratoire à partir du mardi 25 mars durant les périodes de laboratoire. Un horaire est établi pour les entrevues. L'entrevue se fera par équipe de 4 (5 ou 6) étudiants avec un responsable de la partie technique. Durant l'entrevue, le robot devra effectuer une démonstration de son bon comportement devant l'évaluateur. Le code est aussi à placer sous Git avant l'entrevue. Une première évaluation par les pairs est également à rendre quelques jours avant.

Présentation en classe: présentation PowerPoint.



«I am a very bottom-up thinker. If you give me the right kind of Tinker Toys, I can imagine the building. I can sit there and see primitives and recognize their power to build structures a half mile high, if only I had just one more to make it functionally complete. I can see those kinds of things»

-

Ken Thompson, un des développeurs du système d'exploitation Unix et du langage C.


Introduction


Depuis le début de la session, la très grande majorité des exercices ont porté sur des aspects de la carte mère et du microcontrôleur. Le robot lui-même a peu bougé. Il est temps de passer à l'action! On le fera se déplacer pour qu'il suive une trajectoire préprogrammée inscrite en mémoire externe. De plus, ce laboratoire permettra de mettre à profit les notions introduites lors des travaux pratiques précédents.


Une bonne façon de revoir les concepts vus dans les semaines précédentes est de les utiliser de nouveau, mais dans un contexte plus général. Ce qui est proposé comme travail ressemble un peu à un interpréteur de commandes. Les instructions d'un interpréteur de commande sont directement exécutées. Des exemples connus d'interpréteurs de commandes sont Bash (Linux, Mac), PowerShell (Windows) et aussi... Python!


Ici, nous procéderons un peu différemment. Les instructions seront plutôt compilées sur le PC pour produire un pseudo-code binaire (bytecode en anglais). Il s'agit d'octets qui ne peuvent pas être exécutés directement par un processeur, mais qui sont dans un format qui permet une interprétation plus facile puisque les analyses lexicale et syntaxique (parsing) du fichier source sont déjà réalisées. Généralement, ce format est indépendant du matériel et est sous une forme qui est difficile à lire pour l'être humain. Un programme en langage Java passe par un pseudo-code binaire lors de son exécution. Le résultat plus compact d'un pseudo-code binaire par rapport au code source d'origine a cependant des avantages en vitesse à l'exécution, pour le web par exemple avec le JavaScript.


Notre pseudo-code binaire sera inscrit dans la mémoire I2C externe du robot. Le ATmega324PA devra le lire et comprendre la signification des octets pour arriver à faire bouger le robot selon ce qui y est inscrit. Le robot ne fait donc pas l'interprétation directe des instructions d'un fichier source ou d'un interpréteur de commandes, mais d'une liste d'octets encodés ayant une signification équivalente.


Passer par un langage spécialisé pour programmer un robot est assez courant. La société FANUC a opté pour cette approche avec le langage Karel un peu différent de l'original. Cette approche permet le contrôle du robot, mais aussi la simulation et la vérification des opérations prévues pour le robot.


Lors du travail pratique 5, des données ont été lues mémoire. La mémoire contenait alors des données. Elle devra maintenant contenir des instructions à exécuter. Les procédures d'accès à la mémoire seront réutilisées ici, mais dans un contexte un peu différent puisqu'elles auront un effet direct sur les mouvements du robot.


Les paragraphes suivants décrivent les instructions et leur équivalent en pseudo-code binaire. Quelques détails sur la façon de les compiler sur le PC sont aussi fournis.


Instructions


Les commandes forment un langage très simple qui ressemble un peu à un assembleur. Les différentes commandes que vous aurez à considérer sont présentées sous la forme d'instructions binaires au tableau 1.


Instruction binaire

mnémonique

Description

0000 0001

dbt

début

0000 0010

att

attendre

0100 0100

dal

allumer la DEL

0100 0101

det

éteindre la DEL

0100 1000

sgo

jouer une sonorité

0000 1001

sar

arrêter de jouer la sonorité

0110 0000

mar

arrêter les moteurs

0110 0001

mar

arrêter les moteurs

0110 0010

mav

avancer

0110 0011

mre

reculer

0110 0100

trd

tourner à droite

0110 0101

trg

tourner à gauche

1100 0000

dbc

début de boucle

1100 0001

fbc

fin de boucle

1111 1111

fin

fin

Tableau 1: Jeu d'instructions du robot


On peut prendre un éditeur pour écrire un programme dans ce langage tout comme on le fait pour un programme en C/C++. Vous pouvez appeler le compilateur «progmem» sur votre machine Linux pour le compiler de la façon suivante pour produire un fichier de sortie contenant le pseudo-code binaire:


% progmem [-v] -o «fichierDeSortie» «fichierDentree»


Comme en C/C++, les instructions doivent être terminées par un point-virgule. Une instruction doit être écrite soit entièrement en minuscule, soit entièrement en majuscule, mais sans combinaison des deux. Un opérande ayant une valeur entre 0 et 255 peut suivre le terme mnémonique si l'instruction le prévoit. Les symboles %, # et // permettent d'introduire un commentaire sur la ligne. Voici un fichier qui donne un exemple de programme. Ce fichier contient des erreurs. Quand il y a des erreurs, le fichier de sortie n'est pas produit. Un bon exercice pourrait être d'enlever les lignes provoquant les erreurs pour voir comment faire une première compilation et de comprendre les instructions dont les détails sont donnés à la section suivante. L'option -v du programme progmem vous donne plus de détails sur l'opération de production du pseudo-code binaire (très utile).


Pseudo-code binaire (bytecode)


Le pseudo-code binaire sera organisé selon un format uniforme afin de faciliter sa lecture en mémoire. Une instruction est sous forme binaire et comprend 8 bits. Une instruction est toujours suivie d'un opérande de 8 bits. Cet opérande n'est pas significatif si l'instruction n'en prévoit pas. Néanmoins, le fait de le placer dans un format binaire rend l'organisation des octets plus uniforme. Autrement dit, les instructions sont aux adresses paires en mémoire alors que les opérandes (significatifs ou non) sont aux adresses impaires.


Instruction (obligatoire) (8 bits)

Opérande (obligatoire) (8 bits)

Tableau 2: Format des instructions du pseudo-code binaire


La seule exception à cette organisation est pour les deux premiers octets qui forment une donnée de 16 bits. Cette valeur donne le nombre d'octets total du pseudo-code binaire. Par exemple, s'il y a 8 instructions de code source, le fichier binaire aura une taille de 18 octets au total et la lecture des deux premiers octets permet d'obtenir cette information.


Il est toujours possible de consulter les octets du fichier de sortie en pseudo-code binaire avec la commande Linux suivante:


% od -v -t x1 «fichierDeSortie»


Description des instructions


Voici une description complète des instructions illustrées à l'aide d'exemples.


Début

Mnémonique: dbt

Code binaire: 0x01

Description: Indique le début du code. Le programme commence ici (et pas avant).

Opérande: non

Exemple:

att 25 ;# ne sera pas exécuté

dal 2 ;% ne sera pas exécuté

dbt ;# début de l'exécution

att 10 ;# attendre 250 ms


Attendre

Mnémonique: att

Code binaire: 0x02

Description: attendre 25 ms fois la valeur de l'opérande

Opérande: oui

Exemple:

att 25 ;# ne sera pas exécuté

dal 2 ;% ne sera pas exécuté

dbt ;# début

att 10 ;// attendre 250 ms (instruction exécutée)


Allumer les DEL

Mnémonique: dal

Code binaire: 0x44

Description: allumer les DEL en vert ou en rouge selon la valeur de l’opérande (1 pour le vert, 2 pour le rouge. Si l’opérande est autre que 1 ou 2, l'opérande est ignoré et rien ne change.

Opérande: oui

Exemple:

dal 1 ;# allumer la DEL en vert

dal 2 ;# allumer la DEL en rouge

dal 2 ;// ne fera rien puisque la DEL est déjà en rouge

dal 0 ;# ne fera rien, peu importe la situation


Éteindre les DEL

Mnémonique: det

Code binaire: 0x45

Description: éteindre la DEL directement.

Opérande: non

Exemple:

dal 1 ;# DEL en vert

det ;// éteindre la DEL

det ;# instruction sans effet puisque rien n’est allumé


Jouer une sonorité

Mnémonique: sgo

Code binaire: 0x48

Description: jouer une sonorité du tableau 3. Il faut activer une sortie en mode PWM avec la fréquence donnée dans le tableau. Si la valeur n'est pas dans le tableau, la commande est ignorée.

Opérande: oui

Exemple:

sgo 69 ;# émettre un son à 440 Hz

sgo 200 ;# instruction ignorée

sgo 45 ;// émettre un son à 110 Hz


Arrêter de jouer la sonorité

Mnémonique: sar

Code binaire: 0x09

Description: arrêter de jouer la sonorité en cours (s'il y en a une qui joue).

Opérande: non

Exemple:

sgo 54 ;% jouer un son à 180 Hz

sar ;# arrêter de jouer un son

sar ;# instruction sans effet


Arrêter les moteurs

Mnémonique: mar

Code binaire: 0x60 ou 0x61

Description: arrêter les deux moteurs

Opérande: non

Exemple:

...

mar ;# Arrêter

mar ;// instruction sans effet

...


Avancer

Mnémonique: mav

Code binaire: 0x62

Description: avancer en ligne droite à une vitesse donnée par (opérande / 255 * 100%)

Opérande: oui

Exemple:

mar ;# Le robot est immobile

mav 0 ;% Le robot reste immobile

mav 255 ;# vitesse de 100%

mav 128 ;# vitesse de 50% (50% du PWM)


Reculer

Mnémonique: mre

Code binaire: 0x63

Description: reculer en ligne droite à la vitesse (opérande / 255 * 100%)

Opérande: oui

Exemple:

mar ;# moteurs ne tournent pas

mre 0 ;// robot reste immobile

mre 255 ;# vitesse maximale vers l'arrière

mre 64 ;# vitesse de 25% vers l'arrière (25% du PWM)


Tourner à droite

Mnémonique: trd

Code binaire: 0x64

Description: virage du robot de 90 degrés à droite

Opérande: non


Tourner à gauche

Mnémonique: trg

Code binaire: 0x65

Description: virage du robot de 90 degrés à gauche

Opérande: non


Début de boucle

Mnémonique: dbc

Code binaire: 0xC0

Description: emmagasiner l'adresse du présent point d'exécution du code dans une variable pour pouvoir y revenir. De plus, créer une variable qui conservera l'état du compteur de boucle. La boucle s'exécutera une fois de plus que la valeur précisée par l'opérande. Donc, si l'opérande est zéro, le code ne s'exécutera qu'une seule fois. Il ne peut y avoir deux boucles imbriquées (actives au même moment).

Opérande: oui

Exemple: voir l'instruction «fin de boucle» pour un exemple


Fin de boucle

Mnémonique: fbc

Code binaire: 0xC1

Description: si le compteur n'est pas égal à 0, retourner à l'adresse de début de boucle et décrémenter le compteur de boucle. Sinon, ne rien faire.

Opérande: non

Exemple:

...

dbc 100 ;// la boucle s'exécutera 101 fois

dal 1 ;# allumer la DEL en vert

att 1 ;# attendre 25 ms

det ;// éteindre la DEL

att 1 ;# attendre 25 ms

fbc ;# boucler à l'instruction dbc précédente

...

dbc 0 ;# faire le code suivant une fois

dal 2 ;% allumer la DEL en rouge

att 1 ;# attendre 25 ms

det 1 ;% éteindre la DEL

att 1 ;# attendre 25 ms

fbc ;// boucler à l'instruction dbc précédente

...


Fin

Mnémonique: fin

Code binaire: 0xFF

Description: fin de l'exécution du code. Le code qui suit cette instruction sera ignoré. Aucun son ne sera émis après cette instruction et le robot s'arrêtera également.

Opérande: non

Exemple:

att 25 ;# attendre 625 ms

fin ;% fin du programme et robot immobile

att 25 ;# instruction qui ne sera même pas lue


Fréquences des notes MIDI


Si vous êtes musicien, vous pouvez relier la fréquence d'un signal PWM et les notes d'une chanson pour rendre le comportement de votre robot plus agréable. Si vos connaissances en musique sont plus limitées, votre robot devra se contenter d'émettre des sons quelconques. Cette remarque n'a aucune conséquence sur quelque forme d'évaluation que ce soit. Par contre, il est toujours bon de connaître la relation entre la fréquence d'un signal et les notes de musique. Plus d'informations sont disponibles sur Wikipédia pour l'interface MIDI, et le cycle des notes. Voici un tableau des notes MIDI et des fréquences correspondantes.


Note (donnée)

Fréquence (en Hz)

Période (en ms)

Temps actif (en ms)

45

110

9.09

4.545

46

116.54

8.58

4.290

47

123.47

8.10

4.050

48

130.81

7.64

3.822

49

138.59

7.22

3.608

50

146.83

6.81

3.405

51

155.56

6.43

3.214

52

164.81

6.07

3.034

53

174.61

5.73

2.863

54

185.00

5.41

2.703

55

196.00

5.10

2.551

56

207.65

4.82

2.408

57

220

4.55

2.273

58

233.08

4.29

2.145

59

246.94

4.06

2.025

60

261.63

3.82

1.911

61

277.18

3.61

1.804

62

293.66

3.41

1.703

63

311.13

3.21

1.607

64

329.63

3.03

1.517

65

349.23

2.86

1.432

66

369.99

2.70

1.351

67

392.00

2.55

1.276

68

415.30

2.41

1.204

69

440

2.27

1.136

70

466.16

2.15

1.073

71

493.88

2.02

1.012

72

523.25

1.91

0.956

73

554.37

1.80

0.902

74

587.33

1.70

0.851

75

622.25

1.61

0.804

76

659.26

1.52

0.758

77

698.46

1.43

0.716

78

739.99

1.35

0.676

79

783.99

1.28

0.638

80

830.61

1.20

0.602

81

880

1.14

0.568

source: https://www.inspiredacoustics.com/en/MIDI_note_numbers_and_center_frequencies


Tableau 3: fréquences des notes MIDI


Travail à effectuer


Le travail à faire est en deux parties. Il faut d'abord écrire le code qui permet au robot de lire le bytecode en mémoire externe et de l'interpréter correctement, instruction par instruction. Le problème se découpe bien et le travail peut être réparti de façon à ce que chacun des membres de l'équipe puisse contribuer à l'architecture du code autant qu'à la programmation.


Nous vous suggérons quelques trucs pour la mise au point des systèmes logiciels et matériels. Ces conseils vous seront fort utiles puisque la quantité de code à écrire augmentera d'ici la fin de la session.


Avant d'effectuer l'interprétation proprement dite, il serait souhaitable que le robot effectue une petite séquence de démarrage. Ce peut être, par exemple, de faire clignoter la DEL 2 fois rouge et 2 fois vert au départ. De cette façon, on sait que le robot a démarré et qu'il est dans une phase connue très rapidement. Quand les piles sont faibles ou qu'il y a un problème sérieux, on peut le réaliser tout de suite si le robot ne traverse même pas sa séquence de démarrage.


Une deuxième partie consiste à écrire des instructions dans le langage présenté plus haut, de les compiler avec progmem sur le PC, et de les charger en mémoire externe sur la carte mère. Vous êtes libre d'écrire les instructions que vous désirez. Par contre, chaque instruction doit être utilisée au moins une fois. Essayez d'écrire un ensemble d'instructions qui donne une belle chorégraphie à votre robot!


Il vous faudra utiliser l'interface RS-232 pour écrire votre bytecode en mémoire en écrivant un petit programme utilitaire pour effectuer cette tâche. Ce programme d'écriture est donc différent de celui qui ira lire les octets en mémoire. Il faudra peut-être ajuster quelque peu le code inscrit pour le problème 3 de la semaine 5 pour supporter le transfert des octets à charger en mémoire du PC vers la carte. Normalement, ce travail devrait se faire facilement en rajoutant une routine de transfert. Vous n'aurez qu'à ajuster la configuration du périphérique interne de l'AVR pour permettre la réception. Le programme serieViaUSB peut être appelé pour transmettre le bytecode vers la carte par RS-232 via le câble USB avec la commande suivante:


% serieViaUSB -e -f «fichierDeSortie»


Il peut être utile d’invoquer également la commande serieViaUSB avec l’option -h pour découvrir d’autres options possibles avec cette commande. C’est d’ailleurs une bonne habitude à prendre avec bien des commandes Linux puisque la majorité d’entre elles offrent une description de son fonctionnement et diverses options possibles pour modifier son exécution.


Il faudra charger le fichier de sortie complet en mémoire externe (les instructions utilisées autant que celles inutilisées et les deux premiers octets qui donnent la dimension du programme). Une fois le fichier de sortie inscrit en mémoire externe sur la carte, il faudra charger le programme de lecture et d'interprétation des octets en mémoire. Il faudra peut-être osciller quelques fois entre ces deux programmes à charger dans le ATmega324PA lors de la mise au point. Il s'agit d'un inconvénient qui peut être un peu gênant. Par contre, les combiner en un seul programme n'est pas évident (bien que possible) mais est proscrit pour cet exercice. Il faut donc écrire deux programmes différents.


Les derniers détails concernent les sonorités à jouer. Il faudra brancher une sortie PWM au fil rouge du piézoélectrique de votre robot. Le fil noir devra être connecté à une broche adjacente qu'il faudra configurer en sortie à la valeur constante de zéro volt. C'est une façon d’obtenir une mise à la masse sur ce fil. Il est à noter que les fils rouge et noir pourraient être inversés. Le piézoélectrique n'a pas réellement de polarité. Cependant, puisque la carte mère sera bientôt passablement garnie de fils, il est souhaitable de conserver un peu d'ordre dans les couleurs. Autre petit détail important. Il est vrai qu'il faut un signal ayant une forme PWM pour générer un son en sortie. Par contre, le mode CTC d'une minuterie peut être utilisé pour générer cette onde. Ce mode d'opération est différent de celui utilisé pour faire fonctionner les moteurs qui était (et est toujours) le mode PWM. On notera aussi qu'il devrait être possible de jouer une sonorité pendant que le robot se déplace. Il devrait être possible dans la documentation de Atmel de trouver une relation liant la fréquence de PWM à générer et les paramètres à ajuster dans la minuterie, dont la valeur de comparaison maximale à atteindre (TOP).


Aussi, comme vous avez progressé significativement dans le cours corequis INF1015, on vous demande d’avoir quelques classes pour ce travail pratique. Il n’est pas obligatoire d’avoir tout en orienté objet, mais il serait intéressant pour vous de regrouper quelques fonctions-membres avec des données sur lesquelles elles opèrent pour former quelques classes simples et tirer avantage de ce paradigme de programmation dans le code plus long à écrire pour ce travail pratique. Ceci devrait aussi faciliter la répartition du code en quelques fichiers et permettre une meilleure répartition du travail à effectuer entre les membres de l’équipe. Deux ou trois classes peuvent amplement suffirent.


Enfin, il est tout fait naturel d’utiliser la librairie développée au travail pratique 7 et aussi d’utiliser le mécanisme de débogage mis en place au travail pratique 8 pour continuer à améliorer ses pratiques de développement logiciel. Si vous ne voulez pas toucher au code du répertoire du travail pratique 7 qui a été soumis pour correction, vous pouvez le recopier dans le répertoire pour le travail pratique 9. Vous pourrez aussi continuer d’ajouter un peu de code au besoin à votre librairie ici tout en ayant la paix d’esprit de savoir que ces modifications n’auront pas d’impact sur la correction du travail pratique 7.


Dans SimulIDE


Certaines parties du code peuvent être développées dans SimulIDE pour ce travail pratique, mais les limites avec la mémoire externe mentionnées au travail pratique 5 restent présentes. De plus, le chargement d’octets vers le EEPROM interne du ATmega324 est quelque peu compliqué avec l’interface graphique avec le RS232 de SimulIDE. On peut contourner ce problème en utilisant l’interface RS232 vers un vrai port du PC de SimulIDE, mais il faut utiliser des commandes Linux un peu exotiques pour y parvenir. On perd aussi un peu le sens du travail à réaliser. Aussi, comme le robot bouge et s’anime en fonction du peudo-code binaire, il est vraiment plus agréable de travailler avec le vrai robot pour le travail pratique. Cette remarque restera tout aussi vraie pour le projet final d’ailleurs.


Qualité du code


Vous vous rendrez compte que le code à écrire nécessite des boucles et des tests en tout genre. La règle 54 sur les instructions break et continue est à regarder. Celles sur les instructions conditionnelles de 56 à 59 également. Assez naturellement, pour mieux respecter les règles précédentes, revoir l’usage des espaces blancs (règles de 77 à 82) pour augmenter la lisibilité s’impose.


Suivi et remise (IMPORTANT)


Pour la semaine prochaine, avant l'entrevue de laboratoire: