« SE4 2022/2023 EC4 » : différence entre les versions
(199 versions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 20 : | Ligne 20 : | ||
= Travail réalisé = | = Travail réalisé = | ||
= Semaine 1 = | = Semaine 1 = | ||
== Début afficheurs 7 segments == | |||
* Jusqu'à présent, je me suis concentré sur le sous-système avec les afficheurs 7 segments : | * Jusqu'à présent, je me suis concentré sur le sous-système avec les afficheurs 7 segments : | ||
**Inspection du routage du PCB pour voir comment était câblé le connecteur au µC | **Inspection du routage du PCB pour voir comment était câblé le connecteur au µC | ||
Ligne 36 : | Ligne 37 : | ||
Timothé : Ok parfait. | Timothé : Ok parfait. | ||
== Début matrice de LED == | |||
* Je suis passé sur le sous-système avec la matrice de LED : | * Je suis passé sur le sous-système avec la matrice de LED : | ||
**Inspection du routage du PCB pour voir comment était câblé le deuxième connecteur AVR-ISP au deuxième µC | **Inspection du routage du PCB pour voir comment était câblé le deuxième connecteur AVR-ISP au deuxième µC | ||
Ligne 58 : | Ligne 59 : | ||
Timothé : Dans ce cas, je propose que je mette mes questions dans une rubrique à part, et pour le reste nous pouvons continuer d'interagir comme ce qu'on a fait là. | Timothé : Dans ce cas, je propose que je mette mes questions dans une rubrique à part, et pour le reste nous pouvons continuer d'interagir comme ce qu'on a fait là. | ||
== Début maitre SPI == | |||
* J'ai commencé à regarder le troisième sous-système qui sera le maitre SPI | * J'ai commencé à regarder le troisième sous-système qui sera le maitre SPI | ||
**J'ai regardé le makefile et fait quelques recherches sur la façon de programmer ce µC, afin de pouvoir le programmer par la suite via la puce FTDI | **J'ai regardé le makefile et fait quelques recherches sur la façon de programmer ce µC, afin de pouvoir le programmer par la suite via la puce FTDI | ||
Ligne 76 : | Ligne 78 : | ||
Je ne sais pas encore si cela est faisable ni si c'est pratique... Je vais essayer. | Je ne sais pas encore si cela est faisable ni si c'est pratique... Je vais essayer. | ||
Est-ce mieux expliqué, où pas du tout ? | Est-ce mieux expliqué, où pas du tout ? | ||
ReX : OK. | |||
= Semaine 2 = | = Semaine 2 = | ||
Ligne 81 : | Ligne 85 : | ||
**J'ai observé toute la connectique, et repéré où chaque broches des différents connecteurs (J18, J20, J17 et J21) étaient branchées sur les trois ATMega328p | **J'ai observé toute la connectique, et repéré où chaque broches des différents connecteurs (J18, J20, J17 et J21) étaient branchées sur les trois ATMega328p | ||
**J'ai pu constater qu'il n'y avait pour aucun des deux sous-systèmes la broche MISO connectée, il n'est donc pas possible pour les deux esclaves d'envoyer des messages au maitre | **J'ai pu constater qu'il n'y avait pour aucun des deux sous-systèmes la broche MISO connectée, il n'est donc pas possible pour les deux esclaves d'envoyer des messages au maitre | ||
**Cela paraissait évident, cependant j'ai vérifié quand même, les broches des connecteurs J18/J20 et J17/J21 se faisant face, sont bien connectées de la même manière à leur uC respectifs | **Cela paraissait évident, cependant j'ai vérifié quand même, les broches des connecteurs J18/J20 et J17/J21 se faisant face, sont bien connectées de la même manière à leur uC respectifs, à noter que la broche /SS de l'ATMega328p de la matrice de LED, n'est pas connectée à la broche /SS du maitre spi mais à la broche 11 (afin de sélectionner un esclave, le maitre doit avoir autant de broches de /SS que d'esclave, afin de pouvoir les adresser un à un). | ||
ReX : OK. Si vraiment il fallait envoyer des données dans le sens esclaves vers maître, il suffirait de connecter les broches MISO des connecteur AVR-ISP. | |||
= Questions | Timothé : Oui en effet, nous pouvons passer par les connecteurs AVR-ISP qui sont chacun reliés à la broche MISO de leur ATMega328p. | ||
= Questions de la semaine 2 = | |||
Timothé : Oui l'AVR-ISP, je n'avais encore jamais utilisé ce type de connecteur. J'ai donc fait des recherches pour savoir quelles broches étaient utilisées par ces connecteurs, et notamment laquelle de ces broches était relié au 5V de l'ATMega328p sur le PCB, pour éviter de l'envoyer là où il fallait pas et risquer de cramer quelque chose... | Timothé : Oui l'AVR-ISP, je n'avais encore jamais utilisé ce type de connecteur. J'ai donc fait des recherches pour savoir quelles broches étaient utilisées par ces connecteurs, et notamment laquelle de ces broches était relié au 5V de l'ATMega328p sur le PCB, pour éviter de l'envoyer là où il fallait pas et risquer de cramer quelque chose... | ||
Ligne 91 : | Ligne 98 : | ||
Timothé : Dans ce cas, je n'avais pas encore fait le lien entre ce que j'avais utilisé et le fait que c'était un connecteur AVR-ISP... | Timothé : Dans ce cas, je n'avais pas encore fait le lien entre ce que j'avais utilisé et le fait que c'était un connecteur AVR-ISP... | ||
Timothé : En fait, j'avais simplement imaginé envoyer des "ordres" aux autres sous-systèmes au lieu d'envoyer directement ce qu'il devait afficher. Par exemple : envoyer seulement le caractère "A" à l'ATMega328p de la matrice de LED, et non pas le tableau complet contenant le "A" réellement affichable. Il faudrait donc que le sous-système lise le caractère qui lui a été envoyé et l'interprète afin d'afficher ce qui correspond. Je ne sais pas encore si cela est faisable ni si c'est pratique... Je vais essayer. Est-ce mieux expliqué, où pas du tout ? | Timothé : En fait, j'avais simplement imaginé envoyer des "ordres" aux autres sous-systèmes au lieu d'envoyer directement ce qu'il devait afficher. Par exemple : envoyer seulement le caractère "A" à l'ATMega328p de la matrice de LED, et non pas le tableau complet contenant le "A" réellement affichable. Il faudrait donc que le sous-système lise le caractère qui lui a été envoyé et l'interprète afin d'afficher ce qui correspond. Je ne sais pas encore si cela est faisable ni si c'est pratique... Je vais essayer. Est-ce mieux expliqué, où pas du tout ? | ||
Ligne 102 : | Ligne 108 : | ||
= Semaine 3 = | = Semaine 3 = | ||
J'ai continué sur la communication SPI : | |||
*Je me suis d'abord penché sur la communication avec le sous-système comportant les afficheurs 7 segments | |||
**J'ai eu un peu plus de mal à mettre cela en place, ce qui m'a finalement débloqué, est de changer la fréquence de l'horloge cadencant la communication, je l'ai finalement fixée à la fréquence du µC divisé par 64 (avec une fréquence plus élevée la communication cesse de fonctionner) | |||
**Je peux désormais ordonner l'affichage d'un tableau au sous-système des afficheurs 7 segments depuis le sous-système maître | |||
*J'ai également commencé la programmation de la communication SPI avec le deuxième sous-système | |||
**La matrice est capable d'afficher n'importe quelle lettre de l'alphabet, il sera donc possible d'afficher des mots (pas de défilement prévu pour l'instant cependant...) | |||
**En revanche, la communication ne passe pas encore sur ce système, je travaille dessus en ce moment | |||
ReX : OK, merci pour le rapport. | ReX : OK, merci pour le rapport. | ||
*La communication SPI est désormais également fonctionnelle sur la matrice de LEDs | *La communication SPI est désormais également fonctionnelle sur la matrice de LEDs (il est possible d'écrire des mots en faisant apparaitre les lettres successivement) ; | ||
*La commande en séquence des deux sous-systèmes est donc terminé. | |||
*La commande en séquence des deux sous-systèmes est donc terminé | |||
ReX : Ce serait bien d'expliquer les problèmes rencontrés et les solutions apportées. | ReX : Ce serait bien d'expliquer les problèmes rencontrés et les solutions apportées. | ||
Je vais passer sur l'ordonnancement des sous-systèmes : | |||
*Pour l'instant j'imagine faire 2 tâches, chacune s'occupant de la communication SPI d'un des deux sous-systèmes | |||
*Cela ne correspond pas tout à fait à ce que l'on avait dit, mais cela me parait intéressant, je vais essayer... | |||
ReX : Dans un premier temps pourquoi pas, cela mettra les problèmes en exergue. | ReX : Dans un premier temps pourquoi pas, cela mettra les problèmes en exergue. | ||
= Questions semaine 3 = | = Questions de la semaine 3 = | ||
ReX : Ce serait bien d'expliquer les problèmes rencontrés et les solutions apportées. | ReX : Ce serait bien d'expliquer les problèmes rencontrés et les solutions apportées. | ||
Ligne 133 : | Ligne 138 : | ||
= Problèmes rencontrés et solutions apportées = | = Problèmes rencontrés et solutions apportées = | ||
Problèmes | == Problèmes avec la partie afficheurs 7 segments == | ||
Pour ce sous-système, j'ai eu du mal à comprendre le code de prime à bord. | Pour ce sous-système, j'ai eu du mal à comprendre le code de prime à bord. | ||
Je ne comprenais pas l'intérêt de | Je ne comprenais pas l'intérêt de <code>segfont.c</code> puisqu'il comprenait tout les caractères ASCII, alors que les afficheurs peuvent seulement afficher 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, b, C, d, E, F. | ||
J'avais également du mal à comprendre les fonctions d'affichages | |||
ReX : Ce fichier contient la configuration souhaitée pour les segments (chaque bit correspond à un segment) pour chaque caractère ASCII, il est possible d'afficher au delà des caractères hexadécimaux, même si certains caractères ne sont pas très reconnaissables. | |||
J'avais également du mal à comprendre les fonctions d'affichages <code>display_set</code>, <code>display_ascii</code> et <code>display_next</code>. | |||
Notamment l'utilité de | Notamment l'utilité de <code>display_ascii</code> puisque très peu de caractères de ce tableau sont finalement affichables. Ensuite, <code>display_next</code> qui pouvait seulement utiliser la fonction <code>display_ascii</code>. | ||
ReX : Même remarque que ci-dessus. | |||
Solutions : | Solutions : | ||
Ligne 149 : | Ligne 157 : | ||
Finalement, j'ai créé une constante dont chacune des valeurs correspond à chaque caractères affichables. | Finalement, j'ai créé une constante dont chacune des valeurs correspond à chaque caractères affichables. | ||
Je n'utilise donc plus la fonction | ReX : Vous avez donc redéfini <code>segfont.c</code>, c'est dommage de perdre du temps. | ||
Je n'utilise donc plus la fonction <code>display_ascii</code> ni <code>display_next</code> et donc je n'utilise plus non plus le fichier <code>segfont.c</code>. | |||
ReX : Même remarque, c'est une faiblesse de ne pas comprendre un code pour le réutiliser sans réinventer la roue. | |||
Désormais, il suffit pour afficher un caractère d'utiliser la fonction <code>display_set</code>, elle prend en paramètre le tableau <code>groupes[]</code>, qui comprend chaque segment de tous les afficheurs 7segments, ainsi que le numéro de l'afficheur que l'on souhaite, et enfin la constante correspondant au caractère que l'on souhaite afficher. | |||
Cela signifie que je n'utilise plus la fonction <code>cursor_set</code> également, j'utilise directement le paramètre <code>num</code> de la fonction pour sélectionner l'afficheur. | |||
ReX : Même remarque. | |||
les constantes que j'ai créées sont les suivantes : | les constantes que j'ai créées sont les suivantes : | ||
ZERO 63 | |||
UN 6 | |||
DEUX 91 | |||
TROIS 79 | |||
QUATRE 102 | |||
CINQ 109 | |||
SIX 125 | |||
SEPT 39 | |||
HUIT 127 | |||
NEUF 111 | |||
A 119 | |||
B 124 | |||
C 57 | |||
D 94 | |||
E 121 | |||
F 113 | |||
OFF 0 | |||
ReX : OK un sous-ensemble de <code>segfont.c</code> donc. | |||
Timothé (Edit du 05/09/23): J'utilise désormais la fonction <code>display_ascii</code> et donc le fichier <code>segfont.c</code>. Voir [[#Simplification du code de réception d'informations des afficheurs 7 segments]] | |||
== Problèmes avec la matrice de LED == | |||
Pour ce sous-système j'ai eu moins de difficultés, l'organisation est là même que l'autre sous-système et il y avait moins de choses. | |||
J'ai voulu afficher l'alphabet sur la matrice, cependant, le tableau que l'on manipule pour contrôler la matrice de LEDs, n'était pas dans le bon "sens", c'est-à-dire, avec chacune de ses cases correspondant à la même LED sur la matrice dans l'espace. | |||
Par exemple sur une matrice 2,2 les LEDs sont placées de la manière suivante : | |||
1 2 | |||
3 4 | |||
tandis que les cases du tableau, serait disposées ainsi également : | |||
1 2 | |||
3 4 | |||
Solutions : | |||
Après quelques tests, j'ai pu constater que la matrice était pivotée de 90° vers la droite et que les cases étaient en miroir par rapport au sens de lecture. | |||
J'ai donc de nouveau fait des tests jusqu'à ce que je parvienne retirer le miroir puis à pivoter la matrice de 90° vers la gauche. | |||
Ensuite j'ai créé toutes les lettres de l'alphabet avec le système de tableau que j'avais fait. | |||
Voici par exemple la lettre A : | |||
unsigned char A[]={ | |||
0,0,0,0,0,0,0,0, | |||
0,0,0,ON,ON,0,0,0, | |||
0,0,ON,0,0,ON,0,0, | |||
0,0,ON,ON,ON,ON,0,0, | |||
0,0,ON,0,0,ON,0,0, | |||
0,0,ON,0,0,ON,0,0, | |||
0,0,0,0,0,0,0,0, | |||
0,0,0,0,0,0,0,0, | |||
}; | |||
<code>ON</code> correspondant à une LED allumé et <code>0</code> une LED éteinte. | |||
ReX : Mettons, je vous accorde le sens de la matrice. | |||
== Problèmes avec les communications SPI == | |||
J'ai commencé par établir le lien entre les afficheurs 7segments et le maitre. Je ne parvenais pas à transmettre les données, puis à les afficher correctement. | |||
Solutions : | |||
J'ai finalement modifié très légèrement la facon d'envoyer les données et de les recevoir. | |||
Du côté du maitre SPI, la solution fut de sélectionner un prescaler pour l'horloge cadencant la communication. En effet, de prime à bord il n'y en avait pas, et j'ai supposé que l'horloge étant trop rapide cela ne fonctionnait pas. Puisque effectivement, après avoir tester différentes cadences d'horloge, j'ai remarquer que lorsqu'elle était trop élevée la communication ne passait plus. | |||
ReX : C'est possible, le PCB est peut être mal optimisé pour une communication SPI à haute vitesse. Comment avez-vous connecté les broches entre elles ? Par des câbles (cela pourrait perturber le signal) ? Avec un plot de soudure ? | |||
Timothé : Je l'ai fait avec des câbles. En effet, il sera peut-être possible d'augmenter la cadence de l'horloge une fois un plot de soudure fait. Cela réduira grandement la longueur de la ligne et donc les perturbations. | |||
Cela m'a permit d'établir la communication avec les afficheurs 7 segments. Par la suite, j'ai légèrement modifié la façon de recevoir pour la matrice, afin que ce soit identique aux afficheurs. Ce qui ne fonctionnait pas, mais j'avais simplement oublié d'initialiser la communication SPI... | |||
Finalement, il suffit pour les deux sous-système d'envoyer avec la communication SPI un tableau contenant les caractères souhaités. Ces caractères sont envoyés un à un et sont affiché de la manière suivante : | |||
Afficheurs : 2 modes possibles, pour le premier, le premier caractère va au premier afficheur et ainsi de suite. Pour le second, on sélectionne d'abord l'afficheur souhaité puis on envoit son caractère, et ainsi de suite. | |||
Matrice : les caractères sont affichés les uns après les autres avec un intervalle de 800 ms, ce qui permet d'écrire des messages. | |||
(Les fichiers <code>serial.c</code> et <code>serial.h</code> ne sont pas utiles pour les sous-systèmes afficheurs et matrice puisque n'ayant pas de MISO ils ne peuvent remonter d'informations). | |||
ReX : Je ne vois pas trop le rapport entre UART et SPI, mais effectivement les broches RX et TX des deux esclaves n'étant pas utilisées, le code pour l'UART est assez inutile sur les esclaves. | |||
= Semaine 4 = | |||
Création de la rubrique "Problèmes rencontrés et solutions apportées" | |||
= Questions de la semaine 4 = | |||
J'ai créé une rubrique "Problèmes rencontrés et solutions apportées". | |||
Son contenu correspond-t-il à ce que vous souhaitiez ? | |||
ReX : Oui cela permet de mieux comprendre ce qui t'as arrêté. | |||
ReX : Il manque une vidéo de démonstration de l'affichage des caractères sur la matrice et sur les 7 segments. | |||
ReX : Il manque aussi les programmes ... | |||
Timothé : Voir [[#Rendu Intermédiaire]] pour les programmes et [[#Vidéos rendues]] pour les vidéos, plus exactement [[Media:Video demonstration avec 7seg.mp4|celle-ci]] | |||
= Semaine 6 = | |||
Comme vous avez pu le constater, j'ai malheureusement eu un peu moins de temps pour avancer sur le projet dernièrement et ai donc également été moins actif sur le wiki. | |||
ReX : Oui c'est vu. Dommage. | |||
*Documents rendus (voir [[#Rendu Intermédiaire]] pour les codes): | |||
**Codes de la partie 7 segments | |||
**Codes de la partie matrice de LED | |||
**Codes de la partie maitre SPI (fonctionnement séquentiel) | |||
**Début du code d'ordonnancement | |||
**[[Media:Video demonstration avec 7seg.mp4|Vidéo]] démontrant le fonctionnement séquentiel | |||
***Affichage de "bonjour" sur la matrice de LED | |||
***Affichage de FEDCBA sur les afficheurs 7segments (en utilisant la sélection manuel des afficheurs pour afficher à l'envers ce qui est envoyé) | |||
*Ordonnancement : | |||
** J'ai commencé à reprendre bien en détail le fonctionnement de l'ordonnanceur que nous avions mis en place dans le TP ordonnancement du semestre précédent, pour l'adapter à mon sujet | |||
= Semaine 7 = | |||
== Modification du codage des caractères en mémoire (matrice de LED) == | |||
<b>ReX</b> : Le codage des caractères prend trop de place en mémoire, limiter à 8 octets par caractère. | |||
<b>Timothé (EDIT)</b> : J'ai modifié en conséquence la fonction <code>matrix_set</code>. | |||
Précédent code : | |||
void matrix_set(int *groupes,unsigned char *matrix){ | |||
int i; | |||
for(i=0;i<NB_ROWS*NB_COLS;i++) | |||
groupes[indirect_matrix[i]]=matrix[i]; | |||
} | |||
Nouveau code : | |||
void matrix_set(int *groupes,unsigned char *matrix){ | |||
int i; | |||
for(i=0;i<NB_ROWS*NB_COLS;i++) | |||
groupes[indirect_matrix[i]]=((matrix[i/8])&(1<<(7-(i%8))))*BRIGHTNESS; | |||
} | |||
ReX : Heu non. La valeur <code>1<<(7-(i%8))</code> dépend de la position du bit. Ca ne peux pas donner la valeur attendue. Il faut ajouter une expression conditionnelle pour ramener à 0 ou 1. | |||
Timothé : En effet, j'ai corrigé cela, voir [[#Suite de la modification du codage des caractères en mémoire (matrice de LED)]]. | |||
Pour la façon dont je code les octets, il faut pour les décoder, les balayer du bit de poids fort au bit de poids faibles. De la manière précédente, seulement les caractères symétriques s'affichaient correctement, le problème est résolu. | |||
Cela permet pour chacune des huit rangées de LEDs sur la maquette, de récupérer SON octet du caractère en mémoire (grâce à matrix[i/8]). | |||
Et grâce au masque, au décalage, à la multiplication par <code>BRIGHTNESS</code> on allume ou non chacune des LEDs en fonction de la valeur de l'octet récupéré. | |||
Ainsi les caractères prennent désormais 8 octets en mémoire. Pour tester cela j'ai utilisé le caractère B voici le précédent code : | |||
unsigned char B[]={ | |||
0,0,ON,0,0,0,0,0, | |||
0,0,ON,0,0,0,0,0, | |||
0,0,ON,0,0,0,0,0, | |||
0,0,ON,ON,ON,ON,0,0, | |||
0,0,ON,0,0,ON,0,0, | |||
0,0,ON,0,0,ON,0,0, | |||
0,0,ON,ON,ON,ON,0,0, | |||
0,0,0,0,0,0,0,0, | |||
}; | |||
puis le nouveau code du caractère : | |||
unsigned char B[]={32, 32, 32, 60, 36, 36, 60,0}; | |||
Cela fonctionne bien, mais les LEDs n'ont pas tout à fait toutes la même intensité. | |||
ReX : Vu l'erreur notée ci-dessus, forcément. | |||
<b>EDIT :</b> les caractères B, O, N, J, U, R sont codés de cette nouvelle façon | |||
unsigned char B[]={32, 32, 32, 60, 36, 36, 60, 0}; | |||
unsigned char O[]={0, 0, 60, 36, 36, 60, 0, 0}; | |||
unsigned char N[]={0, 68, 100, 84, 76, 68, 0, 0}; | |||
unsigned char J[]={0, 28, 8, 8, 8, 40, 16, 0}; | |||
unsigned char U[]={0, 0, 36, 36, 36, 36, 24, 0}; | |||
unsigned char R[]={0, 60, 36, 60, 48, 40, 36, 0}; | |||
Une nouvelle [[Media:Video demonstration nouveau codage caractere.mp4|vidéo de démonstration]] utilisant ce codage est disponible (nous pouvons effectivement y distinguer que toutes les LEDs n'ont pas la même intensité...) | |||
Timothé (Edit du 05/09/23) : Le problème d'intensité a été réglé, voir [[#Suite de la modification du codage des caractères en mémoire (matrice de LED)]]. | |||
ReX : J'avance lentement car il faut que je corrige la syntaxe du Wiki en même temps que je le lis. Vidéo de démonstration non jointe au Wiki. | |||
== Estimation du temps nécessaire à l'envoi des informations aux TLC == | |||
<b>ReX</b> : Il faudrait justifier les 24ms d'attente par l'analyse de la durée du code d'envoi des informations aux TLC des 7 segments. | |||
<b>Timothé</b> : Une fois les informations à envoyer aux TLC reçus : | |||
*Il y a un <code>switch</code>, dans le pire cas nous allons au dernier <code>case</code> et faisons <b>16 opérations</b> pour y arriver. | |||
*Appel de <code>display_set</code> <b>12 opérations</b> | |||
**Le saut sur la fonction : Sauvegarde du <code>SP</code> --> 4 opérations (masquage pour partie haute et push, masquage pour la partie basse et push), puis chargement de l'adresse de la fonction 2 pulls --> 2 opérations | |||
**Chargement de <code>groupes</code> qui est un pointeur donc une adresse en mémoire sur 2 octets il faut 2 pulls --> 2 opérations | |||
**Chargement de <code>display_number</code> qui est de type <code>uint8_t</code>, un pull --> 1 opération | |||
**Chargement d'une constante toujours codé sur un octet, un pull --> 1 opération | |||
**Retour de la fonction, récupération du <code>SP</code> précédent 2 pulls --> 2 opérations | |||
*Appel de <code>_display_set</code> <b>15 opérations</b> | |||
**Saut sur la fonction, comme la fonction précédente --> 6 opérations | |||
**Chargement de <code>groupes</code> , idem que fonction précédente --> 2 opérations | |||
**Chargement de <code>num</code>, de type <code>int</code> donc sur deux octets, deux pulls --> 2 opérations (<code>num</code> récupérant <code>display_number</code> pourrait être de type <code>uint8_t</code> pour économiser de la place et 1 opération) | |||
**Chargement de <code>value</code>, de type <code>int</code> donc sur deux octets, deux pulls --> 2 opérations (idem que pour <code>num</code>, <code>value</code> pourrait être de type <code>uint8_t</code> car la constante qu'elle récupère est toujours codé sur un octet) | |||
**Retour de la fonction, récupération du <code>SP</code> précédent 2 pulls --> 2 opérations | |||
*Exécution de la fonction <code>_display_set</code> <b>59 opérations</b> | |||
**Multiplication --> 1 opération | |||
**Affectation --> 1 opération | |||
**Test conditionnel --> 1 opération | |||
**Boucle <code>for</code> (8 itérations) --> 7*8 = 56 opérations | |||
***Par boucle, 2 opérations pour son fonctionnement (premier tour : affectation et vérification condition (2 opérations) ; les tours suivants : vérification condition et incrémentation (2 opérations)) | |||
***Par boucle, une addition, un décalage, une multiplication, une affectation, un autre décalage soit --> 5 opérations | |||
Ce qui fait un total de <b>86 opérations</b> | |||
ReX : Tu as bien écrit qu'il y avait 12 appels à une fonction demandant 59 cycles et tu fais une addition ? C'est bien ce que je dois comprendre ? | |||
Timothé : Non ce n'est pas ça. Déjà oublions le <code>switch</code> je vais le retirer. Je disais que lorsque le µC reçoit un octet, il commence par appeler la fonction <code>display_set</code>, j'ai estimé que son appel nécessitait 12 opérations (voir le détail). Ensuite il appel la fonction <code>_display_set</code>, j'ai estimé que son appel nécessitait 15 opérations (voir le détail). Enfin, j'ai estimé l'exécution de cette fonction à 59 opérations, 7 opérations dans la boucle <code>for</code> qui est itérée 8 fois (56 opérations) plus les 3 opérations avant la boucle <code>for</code>. Pour un total de 70 opérations (<code>switch</code> retiré) afin d'envoyer les informations d'un octet reçu aux TLC (voir la suite des calculs ci-dessous). | |||
Timothé : Est-ce mieux expliqué ? | |||
<code>FCPU</code> = 16 MHz --> période = 6,25*10⁻⁸ s | |||
24ms/6,25*10⁻⁸ = 384000 cycles d'horloges (c'est-à-dire, à cette fréquence, 24 ms représentent 384000 cycles), ce qui d'après mon nombre d'opération représente une moyenne de 4465 cycles par opération (si on veut arriver aux 24ms d'exécution pour l'envoi des informations aux TLC). | |||
En effet, certaines opérations étant complexes peuvent prendre quelques dizaines de cycles pour être exécutées. Cependant 4465 me parait très excessif... | |||
Pour ce | Ce qui pourrait expliquer cette erreur : | ||
*Fréquence du uC pas à 16 MHz | |||
*La fonction <code>_delay_ms</code> qui n'est pas toujours très précise selon les conditions dans lesquelles elle est utilisée | |||
*Une erreur majeure de ma part dans l'estimation du nombre d'opérations | |||
En prenant une vingtaine de cycle par opération, j'estime un temps d'exécution pour l'envoi des informations aux TLC de <b>0,11 ms</b> (20*86*6,25*10⁻⁸ = 0,11 ms) | |||
= Semaine 8 = | |||
== Commande permettant de demander (côté maitre) et de signaler (côté esclave), si l'esclave est à l'écoute == | |||
=== Première proposition (pas réalisable) === | |||
<b>ReX</b> : Pour éviter tous ces problèmes de délais, le mieux serait de prévoir une commande pour demander si le périphérique SPI est libre. Si le périphérique est libre un octet particulier est retourné. Si le périphérique se lance dans un traitement autre que l'écoute SPI, il prend soin d'initialiser sa réponse SPI a autre chose que cet octet particulier. | |||
<b>Timothé</b> : Cela ne correspond pas tout à fait à ce que vous avez proposé, mais j'imaginais faire quelque chose dans ce genre là pour ça : | |||
bool isSlaveListening() | |||
{ | |||
// Envoyer un octet factice et observer la ligne MISO | |||
SPDR = 0xFF; // Octet factice à envoyer | |||
while (!(SPSR & (1 << SPIF))); // Attendre la fin de la transmission | |||
// Si MISO est à l'état bas, cela signifie que l'esclave ne répond pas, il est donc probablement occupé | |||
if (!(PINB & (1 << SPI_MISO))) // Vérification de la ligne MISO | |||
{ | |||
return false; // L'esclave est occupé | |||
} | |||
return true; // L'esclave est prêt à écouter | |||
} | |||
ReX : Peux-tu expliquer pourquoi si MISO est à l'état bas c'est que l'esclave est occupé ? | |||
Il suffirait ensuite avant chaque envoie que le maître appel cette fonction en boucle jusqu'à ce qu'elle retourne <code>vrai</code>. | |||
Cela pourrait-t-il fonctionner également ? | |||
ReX : A priori non sauf si j'ai raté quelque chose de fondamental avec le bus SPI. | |||
=== Deuxième proposition === | |||
Je me suis finalement rendu compte que cette façon de faire n'était pas réalisable, puisque l'esclave SPI répondra toujours même s'il fait autre chose. A moins peut-être de lui faire répondre par 0x00, mais cela me parait un peu aléatoire... Et de toute façon, cela envoie en boucle des messages à l'esclave ce qui n'est pas terrible non plus. | |||
J'essaye donc autre chose qui ressemble plus à ce que vous suggériez en plus. | |||
Dès que l'esclave reçoit un octet, il passe sa réponse SPI à 0x0F avant de se lancer dans son traitement, pour signaler qu'il est occupé. Dès qu'il a fini son traitement, il passe de nouveau sa réponse SPI à 0xFF pour signaler qu'il est libre à nouveau. | |||
ReX : Si l'esclave est occupé il ne peut pas préparer sa réponse. D'où ce que je demandais à savoir de préparer cette réponse avant de partir dans un traitement. | |||
Voici le code en question : | |||
[[Fichier:ReponseSPI.jpg|vignette|Réponses SPI reçues par le maitre]] | |||
if(isSlaveSelectActive()) // test de la ligne SS entre chaque octet reçu | |||
{ | |||
received=spi_exch(reponseSPI); //L'esclave a recu une commande... | |||
reponseSPI = 0x0F; //on initialise son octet de reponse à 0x0F, ce qui signifie qu'il est occupé | |||
switch(received){ | |||
.... | |||
} | |||
reponseSPI = 0xFF; //il a fini de traiter ce qu'il a reçu, il est de nouveau à l'écoute on repasse donc son octet de reponse à 0xFF | |||
} | |||
ReX : Le code est plus convaincant que le texte. | |||
Après avoir ajouté la connectique de la ligne MISO, j'ai affiché la réponse que le maitre SPI recevait pour voir si cela fonctionnait bien. [[Media:ReponseSPI.jpg|Voir la photo "Réponses SPI reçues par le maitre"]] | |||
J'ai pu constater que le 0xFF passait bien (255 sur la photo) en revanche le 0x0F lui n'est pas constant... Il faudrait donc du côté du maitre tester si la valeur reçue est bien 255 ce qui signifierait qu'il est libre afin de pouvoir envoyer la suite des données. Cependant, je ne vois pas ce que le maitre est censé faire pour temporiser s'il ne reçoit pas 255. Une tâche dans le vide relativement rapide puis réessayer ? Sachant que de toute façon la tâche sera très rapidement désordonnancée par l'ordonnanceur... | |||
ReX : Il essaye à nouveau. | |||
Timothé : Voir [[#Essaie de la commande permettant de savoir si l'esclave est à l'écoute]] | |||
[[Fichier:AvecDelai24ms.png|vignette|Lors de l'utilisation du délai de 24 ms, le maitre reçoit effectivement seulement des "255", ce qui signifie que l'esclave est toujours prêt à l'écoute.]] | |||
J'ai de nouveau effectué le test mais en mettant cette fois-ci le délai de 24 ms qui fonctionnait très bien avant, et le maitre reçoit effectivement 100% de 255, [[Media:ReponseSPI.jpg|voir la deuxième photo "Lors de l'utilisation du délai de 24 ms..."]] | |||
ReX : Les 24ms pourquoi pas mais pourquoi pas 13ms ou 54ms ? | |||
Timothé : Auparavant, en-deça de 24 ms cela faisait saturé l'affichage des afficheurs 7 segments (le µC ne pouvait plus gérer à la fois l'envoi des infos aux TLC et la vitesse de la réception des informations SPI). Cependant, vu que vous posiez la question, j'ai refait des tests et je peux désormais descendre à 15 ms avant que la saturation arrive (cela devait être dû à des câbles mal branchés pour les 24 ms...). En revanche, cela est toujours loin des 0,11 ms que j'avais estimé en théorie... (voir [[#Estimation du temps nécessaire à l'envoi des informations aux TLC]]) | |||
== Gérer la ligne "chip select" (CS) (ou "slave select" (SS)) == | |||
<b>ReX</b> : Ce serait bien de gérer la ligne SS, par exemple en vérifiant que la ligne n'est pas désélectionnée entre deux octets de la même commande. | |||
<b>Timothé</b> : J'ai créé une fonction permettant de savoir si la ligne SS est toujours active ou non | |||
A chaque fois que l'esclave veut récupérer un octet, il vérifie d'abord que la ligne SS soit toujours active, si ce n'est pas le cas il ne récupère pas l'octet voici très simplement comment cela se présente : | |||
if(isSlaveSelectActive()){ | |||
received=spi_exch(0xFF); | |||
switch(received){ | |||
case 0: //0 | |||
display_set(groupes, display_number, ZERO); | |||
break; | |||
case 1: //1 | |||
display_set(groupes, display_number, UN); | |||
break; | |||
case 2: //2 | |||
display_set(groupes, display_number, DEUX); | |||
break; | |||
case 3: //3 | |||
display_set(groupes, display_number, TROIS); | |||
break; | |||
case 4: //4 | |||
display_set(groupes, display_number, QUATRE); | |||
break; | |||
case 5: //5 | |||
display_set(groupes, display_number, CINQ); | |||
break; | |||
case 6: //6 | |||
display_set(groupes, display_number, SIX); | |||
break; | |||
case 7: //7 | |||
display_set(groupes, display_number, SEPT); | |||
break; | |||
case 8: //8 | |||
display_set(groupes, display_number, HUIT); | |||
break; | |||
case 9: //9 | |||
display_set(groupes, display_number, NEUF); | |||
break; | |||
case 'A': //A | |||
display_set(groupes, display_number, A); | |||
break; | |||
case 'B': //b | |||
display_set(groupes, display_number, B); | |||
break; | |||
case 'C': //C | |||
display_set(groupes, display_number, C); | |||
break; | |||
case 'D': //d | |||
display_set(groupes, display_number, D); | |||
break; | |||
case 'E': //E | |||
display_set(groupes, display_number, E); | |||
break; | |||
case 'F': //F | |||
display_set(groupes, display_number, F); | |||
break; | |||
case 0xFF: | |||
//rien, cela signifie que le maitre demandait si l'esclave était à l'écoute | |||
break; | |||
} | |||
} | |||
ReX : Non et Non. Non parce que j'ai déjà dit que le <code>switch</code> était inutile avec une conception correcte et non parce que tu n'as pas compris "entre deux octets d'une commande". | |||
Timothé : J'ai compris ce que vous vouliez dire pour le <code>switch</code>, j'ai corrigé cela (voir [[#Simplification du code de réception d'informations des afficheurs 7 segments]]). Que voulez-vous dire par "entre deux octets d'une commande" dans ce cas là ? | |||
Voici le code de la fonction : | |||
//renvoi 1 si la ligne SS est au niveau bas et donc active, renvoi 0 sinon | |||
bool isSlaveSelectActive() | |||
{ | |||
return !(SPI_PIN & (1 << SPI_SS)); | |||
} | |||
Cela permettra à l'esclave d'ignorer un octet qui ne lui était finalement pas destiné. | |||
Cela correspond-t-il à ce que vous souhaitiez ? | |||
ReX : Non. | |||
== Poursuite ordonnancement == | |||
Pour l'ordonnancement, j'ai été confronté à un autre problème en faisant des tests. | |||
Lorsque l'ordonnanceur arrête l'exécution d'une tâche en cours et élit une nouvelle tâche, la ligne SS de la précédente tâche reste active. Ce qui fait qu'elle reçoit par la suite également ce qui était destiné à la tâche nouvellement élu par l'ordonnanceur. | |||
ReX : Oui d'où l'idée d'une tâche système qui gère les envois SPI en désactivant les interruption pour envoyer une commande entière sans être préemptée. Tu viens de mettre le doigt sur la difficulté de l'EC. | |||
(La vidéo suivante ne répondait pas à la vérification des fichiers, je vous l'ai donc envoyé par mail, veuillez m'excuser pour la gêne occasionnée). | |||
ReX : Je n'en tiens pas compte. | |||
<b>Voir l'exemple en vidéo</b>, nous pouvons voir que la deuxième fois que le "b" apparait sur la matrice, il y apparait exactement en même temps que celui sur les afficheurs 7 segments, et disparait lorsque le "C" est affiché (les caractères "C", "D" et "F" ne sont actuellement plus supporté par la matrice, c'est pourquoi il y a juste plus rien d'affiché, voir [[#Modification du codage des caractères en mémoire (matrice de LED)]] le changement de codage des caractères). Cela témoigne que la matrice reçoit les octets envoyés aux afficheurs 7 segments. | |||
ReX : Utiliser des balises ref pour pointer sur un pararaphe donné. | |||
Pour l'instant, pour corriger cela je sélectionne et désélectionne les lignes SS dans la routine d'interruptions du timer, en fonction du processus que l'on lâche, et celui que l'on récupère. Ce qui permet de fermer la ligne SS du processus en cours d'exécution, qui est arrêté avant qu'il ne puisse le faire lui même. [[Media:AvecChangementSS.mp4|Voir la vidéo d'illustration]]. Nous pouvons voir dans cette vidéo que lorsque la tâche de la matrice se fait arrêter, l'envoi des caractères "A", "D" et "E" sur les afficheurs 7 segments n'efface pas le "b" qu'avait reçu la matrice au préalable. | |||
Voici le code de la nouvelle routine d'interruption : | |||
//Routine d'interruption du timer | |||
ISR(TIMER1_COMPA_vect, ISR_NAKED) | |||
{ | |||
SAVE_REGISTER(); //On sauvegarde la pile, qui contient le contexte d'exécution du processus | |||
processus[pid].pile=SP; //ainsi qu'où était le pointeur de pile, afin de reprendre où nous en étions lorsque ce processus sera exécuté à nouveau | |||
//On déselectionne la ligne SS correspondant à la tâche arrêté | |||
if(!pid) spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
else spi_unselect(&SPI_PORT_MATRIX,SPI_BIT_MATRIX); | |||
pid=(pid+1)%MAX_PROCS; //on passe au processus suivant | |||
//on sélectionne la ligne SS correspondant à la tâche nouvellement élu | |||
if(!pid) spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
else spi_select(&SPI_PORT_MATRIX,SPI_BIT_MATRIX); | |||
SP = processus[pid].pile; // on récupère où en était le pointeur de pile à la précédente exécution | |||
RESTORE_REGISTER(); //et on recharge la pile | |||
asm volatile("reti"); // on autorise à nouveau les interruptions | |||
} | |||
ReX : Soit un ordonnanceur dépendant des tâches lancées ... Je te laisse imaginer le commentaire que je souhaiterais écrire. | |||
Timothé : Je vois... Avez-vous eu le temps de regarder le début de code d'ordonnancement que j'ai posté dans documents rendus ? Sinon ce n'est pas grave, je vais poster une version mise à jour prochainement (voir [[#Semaine 9]]). | |||
ReX : Non, si le Wiki ne réflète pas ce qui dans le code, c'est qu'il y a un défaut de documentation. | |||
= Semaine 9 = | |||
== Simplification du code de réception d'informations des afficheurs 7 segments == | |||
Comme je vous l'avais dit, j'ai retiré le <code>switch</code>. J'utilise directement la variable récupérant le message SPI, pour récupérer tout d'abord le numéro de l'afficheur puis pour afficher le caractère (j'utilise donc désormais la fonction <code>display_ascii</code> que je n'utilisais pas jusqu'à présent). | |||
Voici le nouveau code pour les afficheurs 7 segments : | |||
int main(void) | |||
{ | |||
uint8_t display_number=0; | |||
init_LED_Drivers(NB_DRIVERS); // requis pour configuration des 2 tlc5947 | |||
spi_init(0); // initialisation de la communication SPI | |||
screen_clear(groupes); | |||
while(1){ | |||
set_LED_Drivers(groupes,NB_DRIVERS); | |||
uint8_t received; | |||
uint8_t reponseSPI = 0xFF; | |||
// quand reponseSPI vaut 0xFF l'esclave est à l'écoute et quand reponseSPI vaut 0x0F cela signifie qu'il est occupé | |||
/*Le premier caractere recu désigne le numéro de l'afficheur souhaité*/ | |||
received=spi_exch(reponseSPI); // l'esclave a recu une commande... | |||
display_number = received; | |||
/*le deuxieme caractere recu designe celui à afficher*/ | |||
received=spi_exch(reponseSPI); // l'esclave a recu une commande... | |||
reponseSPI = 0x0F; // on initialise son octet de reponse à 0x0F, ce qui signifie qu'il est occupé | |||
display_ascii(groupes, display_number, received); | |||
reponseSPI = 0xFF; // il a fini de traiter ce qu'il a reçu, il est de nouveau à l'écoute on repasse donc son octet de reponse à 0xFF | |||
} | |||
return 0; | |||
} | |||
ReX : Pas de vérification du signal "chip select". | |||
ReX : Le premier <code>reponseSPI = 0x0F</code> est inutile. Il est positionné alors que l'esclave a juste une affectation a effectuer. | |||
Timothé : Je l'avais mis par principe mais effectivement le µC ne fait qu'une affectation ici, cela lui prendra même plus de temps au final de gérer les deux affectations (de reponseSPI) que l'unique affectation qu'il avait à faire de base... Je l'ai retiré. | |||
== Suite de la modification du codage des caractères en mémoire (matrice de LED) == | |||
J'ai corrigé le code pour décoder les caractères de la matrice de LED, en effet cela permet d'avoir un éclairage constant pour toutes les LEDs et de la valeur de <code>BRIGHTNESS</code>. | |||
Voici le nouveau code : | |||
void matrix_set(int *groupes,unsigned char *matrix){ | |||
int i; | |||
for(i=0;i<NB_ROWS*NB_COLS;i++) | |||
groupes[indirect_matrix[i]]=((matrix[i/8] >> (7-(i%8))) & 1)*BRIGHTNESS; | |||
} | |||
ReX : Oui. | |||
== Essaie de la commande permettant de savoir si l'esclave est à l'écoute == | |||
J'ai essayé de renvoyer les données sans même une attente très courte lorsque le µC "esclave" renvoie qu'il est occupé. Cependant cela le fait saturer et il ne parvient plus à afficher les caractères (il affiche le premier caractère correctement puis des "morceaux" de caractères). Il faudrait temporiser un peu le fait d'essayer à nouveau cependant cela nous ramène au problème de départ. | |||
Pensez-vous qu'il serait plus intéressant d'utiliser une boucle <code>for</code> (s'incrémentant dans le vide) pour faire une temporisation très rapide plutôt que la fonction <code>_delay_ms</code> ? Ou faut-il impérativement proscrire les temporisations de toutes sortes ? | |||
Voici le code testé : | |||
void task_7segments() | |||
{ | |||
uint8_t display7Segments1[] = {5, 'A', 4, 'B', 3, 'C', 2, 'D', 1, 'E', 0, 'F'}; | |||
uint8_t display7Segments2[] = {0, 'A', 1, 'B', 2, 'C', 3, 'D', 4, 'E', 5, 'F'}; | |||
/*Permet l'envoie d'un tableau sur les afficheurs 7 segments*/ | |||
spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY);//sélection du uC controlant les afficheurs 7 segments | |||
for(int i=0;i<12;i++) //parcours du tableau pour l'envoie des valeurs | |||
{ | |||
if(rotationmessage) | |||
{ | |||
// tant que la réponse est différente de 255 et donc que le µC est occupé, l'octet est renvoyé (cela provoque une saturation...) | |||
while( (reponse = spi_exch(display7Segments1[i])) != 255); | |||
} | |||
else | |||
{ | |||
// idem | |||
while( (reponse = spi_exch(display7Segments2[i])) != 255); | |||
} | |||
} | |||
rotationmessage = (rotationmessage+1)%2; | |||
spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); //déselection du uC | |||
} | |||
ReX : La variable <code>rotationmessage</code> n'est pas mise à jour. | |||
Timothé : Je ne m'en suis pas rendu compte en mettant le code sur le wiki, c'est corrigé. | |||
ReX : Pas cohérent avec le code précédent. Un seul octet envoyé par message. | |||
Timothé : Pas cohérent avec le code de la partie des afficheurs 7 segments vous voulez-dire ? (celui présent dans [[#Simplification du code de réception d'informations des afficheurs 7 segments]]) | |||
Timothé : Si oui, c'est bien cela. Le code précédent récupère avec un tour de la boucle <code>while</code> 2 octets successivement. Le premier désignant la position de l'afficheur et le deuxième le caractère à afficher. Afin d'écrire sur l'ensemble des 6 afficheurs, 6 tours de la boucle <code>while</code> sont nécessaires. Afin de récupérer tour après tour, les 2 octets requis pour chacun des afficheurs. | |||
ReX : Oui et pourquoi ton code de test ci-dessus n'envoie qu'un octet ? | |||
ReX : En cas d'échec désactiver le CS et recommencer. Une attente de quelques microsecondes sur changement de CS est acceptable. | |||
Timothé : Voir [[#Suite de l'essai de la commande permettant de savoir si l'esclave est à l'écoute]]. | |||
Voici une version mise à jour du programme d'ordonnancement : | |||
[[Fichier:DeuxiemeRenduOrdonnancement.zip|vignette]] | |||
ReX : J'attendrais la version synthétique sur le Wiki. | |||
== Suite de l'essai de la commande permettant de savoir si l'esclave est à l'écoute == | |||
En cas d'échec, désormais je désactive la ligne CS et la réactive avant de réessayer de transmettre. | |||
Voici le code : | |||
void task_7segments() | |||
{ | |||
uint8_t display7Segments1[] = {5, 'A', 4, 'B', 3, 'C', 2, 'D', 1, 'E', 0, 'F'}; | |||
uint8_t display7Segments2[] = {0, 'A', 1, 'B', 2, 'C', 3, 'D', 4, 'E', 5, 'F'}; | |||
uint8_t reponse; | |||
uint8_t rotationmessage=0; | |||
/*Permet l'envoie d'un tableau sur les afficheurs 7 segments*/ | |||
spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY);//sélection du uC controlant les afficheurs 7 segments | |||
for(int i=0;i<12;i++) //parcours du tableau pour l'envoi des valeurs | |||
{ | |||
if(rotationmessage) | |||
{ | |||
// tant que la réponse est différente de 255 et donc que le µC est occupé, l'octet est renvoyé après désactivation et réactivation du chip select (cela provoque une saturation...) | |||
while( (reponse = spi_exch(display7Segments1[i])) != 255) | |||
{ | |||
spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
_delay_us(5); | |||
spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
_delay_us(5); | |||
} | |||
} | |||
else | |||
{ | |||
// idem | |||
while( (reponse = spi_exch(display7Segments2[i])) != 255) | |||
{ | |||
spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
_delay_us(5); | |||
spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
_delay_us(5); | |||
} | |||
} | |||
} | |||
rotationmessage = (rotationmessage+1)%2; | |||
spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); //déselection du uC | |||
} | |||
Cela devrait permettre de "relancer" la communication avec l'esclave en sélectionnant à nouveau son chip select afin de retenter la transmission. | |||
Le fait de désactiver et réactiver le CS "remet" l'esclave en mode écoute et donc prêt à recevoir et/ou à transmettre des informations avec le maitre. | |||
<b>C'est de cette façon qu'il faut gérer la ligne CS sur les esclaves :</b> | |||
// Permet de recevoir et d'envoyer un octet sur le bus SPI | |||
uint8_t spi_exch(uint8_t output) | |||
{ | |||
SPDR = output; | |||
while(!(SPSR & (1<<SPIF)) && !(SPI_PIN & (1<<SPI_SS))); //ajout de la vérification de l'état du CS lors des transmissions | |||
return SPDR | |||
} | |||
ReX : Il faudrait surtout vérifier que le CS est actif entre la réception des deux octets et revenir à la réception du premier sinon. | |||
= Bilan du projet = | |||
== Résumé travail effectué == | |||
=== Partie afficheurs 7 segments === | |||
* Cette partie est bien fonctionnel en séquentiel. | |||
Cependant, voir [[#Ce qu'il faut terminer, ce qu'il reste à faire dans le projet]] pour les améliorations possibles/nécessaires et l'ordonnancement. | |||
les codes sont ici [[#Rendu final]] | |||
=== Partie matrice de LED === | |||
* Cette partie est bien fonctionnel en séquentiel. | |||
Cependant, voir [[#Ce qu'il faut terminer, ce qu'il reste à faire dans le projet]] pour les améliorations possibles/nécessaires et l'ordonnancement. | |||
les codes sont ici [[#Rendu final]] | |||
=== Partie maitre SPI === | |||
* Cette partie est bien fonctionnel en séquentiel. | |||
Cependant, voir [[#Ce qu'il faut terminer, ce qu'il reste à faire dans le projet]] pour les améliorations possibles/nécessaires et l'ordonnancement. | |||
les codes sont ici [[#Rendu final]] | |||
== Où j'en suis dans la suite du projet == | |||
=== Ce qui est fait sur l'ordonnancement === | |||
Les codes sont ici [[#Rendu final]] | |||
* Les macros permettant de sauvegarder la pile et la recharger, pour la rotation des processus (voir code partie ordonnancement). | |||
* La structure d'un processus : | |||
//déclaration de la structure contenant un processus | |||
typedef struct scheduler_t { | |||
uint16_t pile; //position courante dans la fonction associé au processus | |||
void (*fonction) (void); // fonction associée au processus | |||
}scheduler_t; | |||
* Création d'un tableau de processus contenant tous nos processus : | |||
//remplissage de la structure contenant les processus | |||
scheduler_t processus[MAX_PROCS]={ | |||
{0x500,task_7segments}, | |||
{0x700,task_matriceLEDs}, | |||
{0x600,task_CommunicationSPI}, | |||
}; | |||
* L'initialisation du timer (peut-être qu'il faudra changer la valeur du prescaler pour optimiser le temps de rotation des processus): | |||
//initialisation du timer qui permettra la rotation des processus | |||
void init_timer() | |||
{ | |||
TCCR1B |= _BV(WGM12); // CTC mode with value in OCR1A | |||
TCCR1B |= _BV(CS12); // CS12 = 1; CS11 = 0; CS10 =1 => CLK/1024 prescaler | |||
TCCR1B |= _BV(CS10); | |||
OCR1A = NB_TICK; | |||
TIMSK1 |= _BV(OCIE1A); | |||
} | |||
* Routine d'interruption du timer, elle gère le passage d'un processus à un autre. En relançant le nouveau processus où il en était et en permettant au précédent de pouvoir être relancé où il en était : | |||
//Routine d'interruption du timer | |||
ISR(TIMER1_COMPA_vect, ISR_NAKED) | |||
{ | |||
SAVE_REGISTER(); //On sauvegarde la pile, qui contient le contexte d'exécution du processus | |||
processus[pid].pile=SP; //ainsi qu'où était le pointeur de pile, afin de reprendre où nous en étions lorsque ce processus sera exécuté à nouveau | |||
pid=(pid+1)%MAX_PROCS; //on passe au processus suivant | |||
SP = processus[pid].pile; // on récupère où en était le pointeur de pile à la précédente exécution | |||
RESTORE_REGISTER(); //et on recharge la pile | |||
asm volatile("reti"); // on autorise à nouveau les interruptions | |||
} | |||
* Initialisation de l'ensemble des processus contenu dans notre tableau de processus | |||
//fonction permettant la préparation des processus avant la première exécution | |||
void init_proc(int i) | |||
{ | |||
uint16_t old = SP; // on sauvegarde où était le pointeur de pile avant l'initialisation du processus | |||
SP = processus[i].pile; // on va à l'emplacement que l'on à initialement choisi pour notre processus lorsque l'on a rempli la structure | |||
int adresse=(int)processus[i].fonction; //on recupere l'adresse où a été mis la fonction de notre processus pour la pousser sur la pile | |||
asm volatile("push %0" : : "r" (adresse & 0x00ff) ); //d'abord les 8 bits de poids faibles | |||
asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8)); //puis les 8 bits de poids forts que l'on décale donc de 8 bits | |||
SAVE_REGISTER(); // on sauvegarde la pile | |||
processus[i].pile = SP; //ainsi qu'où était le pointeur de pile, le processus est prêt pour sa première utilisation | |||
SP = old; //on remet le pointeur de pile où il était avant l'initialisation du processus | |||
} | |||
* Le main, permettant le lancement de l'ordonnanceur | |||
int main(void) | |||
{ | |||
spi_init(1); | |||
_delay_ms(1000); | |||
spi_initcs(&SPI_DDR_DISPLAY,SPI_BIT_DISPLAY); | |||
spi_initcs(&SPI_DDR_MATRIX,SPI_BIT_MATRIX); | |||
spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); | |||
spi_unselect(&SPI_PORT_MATRIX,SPI_BIT_MATRIX); | |||
init_timer(); //initialisation du timer | |||
for(int i=0;i<MAX_PROCS;i++) init_proc(i); // initialisation des processus | |||
sei(); //autorisation des interruptions | |||
processus[0].fonction(); // lancement manuel du premier processus | |||
return 0; | |||
} | |||
ReX : A quoi sert le lancement manuel dans la mesure où l'ISR peut le faire ? | |||
Je n'ai malheureusement pas pu tester convenablement cette partie du code puisque j'étais bloqué plus tôt et ai manqué cruellement de temps sur la fin. Il est donc probable qu'il y ait des problèmes dont je n'ai pas conscience dans ce programme. | |||
Je parlerai des tâches de ce programme dans la partie [[#Ce qu'il faut terminer, ce qu'il reste à faire dans le projet]] | |||
=== Ce qu'il faut terminer, ce qu'il reste à faire dans le projet === | |||
==== Améliorations possibles/nécessaires ==== | |||
Pour la matrice : Il y a parfois des caractères qui se "perdent", cela peut-être du à l'utilisation de fils et non de plot de soudure (perturbations) il faudrait mettre des plots de soudure (qui pourraient également permettre la monter en fréquence de la communication SPI). Le <code>switch</code> est toujours présent dans cette partie. Il faudrait refaire avec le nouveau codage les caractères de l'alphabet manquant. Ajout des deux lignes permettant de préparer la réponse SPI (je viens de me rendre compte que je l'ai oublié dans le rendu final). | |||
Pour les afficheurs : Mettre des plots de soudure pour ne plus avoir à utiliser les fils, et peut-être pouvoir monter la fréquence de l'horloge cadençant la communication SPI. | |||
==== Pour la suite de l'ordonnancement ==== | |||
Pour les esclaves SPI : Mise en place de la vérification du CS entre 2 octets d'une même commande. <b>Finalement c'est bon, voir</b> [[#Suite de l'essai de la commande permettant de savoir si l'esclave est à l'écoute]] | |||
ReX : Non pas de modification au niveau de l'esclave. | |||
Pour le maitre SPI : Finir la commande permettant de savoir si les esclaves sont à l'écoute ou non (ce qui permettra de supprimer les délais). | |||
Il faudrait trois tâches, une pour chacun des sous-systèmes, et une gérant la communication SPI. Celle gérant la communication SPI, devra bloquer les interruptions pour ne pas être préemptée lors de l'envoi de commande (pour éviter les soucis liés à l'envoi d'un octet aux deux esclaves au lieu d'un seul). | |||
Après, il faudrait intégrer ces trois tâches dans le tableau des processus et utiliser la partie [[#Ce qui est fait sur l'ordonnancement]] pour les ordonnancer. | |||
Enfin du moins son principe puisque n'ayant pu la tester car j'ai manqué cruellement de temps sur la fin, il y aura probablement des choses à revoir. | |||
Les codes sont ici [[#Rendu final]] | |||
Dans le rendu : Je me servais de la tâche pour les afficheurs 7 segments, afin de tester la commande permettant de savoir si les esclaves sont à l'écoute ou non. La tâche pour la matrice était la même que dans la partie séquentielle. La tâche pour la communication SPI comprend les fonctions permettant de bloquer les interruptions puis les activer à nouveaux. | |||
= Documents Rendus = | = Documents Rendus = | ||
== Rendu Intermédiaire == | |||
<u>Codes de la partie 7 segments de la maquette :</u> | |||
[[Fichier:7segSPI.zip|vignette|Codes de la partie 7 segments de la maquette.]] | |||
<u>Codes de la partie matrice de LED de la maquette :</u> | |||
[[Fichier:MatrixSPI.zip|vignette|Codes de la partie matrice de LED de la maquette]] | |||
<u>Codes de la partie maitre SPI de la maquette (fonctionnement séquentiel) :</u> | |||
[[Fichier:OrdonneTests.zip|vignette|Codes de la partie maitre SPI de la maquette]] | |||
<u>Codes contenant le début de l'ordonnancement :</u> | |||
[[Fichier:Debut ordonnancement.zip|vignette|Codes maitre SPI contenant le début de l'ordonnancement]] | |||
=== Echanges autour de ces derniers === | |||
<u>Codes de la partie 7 segments de la maquette :</u> | |||
ReX : Je ne comprends pas les codes envoyés pour la position du caractère à afficher ? Pourquoi pas directement le numéro de l'afficheur ? | |||
Timothé : En effet, c'est bien plus simple à faire et à utiliser (J'avais utilisé le code ASCII de segfont.c). C'est modifié, idem pour la matrice. | |||
ReX : Même chose pour les caractères eux-mêmes. | |||
ReX : Les deux remarques précédentes ont pour but de vous faire simplifier le code en supprimant les redondances. | |||
ReX : Ce serait bien de gérer la ligne SS, par exemple en vérifiant que la ligne n'est pas désélectionnée entre deux octets de la même commande. | |||
Timothé : Voir [[#Gérer la ligne "chip select" (CS) (ou "slave select" (SS))]]. | |||
<u>Codes de la partie matrice de LED de la maquette :</u> | |||
ReX : Le codage des caractères prend trop de place en mémoire, limiter à 8 octets par caractère. | |||
Timothé : Voir [[#Modification du codage des caractères en mémoire (matrice de LED)]]. | |||
ReX : Même remarque sur l'incohérence des codes que pour les 7 segments. | |||
Timothé : J'utilise également directement les caractères désormais. | |||
<u>Codes de la partie maitre SPI de la maquette (fonctionnement séquentiel) :</u> | |||
ReX ; Ne pas changer l'affichage des 7 segments rend la démonstration moins convaincante. | |||
Timothé : J'ai ajouté une [[Media:Video demonstration avec 7seg.mp4|nouvelle vidéo]] le démontrant. | |||
ReX : Vous n'envoyez qu'un octet par message pour les 7 segments ? Ou est l'octet de la position du caractère ? | |||
Timothé : Pour les 7 segments, j'envoie 12 octets successivement. Le premier est la position du caractère, le deuxième le caractère à afficher, et ainsi de suite pour les six afficheurs. | |||
ReX : Il faudrait justifier les 24ms d'attente par l'analyse de la durée du code d'envoi des informations aux TLC des 7 segments. | |||
Timothé : Voir [[#Estimation du temps nécessaire à l'envoi des informations aux TLC]]. | |||
ReX : Même remarque pour les 800ms pour la matrice. | |||
Timothé : Pour la matrice, les 800 ms sont prévues pour nous laisser le temps de lire correctement le caractère, ce n'est pas limité par le matériel. | |||
ReX : Pour éviter tous ces problèmes de délais, le mieux serait de prévoir une commande pour demander si le périphérique SPI est libre. Si le périphérique est libre un octet particulier est retourné. Si le périphérique se lance dans un traitement autre que l'écoute SPI, il prend soin d'initialiser sa réponse SPI a autre chose que cet octet particulier. | |||
Timothé : Voir [[#Commande permettant de demander (côté maitre) et de signaler (côté esclave), si l'esclave est à l'écoute]]. | |||
<u>Codes contenant le début de l'ordonnancement :</u> | |||
Timothé : Il faudra retirer des tâches task_7segments et task_matriceLEDs les délais et mettre en place la commande dont vous avez parlé, qui permettra au maitre spi de savoir si l'esclave auquel il veut parler est à l'écoute ou s'il fait autre chose. Peut-être même ajouter une tâche à part entière qui s'occupera de ça. Avec les délais, l'ordonnancement ne peut pas fonctionner puisque le maître spi sera bloqué trop longtemps dans ces derniers. | |||
== Rendu final == | |||
<u>Codes de la partie 7 segments de la maquette :</u> | |||
[[Fichier:7segSPI final.zip|vignette]] | |||
<u>Codes de la partie matrice de LED de la maquette :</u> | |||
[[Fichier:MatrixSPI final.zip|vignette]] | |||
<u>Codes de la partie maitre SPI de la maquette (fonctionnement séquentiel) :</u> | |||
[[Fichier:OrdonneTests Final.zip|vignette]] | |||
<u>Codes contenant où j'en suis dans l'ordonnancement :</u> | |||
[[Fichier:Ordonnancement.zip|vignette]] | |||
== Vidéos rendues == | |||
[[Fichier:Video demonstration.mp4|vignette|left|Démonstration du fonctionnement séquentielle de la maquette, affichage de "bonjour" sur la matrice de LED et de "ABCDEF" sur les afficheurs 7 segments (à l'envers grâce à la sélection manuelle des afficheurs)]] | |||
[[Fichier:Video demonstration avec 7seg.mp4|vignette|right|Ajout du changement de l'affichage des 7 segments à la démonstration du fonctionnement séquentiel de la maquette.]] | |||
[[Fichier:Video demonstration nouveau codage caractere.mp4|vignette|left|Nouvelle vidéo de démonstration du fonctionnement séquentiel de la maquette. Elle met en avant le changement des caractères des 7 segments, ainsi que la nouvelle façon de coder les caractères pour la matrice. Nous pouvons constater un léger problème d'intensité des LEDs dû à cette méthode...]] | |||
[[Fichier:AvecChangementSS.mp4|vignette|right|Vidéo illustrant la solution actuellement mise en place pour éviter d'envoyer des octets à un uC n'étant pas censé les recevoir.]] |
Version actuelle datée du 6 septembre 2023 à 09:42
Objectifs
Il vous est demandé de :
- réaliser des communications SPI entre trois ATMega328p, pour cela vous utiliserez une carte comportant 3 sous-systèmes :
- un sous-système de type Arduino Uno programmable via une puce FTDI,
- un sous-système de gestion d'afficheurs 7 segments par un ATMega328p, programmable via un connecteur AVR-ISP,
- un sous-système de gestion de matrice de LEDs par un ATMega328p, aussi programmable via un connecteur AVR-ISP ;
- commencez par gérer les afficheurs et les pilotes de LED sur les sous-systèmes correspondants ;
- écrivez les fonctions de communication entre les sous-systèmes : le premier sous-système étant le maître SPI ;
- écrivez sur le sous-système maître un programme pour commander les deux autres sous-systèmes en séquence ;
- utilisez l'ordonnanceur implanté en TP, en l'étendant, pour commander les deux autres sous-systèmes par deux tâches concurrentes.
Matériel nécessaire
Vous aurez besoin de la carte de développement cité et d'un Arduino configuré en programmateur AVR ISP.
Le dernier projet sur ce sujet est disponible sur l'ancien Wiki : [1]. Vous y trouverez le schéma et le routage de la carte. Vous y trouverez même des embryons des programmes demandés.
Travail réalisé
Semaine 1
Début afficheurs 7 segments
- Jusqu'à présent, je me suis concentré sur le sous-système avec les afficheurs 7 segments :
- Inspection du routage du PCB pour voir comment était câblé le connecteur au µC
- Recherche sur le fonctionnement de la programmation via un connecteur AVR-ISP
- Début de compréhension du code et du makefile
- Pour l'instant je peux sur chacun des afficheurs :
- Afficher le caractère souhaité parmi les caractères hexadécimaux (0 1 2 3 4 5 6 7 8 9 A b C d E F)
- Allumer le point
- Eteindre l'afficheur
ReX : Quel connecteur ? L'AVR-ISP comme mentionné ci-dessous ? C'est standard comme comme câblage ...
Timothé : Oui l'AVR-ISP, je n'avais encore jamais utilisé ce type de connecteur. J'ai donc fait des recherches pour savoir quelles broches étaient utilisées par ces connecteurs, et notamment laquelle de ces broches était relié au 5V de l'ATMega328p sur le PCB, pour éviter de l'envoyer là où il fallait pas et risquer de cramer quelque chose...
ReX : OK pour les fonctionnalités sur les 7 segments.
Timothé : Ok parfait.
Début matrice de LED
- Je suis passé sur le sous-système avec la matrice de LED :
- Inspection du routage du PCB pour voir comment était câblé le deuxième connecteur AVR-ISP au deuxième µC
- Début de compréhension du code et du makefile
- Cependant, j'ai changé la matrice "indirect_matrix". Je l'ai retourné, puis l'ai ensuite pivoté d'un quart de tour vers la droite (Cela permet de l'avoir dans le même sens que les LED sur la carte et dans le sens de lecture des afficheurs 7 segments)
- Ce qui permettra de facilement créer et afficher des caractères sur la matrice de LED
- A ce propos... j'aurais quelques questions sur ce que vous souhaitiez que ces deux sous-systèmes puissent faire :
- Quels caractère souhaitez-vous pour la matrice (par exemple : l'alphabet, les chiffres) ?
- Plus largement, que souhaitez-vous comme fonctionnalités pour la matrice de LED (par exemple : afficher des lettres, afficher des chiffres, faire défiler des messages) ?
- Même question pour les afficheurs 7 segments, que souhaitez-vous comme fonctionnalités (par exemple : compter en décimal, en hexadécimal, faire défiler des chiffres) ?
- Dites moi également si vous préférez que je réserve cet endroit pour le travail réalisé, et que je vous pose uniquement les questions par mail, ou que je fasse une nouvelle rubrique seulement pour les questions de la même manière que pour "Semaine 1".
ReX : Pour ce qui peut être affiché sur la matrice c'est comme vous voulez, soit un caractère alphanumérique centré, soit des caractères alphanumériques avec défilement de gauche à droite soit toute figure possible en 8x8 pixels, ce qui est important c'est que l'ATMega328p puisse commander l'ATMega328p de la matrice et lui faire faire quelque chose.
Timothé : Ok ça marche.
ReX : Idem pour les afficheurs 7 segments, le plus simple étant d'envoyer les 6 "caractères" à afficher.
ReX : Pour l'organisation du Wiki, c'est à vous de décider :)
Timothé : Dans ce cas, je propose que je mette mes questions dans une rubrique à part, et pour le reste nous pouvons continuer d'interagir comme ce qu'on a fait là.
Début maitre SPI
- J'ai commencé à regarder le troisième sous-système qui sera le maitre SPI
- J'ai regardé le makefile et fait quelques recherches sur la façon de programmer ce µC, afin de pouvoir le programmer par la suite via la puce FTDI
- Je suis parvenu à le flasher et à récupérer des messages sur la liaison série avec le PC
- (Dans le sous-système précédent) J'ai créer les caractères A,b,C,d,E,F,G,H,I,J ; qui sont affichable sur la matrice de LED (il est possible d'en créer d'autres facilement)
- Je me suis également penché sur le code de ce sous-système (Maitre SPI)
- J'ai pu constater que la façon de "contrôler" les autres sous-systèmes, n'est pas celle que j'avais imaginée
- Je vais faire des essais pour voir s'il est possible et intéressant de faire ce que j'avais prévu avec la communication SPI (pour contrôler les autres sous-systèmes)
ReX : L'ATMega328p maître était déjà programmable par série (comme un Arduino Uno classique).
Timothé : Oui je m'en suis rendu compte par la suite, j'avais cru comprendre que l'ATMega328p était vide et qu'il fallait commencer par l'amorcer avec l'Arduino uno, afin de pouvoir ensuite le programmer en série. Mais cela avait déjà été fait. Cependant je devrais maintenant savoir le faire si besoin.
ReX : Votre discussion sur le contrôle par SPI n'est pas claire du tout, précisez, comparez des protocoles explicités.
Timothé : En fait, j'avais simplement imaginé envoyer des "ordres" aux autres sous-systèmes au lieu d'envoyer directement ce qu'il devait afficher. Par exemple : envoyer seulement le caractère "A" à l'ATMega328p de la matrice de LED, et non pas le tableau complet contenant le "A" réellement affichable. Il faudrait donc que le sous-système lise le caractère qui lui a été envoyé et l'interprète afin d'afficher ce qui correspond. Je ne sais pas encore si cela est faisable ni si c'est pratique... Je vais essayer. Est-ce mieux expliqué, où pas du tout ?
ReX : OK.
Semaine 2
- J'ai commencé à me pencher plus sur la communication SPI :
- J'ai observé toute la connectique, et repéré où chaque broches des différents connecteurs (J18, J20, J17 et J21) étaient branchées sur les trois ATMega328p
- J'ai pu constater qu'il n'y avait pour aucun des deux sous-systèmes la broche MISO connectée, il n'est donc pas possible pour les deux esclaves d'envoyer des messages au maitre
- Cela paraissait évident, cependant j'ai vérifié quand même, les broches des connecteurs J18/J20 et J17/J21 se faisant face, sont bien connectées de la même manière à leur uC respectifs, à noter que la broche /SS de l'ATMega328p de la matrice de LED, n'est pas connectée à la broche /SS du maitre spi mais à la broche 11 (afin de sélectionner un esclave, le maitre doit avoir autant de broches de /SS que d'esclave, afin de pouvoir les adresser un à un).
ReX : OK. Si vraiment il fallait envoyer des données dans le sens esclaves vers maître, il suffirait de connecter les broches MISO des connecteur AVR-ISP.
Timothé : Oui en effet, nous pouvons passer par les connecteurs AVR-ISP qui sont chacun reliés à la broche MISO de leur ATMega328p.
Questions de la semaine 2
Timothé : Oui l'AVR-ISP, je n'avais encore jamais utilisé ce type de connecteur. J'ai donc fait des recherches pour savoir quelles broches étaient utilisées par ces connecteurs, et notamment laquelle de ces broches était relié au 5V de l'ATMega328p sur le PCB, pour éviter de l'envoyer là où il fallait pas et risquer de cramer quelque chose...
ReX : Si vous l'avez utilisé en tutorat USB en 2022/2023.
Timothé : Dans ce cas, je n'avais pas encore fait le lien entre ce que j'avais utilisé et le fait que c'était un connecteur AVR-ISP...
Timothé : En fait, j'avais simplement imaginé envoyer des "ordres" aux autres sous-systèmes au lieu d'envoyer directement ce qu'il devait afficher. Par exemple : envoyer seulement le caractère "A" à l'ATMega328p de la matrice de LED, et non pas le tableau complet contenant le "A" réellement affichable. Il faudrait donc que le sous-système lise le caractère qui lui a été envoyé et l'interprète afin d'afficher ce qui correspond. Je ne sais pas encore si cela est faisable ni si c'est pratique... Je vais essayer. Est-ce mieux expliqué, où pas du tout ?
ReX : C'est faisable et ça me va aussi.
Timothé : Ok parfait, je vais essayer les deux. Je suis pas encore rendu à l'ordonnancement, mais je me demande si raccourcir le temps de communication pour les ordres SPI, ne pourrait pas aider à avoir quelque chose de plus rapide. Après les différences seront peut-être plus que négligeables...
ReX : Non non c'est une bonne idée de raccourcir les temps de transmission.
Semaine 3
J'ai continué sur la communication SPI :
- Je me suis d'abord penché sur la communication avec le sous-système comportant les afficheurs 7 segments
- J'ai eu un peu plus de mal à mettre cela en place, ce qui m'a finalement débloqué, est de changer la fréquence de l'horloge cadencant la communication, je l'ai finalement fixée à la fréquence du µC divisé par 64 (avec une fréquence plus élevée la communication cesse de fonctionner)
- Je peux désormais ordonner l'affichage d'un tableau au sous-système des afficheurs 7 segments depuis le sous-système maître
- J'ai également commencé la programmation de la communication SPI avec le deuxième sous-système
- La matrice est capable d'afficher n'importe quelle lettre de l'alphabet, il sera donc possible d'afficher des mots (pas de défilement prévu pour l'instant cependant...)
- En revanche, la communication ne passe pas encore sur ce système, je travaille dessus en ce moment
ReX : OK, merci pour le rapport.
- La communication SPI est désormais également fonctionnelle sur la matrice de LEDs (il est possible d'écrire des mots en faisant apparaitre les lettres successivement) ;
- La commande en séquence des deux sous-systèmes est donc terminé.
ReX : Ce serait bien d'expliquer les problèmes rencontrés et les solutions apportées.
Je vais passer sur l'ordonnancement des sous-systèmes :
- Pour l'instant j'imagine faire 2 tâches, chacune s'occupant de la communication SPI d'un des deux sous-systèmes
- Cela ne correspond pas tout à fait à ce que l'on avait dit, mais cela me parait intéressant, je vais essayer...
ReX : Dans un premier temps pourquoi pas, cela mettra les problèmes en exergue.
Questions de la semaine 3
ReX : Ce serait bien d'expliquer les problèmes rencontrés et les solutions apportées.
Timothe : Depuis le début de mon travail ou seulement dernièrement ? Je vous mets ça dans une rubrique à part ?
ReX : Tout le temps ! La validation de l'EC dépend autant de la documentation régulière que du résultat.
Problèmes rencontrés et solutions apportées
Problèmes avec la partie afficheurs 7 segments
Pour ce sous-système, j'ai eu du mal à comprendre le code de prime à bord.
Je ne comprenais pas l'intérêt de segfont.c
puisqu'il comprenait tout les caractères ASCII, alors que les afficheurs peuvent seulement afficher 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, b, C, d, E, F.
ReX : Ce fichier contient la configuration souhaitée pour les segments (chaque bit correspond à un segment) pour chaque caractère ASCII, il est possible d'afficher au delà des caractères hexadécimaux, même si certains caractères ne sont pas très reconnaissables.
J'avais également du mal à comprendre les fonctions d'affichages display_set
, display_ascii
et display_next
.
Notamment l'utilité de display_ascii
puisque très peu de caractères de ce tableau sont finalement affichables. Ensuite, display_next
qui pouvait seulement utiliser la fonction display_ascii
.
ReX : Même remarque que ci-dessus.
Solutions :
Finalement, j'ai créé une constante dont chacune des valeurs correspond à chaque caractères affichables.
ReX : Vous avez donc redéfini segfont.c
, c'est dommage de perdre du temps.
Je n'utilise donc plus la fonction display_ascii
ni display_next
et donc je n'utilise plus non plus le fichier segfont.c
.
ReX : Même remarque, c'est une faiblesse de ne pas comprendre un code pour le réutiliser sans réinventer la roue.
Désormais, il suffit pour afficher un caractère d'utiliser la fonction display_set
, elle prend en paramètre le tableau groupes[]
, qui comprend chaque segment de tous les afficheurs 7segments, ainsi que le numéro de l'afficheur que l'on souhaite, et enfin la constante correspondant au caractère que l'on souhaite afficher.
Cela signifie que je n'utilise plus la fonction cursor_set
également, j'utilise directement le paramètre num
de la fonction pour sélectionner l'afficheur.
ReX : Même remarque.
les constantes que j'ai créées sont les suivantes :
ZERO 63 UN 6 DEUX 91 TROIS 79 QUATRE 102 CINQ 109 SIX 125 SEPT 39 HUIT 127 NEUF 111 A 119 B 124 C 57 D 94 E 121 F 113 OFF 0
ReX : OK un sous-ensemble de segfont.c
donc.
Timothé (Edit du 05/09/23): J'utilise désormais la fonction display_ascii
et donc le fichier segfont.c
. Voir #Simplification du code de réception d'informations des afficheurs 7 segments
Problèmes avec la matrice de LED
Pour ce sous-système j'ai eu moins de difficultés, l'organisation est là même que l'autre sous-système et il y avait moins de choses.
J'ai voulu afficher l'alphabet sur la matrice, cependant, le tableau que l'on manipule pour contrôler la matrice de LEDs, n'était pas dans le bon "sens", c'est-à-dire, avec chacune de ses cases correspondant à la même LED sur la matrice dans l'espace.
Par exemple sur une matrice 2,2 les LEDs sont placées de la manière suivante :
1 2 3 4
tandis que les cases du tableau, serait disposées ainsi également :
1 2 3 4
Solutions :
Après quelques tests, j'ai pu constater que la matrice était pivotée de 90° vers la droite et que les cases étaient en miroir par rapport au sens de lecture.
J'ai donc de nouveau fait des tests jusqu'à ce que je parvienne retirer le miroir puis à pivoter la matrice de 90° vers la gauche.
Ensuite j'ai créé toutes les lettres de l'alphabet avec le système de tableau que j'avais fait.
Voici par exemple la lettre A :
unsigned char A[]={ 0,0,0,0,0,0,0,0, 0,0,0,ON,ON,0,0,0, 0,0,ON,0,0,ON,0,0, 0,0,ON,ON,ON,ON,0,0, 0,0,ON,0,0,ON,0,0, 0,0,ON,0,0,ON,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, };
ON
correspondant à une LED allumé et 0
une LED éteinte.
ReX : Mettons, je vous accorde le sens de la matrice.
Problèmes avec les communications SPI
J'ai commencé par établir le lien entre les afficheurs 7segments et le maitre. Je ne parvenais pas à transmettre les données, puis à les afficher correctement.
Solutions :
J'ai finalement modifié très légèrement la facon d'envoyer les données et de les recevoir.
Du côté du maitre SPI, la solution fut de sélectionner un prescaler pour l'horloge cadencant la communication. En effet, de prime à bord il n'y en avait pas, et j'ai supposé que l'horloge étant trop rapide cela ne fonctionnait pas. Puisque effectivement, après avoir tester différentes cadences d'horloge, j'ai remarquer que lorsqu'elle était trop élevée la communication ne passait plus.
ReX : C'est possible, le PCB est peut être mal optimisé pour une communication SPI à haute vitesse. Comment avez-vous connecté les broches entre elles ? Par des câbles (cela pourrait perturber le signal) ? Avec un plot de soudure ?
Timothé : Je l'ai fait avec des câbles. En effet, il sera peut-être possible d'augmenter la cadence de l'horloge une fois un plot de soudure fait. Cela réduira grandement la longueur de la ligne et donc les perturbations.
Cela m'a permit d'établir la communication avec les afficheurs 7 segments. Par la suite, j'ai légèrement modifié la façon de recevoir pour la matrice, afin que ce soit identique aux afficheurs. Ce qui ne fonctionnait pas, mais j'avais simplement oublié d'initialiser la communication SPI...
Finalement, il suffit pour les deux sous-système d'envoyer avec la communication SPI un tableau contenant les caractères souhaités. Ces caractères sont envoyés un à un et sont affiché de la manière suivante :
Afficheurs : 2 modes possibles, pour le premier, le premier caractère va au premier afficheur et ainsi de suite. Pour le second, on sélectionne d'abord l'afficheur souhaité puis on envoit son caractère, et ainsi de suite.
Matrice : les caractères sont affichés les uns après les autres avec un intervalle de 800 ms, ce qui permet d'écrire des messages.
(Les fichiers serial.c
et serial.h
ne sont pas utiles pour les sous-systèmes afficheurs et matrice puisque n'ayant pas de MISO ils ne peuvent remonter d'informations).
ReX : Je ne vois pas trop le rapport entre UART et SPI, mais effectivement les broches RX et TX des deux esclaves n'étant pas utilisées, le code pour l'UART est assez inutile sur les esclaves.
Semaine 4
Création de la rubrique "Problèmes rencontrés et solutions apportées"
Questions de la semaine 4
J'ai créé une rubrique "Problèmes rencontrés et solutions apportées".
Son contenu correspond-t-il à ce que vous souhaitiez ?
ReX : Oui cela permet de mieux comprendre ce qui t'as arrêté.
ReX : Il manque une vidéo de démonstration de l'affichage des caractères sur la matrice et sur les 7 segments.
ReX : Il manque aussi les programmes ...
Timothé : Voir #Rendu Intermédiaire pour les programmes et #Vidéos rendues pour les vidéos, plus exactement celle-ci
Semaine 6
Comme vous avez pu le constater, j'ai malheureusement eu un peu moins de temps pour avancer sur le projet dernièrement et ai donc également été moins actif sur le wiki.
ReX : Oui c'est vu. Dommage.
- Documents rendus (voir #Rendu Intermédiaire pour les codes):
- Codes de la partie 7 segments
- Codes de la partie matrice de LED
- Codes de la partie maitre SPI (fonctionnement séquentiel)
- Début du code d'ordonnancement
- Vidéo démontrant le fonctionnement séquentiel
- Affichage de "bonjour" sur la matrice de LED
- Affichage de FEDCBA sur les afficheurs 7segments (en utilisant la sélection manuel des afficheurs pour afficher à l'envers ce qui est envoyé)
- Ordonnancement :
- J'ai commencé à reprendre bien en détail le fonctionnement de l'ordonnanceur que nous avions mis en place dans le TP ordonnancement du semestre précédent, pour l'adapter à mon sujet
Semaine 7
Modification du codage des caractères en mémoire (matrice de LED)
ReX : Le codage des caractères prend trop de place en mémoire, limiter à 8 octets par caractère.
Timothé (EDIT) : J'ai modifié en conséquence la fonction matrix_set
.
Précédent code :
void matrix_set(int *groupes,unsigned char *matrix){ int i; for(i=0;i<NB_ROWS*NB_COLS;i++) groupes[indirect_matrix[i]]=matrix[i]; }
Nouveau code :
void matrix_set(int *groupes,unsigned char *matrix){ int i; for(i=0;i<NB_ROWS*NB_COLS;i++) groupes[indirect_matrix[i]]=((matrix[i/8])&(1<<(7-(i%8))))*BRIGHTNESS; }
ReX : Heu non. La valeur 1<<(7-(i%8))
dépend de la position du bit. Ca ne peux pas donner la valeur attendue. Il faut ajouter une expression conditionnelle pour ramener à 0 ou 1.
Timothé : En effet, j'ai corrigé cela, voir #Suite de la modification du codage des caractères en mémoire (matrice de LED).
Pour la façon dont je code les octets, il faut pour les décoder, les balayer du bit de poids fort au bit de poids faibles. De la manière précédente, seulement les caractères symétriques s'affichaient correctement, le problème est résolu.
Cela permet pour chacune des huit rangées de LEDs sur la maquette, de récupérer SON octet du caractère en mémoire (grâce à matrix[i/8]).
Et grâce au masque, au décalage, à la multiplication par BRIGHTNESS
on allume ou non chacune des LEDs en fonction de la valeur de l'octet récupéré.
Ainsi les caractères prennent désormais 8 octets en mémoire. Pour tester cela j'ai utilisé le caractère B voici le précédent code :
unsigned char B[]={ 0,0,ON,0,0,0,0,0, 0,0,ON,0,0,0,0,0, 0,0,ON,0,0,0,0,0, 0,0,ON,ON,ON,ON,0,0, 0,0,ON,0,0,ON,0,0, 0,0,ON,0,0,ON,0,0, 0,0,ON,ON,ON,ON,0,0, 0,0,0,0,0,0,0,0, };
puis le nouveau code du caractère :
unsigned char B[]={32, 32, 32, 60, 36, 36, 60,0};
Cela fonctionne bien, mais les LEDs n'ont pas tout à fait toutes la même intensité.
ReX : Vu l'erreur notée ci-dessus, forcément.
EDIT : les caractères B, O, N, J, U, R sont codés de cette nouvelle façon
unsigned char B[]={32, 32, 32, 60, 36, 36, 60, 0}; unsigned char O[]={0, 0, 60, 36, 36, 60, 0, 0}; unsigned char N[]={0, 68, 100, 84, 76, 68, 0, 0}; unsigned char J[]={0, 28, 8, 8, 8, 40, 16, 0}; unsigned char U[]={0, 0, 36, 36, 36, 36, 24, 0}; unsigned char R[]={0, 60, 36, 60, 48, 40, 36, 0};
Une nouvelle vidéo de démonstration utilisant ce codage est disponible (nous pouvons effectivement y distinguer que toutes les LEDs n'ont pas la même intensité...)
Timothé (Edit du 05/09/23) : Le problème d'intensité a été réglé, voir #Suite de la modification du codage des caractères en mémoire (matrice de LED).
ReX : J'avance lentement car il faut que je corrige la syntaxe du Wiki en même temps que je le lis. Vidéo de démonstration non jointe au Wiki.
Estimation du temps nécessaire à l'envoi des informations aux TLC
ReX : Il faudrait justifier les 24ms d'attente par l'analyse de la durée du code d'envoi des informations aux TLC des 7 segments.
Timothé : Une fois les informations à envoyer aux TLC reçus :
- Il y a un
switch
, dans le pire cas nous allons au derniercase
et faisons 16 opérations pour y arriver. - Appel de
display_set
12 opérations- Le saut sur la fonction : Sauvegarde du
SP
--> 4 opérations (masquage pour partie haute et push, masquage pour la partie basse et push), puis chargement de l'adresse de la fonction 2 pulls --> 2 opérations - Chargement de
groupes
qui est un pointeur donc une adresse en mémoire sur 2 octets il faut 2 pulls --> 2 opérations - Chargement de
display_number
qui est de typeuint8_t
, un pull --> 1 opération - Chargement d'une constante toujours codé sur un octet, un pull --> 1 opération
- Retour de la fonction, récupération du
SP
précédent 2 pulls --> 2 opérations
- Le saut sur la fonction : Sauvegarde du
- Appel de
_display_set
15 opérations- Saut sur la fonction, comme la fonction précédente --> 6 opérations
- Chargement de
groupes
, idem que fonction précédente --> 2 opérations - Chargement de
num
, de typeint
donc sur deux octets, deux pulls --> 2 opérations (num
récupérantdisplay_number
pourrait être de typeuint8_t
pour économiser de la place et 1 opération) - Chargement de
value
, de typeint
donc sur deux octets, deux pulls --> 2 opérations (idem que pournum
,value
pourrait être de typeuint8_t
car la constante qu'elle récupère est toujours codé sur un octet) - Retour de la fonction, récupération du
SP
précédent 2 pulls --> 2 opérations
- Exécution de la fonction
_display_set
59 opérations- Multiplication --> 1 opération
- Affectation --> 1 opération
- Test conditionnel --> 1 opération
- Boucle
for
(8 itérations) --> 7*8 = 56 opérations- Par boucle, 2 opérations pour son fonctionnement (premier tour : affectation et vérification condition (2 opérations) ; les tours suivants : vérification condition et incrémentation (2 opérations))
- Par boucle, une addition, un décalage, une multiplication, une affectation, un autre décalage soit --> 5 opérations
Ce qui fait un total de 86 opérations
ReX : Tu as bien écrit qu'il y avait 12 appels à une fonction demandant 59 cycles et tu fais une addition ? C'est bien ce que je dois comprendre ?
Timothé : Non ce n'est pas ça. Déjà oublions le switch
je vais le retirer. Je disais que lorsque le µC reçoit un octet, il commence par appeler la fonction display_set
, j'ai estimé que son appel nécessitait 12 opérations (voir le détail). Ensuite il appel la fonction _display_set
, j'ai estimé que son appel nécessitait 15 opérations (voir le détail). Enfin, j'ai estimé l'exécution de cette fonction à 59 opérations, 7 opérations dans la boucle for
qui est itérée 8 fois (56 opérations) plus les 3 opérations avant la boucle for
. Pour un total de 70 opérations (switch
retiré) afin d'envoyer les informations d'un octet reçu aux TLC (voir la suite des calculs ci-dessous).
Timothé : Est-ce mieux expliqué ?
FCPU
= 16 MHz --> période = 6,25*10⁻⁸ s
24ms/6,25*10⁻⁸ = 384000 cycles d'horloges (c'est-à-dire, à cette fréquence, 24 ms représentent 384000 cycles), ce qui d'après mon nombre d'opération représente une moyenne de 4465 cycles par opération (si on veut arriver aux 24ms d'exécution pour l'envoi des informations aux TLC).
En effet, certaines opérations étant complexes peuvent prendre quelques dizaines de cycles pour être exécutées. Cependant 4465 me parait très excessif...
Ce qui pourrait expliquer cette erreur :
- Fréquence du uC pas à 16 MHz
- La fonction
_delay_ms
qui n'est pas toujours très précise selon les conditions dans lesquelles elle est utilisée - Une erreur majeure de ma part dans l'estimation du nombre d'opérations
En prenant une vingtaine de cycle par opération, j'estime un temps d'exécution pour l'envoi des informations aux TLC de 0,11 ms (20*86*6,25*10⁻⁸ = 0,11 ms)
Semaine 8
Commande permettant de demander (côté maitre) et de signaler (côté esclave), si l'esclave est à l'écoute
Première proposition (pas réalisable)
ReX : Pour éviter tous ces problèmes de délais, le mieux serait de prévoir une commande pour demander si le périphérique SPI est libre. Si le périphérique est libre un octet particulier est retourné. Si le périphérique se lance dans un traitement autre que l'écoute SPI, il prend soin d'initialiser sa réponse SPI a autre chose que cet octet particulier.
Timothé : Cela ne correspond pas tout à fait à ce que vous avez proposé, mais j'imaginais faire quelque chose dans ce genre là pour ça :
bool isSlaveListening() { // Envoyer un octet factice et observer la ligne MISO SPDR = 0xFF; // Octet factice à envoyer while (!(SPSR & (1 << SPIF))); // Attendre la fin de la transmission // Si MISO est à l'état bas, cela signifie que l'esclave ne répond pas, il est donc probablement occupé if (!(PINB & (1 << SPI_MISO))) // Vérification de la ligne MISO { return false; // L'esclave est occupé } return true; // L'esclave est prêt à écouter }
ReX : Peux-tu expliquer pourquoi si MISO est à l'état bas c'est que l'esclave est occupé ?
Il suffirait ensuite avant chaque envoie que le maître appel cette fonction en boucle jusqu'à ce qu'elle retourne vrai
.
Cela pourrait-t-il fonctionner également ?
ReX : A priori non sauf si j'ai raté quelque chose de fondamental avec le bus SPI.
Deuxième proposition
Je me suis finalement rendu compte que cette façon de faire n'était pas réalisable, puisque l'esclave SPI répondra toujours même s'il fait autre chose. A moins peut-être de lui faire répondre par 0x00, mais cela me parait un peu aléatoire... Et de toute façon, cela envoie en boucle des messages à l'esclave ce qui n'est pas terrible non plus.
J'essaye donc autre chose qui ressemble plus à ce que vous suggériez en plus.
Dès que l'esclave reçoit un octet, il passe sa réponse SPI à 0x0F avant de se lancer dans son traitement, pour signaler qu'il est occupé. Dès qu'il a fini son traitement, il passe de nouveau sa réponse SPI à 0xFF pour signaler qu'il est libre à nouveau.
ReX : Si l'esclave est occupé il ne peut pas préparer sa réponse. D'où ce que je demandais à savoir de préparer cette réponse avant de partir dans un traitement.
Voici le code en question :
if(isSlaveSelectActive()) // test de la ligne SS entre chaque octet reçu { received=spi_exch(reponseSPI); //L'esclave a recu une commande... reponseSPI = 0x0F; //on initialise son octet de reponse à 0x0F, ce qui signifie qu'il est occupé switch(received){ .... } reponseSPI = 0xFF; //il a fini de traiter ce qu'il a reçu, il est de nouveau à l'écoute on repasse donc son octet de reponse à 0xFF }
ReX : Le code est plus convaincant que le texte.
Après avoir ajouté la connectique de la ligne MISO, j'ai affiché la réponse que le maitre SPI recevait pour voir si cela fonctionnait bien. Voir la photo "Réponses SPI reçues par le maitre"
J'ai pu constater que le 0xFF passait bien (255 sur la photo) en revanche le 0x0F lui n'est pas constant... Il faudrait donc du côté du maitre tester si la valeur reçue est bien 255 ce qui signifierait qu'il est libre afin de pouvoir envoyer la suite des données. Cependant, je ne vois pas ce que le maitre est censé faire pour temporiser s'il ne reçoit pas 255. Une tâche dans le vide relativement rapide puis réessayer ? Sachant que de toute façon la tâche sera très rapidement désordonnancée par l'ordonnanceur...
ReX : Il essaye à nouveau.
Timothé : Voir #Essaie de la commande permettant de savoir si l'esclave est à l'écoute
J'ai de nouveau effectué le test mais en mettant cette fois-ci le délai de 24 ms qui fonctionnait très bien avant, et le maitre reçoit effectivement 100% de 255, voir la deuxième photo "Lors de l'utilisation du délai de 24 ms..."
ReX : Les 24ms pourquoi pas mais pourquoi pas 13ms ou 54ms ?
Timothé : Auparavant, en-deça de 24 ms cela faisait saturé l'affichage des afficheurs 7 segments (le µC ne pouvait plus gérer à la fois l'envoi des infos aux TLC et la vitesse de la réception des informations SPI). Cependant, vu que vous posiez la question, j'ai refait des tests et je peux désormais descendre à 15 ms avant que la saturation arrive (cela devait être dû à des câbles mal branchés pour les 24 ms...). En revanche, cela est toujours loin des 0,11 ms que j'avais estimé en théorie... (voir #Estimation du temps nécessaire à l'envoi des informations aux TLC)
Gérer la ligne "chip select" (CS) (ou "slave select" (SS))
ReX : Ce serait bien de gérer la ligne SS, par exemple en vérifiant que la ligne n'est pas désélectionnée entre deux octets de la même commande.
Timothé : J'ai créé une fonction permettant de savoir si la ligne SS est toujours active ou non
A chaque fois que l'esclave veut récupérer un octet, il vérifie d'abord que la ligne SS soit toujours active, si ce n'est pas le cas il ne récupère pas l'octet voici très simplement comment cela se présente :
if(isSlaveSelectActive()){ received=spi_exch(0xFF); switch(received){ case 0: //0 display_set(groupes, display_number, ZERO); break; case 1: //1 display_set(groupes, display_number, UN); break; case 2: //2 display_set(groupes, display_number, DEUX); break; case 3: //3 display_set(groupes, display_number, TROIS); break; case 4: //4 display_set(groupes, display_number, QUATRE); break; case 5: //5 display_set(groupes, display_number, CINQ); break; case 6: //6 display_set(groupes, display_number, SIX); break; case 7: //7 display_set(groupes, display_number, SEPT); break; case 8: //8 display_set(groupes, display_number, HUIT); break; case 9: //9 display_set(groupes, display_number, NEUF); break; case 'A': //A display_set(groupes, display_number, A); break; case 'B': //b display_set(groupes, display_number, B); break; case 'C': //C display_set(groupes, display_number, C); break; case 'D': //d display_set(groupes, display_number, D); break; case 'E': //E display_set(groupes, display_number, E); break; case 'F': //F display_set(groupes, display_number, F); break; case 0xFF: //rien, cela signifie que le maitre demandait si l'esclave était à l'écoute break; } }
ReX : Non et Non. Non parce que j'ai déjà dit que le switch
était inutile avec une conception correcte et non parce que tu n'as pas compris "entre deux octets d'une commande".
Timothé : J'ai compris ce que vous vouliez dire pour le switch
, j'ai corrigé cela (voir #Simplification du code de réception d'informations des afficheurs 7 segments). Que voulez-vous dire par "entre deux octets d'une commande" dans ce cas là ?
Voici le code de la fonction :
//renvoi 1 si la ligne SS est au niveau bas et donc active, renvoi 0 sinon bool isSlaveSelectActive() { return !(SPI_PIN & (1 << SPI_SS)); }
Cela permettra à l'esclave d'ignorer un octet qui ne lui était finalement pas destiné.
Cela correspond-t-il à ce que vous souhaitiez ?
ReX : Non.
Poursuite ordonnancement
Pour l'ordonnancement, j'ai été confronté à un autre problème en faisant des tests.
Lorsque l'ordonnanceur arrête l'exécution d'une tâche en cours et élit une nouvelle tâche, la ligne SS de la précédente tâche reste active. Ce qui fait qu'elle reçoit par la suite également ce qui était destiné à la tâche nouvellement élu par l'ordonnanceur.
ReX : Oui d'où l'idée d'une tâche système qui gère les envois SPI en désactivant les interruption pour envoyer une commande entière sans être préemptée. Tu viens de mettre le doigt sur la difficulté de l'EC.
(La vidéo suivante ne répondait pas à la vérification des fichiers, je vous l'ai donc envoyé par mail, veuillez m'excuser pour la gêne occasionnée).
ReX : Je n'en tiens pas compte.
Voir l'exemple en vidéo, nous pouvons voir que la deuxième fois que le "b" apparait sur la matrice, il y apparait exactement en même temps que celui sur les afficheurs 7 segments, et disparait lorsque le "C" est affiché (les caractères "C", "D" et "F" ne sont actuellement plus supporté par la matrice, c'est pourquoi il y a juste plus rien d'affiché, voir #Modification du codage des caractères en mémoire (matrice de LED) le changement de codage des caractères). Cela témoigne que la matrice reçoit les octets envoyés aux afficheurs 7 segments.
ReX : Utiliser des balises ref pour pointer sur un pararaphe donné.
Pour l'instant, pour corriger cela je sélectionne et désélectionne les lignes SS dans la routine d'interruptions du timer, en fonction du processus que l'on lâche, et celui que l'on récupère. Ce qui permet de fermer la ligne SS du processus en cours d'exécution, qui est arrêté avant qu'il ne puisse le faire lui même. Voir la vidéo d'illustration. Nous pouvons voir dans cette vidéo que lorsque la tâche de la matrice se fait arrêter, l'envoi des caractères "A", "D" et "E" sur les afficheurs 7 segments n'efface pas le "b" qu'avait reçu la matrice au préalable.
Voici le code de la nouvelle routine d'interruption :
//Routine d'interruption du timer ISR(TIMER1_COMPA_vect, ISR_NAKED) { SAVE_REGISTER(); //On sauvegarde la pile, qui contient le contexte d'exécution du processus processus[pid].pile=SP; //ainsi qu'où était le pointeur de pile, afin de reprendre où nous en étions lorsque ce processus sera exécuté à nouveau //On déselectionne la ligne SS correspondant à la tâche arrêté if(!pid) spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); else spi_unselect(&SPI_PORT_MATRIX,SPI_BIT_MATRIX); pid=(pid+1)%MAX_PROCS; //on passe au processus suivant //on sélectionne la ligne SS correspondant à la tâche nouvellement élu if(!pid) spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); else spi_select(&SPI_PORT_MATRIX,SPI_BIT_MATRIX); SP = processus[pid].pile; // on récupère où en était le pointeur de pile à la précédente exécution RESTORE_REGISTER(); //et on recharge la pile asm volatile("reti"); // on autorise à nouveau les interruptions }
ReX : Soit un ordonnanceur dépendant des tâches lancées ... Je te laisse imaginer le commentaire que je souhaiterais écrire.
Timothé : Je vois... Avez-vous eu le temps de regarder le début de code d'ordonnancement que j'ai posté dans documents rendus ? Sinon ce n'est pas grave, je vais poster une version mise à jour prochainement (voir #Semaine 9).
ReX : Non, si le Wiki ne réflète pas ce qui dans le code, c'est qu'il y a un défaut de documentation.
Semaine 9
Simplification du code de réception d'informations des afficheurs 7 segments
Comme je vous l'avais dit, j'ai retiré le switch
. J'utilise directement la variable récupérant le message SPI, pour récupérer tout d'abord le numéro de l'afficheur puis pour afficher le caractère (j'utilise donc désormais la fonction display_ascii
que je n'utilisais pas jusqu'à présent).
Voici le nouveau code pour les afficheurs 7 segments :
int main(void) { uint8_t display_number=0; init_LED_Drivers(NB_DRIVERS); // requis pour configuration des 2 tlc5947 spi_init(0); // initialisation de la communication SPI screen_clear(groupes); while(1){ set_LED_Drivers(groupes,NB_DRIVERS); uint8_t received; uint8_t reponseSPI = 0xFF; // quand reponseSPI vaut 0xFF l'esclave est à l'écoute et quand reponseSPI vaut 0x0F cela signifie qu'il est occupé /*Le premier caractere recu désigne le numéro de l'afficheur souhaité*/ received=spi_exch(reponseSPI); // l'esclave a recu une commande... display_number = received; /*le deuxieme caractere recu designe celui à afficher*/ received=spi_exch(reponseSPI); // l'esclave a recu une commande... reponseSPI = 0x0F; // on initialise son octet de reponse à 0x0F, ce qui signifie qu'il est occupé display_ascii(groupes, display_number, received); reponseSPI = 0xFF; // il a fini de traiter ce qu'il a reçu, il est de nouveau à l'écoute on repasse donc son octet de reponse à 0xFF } return 0; }
ReX : Pas de vérification du signal "chip select".
ReX : Le premier reponseSPI = 0x0F
est inutile. Il est positionné alors que l'esclave a juste une affectation a effectuer.
Timothé : Je l'avais mis par principe mais effectivement le µC ne fait qu'une affectation ici, cela lui prendra même plus de temps au final de gérer les deux affectations (de reponseSPI) que l'unique affectation qu'il avait à faire de base... Je l'ai retiré.
Suite de la modification du codage des caractères en mémoire (matrice de LED)
J'ai corrigé le code pour décoder les caractères de la matrice de LED, en effet cela permet d'avoir un éclairage constant pour toutes les LEDs et de la valeur de BRIGHTNESS
.
Voici le nouveau code :
void matrix_set(int *groupes,unsigned char *matrix){ int i; for(i=0;i<NB_ROWS*NB_COLS;i++) groupes[indirect_matrix[i]]=((matrix[i/8] >> (7-(i%8))) & 1)*BRIGHTNESS; }
ReX : Oui.
Essaie de la commande permettant de savoir si l'esclave est à l'écoute
J'ai essayé de renvoyer les données sans même une attente très courte lorsque le µC "esclave" renvoie qu'il est occupé. Cependant cela le fait saturer et il ne parvient plus à afficher les caractères (il affiche le premier caractère correctement puis des "morceaux" de caractères). Il faudrait temporiser un peu le fait d'essayer à nouveau cependant cela nous ramène au problème de départ.
Pensez-vous qu'il serait plus intéressant d'utiliser une boucle for
(s'incrémentant dans le vide) pour faire une temporisation très rapide plutôt que la fonction _delay_ms
? Ou faut-il impérativement proscrire les temporisations de toutes sortes ?
Voici le code testé :
void task_7segments() { uint8_t display7Segments1[] = {5, 'A', 4, 'B', 3, 'C', 2, 'D', 1, 'E', 0, 'F'}; uint8_t display7Segments2[] = {0, 'A', 1, 'B', 2, 'C', 3, 'D', 4, 'E', 5, 'F'}; /*Permet l'envoie d'un tableau sur les afficheurs 7 segments*/ spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY);//sélection du uC controlant les afficheurs 7 segments for(int i=0;i<12;i++) //parcours du tableau pour l'envoie des valeurs { if(rotationmessage) { // tant que la réponse est différente de 255 et donc que le µC est occupé, l'octet est renvoyé (cela provoque une saturation...) while( (reponse = spi_exch(display7Segments1[i])) != 255); } else { // idem while( (reponse = spi_exch(display7Segments2[i])) != 255); } } rotationmessage = (rotationmessage+1)%2; spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); //déselection du uC }
ReX : La variable rotationmessage
n'est pas mise à jour.
Timothé : Je ne m'en suis pas rendu compte en mettant le code sur le wiki, c'est corrigé.
ReX : Pas cohérent avec le code précédent. Un seul octet envoyé par message.
Timothé : Pas cohérent avec le code de la partie des afficheurs 7 segments vous voulez-dire ? (celui présent dans #Simplification du code de réception d'informations des afficheurs 7 segments)
Timothé : Si oui, c'est bien cela. Le code précédent récupère avec un tour de la boucle while
2 octets successivement. Le premier désignant la position de l'afficheur et le deuxième le caractère à afficher. Afin d'écrire sur l'ensemble des 6 afficheurs, 6 tours de la boucle while
sont nécessaires. Afin de récupérer tour après tour, les 2 octets requis pour chacun des afficheurs.
ReX : Oui et pourquoi ton code de test ci-dessus n'envoie qu'un octet ?
ReX : En cas d'échec désactiver le CS et recommencer. Une attente de quelques microsecondes sur changement de CS est acceptable.
Timothé : Voir #Suite de l'essai de la commande permettant de savoir si l'esclave est à l'écoute.
Voici une version mise à jour du programme d'ordonnancement : Fichier:DeuxiemeRenduOrdonnancement.zip
ReX : J'attendrais la version synthétique sur le Wiki.
Suite de l'essai de la commande permettant de savoir si l'esclave est à l'écoute
En cas d'échec, désormais je désactive la ligne CS et la réactive avant de réessayer de transmettre.
Voici le code :
void task_7segments() { uint8_t display7Segments1[] = {5, 'A', 4, 'B', 3, 'C', 2, 'D', 1, 'E', 0, 'F'}; uint8_t display7Segments2[] = {0, 'A', 1, 'B', 2, 'C', 3, 'D', 4, 'E', 5, 'F'}; uint8_t reponse; uint8_t rotationmessage=0; /*Permet l'envoie d'un tableau sur les afficheurs 7 segments*/ spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY);//sélection du uC controlant les afficheurs 7 segments for(int i=0;i<12;i++) //parcours du tableau pour l'envoi des valeurs { if(rotationmessage) { // tant que la réponse est différente de 255 et donc que le µC est occupé, l'octet est renvoyé après désactivation et réactivation du chip select (cela provoque une saturation...) while( (reponse = spi_exch(display7Segments1[i])) != 255) { spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); _delay_us(5); spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); _delay_us(5); } } else { // idem while( (reponse = spi_exch(display7Segments2[i])) != 255) { spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); _delay_us(5); spi_select(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); _delay_us(5); } } } rotationmessage = (rotationmessage+1)%2; spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); //déselection du uC }
Cela devrait permettre de "relancer" la communication avec l'esclave en sélectionnant à nouveau son chip select afin de retenter la transmission.
Le fait de désactiver et réactiver le CS "remet" l'esclave en mode écoute et donc prêt à recevoir et/ou à transmettre des informations avec le maitre.
C'est de cette façon qu'il faut gérer la ligne CS sur les esclaves :
// Permet de recevoir et d'envoyer un octet sur le bus SPI uint8_t spi_exch(uint8_t output) { SPDR = output; while(!(SPSR & (1<<SPIF)) && !(SPI_PIN & (1<<SPI_SS))); //ajout de la vérification de l'état du CS lors des transmissions return SPDR }
ReX : Il faudrait surtout vérifier que le CS est actif entre la réception des deux octets et revenir à la réception du premier sinon.
Bilan du projet
Résumé travail effectué
Partie afficheurs 7 segments
- Cette partie est bien fonctionnel en séquentiel.
Cependant, voir #Ce qu'il faut terminer, ce qu'il reste à faire dans le projet pour les améliorations possibles/nécessaires et l'ordonnancement.
les codes sont ici #Rendu final
Partie matrice de LED
- Cette partie est bien fonctionnel en séquentiel.
Cependant, voir #Ce qu'il faut terminer, ce qu'il reste à faire dans le projet pour les améliorations possibles/nécessaires et l'ordonnancement.
les codes sont ici #Rendu final
Partie maitre SPI
- Cette partie est bien fonctionnel en séquentiel.
Cependant, voir #Ce qu'il faut terminer, ce qu'il reste à faire dans le projet pour les améliorations possibles/nécessaires et l'ordonnancement.
les codes sont ici #Rendu final
Où j'en suis dans la suite du projet
Ce qui est fait sur l'ordonnancement
Les codes sont ici #Rendu final
- Les macros permettant de sauvegarder la pile et la recharger, pour la rotation des processus (voir code partie ordonnancement).
- La structure d'un processus :
//déclaration de la structure contenant un processus typedef struct scheduler_t { uint16_t pile; //position courante dans la fonction associé au processus void (*fonction) (void); // fonction associée au processus }scheduler_t;
- Création d'un tableau de processus contenant tous nos processus :
//remplissage de la structure contenant les processus scheduler_t processus[MAX_PROCS]={ {0x500,task_7segments}, {0x700,task_matriceLEDs}, {0x600,task_CommunicationSPI}, };
- L'initialisation du timer (peut-être qu'il faudra changer la valeur du prescaler pour optimiser le temps de rotation des processus):
//initialisation du timer qui permettra la rotation des processus void init_timer() { TCCR1B |= _BV(WGM12); // CTC mode with value in OCR1A TCCR1B |= _BV(CS12); // CS12 = 1; CS11 = 0; CS10 =1 => CLK/1024 prescaler TCCR1B |= _BV(CS10); OCR1A = NB_TICK; TIMSK1 |= _BV(OCIE1A); }
- Routine d'interruption du timer, elle gère le passage d'un processus à un autre. En relançant le nouveau processus où il en était et en permettant au précédent de pouvoir être relancé où il en était :
//Routine d'interruption du timer ISR(TIMER1_COMPA_vect, ISR_NAKED) { SAVE_REGISTER(); //On sauvegarde la pile, qui contient le contexte d'exécution du processus processus[pid].pile=SP; //ainsi qu'où était le pointeur de pile, afin de reprendre où nous en étions lorsque ce processus sera exécuté à nouveau pid=(pid+1)%MAX_PROCS; //on passe au processus suivant SP = processus[pid].pile; // on récupère où en était le pointeur de pile à la précédente exécution RESTORE_REGISTER(); //et on recharge la pile asm volatile("reti"); // on autorise à nouveau les interruptions }
- Initialisation de l'ensemble des processus contenu dans notre tableau de processus
//fonction permettant la préparation des processus avant la première exécution void init_proc(int i) { uint16_t old = SP; // on sauvegarde où était le pointeur de pile avant l'initialisation du processus SP = processus[i].pile; // on va à l'emplacement que l'on à initialement choisi pour notre processus lorsque l'on a rempli la structure int adresse=(int)processus[i].fonction; //on recupere l'adresse où a été mis la fonction de notre processus pour la pousser sur la pile asm volatile("push %0" : : "r" (adresse & 0x00ff) ); //d'abord les 8 bits de poids faibles asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8)); //puis les 8 bits de poids forts que l'on décale donc de 8 bits SAVE_REGISTER(); // on sauvegarde la pile processus[i].pile = SP; //ainsi qu'où était le pointeur de pile, le processus est prêt pour sa première utilisation SP = old; //on remet le pointeur de pile où il était avant l'initialisation du processus }
- Le main, permettant le lancement de l'ordonnanceur
int main(void) { spi_init(1); _delay_ms(1000); spi_initcs(&SPI_DDR_DISPLAY,SPI_BIT_DISPLAY); spi_initcs(&SPI_DDR_MATRIX,SPI_BIT_MATRIX); spi_unselect(&SPI_PORT_DISPLAY,SPI_BIT_DISPLAY); spi_unselect(&SPI_PORT_MATRIX,SPI_BIT_MATRIX); init_timer(); //initialisation du timer for(int i=0;i<MAX_PROCS;i++) init_proc(i); // initialisation des processus sei(); //autorisation des interruptions processus[0].fonction(); // lancement manuel du premier processus return 0; }
ReX : A quoi sert le lancement manuel dans la mesure où l'ISR peut le faire ?
Je n'ai malheureusement pas pu tester convenablement cette partie du code puisque j'étais bloqué plus tôt et ai manqué cruellement de temps sur la fin. Il est donc probable qu'il y ait des problèmes dont je n'ai pas conscience dans ce programme.
Je parlerai des tâches de ce programme dans la partie #Ce qu'il faut terminer, ce qu'il reste à faire dans le projet
Ce qu'il faut terminer, ce qu'il reste à faire dans le projet
Améliorations possibles/nécessaires
Pour la matrice : Il y a parfois des caractères qui se "perdent", cela peut-être du à l'utilisation de fils et non de plot de soudure (perturbations) il faudrait mettre des plots de soudure (qui pourraient également permettre la monter en fréquence de la communication SPI). Le switch
est toujours présent dans cette partie. Il faudrait refaire avec le nouveau codage les caractères de l'alphabet manquant. Ajout des deux lignes permettant de préparer la réponse SPI (je viens de me rendre compte que je l'ai oublié dans le rendu final).
Pour les afficheurs : Mettre des plots de soudure pour ne plus avoir à utiliser les fils, et peut-être pouvoir monter la fréquence de l'horloge cadençant la communication SPI.
Pour la suite de l'ordonnancement
Pour les esclaves SPI : Mise en place de la vérification du CS entre 2 octets d'une même commande. Finalement c'est bon, voir #Suite de l'essai de la commande permettant de savoir si l'esclave est à l'écoute
ReX : Non pas de modification au niveau de l'esclave.
Pour le maitre SPI : Finir la commande permettant de savoir si les esclaves sont à l'écoute ou non (ce qui permettra de supprimer les délais).
Il faudrait trois tâches, une pour chacun des sous-systèmes, et une gérant la communication SPI. Celle gérant la communication SPI, devra bloquer les interruptions pour ne pas être préemptée lors de l'envoi de commande (pour éviter les soucis liés à l'envoi d'un octet aux deux esclaves au lieu d'un seul).
Après, il faudrait intégrer ces trois tâches dans le tableau des processus et utiliser la partie #Ce qui est fait sur l'ordonnancement pour les ordonnancer. Enfin du moins son principe puisque n'ayant pu la tester car j'ai manqué cruellement de temps sur la fin, il y aura probablement des choses à revoir.
Les codes sont ici #Rendu final
Dans le rendu : Je me servais de la tâche pour les afficheurs 7 segments, afin de tester la commande permettant de savoir si les esclaves sont à l'écoute ou non. La tâche pour la matrice était la même que dans la partie séquentielle. La tâche pour la communication SPI comprend les fonctions permettant de bloquer les interruptions puis les activer à nouveaux.
Documents Rendus
Rendu Intermédiaire
Codes de la partie 7 segments de la maquette :
Codes de la partie matrice de LED de la maquette :
Codes de la partie maitre SPI de la maquette (fonctionnement séquentiel) :
Codes contenant le début de l'ordonnancement :
Fichier:Debut ordonnancement.zip
Echanges autour de ces derniers
Codes de la partie 7 segments de la maquette :
ReX : Je ne comprends pas les codes envoyés pour la position du caractère à afficher ? Pourquoi pas directement le numéro de l'afficheur ?
Timothé : En effet, c'est bien plus simple à faire et à utiliser (J'avais utilisé le code ASCII de segfont.c). C'est modifié, idem pour la matrice.
ReX : Même chose pour les caractères eux-mêmes.
ReX : Les deux remarques précédentes ont pour but de vous faire simplifier le code en supprimant les redondances.
ReX : Ce serait bien de gérer la ligne SS, par exemple en vérifiant que la ligne n'est pas désélectionnée entre deux octets de la même commande.
Timothé : Voir #Gérer la ligne "chip select" (CS) (ou "slave select" (SS)).
Codes de la partie matrice de LED de la maquette :
ReX : Le codage des caractères prend trop de place en mémoire, limiter à 8 octets par caractère.
Timothé : Voir #Modification du codage des caractères en mémoire (matrice de LED).
ReX : Même remarque sur l'incohérence des codes que pour les 7 segments.
Timothé : J'utilise également directement les caractères désormais.
Codes de la partie maitre SPI de la maquette (fonctionnement séquentiel) :
ReX ; Ne pas changer l'affichage des 7 segments rend la démonstration moins convaincante.
Timothé : J'ai ajouté une nouvelle vidéo le démontrant.
ReX : Vous n'envoyez qu'un octet par message pour les 7 segments ? Ou est l'octet de la position du caractère ?
Timothé : Pour les 7 segments, j'envoie 12 octets successivement. Le premier est la position du caractère, le deuxième le caractère à afficher, et ainsi de suite pour les six afficheurs.
ReX : Il faudrait justifier les 24ms d'attente par l'analyse de la durée du code d'envoi des informations aux TLC des 7 segments.
Timothé : Voir #Estimation du temps nécessaire à l'envoi des informations aux TLC.
ReX : Même remarque pour les 800ms pour la matrice.
Timothé : Pour la matrice, les 800 ms sont prévues pour nous laisser le temps de lire correctement le caractère, ce n'est pas limité par le matériel.
ReX : Pour éviter tous ces problèmes de délais, le mieux serait de prévoir une commande pour demander si le périphérique SPI est libre. Si le périphérique est libre un octet particulier est retourné. Si le périphérique se lance dans un traitement autre que l'écoute SPI, il prend soin d'initialiser sa réponse SPI a autre chose que cet octet particulier.
Timothé : Voir #Commande permettant de demander (côté maitre) et de signaler (côté esclave), si l'esclave est à l'écoute.
Codes contenant le début de l'ordonnancement :
Timothé : Il faudra retirer des tâches task_7segments et task_matriceLEDs les délais et mettre en place la commande dont vous avez parlé, qui permettra au maitre spi de savoir si l'esclave auquel il veut parler est à l'écoute ou s'il fait autre chose. Peut-être même ajouter une tâche à part entière qui s'occupera de ça. Avec les délais, l'ordonnancement ne peut pas fonctionner puisque le maître spi sera bloqué trop longtemps dans ces derniers.
Rendu final
Codes de la partie 7 segments de la maquette :
Codes de la partie matrice de LED de la maquette :
Codes de la partie maitre SPI de la maquette (fonctionnement séquentiel) :
Fichier:OrdonneTests Final.zip
Codes contenant où j'en suis dans l'ordonnancement :