<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>https://projets-se.plil.fr/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Rboursau</id>
	<title>projets-se.plil.fr - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="https://projets-se.plil.fr/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Rboursau"/>
	<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php/Sp%C3%A9cial:Contributions/Rboursau"/>
	<updated>2026-09-21T05:41:55Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.1</generator>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7398</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7398"/>
		<updated>2025-01-26T17:04:52Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* NOTES */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests sur oscilloscope, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&lt;br /&gt;
&lt;br /&gt;
=== Test avec protocole connu ===&lt;br /&gt;
Tout d'abord, nous allons vérifier que la carte fonctionne avec un protocole connu. Nous allons donc utiliser l'exemple de carte RNDIS fourni dans la LUFA donnée au semestre précédent. &lt;br /&gt;
&lt;br /&gt;
En uploadant la LUFA, on voit bien après un lsusb que le périphérique apparaît dans la liste :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Ipa apres configuration ip.png|vignette|408x408px|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons tester le fonctionnement de la carte avec un ping. Nous devons donc ajouter des addresses IP associées à l'interface de la carte. On efffectue la commande ip address add 10.0.0.100/24, on up l'interface avec ip link set usb0 up puis on vérifie avec ip a que l'interface a bien les addresses ip et est à l'état haut : &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit que ç'est le cas.&lt;br /&gt;
En utilisant le programme ether fourni en TP de réseau, nous allons regarder les paquets passant sur l'interface usb0 tout en effectuant un ping sur l'addresse 10.0.0.2. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Video ping.mp4|vignette|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finalement on voit que la carte fonctionne : elle reçoit le ping envoyé par le pc et répond. On peut s'atteler à la suite.&lt;br /&gt;
&lt;br /&gt;
=== Création du protocole ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération/commande sera le premier octet dans les données, suivi d'éventuels arguments supplémentaires.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cette structure a été ajoutée dans un nouveau fichier appelé Proto.h accompagné de son .c . Dans ce .c, on crée une fonction Proto_ProcessIPPacket(void* InDataStart, void* OutDataStart) (mauvais nom de fonction, il n'y a pas d'IP)qui enverra une réponse lorsque la carte reçoit un paquet.  Pour que ce protocole soit effectivement traité, il faut inclure ce fichier dans Ethernet.c, et ajouter un cas correspondant à notre protocole dans le traitement de paquet. On ajoute le protocole en ajoutant la ligne  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define ETHERTYPE_PROTO                    0x8888 &lt;br /&gt;
&lt;br /&gt;
dans EtherrnetProtocols.h. Ensuite, on doit créé une fonction DecodeProtoHeader dans ProtocolDecoders.c, comme pour les autres protocoles déjà existants, en l'adaptant à la structure de protocole choisi. &lt;br /&gt;
&lt;br /&gt;
Pour alléger le programme, nous enlevons les autres protocoles qui ne nous serons pas utiles dans le Makefile et sur les fichiers déjà cités. &lt;br /&gt;
&lt;br /&gt;
On upload le résultat sur la carte RNDIS, on remet l'interface à l'état haut. On veut maintenant utiliser ether mais il y a un problème : ce programme ne connaît pas notre protocole. Nous allons devoir le modifier pour qu'il corresponde à nos besoins.  &lt;br /&gt;
&lt;br /&gt;
=== Modification de ether ===&lt;br /&gt;
On doit, dans bpf.c, remplacer le pro tocole dummy (qui est un protocole non utilisé et destiné à être remplacé) par le nôtre. Ensuite, nous allons faire en sorte que les paquets reçus soient enregistrés dans des fichiers textes, de sorte à ce qu'ils puissent être analysés par la suite. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
En envoyant des paquets avec ether -s suivant notre protocole, on constate bien que la carte RNDIS renvoie un paquet réponse forgé par la fonction Proto_ProcessIPPacket et que ce dernier est stocké dans un fichier à chaque fois.&lt;br /&gt;
&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
Maintenant que la carte est capable de répondre à un paquet suivant notre protocole, nous créons une fonction int16_t Proto_SendIPPacket(void*OutDataStart, uint8_t data[MAX_COMMAND]) (la encore, nom mal choisi) dans Proto.c, qui permet d'envoyer spontanément un paquet suivant notre protocole. Cela se fait grâce à FrameOut. Lorsque l'on ajoute un paquet dans FrameOut, celui-ci est envoyé automatiquement par la carte grâce à la LUFA. Dans la fonction Proto_SendIPPacket, nous allons construire un paquet contenant les données data. Celui ci sera alors envoyé par la carte.&lt;br /&gt;
&lt;br /&gt;
Pour la suite, nous avons eu des problèmes. Il s'agissait d'ajouter un ISR et la communication SPI sur la LUFA. Pour cela, nous avons dû ajouter un bit à 1 sur SPCR : SPCR |= (1&amp;lt;&amp;lt;SPIE); Pour activer les interruptions sur la SPI. Ensuite, dans le fichier RNDISEthernet.c, nous ajoutons un ISR qui va donc s'activer chaque fois qu'une commande est reçue pour la traiter. Pour tester, nous avons d'abord essayé d'envoyer des paquets avec des données incrémentant de 1 à chaque octet, chaque fois qu'une commande SPI est reçue.  En dernier octet des données se trouve un compteur de paquets comptant les paquets envoyés.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Packetwitdata.png|vignette|449x449px]]&lt;br /&gt;
|}&lt;br /&gt;
En faisant cela, et en utilisant notre ether modifié, on constate que ça fonctionne, seulement nous avons des pertes inexpliqués: le compteur de paquets passe de manièere aléatoire des paquets. Deux causes nous semble possibles : soit le délai entre deux envois de paquets est trop court, soit quelque chose dans la LUFA fait que l'ISR n'est pas appelé à chaque fois. En ajoutant un xor sur une led à chaque appel de l'ISR, et en envoyant un ordre toutes les secondes via la carte mère, nous constatons que la LED clignote deux fois plus lentement qu'elle devrait. Il est donc possible que nous ayons également un problème de configuration de fréquence sur la carte réseau.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous essayons alors de faire un programme tout simple pour la carte réseau en mettant un ISR (dans ./Programs/TestsSPI/Slave). En utilisant le programme maître fourni par Monsieur Redon (dans PingPongSPI), nous constatons que l'envoi et la réception de commande avec l'ISR fonctionne (nous avons bien la même chose que dans le test SPI avec Pong), mais la LED clignote quand même deux fois plus lentement que ce qu'elle devrait), et nous ne savons pas comment expliquer ce phénomène.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Notre méthode fonctionne donc bien mais nous avons à priori un problème dans notre application de celle-ci.&lt;br /&gt;
&lt;br /&gt;
Le but était par la suite d'introduire une machine à états dans l'ISR: &lt;br /&gt;
&lt;br /&gt;
* Un état LISTENING qui attend un ordre&lt;br /&gt;
* Un état pour chaque commande demandée qui attend la réponse du PC puis qui la traduit en une réponse SPI pour la carte mère. Une fois l'état fini, on revient à l'état Listening.&lt;br /&gt;
&lt;br /&gt;
Grâce à cela, nous aurions pû gérer chaque commande envoyée par la carte mère puis faire en sorte que la carte réseau ait le comportement approprié.&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
A la racine S8-Pico-Binome-7/Programs/SPIfichierDistants/PC/ProtoPC.c de notre git, se trouve le code permettant à l'ordinateur d'interpréter les paquets réseau envoyé par la carte RNDIS. Les paquets RNDIS sont enregistrés/écrasés dans des fichiers texte sur le PC. Ces fichiers textes sont décodés par le code et décortiqués. Chaque capsule du paquet et enregistre chaque partie de des capsules Ethernet et de notre Protocole Personnalisé dans des variables pour être réutilisés plus tard. Par exemple, on enregistre les adresses mac destination et sources pour les inverser lors du renvoie du paquet. Ou encore l'enregistrement de la commande (1er octet des données) qui est ensuite interprété en &amp;quot;ls&amp;quot;. La suite des données est ensuite interprété comme le path à exécuté avec le &amp;quot;ls&amp;quot;. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Paquet réseau exemple.png|alt=paquet réseau exemple|vignette|paquet réseau exemple]]&lt;br /&gt;
!Les deux premières ligne sont notre paquet.txt. Notre code décortique ensuite chaque partie des capsules Ethernet/Notre Protocole et les enregistre et les print.&lt;br /&gt;
Vous pouvez aussi voir l'interprétation des données en ASCII. Ces données retranscrites en ASCII sont ensuite utilisés pour exécuté un ls. &lt;br /&gt;
Le résultat de ce ls sera ensuite enregistré dans un autre fichier texte qui sera retransformé en paquet Réseau Ethernet encapsulation notre Proto. &lt;br /&gt;
On enverra ensuite chaque nom de fichier dans ce fichier texte un par un avec la commande 0x11 pour dire à la carte réseau qu'il y a encore un fichier.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous avions un bon rythme de travail, dû à la conception du Shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes. Une fois ces problèmes réglés nous avons pu aider les autre élèves à les surpasser pour qu'ils pussent avancer de leur coté.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
Dû a l'erreur causé par le redresseur et la communication SPI, Monsieur REDON nous a donné un code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt; qui communique par SPI entre la carte réseau et le Shield. Le Shield envoie un octet et la carte-réseau renvoie l'octet du coup d'horloge précédent. Il y a un problèmes, les câbles SPI reliant nos cartes sont défectueux et peuvent parfois renvoyés un octet qui n'a pas lieu d'être. Par exemple, nous envoyons un octet de 16 et nous recevons au coup d'horloge suivant, 192, 108 ou encore 65,..., qui ne sont pas des octets envoyables par le code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt;. Ces cas sont rares mais ils peuvent amener à des problèmes de communication de paquets réseaux&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!Voici un exemple de la fonction PONG fonctionnelle&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet. &lt;br /&gt;
&lt;br /&gt;
Dû à une erreur de design, la carte réseau ne peut être alimentée qu'avec la carte mère alimentée&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteRNDISsoudée.jpg|alt=Carte RNDIS soudée|vignette|357x357px|Carte RNDIS soudée|center]]&lt;br /&gt;
|[[Fichier:Visu 3D RNDIS.png|alt=visu 3D RNDIS|vignette|visu 3D RNDIS|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
En conclusion, ce projet nous a permis de mettre en application les différentes parties du programme de ce semestre. Nous avons revu la CAO, avons vu en détail comment l'ordonnancement fonctionnait et comment les éléments constituant des échanges réseaux intéragissaient. &lt;br /&gt;
&lt;br /&gt;
Nous avons réussi à effectuer toutes les communications nécessaires au projet final, en faisant cela nous avons une preuve que nous pouvons, à partir de notre travail, concevoir le soft prenant en charge toutes les commandes demandées.&lt;br /&gt;
&lt;br /&gt;
Cependant, ayant rencontrés de nombreux problèmes en essayant de faire fonctionner ces interactions (problème sur le releveur de niveau, problème de perte de paquets, ...) nous n'avons pas pu automatiser ces différentes interactions pour concevoir la LUFA finale.&lt;br /&gt;
&lt;br /&gt;
Malgré tout, grâce à cela, nous avons beaucoup mis à l'épreuve notre capacité à résoudre des problèmes sur tous les plans (électronique et informatique) et cela nous a sans doute plus appris que si nous n'avions eu aucun problème et avions réussi du premier coup chaque partie du projet.&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7393</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7393"/>
		<updated>2025-01-26T16:50:00Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Envoi de paquet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&lt;br /&gt;
&lt;br /&gt;
=== Test avec protocole connu ===&lt;br /&gt;
Tout d'abord, nous allons vérifier que la carte fonctionne avec un protocole connu. Nous allons donc utiliser l'exemple de carte RNDIS fourni dans la LUFA donnée au semestre précédent. &lt;br /&gt;
&lt;br /&gt;
En uploadant la LUFA, on voit bien après un lsusb que le périphérique apparaît dans la liste :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Ipa apres configuration ip.png|vignette|408x408px|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons tester le fonctionnement de la carte avec un ping. Nous devons donc ajouter des addresses IP associées à l'interface de la carte. On efffectue la commande ip address add 10.0.0.100/24, on up l'interface avec ip link set usb0 up puis on vérifie avec ip a que l'interface a bien les addresses ip et est à l'état haut : &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit que ç'est le cas.&lt;br /&gt;
En utilisant le programme ether fourni en TP de réseau, nous allons regarder les paquets passant sur l'interface usb0 tout en effectuant un ping sur l'addresse 10.0.0.2. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Video ping.mp4|vignette|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finalement on voit que la carte fonctionne : elle reçoit le ping envoyé par le pc et répond. On peut s'atteler à la suite.&lt;br /&gt;
&lt;br /&gt;
=== Création du protocole ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération/commande sera le premier octet dans les données, suivi d'éventuels arguments supplémentaires.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cette structure a été ajoutée dans un nouveau fichier appelé Proto.h accompagné de son .c . Dans ce .c, on crée une fonction Proto_ProcessIPPacket(void* InDataStart, void* OutDataStart) (mauvais nom de fonction, il n'y a pas d'IP)qui enverra une réponse lorsque la carte reçoit un paquet.  Pour que ce protocole soit effectivement traité, il faut inclure ce fichier dans Ethernet.c, et ajouter un cas correspondant à notre protocole dans le traitement de paquet. On ajoute le protocole en ajoutant la ligne  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define ETHERTYPE_PROTO                    0x8888 &lt;br /&gt;
&lt;br /&gt;
dans EtherrnetProtocols.h. Ensuite, on doit créé une fonction DecodeProtoHeader dans ProtocolDecoders.c, comme pour les autres protocoles déjà existants, en l'adaptant à la structure de protocole choisi. &lt;br /&gt;
&lt;br /&gt;
Pour alléger le programme, nous enlevons les autres protocoles qui ne nous serons pas utiles dans le Makefile et sur les fichiers déjà cités. &lt;br /&gt;
&lt;br /&gt;
On upload le résultat sur la carte RNDIS, on remet l'interface à l'état haut. On veut maintenant utiliser ether mais il y a un problème : ce programme ne connaît pas notre protocole. Nous allons devoir le modifier pour qu'il corresponde à nos besoins.  &lt;br /&gt;
&lt;br /&gt;
=== Modification de ether ===&lt;br /&gt;
On doit, dans bpf.c, remplacer le pro tocole dummy (qui est un protocole non utilisé et destiné à être remplacé) par le nôtre. Ensuite, nous allons faire en sorte que les paquets reçus soient enregistrés dans des fichiers textes, de sorte à ce qu'ils puissent être analysés par la suite. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
En envoyant des paquets avec ether -s suivant notre protocole, on constate bien que la carte RNDIS renvoie un paquet réponse forgé par la fonction Proto_ProcessIPPacket et que ce dernier est stocké dans un fichier à chaque fois.&lt;br /&gt;
&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
Maintenant que la carte est capable de répondre à un paquet suivant notre protocole, nous créons une fonction int16_t Proto_SendIPPacket(void*OutDataStart, uint8_t data[MAX_COMMAND]) (la encore, nom mal choisi) dans Proto.c, qui permet d'envoyer spontanément un paquet suivant notre protocole. Cela se fait grâce à FrameOut. Lorsque l'on ajoute un paquet dans FrameOut, celui-ci est envoyé automatiquement par la carte grâce à la LUFA. Dans la fonction Proto_SendIPPacket, nous allons construire un paquet contenant les données data. Celui ci sera alors envoyé par la carte.&lt;br /&gt;
&lt;br /&gt;
Pour la suite, nous avons eu des problèmes. Il s'agissait d'ajouter un ISR et la communication SPI sur la LUFA. Pour cela, nous avons dû ajouter un bit à 1 sur SPCR : SPCR |= (1&amp;lt;&amp;lt;SPIE); Pour activer les interruptions sur la SPI. Ensuite, dans le fichier RNDISEthernet.c, nous ajoutons un ISR qui va donc s'activer chaque fois qu'une commande est reçue pour la traiter. Pour tester, nous avons d'abord essayé d'envoyer des paquets avec des données incrémentant de 1 à chaque octet, chaque fois qu'une commande SPI est reçue.  En dernier octet des données se trouve un compteur de paquets comptant les paquets envoyés.&lt;br /&gt;
&lt;br /&gt;
En faisant cela, et en utilisant notre ether modifié, on constate que ça fonctionne, seulement nous avons des pertes inexpliqués: le compteur de paquets passe de manièere aléatoire des paquets. Deux causes nous semble possibles : soit le délai entre deux envois de paquets est trop court, soit quelque chose dans la LUFA fait que l'ISR n'est pas appelé à chaque fois. En ajoutant un xor sur une led à chaque appel de l'ISR, et en envoyant un ordre toutes les secondes via la carte mère, nous constatons que la LED clignote deux fois plus lentement qu'elle devrait. Il est donc possible que nous ayons également un problème de configuration de fréquence sur la carte réseau.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous essayons alors de faire un programme tout simple pour la carte réseau en mettant un ISR (dans ./Programs/TestsSPI/Slave). En utilisant le programme maître fourni par Monsieur Redon (dans PingPongSPI), nous constatons que l'envoi et la réception de commande avec l'ISR fonctionne (nous avons bien la même chose que dans le test SPI avec Pong), mais la LED clignote quand même deux fois plus lentement que ce qu'elle devrait), et nous ne savons pas comment expliquer ce phénomène.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Notre méthode fonctionne donc bien mais nous avons à priori un problème dans notre application de celle-ci.&lt;br /&gt;
&lt;br /&gt;
Le but était par la suite d'introduire une machine à états dans l'ISR: &lt;br /&gt;
&lt;br /&gt;
* Un état LISTENING qui attend un ordre&lt;br /&gt;
* Un état pour chaque commande demandée qui attend la réponse du PC puis qui la traduit en une réponse SPI pour la carte mère. Une fois l'état fini, on revient à l'état Listening.&lt;br /&gt;
&lt;br /&gt;
Grâce à cela, nous aurions pû gérer chaque commande envoyée par la carte mère puis faire en sorte que la carte réseau ait le comportement approprié.&lt;br /&gt;
[[Fichier:Packetwitdata.png|vignette]]&lt;br /&gt;
&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
A la racine S8-Pico-Binome-7/Programs/SPIfichierDistants/PC/ProtoPC.c de notre git, se trouve le code permettant à l'ordinateur d'interpréter les paquets réseau envoyé par la carte RNDIS. Les paquets RNDIS sont enregistrés/écrasés dans des fichiers texte sur le PC. Ces fichiers textes sont décodés par le code et décortiqués. Chaque capsule du paquet et enregistre chaque partie de des capsules Ethernet et de notre Protocole Personnalisé dans des variables pour être réutilisés plus tard. Par exemple, on enregistre les adresses mac destination et sources pour les inverser lors du renvoie du paquet. Ou encore l'enregistrement de la commande (1er octet des données) qui est ensuite interprété en &amp;quot;ls&amp;quot;. La suite des données est ensuite interprété comme le path à exécuté avec le &amp;quot;ls&amp;quot;. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Paquet réseau exemple.png|alt=paquet réseau exemple|vignette|paquet réseau exemple]]&lt;br /&gt;
!Les deux premières ligne sont notre paquet.txt. Notre code décortique ensuite chaque partie des capsules Ethernet/Notre Protocole et les enregistre et les print.&lt;br /&gt;
Vous pouvez aussi voir l'interprétation des données en ASCII. Ces données retranscrites en ASCII sont ensuite utilisés pour exécuté un ls. &lt;br /&gt;
Le résultat de ce ls sera ensuite enregistré dans un autre fichier texte qui sera retransformé en paquet Réseau Ethernet encapsulation notre Proto. &lt;br /&gt;
On enverra ensuite chaque nom de fichier dans ce fichier texte un par un avec la commande 0x11 pour dire à la carte réseau qu'il y a encore un fichier.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous avions un bon rythme de travail, dû à la conception du Shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes. Une fois ces problèmes réglés nous avons pu aider les autre élèves à les surpasser pour qu'ils pussent avancer de leur coté.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
Dû a l'erreur causé par le redresseur et la communication SPI, Monsieur REDON nous a donné un code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt; qui communique par SPI entre la carte réseau et le Shield. Le Shield envoie un octet et la carte-réseau renvoie l'octet du coup d'horloge précédent. Il y a un problèmes, les câbles SPI reliant nos cartes sont défectueux et peuvent parfois renvoyés un octet qui n'a pas lieu d'être. Par exemple, nous envoyons un octet de 16 et nous recevons au coup d'horloge suivant, 192, 108 ou encore 65,..., qui ne sont pas des octets envoyables par le code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt;. Ces cas sont rares mais ils peuvent amener à des problèmes de communication de paquets réseaux&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!Voici un exemple de la fonction PONG fonctionnelle&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet. &lt;br /&gt;
&lt;br /&gt;
Dû à une erreur de design, la carte réseau ne peut être alimentée qu'avec la carte mère alimentée&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteRNDISsoudée.jpg|alt=Carte RNDIS soudée|vignette|357x357px|Carte RNDIS soudée|center]]&lt;br /&gt;
|[[Fichier:Visu 3D RNDIS.png|alt=visu 3D RNDIS|vignette|visu 3D RNDIS|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Packetwitdata.png&amp;diff=7387</id>
		<title>Fichier:Packetwitdata.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Packetwitdata.png&amp;diff=7387"/>
		<updated>2025-01-26T16:39:23Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;paquet&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7375</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7375"/>
		<updated>2025-01-26T16:19:57Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Envoi de paquet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&lt;br /&gt;
&lt;br /&gt;
=== Test avec protocole connu ===&lt;br /&gt;
Tout d'abord, nous allons vérifier que la carte fonctionne avec un protocole connu. Nous allons donc utiliser l'exemple de carte RNDIS fourni dans la LUFA donnée au semestre précédent. &lt;br /&gt;
&lt;br /&gt;
En uploadant la LUFA, on voit bien après un lsusb que le périphérique apparaît dans la liste :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Ipa apres configuration ip.png|vignette|408x408px|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons tester le fonctionnement de la carte avec un ping. Nous devons donc ajouter des addresses IP associées à l'interface de la carte. On efffectue la commande ip address add 10.0.0.100/24, on up l'interface avec ip link set usb0 up puis on vérifie avec ip a que l'interface a bien les addresses ip et est à l'état haut : &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit que ç'est le cas.&lt;br /&gt;
En utilisant le programme ether fourni en TP de réseau, nous allons regarder les paquets passant sur l'interface usb0 tout en effectuant un ping sur l'addresse 10.0.0.2. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Video ping.mp4|vignette|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finalement on voit que la carte fonctionne : elle reçoit le ping envoyé par le pc et répond. On peut s'atteler à la suite.&lt;br /&gt;
&lt;br /&gt;
=== Création du protocole ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération/commande sera le premier octet dans les données, suivi d'éventuels arguments supplémentaires.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cette structure a été ajoutée dans un nouveau fichier appelé Proto.h accompagné de son .c . Dans ce .c, on crée une fonction Proto_ProcessIPPacket(void* InDataStart, void* OutDataStart) (mauvais nom de fonction, il n'y a pas d'IP)qui enverra une réponse lorsque la carte reçoit un paquet.  Pour que ce protocole soit effectivement traité, il faut inclure ce fichier dans Ethernet.c, et ajouter un cas correspondant à notre protocole dans le traitement de paquet. On ajoute le protocole en ajoutant la ligne  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define ETHERTYPE_PROTO                    0x8888 &lt;br /&gt;
&lt;br /&gt;
dans EtherrnetProtocols.h. Ensuite, on doit créé une fonction DecodeProtoHeader dans ProtocolDecoders.c, comme pour les autres protocoles déjà existants, en l'adaptant à la structure de protocole choisi. &lt;br /&gt;
&lt;br /&gt;
Pour alléger le programme, nous enlevons les autres protocoles qui ne nous serons pas utiles dans le Makefile et sur les fichiers déjà cités. &lt;br /&gt;
&lt;br /&gt;
On upload le résultat sur la carte RNDIS, on remet l'interface à l'état haut. On veut maintenant utiliser ether mais il y a un problème : ce programme ne connaît pas notre protocole. Nous allons devoir le modifier pour qu'il corresponde à nos besoins.  &lt;br /&gt;
&lt;br /&gt;
=== Modification de ether ===&lt;br /&gt;
On doit, dans bpf.c, remplacer le pro tocole dummy (qui est un protocole non utilisé et destiné à être remplacé) par le nôtre. Ensuite, nous allons faire en sorte que les paquets reçus soient enregistrés dans des fichiers textes, de sorte à ce qu'ils puissent être analysés par la suite. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
En envoyant des paquets avec ether -s suivant notre protocole, on constate bien que la carte RNDIS renvoie un paquet réponse forgé par la fonction Proto_ProcessIPPacket et que ce dernier est stocké dans un fichier à chaque fois.&lt;br /&gt;
&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
Maintenant que la carte est capable de répondre à un paquet suivant notre protocole, nous créons une fonction int16_t Proto_SendIPPacket(void*OutDataStart, uint8_t data[MAX_COMMAND]) (la encore, nom mal choisi), qui permet d'envoyer spontanément un paquet suivant notre protocole. Cela se fait grâce à FrameOut. Lorsque l'on ajoute un paquet dans FrameOut, celui-ci est envoyé automatiquement par la carte grâce à la LUFA.&lt;br /&gt;
&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
A la racine S8-Pico-Binome-7/Programs/SPIfichierDistants/PC/ProtoPC.c de notre git, se trouve le code permettant à l'ordinateur d'interpréter les paquets réseau envoyé par la carte RNDIS. Les paquets RNDIS sont enregistrés/écrasés dans des fichiers texte sur le PC. Ces fichiers textes sont décodés par le code et décortiqués. Chaque capsule du paquet et enregistre chaque partie de des capsules Ethernet et de notre Protocole Personnalisé dans des variables pour être réutilisés plus tard. Par exemple, on enregistre les adresses mac destination et sources pour les inverser lors du renvoie du paquet. Ou encore l'enregistrement de la commande (1er octet des données) qui est ensuite interprété en &amp;quot;ls&amp;quot;. La suite des données est ensuite interprété comme le path à exécuté avec le &amp;quot;ls&amp;quot;. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Paquet réseau exemple.png|alt=paquet réseau exemple|vignette|paquet réseau exemple]]&lt;br /&gt;
!Les deux premières ligne sont notre paquet.txt. Notre code décortique ensuite chaque partie des capsules Ethernet/Notre Protocole et les enregistre et les print.&lt;br /&gt;
Vous pouvez aussi voir l'interprétation des données en ASCII. Ces données retranscrites en ASCII sont ensuite utilisés pour exécuté un ls. &lt;br /&gt;
Le résultat de ce ls sera ensuite enregistré dans un autre fichier texte qui sera retransformé en paquet Réseau Ethernet encapsulation notre Proto. &lt;br /&gt;
On enverra ensuite chaque nom de fichier dans ce fichier texte un par un avec la commande 0x11 pour dire à la carte réseau qu'il y a encore un fichier.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous avions un bon rythme de travail, dû à la conception du Shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes. Une fois ces problèmes réglés nous avons pu aider les autre élèves à les surpasser pour qu'ils pussent avancer de leur coté.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
Dû a l'erreur causé par le redresseur et la communication SPI, Monsieur REDON nous a donné un code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt; qui communique par SPI entre la carte réseau et le Shield. Le Shield envoie un octet et la carte-réseau renvoie l'octet du coup d'horloge précédent. Il y a un problèmes, les câbles SPI reliant nos cartes sont défectueux et peuvent parfois renvoyés un octet qui n'a pas lieu d'être. Par exemple, nous envoyons un octet de 16 et nous recevons au coup d'horloge suivant, 192, 108 ou encore 65,..., qui ne sont pas des octets envoyables par le code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt;. Ces cas sont rares mais ils peuvent amener à des problèmes de communication de paquets réseaux&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!Voici un exemple de la fonction PONG fonctionnelle&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet. &lt;br /&gt;
&lt;br /&gt;
Dû à une erreur de design, la carte réseau ne peut être alimentée qu'avec la carte mère alimentée&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteRNDISsoudée.jpg|alt=Carte RNDIS soudée|vignette|357x357px|Carte RNDIS soudée|center]]&lt;br /&gt;
|[[Fichier:Visu 3D RNDIS.png|alt=visu 3D RNDIS|vignette|visu 3D RNDIS|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7359</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7359"/>
		<updated>2025-01-26T16:02:29Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Envoi de paquet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&lt;br /&gt;
&lt;br /&gt;
=== Test avec protocole connu ===&lt;br /&gt;
Tout d'abord, nous allons vérifier que la carte fonctionne avec un protocole connu. Nous allons donc utiliser l'exemple de carte RNDIS fourni dans la LUFA donnée au semestre précédent. &lt;br /&gt;
&lt;br /&gt;
En uploadant la LUFA, on voit bien après un lsusb que le périphérique apparaît dans la liste :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Ipa apres configuration ip.png|vignette|408x408px|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons tester le fonctionnement de la carte avec un ping. Nous devons donc ajouter des addresses IP associées à l'interface de la carte. On efffectue la commande ip address add 10.0.0.100/24, on up l'interface avec ip link set usb0 up puis on vérifie avec ip a que l'interface a bien les addresses ip et est à l'état haut : &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit que ç'est le cas.&lt;br /&gt;
En utilisant le programme ether fourni en TP de réseau, nous allons regarder les paquets passant sur l'interface usb0 tout en effectuant un ping sur l'addresse 10.0.0.2. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Video ping.mp4|vignette|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finalement on voit que la carte fonctionne : elle reçoit le ping envoyé par le pc et répond. On peut s'atteler à la suite.&lt;br /&gt;
&lt;br /&gt;
=== Création du protocole ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération/commande sera le premier octet dans les données, suivi d'éventuels arguments supplémentaires.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cette structure a été ajoutée dans un nouveau fichier appelé Proto.h accompagné de son .c . Dans ce .c, on crée une fonction Proto_ProcessIPPacket(void* InDataStart, void* OutDataStart) (mauvais nom de fonction, il n'y a pas d'IP)qui enverra une réponse lorsque la carte reçoit un paquet.  Pour que ce protocole soit effectivement traité, il faut inclure ce fichier dans Ethernet.c, et ajouter un cas correspondant à notre protocole dans le traitement de paquet. On ajoute le protocole en ajoutant la ligne  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define ETHERTYPE_PROTO                    0x8888 &lt;br /&gt;
&lt;br /&gt;
dans EtherrnetProtocols.h. Ensuite, on doit créé une fonction DecodeProtoHeader dans ProtocolDecoders.c, comme pour les autres protocoles déjà existants, en l'adaptant à la structure de protocole choisi. &lt;br /&gt;
&lt;br /&gt;
Pour alléger le programme, nous enlevons les autres protocoles qui ne nous serons pas utiles dans le Makefile et sur les fichiers déjà cités. &lt;br /&gt;
&lt;br /&gt;
On upload le résultat sur la carte RNDIS, on remet l'interface à l'état haut. On veut maintenant utiliser ether mais il y a un problème : ce programme ne connaît pas notre protocole. Nous allons devoir le modifier pour qu'il corresponde à nos besoins.  &lt;br /&gt;
&lt;br /&gt;
=== Modification de ether ===&lt;br /&gt;
On doit, dans bpf.c, remplacer le pro tocole dummy (qui est un protocole non utilisé et destiné à être remplacé) par le nôtre. Ensuite, nous allons faire en sorte que les paquets reçus soient enregistrés dans des fichiers textes, de sorte à ce qu'ils puissent être analysés par la suite. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
En envoyant des paquets avec ether -s suivant notre protocole, on constate bien que la carte RNDIS renvoie un paquet réponse forgé par la fonction Proto_ProcessIPPacket et que ce dernier est stocké dans un fichier à chaque fois.&lt;br /&gt;
&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
A la racine S8-Pico-Binome-7/Programs/SPIfichierDistants/PC/ProtoPC.c de notre git, se trouve le code permettant à l'ordinateur d'interpréter les paquets réseau envoyé par la carte RNDIS. Les paquets RNDIS sont enregistrés/écrasés dans des fichiers texte sur le PC. Ces fichiers textes sont décodés par le code et décortiqués. Chaque capsule du paquet et enregistre chaque partie de des capsules Ethernet et de notre Protocole Personnalisé dans des variables pour être réutilisés plus tard. Par exemple, on enregistre les adresses mac destination et sources pour les inverser lors du renvoie du paquet. Ou encore l'enregistrement de la commande (1er octet des données) qui est ensuite interprété en &amp;quot;ls&amp;quot;. La suite des données est ensuite interprété comme le path à exécuté avec le &amp;quot;ls&amp;quot;. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Paquet réseau exemple.png|alt=paquet réseau exemple|vignette|paquet réseau exemple]]&lt;br /&gt;
!Les deux premières ligne sont notre paquet.txt. Notre code décortique ensuite chaque partie des capsules Ethernet/Notre Protocole et les enregistre et les print.&lt;br /&gt;
Vous pouvez aussi voir l'interprétation des données en ASCII. Ces données retranscrites en ASCII sont ensuite utilisés pour exécuté un ls. &lt;br /&gt;
Le résultat de ce ls sera ensuite enregistré dans un autre fichier texte qui sera retransformé en paquet Réseau Ethernet encapsulation notre Proto. &lt;br /&gt;
On enverra ensuite chaque nom de fichier dans ce fichier texte un par un avec la commande 0x11 pour dire à la carte réseau qu'il y a encore un fichier.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du Shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
Dû a l'erreur causé par le redresseur et la communication SPI, Monsieur REDON nous a donné un code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt; qui communique par SPI entre la carte réseau et le Shield. Le Shield envoie un octet et la carte-réseau renvoie l'octet du coup d'horloge précédent. Il y a un problèmes, les câbles SPI reliant nos cartes sont défectueux et peuvent parfois renvoyés un octet qui n'a pas lieu d'être. Par exemple, nous envoyons un octet de 16 et nous recevons au coup d'horloge suivant, 192, 108 ou encore 65,..., qui ne sont pas des octets envoyables par le code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt;. Ces cas sont rares mais ils peuvent amener à des problèmes de communication de paquets réseaux&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7353</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7353"/>
		<updated>2025-01-26T15:56:50Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Création du protocole */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&lt;br /&gt;
&lt;br /&gt;
=== Test avec protocole connu ===&lt;br /&gt;
Tout d'abord, nous allons vérifier que la carte fonctionne avec un protocole connu. Nous allons donc utiliser l'exemple de carte RNDIS fourni dans la LUFA donnée au semestre précédent. &lt;br /&gt;
&lt;br /&gt;
En uploadant la LUFA, on voit bien après un lsusb que le périphérique apparaît dans la liste :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Ipa apres configuration ip.png|vignette|408x408px|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons tester le fonctionnement de la carte avec un ping. Nous devons donc ajouter des addresses IP associées à l'interface de la carte. On efffectue la commande ip address add 10.0.0.100/24, on up l'interface avec ip link set usb0 up puis on vérifie avec ip a que l'interface a bien les addresses ip et est à l'état haut : &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit que ç'est le cas.&lt;br /&gt;
En utilisant le programme ether fourni en TP de réseau, nous allons regarder les paquets passant sur l'interface usb0 tout en effectuant un ping sur l'addresse 10.0.0.2. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Video ping.mp4|vignette|center]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finalement on voit que la carte fonctionne : elle reçoit le ping envoyé par le pc et répond. On peut s'atteler à la suite.&lt;br /&gt;
&lt;br /&gt;
=== Création du protocole ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération/commande sera le premier octet dans les données, suivi d'éventuels arguments supplémentaires.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cette structure a été ajoutée dans un nouveau fichier appelé Proto.h accompagné de son .c . Dans ce .c, on crée une fonction Proto_ProcessIPPacket(void* InDataStart, void* OutDataStart) qui enverra une réponse lorsque la carte reçoit un paquet.  Pour que ce protocole soit effectivement traité, il faut inclure ce fichier dans Ethernet.c, et ajouter un cas correspondant à notre protocole dans le traitement de paquet. On ajoute le protocole en ajoutant la ligne  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define ETHERTYPE_PROTO                    0x8888 &lt;br /&gt;
&lt;br /&gt;
dans EtherrnetProtocols.h. Ensuite, on doit créé une fonction DecodeProtoHeader dans ProtocolDecoders.c, comme pour les autres protocoles déjà existants, en l'adaptant à la structure de protocole choisi. &lt;br /&gt;
&lt;br /&gt;
Pour alléger le programme, nous enlevons les autres protocoles qui ne nous serons pas utiles dans le Makefile et sur les fichiers déjà cités. &lt;br /&gt;
&lt;br /&gt;
On upload le résultat sur la carte RNDIS, on remet l'interface à l'état haut. On veut maintenant utiliser ether mais il y a un problème : ce programme ne connaît pas notre protocole. Nous allons devoir le modifier pour qu'il corresponde à nos besoins.  &lt;br /&gt;
&lt;br /&gt;
=== Modification de ether ===&lt;br /&gt;
On doit, dans bpf.c, remplacer le pro tocole dummy (qui est un protocole non utilisé et destiné à être remplacé) par le nôtre. Ensuite, nous allons faire en sorte que les paquets reçus soient enregistrés dans des fichiers textes, de sorte à ce qu'ils puissent être analysés par la suite. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
Nous allons maintenant pouvoir vérifier que notre protocole fonctionne.&lt;br /&gt;
&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
A la racine S8-Pico-Binome-7/Programs/SPIfichierDistants/PC/ProtoPC.c de notre git, se trouve le code permettant à l'ordinateur d'interpréter les paquets réseau envoyé par la carte RNDIS. Les paquets RNDIS sont enregistrés/écrasés dans des fichiers texte sur le PC. Ces fichiers textes sont décodés par le code et décortiqués. Chaque capsule du paquet et enregistre chaque partie de des capsules Ethernet et de notre Protocole Personnalisé dans des variables pour être réutilisés plus tard. Par exemple, on enregistre les adresses mac destination et sources pour les inverser lors du renvoie du paquet. Ou encore l'enregistrement de la commande (1er octet des données) qui est ensuite interprété en &amp;quot;ls&amp;quot;. La suite des données est ensuite interprété comme le path à exécuté avec le &amp;quot;ls&amp;quot;. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Résultat décorticage d'un paquet réseau par notre code .png|alt=résultat décorticage d'un paquet réseau par notre code |vignette|379x379px|résultat décorticage d'un paquet réseau par notre code ]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du Shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
Dû a l'erreur causé par le redresseur et la communication SPI, Monsieur REDON nous a donné un code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt; qui communique par SPI entre la carte réseau et le Shield. Le Shield envoie un octet et la carte-réseau renvoie l'octet du coup d'horloge précédent. Il y a un problèmes, les câbles SPI reliant nos cartes sont défectueux et peuvent parfois renvoyés un octet qui n'a pas lieu d'être. Par exemple, nous envoyons un octet de 16 et nous recevons au coup d'horloge suivant, 192, 108 ou encore 65,..., qui ne sont pas des octets envoyables par le code &amp;lt;code&amp;gt;pong.c&amp;lt;/code&amp;gt;. Ces cas sont rares mais ils peuvent amener à des problèmes de communication de paquets réseaux&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7334</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7334"/>
		<updated>2025-01-26T15:14:20Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Programmation de la carte RNDIS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&lt;br /&gt;
&lt;br /&gt;
=== Test avec protocole connu ===&lt;br /&gt;
Tout d'abord, nous allons vérifier que la carte fonctionne avec un protocole connu. Nous allons donc utiliser l'exemple de carte RNDIS fourni dans la LUFA donnée au semestre précédent. &lt;br /&gt;
&lt;br /&gt;
En uploadant la LUFA, on voit bien après un lsusb que le périphérique apparaît dans la liste :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous allons tester le fonctionnement de la carte avec un ping. Nous devons donc ajouter des addresses IP associées à l'interface de la carte. On efffectue la commande ip address add 10.0.0.100/24, on up l'interface avec ip link set usb0 up puis on vérifie avec ip a que l'interface a bien les addresses ip et est à l'état haut : &lt;br /&gt;
[[Fichier:Ipa apres configuration ip.png|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit que ç'est le cas.&lt;br /&gt;
[[Fichier:Video ping.mp4|vignette]]&lt;br /&gt;
En utilisant le programme ether fourni en TP de réseau, nous allons regarder les paquets passant sur l'interface usb0 tout en effectuant un ping sur l'addresse 10.0.0.2. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finalement on voit que la carte fonctionne : elle reçoit le ping envoyé par le pc et répond. On peut s'atteler à la suite.&lt;br /&gt;
&lt;br /&gt;
=== Création du protocole ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération/commande sera le premier octet dans les données, suivi d'éventuels arguments supplémentaires.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cette structure a été ajoutée dans un nouveau fichier appelé Proto.h accompagné de son .c . Pour que ce protocole soit effectivement traité, il faut inclure ce fichier dans Ethernet.c, et ajouter un cas correspondant à notre protocole dans le traitement de paquet. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Video_ping.mp4&amp;diff=7332</id>
		<title>Fichier:Video ping.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Video_ping.mp4&amp;diff=7332"/>
		<updated>2025-01-26T15:08:03Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ping&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Ipa_apres_configuration_ip.png&amp;diff=7331</id>
		<title>Fichier:Ipa apres configuration ip.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Ipa_apres_configuration_ip.png&amp;diff=7331"/>
		<updated>2025-01-26T15:01:27Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;ipa&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7330</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7330"/>
		<updated>2025-01-26T14:12:51Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Programmation de la carte RNDIS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ether customisé ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération est la commande qui est demandé par la carte-mère donc 0x01: listing des fichiers, 0x11 : téléchargement des fichiers, 0x03,... Où nous en sommes nous n'avons pas utilisé cet octet. L'opération/commande est le premier octet dans les données.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;De cette façon, lorsqu'une trame respectant notre forme et utilisant notre protocole est envoyée sur l'interface, la LED de la carte devrait clignoter. Enfin, il faut aussi adapter le fichier &amp;quot;ProtocolsDecoders&amp;quot; afin que la LUFA arrive à décoder notre trame &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après cela, nous pouvons envoyer une trame sur l'interface &amp;quot;usb0&amp;quot; utilisant notre protocole. Pour cela, nous avons modifié l'outil &amp;quot;ether&amp;quot; pour observer directement l'interface &amp;quot;usb0&amp;quot; et aussi pour que le filtre accepte notre protocole. En envoyant alors la trame, la LED s'allume bien correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7329</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7329"/>
		<updated>2025-01-26T14:12:28Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Programmation de la carte RNDIS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets associés au projet, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC.&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ether customisé ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération est la commande qui est demandé par la carte-mère donc 0x01: listing des fichiers, 0x11 : téléchargement des fichiers, 0x03,... Où nous en sommes nous n'avons pas utilisé cet octet. L'opération/commande est le premier octet dans les données.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;De cette façon, lorsqu'une trame respectant notre forme et utilisant notre protocole est envoyée sur l'interface, la LED de la carte devrait clignoter. Enfin, il faut aussi adapter le fichier &amp;quot;ProtocolsDecoders&amp;quot; afin que la LUFA arrive à décoder notre trame &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après cela, nous pouvons envoyer une trame sur l'interface &amp;quot;usb0&amp;quot; utilisant notre protocole. Pour cela, nous avons modifié l'outil &amp;quot;ether&amp;quot; pour observer directement l'interface &amp;quot;usb0&amp;quot; et aussi pour que le filtre accepte notre protocole. En envoyant alors la trame, la LED s'allume bien correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7328</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7328"/>
		<updated>2025-01-26T14:11:37Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Programmation de la carte RNDIS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
Pour utiliser la carte RNDIS, nous allons créer notre protocole Ethernet en s'inspirant de l'ARP. Cela permettra de ne traiter que les paquets dont on a besoin, et de donner une structure adaptée à nos besoins. Pour rappel, le but est d'envoyer des paquets de la carte réseau  chaque fois que la carte mère envoie une commande à la carte réseau. La commande contiendra éventuellement des données supplémentaires (si la commande est un ls, il faut préciser le chemin du dossier dans lequel l'éxecuter) qui seront traitées par le PC dans un programme qui tournera en continu. Ce programme réceptionne les paquets, les interprète et envoie les réponses à la carte RNDIS. Puis la carte RNDIS décode le paquet réponse, et envoie les données par SPI à la carte mère.&lt;br /&gt;
&lt;br /&gt;
Pour résumer, la carte RNDIS a 4 tâches principales :&lt;br /&gt;
&lt;br /&gt;
* Recevoir les commandes SPI envoyées par la carte mère&lt;br /&gt;
* Interpréter ces commandes et former puis envoyer les paquets correspondants&lt;br /&gt;
* Recevoir les réponses envoyées par le PC.&lt;br /&gt;
* Envoyer les données contenu dans les réponses par SPI à la carte mère.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ether customisé ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération est la commande qui est demandé par la carte-mère donc 0x01: listing des fichiers, 0x11 : téléchargement des fichiers, 0x03,... Où nous en sommes nous n'avons pas utilisé cet octet. L'opération/commande est le premier octet dans les données.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;De cette façon, lorsqu'une trame respectant notre forme et utilisant notre protocole est envoyée sur l'interface, la LED de la carte devrait clignoter. Enfin, il faut aussi adapter le fichier &amp;quot;ProtocolsDecoders&amp;quot; afin que la LUFA arrive à décoder notre trame &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après cela, nous pouvons envoyer une trame sur l'interface &amp;quot;usb0&amp;quot; utilisant notre protocole. Pour cela, nous avons modifié l'outil &amp;quot;ether&amp;quot; pour observer directement l'interface &amp;quot;usb0&amp;quot; et aussi pour que le filtre accepte notre protocole. En envoyant alors la trame, la LED s'allume bien correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
==== Test de la SPI avec PONG ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Pong screen.png|alt=Pong montrant la comm SPI entre le shield et la carte RNDIS|vignette|Pong montrant la comm SPI entre le shield et la carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Erreur de design =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:PIN ALIM non connecté.png|alt=PIN ALIM non connecté|vignette|PIN ALIM non connecté]]&lt;br /&gt;
!Comme vous pouvez l'observe, le pin ALIM de notre Carte RNDIS n'est pas connecté avec les pins VCC. &lt;br /&gt;
Par conséquent, notre carte carte RNDIS ne peut-être alimentée que quand, &lt;br /&gt;
&lt;br /&gt;
le Shield où la carte mère est alimentée en 5V.&lt;br /&gt;
&lt;br /&gt;
Solution : connecter cette PIN ALIM avec les VCC en plus de la PIN ALIM de l'USB + mettre un cavalier pour basculer entre l'alimentation&lt;br /&gt;
par la carte-mère où directement par USB&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:PIN ALIM non connecté USB.png|alt=PIN ALIM non connecté USB|vignette|PIN ALIM non connecté USB]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7319</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=7319"/>
		<updated>2025-01-26T13:58:10Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Problèmes rencontrés avec la COMMUNICATION SPI ===&lt;br /&gt;
&lt;br /&gt;
Lors de la programmation de notre carte RNDIS, nous avons rencontré un problème de communication SPI de notre carte RNDIS vers notre Shield. &lt;br /&gt;
Nous pouvons envoyer des paquets/données du Shield vers la carte RNDIS mais pas dans l'autre sens. Le problème résidait sur le Shield. Plus particulièrement sur le releveur de niveau de tension. La résistance soudée à la sortie du &amp;lt;code&amp;gt;PIN MOSI_1&amp;lt;/code&amp;gt; ne devrait pas être ici. Dû à cette erreur de design, le releveur de niveau faisait l'inverse de ce qu'il doit normalement faire : il baissait notre niveau de tension.&lt;br /&gt;
Normalement, le releveur de niveau doit augmenter la tension pour quelle puisse être utilisée/utilisable par le MicroP. Lors de nos tests, nous avions &amp;lt;code&amp;gt;+5V&amp;lt;/code&amp;gt; du Shield vers notre carte RNDIS, ce qui est la tension optimale mais n'obtenons que &amp;lt;code&amp;gt;+1.5V&amp;lt;/code&amp;gt; de notre carte RNDIS vers notre Shield ce qui est bien insuffisant car les MicroP que nous avons ne fonctionne qu'avec des tensions à minima de &amp;lt;code&amp;gt;+3.3V&amp;lt;/code&amp;gt;.&lt;br /&gt;
Nous avons d'abord tenter de couper la piste à l'entrée du &amp;lt;code&amp;gt;PIN MISO&amp;lt;/code&amp;gt; et d'y mettre la résistance qui est en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; et de remplacer la résistance en sortie du &amp;lt;code&amp;gt;PIN MISO_1&amp;lt;/code&amp;gt; par un fil. Cela régla notre problème de tension mais nous ne pouvions plus détecter la carte SD.&lt;br /&gt;
Nous avons donc enlever toutes les résistances et fils des pistes &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; à l'entrée et sortie du releveur de niveau et avons court-circuité les deux pistes. Il faut aussi sectionner le fil de cuivre &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; sinon le courant ira en sur le fil de cuivre &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; ET le fil  &amp;lt;code&amp;gt;MOSI&amp;lt;/code&amp;gt; et nous voulons que le courant aille uniquement sur &amp;lt;code&amp;gt;MISO_1&amp;lt;/code&amp;gt; . Cela régla notre problème de tension ET de détection de carte SD.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO test1.png|gauche|vignette|Schéma montrant les tests que nous avons fait pour régler le problème de tension du MISO]]&lt;br /&gt;
![[Fichier:Releveur de niveau PinMISO.png|alt=Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD|vignette|Schéma montrant comment nous avons pu régler le problème de tension en MISO et de détection de la carte SD]]&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Programmation de la carte RNDIS ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Ether customisé ===&lt;br /&gt;
Voici la structure de notre Protocole Ethernet, elle est fortement inspiré de la structure d'un paquet ARP. L'opération est la commande qui est demandé par la carte-mère donc 0x01: listing des fichiers, 0x11 : téléchargement des fichiers, 0x03,... Où nous en sommes nous n'avons pas utilisé cet octet. L'opération/commande est le premier octet dans les données.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
		{&lt;br /&gt;
			uint16_t      Operation;/**&amp;lt; Type of operation, either PROTO_OPERATION_REQUEST or PROTO_OPERATION_REPLY */&lt;br /&gt;
            uint16_t ProtocolType; /** ETHERTYPE_PROTO**/&lt;br /&gt;
			MAC_Address_t Sender_MACADD; /**&amp;lt; Sender's hardware address */&lt;br /&gt;
			MAC_Address_t Target_MACADD; /**&amp;lt; Target's hardware address */&lt;br /&gt;
            uint16_t      Length;&lt;br /&gt;
            uint8_t      Data[MAX_COMMAND];&lt;br /&gt;
&lt;br /&gt;
		} Proto_Header_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;De cette façon, lorsqu'une trame respectant notre forme et utilisant notre protocole est envoyée sur l'interface, la LED de la carte devrait clignoter. Enfin, il faut aussi adapter le fichier &amp;quot;ProtocolsDecoders&amp;quot; afin que la LUFA arrive à décoder notre trame &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après cela, nous pouvons envoyer une trame sur l'interface &amp;quot;usb0&amp;quot; utilisant notre protocole. Pour cela, nous avons modifié l'outil &amp;quot;ether&amp;quot; pour observer directement l'interface &amp;quot;usb0&amp;quot; et aussi pour que le filtre accepte notre protocole. En envoyant alors la trame, la LED s'allume bien correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Led ether.mp4|vignette|Vidéo montrant une led clignoter quand nous recevons un paquet avec notre protocole Ethernet (0x8888)]]&lt;br /&gt;
!Vous pouvez retrouver notre ether customisé dans notre git à la racine&lt;br /&gt;
S8-Pico-Binome-7/Programs/ListeningEther/customether&lt;br /&gt;
|}&lt;br /&gt;
=== Envoi de paquet ===&lt;br /&gt;
=== Décodage des paquets de l'ordinateur ===&lt;br /&gt;
&lt;br /&gt;
=== Limitations dû à la SPI ===&lt;br /&gt;
Nous étions les élèves les plus en avance sur les autres groupes, dû à la conception du shield et à d'autres problèmes rencontrés, nous avons été ralenti par ces problèmes et devions tracer le chemin des autres équipes lors de la résolution de ces problèmes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6771</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6771"/>
		<updated>2024-11-19T13:58:05Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, nous changeons l'état de la tâche à endormir en &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;, puis assignons la valeur &amp;lt;code&amp;gt;time&amp;lt;/code&amp;gt; donnée en paramètre au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; soit le bon(la fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
*Si la &amp;lt;code&amp;gt;currentTask&amp;lt;/code&amp;gt; est en mode &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;:&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est positif, nous soustrayons une durée &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; fixe correspondant à une période d'éxecution au &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque &amp;lt;code&amp;gt;elapsed_time&amp;lt;/code&amp;gt; est fixe et indépendant du timer &amp;lt;code&amp;gt;TCNT1&amp;lt;/code&amp;gt;, il ne prend pas en compte le temps auquel s'est éxecuté le &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
**Si son &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt; est négatif ou nul, nous restaurons l'état de tâche à &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est &amp;lt;code&amp;gt;SLEEPING&amp;lt;/code&amp;gt;. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode &amp;lt;code&amp;gt;ACTIVE&amp;lt;/code&amp;gt;, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans &amp;lt;code&amp;gt;comm_spi.c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction &amp;lt;code&amp;gt;spi_init&amp;lt;/code&amp;gt;, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  &amp;lt;code&amp;gt;spi_select&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_deselect&amp;lt;/code&amp;gt; qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à &amp;lt;code&amp;gt;spi_activer&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;spi_desactiver&amp;lt;/code&amp;gt; avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction &amp;lt;code&amp;gt;spi_echange&amp;lt;/code&amp;gt; envoie un octet sur le port SPI, correspondant à &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons &amp;lt;code&amp;gt;spi_clearDisplay&amp;lt;/code&amp;gt; qui réinitialise l'affichage à l'aide de la commande &amp;lt;code&amp;gt;0x76&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;spi_setLight&amp;lt;/code&amp;gt; qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de &amp;lt;code&amp;gt;spi_échange&amp;lt;/code&amp;gt;, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction &amp;lt;code&amp;gt;sevenseg&amp;lt;/code&amp;gt;, disponible dans &amp;lt;code&amp;gt;ordonnanceur1.c &amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6764</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6764"/>
		<updated>2024-11-19T13:04:41Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* COMMUNICATION SPI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction delay, nous changeons l'état de la tâche à endormir en SLEEPING, puis assignons la valeur time donnée en paramètre au delay de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le delay soit le bon(la fonction delay s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le delay).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état SLEEPING. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;Si la currentTask est en mode SLEEPING:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est positif, nous soustrayons une durée elapsed_time fixe correspondant à une période d'éxecution au delay de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque elapsed_time est fixe et indépendant du timer TCNT1, il ne prend pas en compte le temps auquel s'est éxecuté le delay (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est négatif ou nul, nous restaurons l'état de tâche à ACTIVE.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est SLEEPING. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode ACTIVE, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans comm_spi.c.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction spi_init, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  spi_select et spi_deselect qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à spi_activer et spi_desactiver avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction spi_echange envoie un octet sur le port SPI, correspondant à output qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons spi_clearDisplay qui réinitialise l'affichage à l'aide de la commande 0x76, et spi_setLight qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de spi_échange, et désactiver le périphérique. &lt;br /&gt;
&lt;br /&gt;
Vous trouverez ci-dessous une photo de l'afficheur en fonctionnement, puis une vidéo montrant l'éxecution de la tâche associée à la fonction sevenseg, disponible dans ordonnanceur1.c &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6758</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6758"/>
		<updated>2024-11-18T11:21:01Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* COMMUNICATION SPI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction delay, nous changeons l'état de la tâche à endormir en SLEEPING, puis assignons la valeur time donnée en paramètre au delay de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le delay soit le bon(la fonction delay s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le delay).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état SLEEPING. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;Si la currentTask est en mode SLEEPING:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est positif, nous soustrayons une durée elapsed_time fixe correspondant à une période d'éxecution au delay de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque elapsed_time est fixe et indépendant du timer TCNT1, il ne prend pas en compte le temps auquel s'est éxecuté le delay (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est négatif ou nul, nous restaurons l'état de tâche à ACTIVE.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est SLEEPING. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode ACTIVE, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans &amp;lt;code&amp;gt;comm_serie.c&amp;lt;/code&amp;gt;. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt; ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tapons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; correspondant au port série. Puis, la fonction donne la valeur 0 a &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeur nulle.&lt;br /&gt;
&lt;br /&gt;
Dans &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;, nous attendons qu'une valeur soit écrite sur &amp;lt;code&amp;gt;UDR0&amp;lt;/code&amp;gt; puis assignons cette valeur à data, et passons &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
&lt;br /&gt;
Plus simplement, lorsqu'on écrit un caractère sur le clavier de l'ordinateur, ce dernier est transmis à l'Arduino via la connexion USB. Dès que l'Arduino reçoit ce caractère, la variable &amp;lt;code&amp;gt;is_data_received&amp;lt;/code&amp;gt; passe à 1 grâce à la fonction &amp;lt;code&amp;gt;serie_recevoir&amp;lt;/code&amp;gt;. Ensuite, l'Arduino renvoie ce même caractère au terminal (Minicom) en utilisant la fonction &amp;lt;code&amp;gt;serie_envoyer&amp;lt;/code&amp;gt;. Ainsi, on peut voir ce caractère affiché sur le terminal, ce qui confirme que la communication série fonctionne correctement.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
Après la communication série, passons à la SPI. Par manque d'originalité, toutes les fonctions sont regroupées dans comm_spi.c.&lt;br /&gt;
&lt;br /&gt;
Notre but est de communiquer avec un afficheur 7 segments. Celui utilisé accepte des messages de 4x8octets, avec chaque octet correspondant à un des chiffres affichés sur le 7 Segment. Il y a également des commandes spéciales pour réinitialiser l'affichage.&lt;br /&gt;
&lt;br /&gt;
Nous commençons par initialiser la communication. Pour cela, nous avons au préalable défini des macros conçernant nos PIN de MISO, MOSI, et les pins reliés à un connecteur HE-10, (qui dépendent de comment à été routé le shield). Nous les utilisons dans une fonction spi_init, qui va définir les entrées et sorties, et activer la communication spi en mode maître sur l'arduino.&lt;br /&gt;
&lt;br /&gt;
Nous avons des fonctions  spi_select et spi_deselect qui (dé)sélectionnent le pin slave sur lequel communiquer.&lt;br /&gt;
&lt;br /&gt;
A chaque message ou commande spéciale à envoyer, nous devrons faire appel à spi_activer et spi_desactiver avant et après respectivement, pour (dés)activer le périphérique.&lt;br /&gt;
&lt;br /&gt;
La fonction spi_echange envoie un octet sur le port SPI, correspondant à output qui est en paramètre.&lt;br /&gt;
&lt;br /&gt;
Finalement nous avons spi_clearDisplay qui réinitialise l'affichage à l'aide de la commande 0x76, et spi_setLight qui configure la luminosité de l'afficheur.&lt;br /&gt;
&lt;br /&gt;
Pour afficher des caractères nous devons donc activer le périphérique puis envoyer 4 fois un octet à l'aide de spi_échange, et désactiver le périphérique. &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6740</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6740"/>
		<updated>2024-11-18T10:59:35Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* COMMUNICATION SERIE */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Les tâches à éxecuter seront toutes regroupées dans une liste de tâches Task TaskList[NB_TASKS] dans l'ordonnanceur.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;currentTask = (currentTask + 1) % NB_TASKS;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction delay, nous changeons l'état de la tâche à endormir en SLEEPING, puis assignons la valeur time donnée en paramètre au delay de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le delay soit le bon(la fonction delay s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le delay).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état SLEEPING. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;Si la currentTask est en mode SLEEPING:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est positif, nous soustrayons une durée elapsed_time fixe correspondant à une période d'éxecution au delay de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque elapsed_time est fixe et indépendant du timer TCNT1, il ne prend pas en compte le temps auquel s'est éxecuté le delay (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est négatif ou nul, nous restaurons l'état de tâche à ACTIVE.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est SLEEPING. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode ACTIVE, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maintenant que tout est fonctionnel, nous nous attaquons à la communication série. Toutes les fonctions sont disponibles dans comm_serie.c. Nous commençons par initialiser la communication en définissant la vitesse, configurons le mode (envoi et réception), puis programmons deux fonctions serie_recevoir et serie_envoyer ayant un nom peu équivoque. Le but est pour l'instant d'échanger via minicom, nous tappons des caractères au clavier qui seront renvoyés par l'arduino dans le terminal.&lt;br /&gt;
&lt;br /&gt;
Dans serie_envoyer, nous vérifions si nous avons reçu un caractère. Si c'est le cas, nous écrivons la data reçue sur UDR0 correspondant au port série. Puis, la fonction donne la valeur 0 a is_data_received (variable globale), signifiant qu'il n'y a plus de data à envoyer, et remettons data a une valeure nulle.&lt;br /&gt;
&lt;br /&gt;
Dans serie_recevoir, nous attendons qu'une valeur soit écrite sur UDR0 puis assignons cette valeur à data, et passons is_data_received à 1, signifiant que nous avons reçu une data qu'il faut envoyer.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous intégrons ces deux fonctions dans des tâches que nous ajoutons à note liste de tâches à éxecuter. Vous pouvez trouver ci-dessous le résultat.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
Nous avons choisi de réaliser la carte réseau RNDIS, car nous pensons que ce projet était aussi en lien avec nos cours de réseau et de système d'exploitation, cette carte était pour nous un moyen d'en apprendre plus sur ces aspects.&lt;br /&gt;
&lt;br /&gt;
La première chose à faire était de réaliser la carte électronique. Pour cela, des informations nous sont fournies. On sait alors que le microcontrôleur à utiliser doit avoir des capacités USB et que l'on peut utiliser soit un ATMega16u2, un ATMega32u4 ou un AT90USB suivant la mémoire que l'on veut disposer. Nous avons choisi d'utiliser un AT90USB1286-A pour sa mémoire plus grande. Nous avons pu observer sur les projets SE4 de l'année dernière que les élèves n'avait pas assez de mémoire pour des paquets réseaux importants.&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous avons commencé à faire le schéma électrique de la carte réseau. Nous avons commencé à mettre les composants pour faire fonctionner le microcontrôleur de la même façon que sur le projet de SE3. Après cela, nous avons commencé à faire les entrées et sorties. On sait que la carte va communiquer avec la carte mère via un connecteur HE10, des signaux MISO, MOSI, SCK et CS sont alors à connecter au microcontrôleur. Nous avons choisi d'utiliser les ports suivants :&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6730</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6730"/>
		<updated>2024-11-18T10:24:57Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* FONCTION DELAY */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
Finalement, nous ajoutons cette ligne : &lt;br /&gt;
&lt;br /&gt;
currentTask = (currentTask + 1) % NB_TASKS;&lt;br /&gt;
&lt;br /&gt;
dans l'ordonnanceur, ce qui permet de passer d'une tâche à la suivante à chaque interruption.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans &amp;lt;code&amp;gt;ordonnanceur1.c&amp;lt;/code&amp;gt; à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème concernant la manière dont ces tâches sont programmées : nous utilisons la fonction &amp;lt;code&amp;gt;_delay_ms&amp;lt;/code&amp;gt; de la bibliothèque &amp;lt;code&amp;gt;delay.h&amp;lt;/code&amp;gt;, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même exécutée et ne fait qu'attendre pendant son temps d'exécution. Plutôt que de faire ça, on va créer une fonction &amp;lt;code&amp;gt;delay&amp;lt;/code&amp;gt;, qui va endormir la tâche pendant le temps désiré et qui permettra de libérer ce temps de travail inutile pour plutôt exécuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
Pour cette fonction delay, nous changeons l'état de la tâche à endormir en SLEEPING, puis assignons la valeur time donnée en paramètre au delay de la tâche. Nous remettons le compteur à 0 et appelons l'ISR pour être sûr que le delay soit le bon(la fonction delay s'éxecute pendant une éxecution de tâche, on verra juste après pourquoi cela risque de fausser le delay).&lt;br /&gt;
&lt;br /&gt;
void delay(int time)&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].state = SLEEPING;&lt;br /&gt;
&lt;br /&gt;
  TaskList[currentTask].data.data.delay = time;&lt;br /&gt;
&lt;br /&gt;
  TCNT1 = 0;&lt;br /&gt;
&lt;br /&gt;
  TIMER1_COMPA_vect();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous allons changer l'ordonnanceur pour qu'il puisse gérer l'état SLEEPING. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;Si la currentTask est en mode SLEEPING:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est positif, nous soustrayons une durée elapsed_time fixe correspondant à une période d'éxecution au delay de cette tâche. C'est ici qu'on voit l'intérêt de reset le timer et d'appeler l'ISR : puisque elapsed_time est fixe et indépendant du timer TCNT1, il ne prend pas en compte le temps auquel s'est éxecuté le delay (le timer n'était pas à un multiple fixe de la période).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;**&amp;lt;/nowiki&amp;gt;Si son delay est négatif ou nul, nous restaurons l'état de tâche à ACTIVE.&lt;br /&gt;
&lt;br /&gt;
Puis, dans la sélection de tâche à éxecuter ensuite, nous testons si l'état de la tâche est SLEEPING. Si c'est le cas, nous regardons la suivante, jusqu'à tomber sur une tâche en mode ACTIVE, que l'on va alors éxecuter.&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
|[[Fichier:PCB RNDIS.png|500px|PCB de la carte RNDIS]]&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:Connecteur USB ALIM DATA.png|vignette|upright=1.75|Connecteur USB permettant l'ALIM de la carte en solo et le transfert DATA en réseau avec un ordinateur connecté à Internet]]&lt;br /&gt;
|Connecteur USB permettant l'ALIM de la carte en solo (sans shield et carte-mère) et le transfert de DATA &lt;br /&gt;
en réseau avec un ordinateur connecté à Internet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6705</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6705"/>
		<updated>2024-11-18T10:00:08Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* COMMUNICATION SPI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans ordonnanceur1.c à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème conçernant la manière dont ces tâches sont programmées : nous utilisons la fonction _delay_ms de la bibliothèque delay.h, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même éxecutée et ne fait qu'attendre pendant son temps d'éxecution. Plutôt que de faire ça, on va créer une fonction delay, qui va endormir la tâche pendant le temps désiré et qui permetttra de libérer ce temps de travail inutile pour plutôt éxecuter d'autres tâches à la place.&lt;br /&gt;
[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6704</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6704"/>
		<updated>2024-11-18T09:59:11Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* CLIGNOTEMENT DES LEDs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans ordonnanceur1.c à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème conçernant la manière dont ces tâches sont programmées : nous utilisons la fonction _delay_ms de la bibliothèque delay.h, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même éxecutée et ne fait qu'attendre pendant son temps d'éxecution. Plutôt que de faire ça, on va créer une fonction delay, qui va endormir la tâche pendant le temps désiré et qui permetttra de libérer ce temps de travail inutile pour plutôt éxecuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION DELAY ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6702</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6702"/>
		<updated>2024-11-18T09:58:35Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* CLIGNOTEMENT DES LEDs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans ordonnanceur1.c à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème conçernant la manière dont ces tâches sont programmées : nous utilisons la fonction _delay_ms de la bibliothèque delay.h, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même éxecutée et ne fait qu'attendre pendant son temps d'éxecution. Plutôt que de faire ça, on va créer une fonction sleep, qui va endormir la tâche pendant le temps désiré et qui permetttra de libérer ce temps de travail inutile pour plutôt éxecuter d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6701</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6701"/>
		<updated>2024-11-18T09:58:02Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* CLIGNOTEMENT DES LEDs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, &amp;lt;code&amp;gt;task_led1&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;task_led2&amp;lt;/code&amp;gt;, dans ordonnanceur1.c à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat. Cependant, il y a un léger problème conçernant la manière dont ces tâches sont programmées : nous utilisons la fonction _delay_ms de la bibliothèque delay.h, plutôt que d'endormir la tâche. Cela signifie que la tâche est quand même éxecutée et ne fait qu'attendre pendant son temps d'éxecution. Plutôt que de faire ça, on va créer une fonction sleep, qui va endormir la tâche pendant le temps désiré et qui permetttra de libérer ce temps de travail inutile pour plutôt d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6697</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6697"/>
		<updated>2024-11-18T09:53:11Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons utilisé cette structure pour créer deux tâches de clignotement de LEDs, task_led1 et task_led2, dans ordonnanceur1.c à des fréquences premières entre elles. Vous trouverez ci-dessous une vidéo du résultat.&lt;br /&gt;
&lt;br /&gt;
Cependant, il y a un léger problème conçernant la manière dont ces tâches sont programmées : nous utilisons la fonction _delay_ms de la bibliothèque delay.h, plutôt que d'endormir la tâche. Cela signifie que la tâche est&lt;br /&gt;
&lt;br /&gt;
quand même éxecutée et ne fait qu'attendre pendant son temps d'éxecution. Plutôt que de faire ça, on va créer une fonction sleep, qui va endormir la tâche pendant le temps désiré et qui permetttra de libérer ce temps&lt;br /&gt;
&lt;br /&gt;
de travail inutile pour plutôt d'autres tâches à la place.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:SCHEMATIQUE CarteRNDIS.pdf|vignette|SCHEMATIQUE de la Carte RNDIS]]&lt;br /&gt;
!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6658</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6658"/>
		<updated>2024-11-13T11:11:02Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino, et que celles-ci nous semblent simultanées. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. Cela est vital car lorsque l'on voudra continuer l'exécution de cette fonction par la suite, nous devons savoir l'état dans lequel elle s'était arrêtée.  L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6650</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6650"/>
		<updated>2024-11-13T11:05:21Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (&amp;lt;code&amp;gt;diviseur&amp;lt;/code&amp;gt;), le mode de la minuterie (&amp;lt;code&amp;gt;CTC1&amp;lt;/code&amp;gt;)) et la période (&amp;lt;code&amp;gt;periode&amp;lt;/code&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros &amp;lt;code&amp;gt;SAVE_REGISTERS()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;RESTORE_REGISTERS()&amp;lt;/code&amp;gt; que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La structure des tâches est la suivante : &lt;br /&gt;
&lt;br /&gt;
typedef struct Task {&lt;br /&gt;
&lt;br /&gt;
   void (*fonction)(void);    // Pointeur vers une fonction prenant aucun paramètre et ne retournant rien&lt;br /&gt;
&lt;br /&gt;
   uint16_t SPointer;         // Pointeur de pile&lt;br /&gt;
&lt;br /&gt;
   int state;                 // État de la tâche&lt;br /&gt;
&lt;br /&gt;
   struct prog_data data;     // Données de la tâche&lt;br /&gt;
&lt;br /&gt;
} Task;&lt;br /&gt;
&lt;br /&gt;
struct prog_data {&lt;br /&gt;
&lt;br /&gt;
   int Type;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
&lt;br /&gt;
       int delay;  // On peut ajouter d'autres types de données ici si nécessaire&lt;br /&gt;
&lt;br /&gt;
   } data;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Nous avons un pointeur destiné à pointer vers la fonction à exécuter pour cette tâche. Le pointeur SPointer sert à sauvegarder la position du pointeur de pile pour cette fonction à l'interruption, chaque fonction ayant sa propre pile d'exécution. L'état permet d'intégrer une fonction sleep par la suite, qui rendra la tâche inactive pendant le delai delay contenu dans data.&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|-&lt;br /&gt;
|LED[1-4]&lt;br /&gt;
|PC[0-3]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6643</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6643"/>
		<updated>2024-11-13T10:54:32Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (diviseur), le mode de la minuterie (CTC1) et la période (periode).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros SAVE_REGISTERS() et RESTORE_REGISTERS() que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Puis nous créons notre fonction ordonnanceur, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6642</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6642"/>
		<updated>2024-11-13T10:53:50Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== TESTS PRELIMINAIRES ===&lt;br /&gt;
&lt;br /&gt;
==== Vérification des connecteurs HE-10 ====&lt;br /&gt;
Nous avons testé un 7-segments sur tous les connecteurs HE-10. Vous pouvez le voir dans la sous-section COMMUNICATION SPI de la section ORDONNANCEUR.&lt;br /&gt;
&lt;br /&gt;
==== Lecture Carte SD ====&lt;br /&gt;
Cette image montre la detection de la carte SD sur le Shield par l'ordinateur. On peut observer son type ou sa taille et d'autres informations.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:CarteSD.png|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Fonctionnement des LEDs ====&lt;br /&gt;
Avant de nous aventurer dans les méandres de l'ordonnanceur, nous devons vérifier que toutes les LEDs sont bien connectées. Nous n'avons pas de photos du dit test mais nous vous invitons à aller voir la sous-section CLIGNOTEMENT DES LEDs dans la section ORDONNANCEUR. Vous pouvez même observer une différence de fréquence entre les clignotements.&lt;br /&gt;
&lt;br /&gt;
=== ORDONNANCEUR ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
Le but de l'ordonnanceur est de répartir l'éxecution des multiples tâches que doit exécuter l'arduino. Pour faire cela, à intervalle régulier (puisque nous fonctionnons en Round Robin),l'Interrupt Service Routine (ISR) va sauvegarder l'état des registres, interrompre la tâche en cours, puis appeler l'ordonnanceur qui va choisir quelle tâche doit maintenant s'exécuter, puis restaurer l'état des registres.&lt;br /&gt;
&lt;br /&gt;
Pour réaliser l'ordonnanceur, nous commençons par initialiser un minuteur qui va définir la fréquence d'interruption (diviseur), le mode de la minuterie (CTC1) et la période (periode).&lt;br /&gt;
&lt;br /&gt;
Ensuite, nous définissons le comportement qu'il va adopter à chaque interruption. Cela est géré par la fonction ISR en mode NAKED, ce qui implique que l'on va devoir gérer les sauvegardes &lt;br /&gt;
&lt;br /&gt;
des registres nous mêmes (lors d'une interruption, nous commençons toujours par sauvegarder l'état des registres et les restaurons à la fin, sans quoi nous perdons tout le contexte la précédant).&lt;br /&gt;
&lt;br /&gt;
Nous définissons donc en parallèle deux macros SAVE_REGISTERS() et RESTORE_REGISTERS() que nous appelons respectivement au début et à la fin de chaque interruption.&lt;br /&gt;
&lt;br /&gt;
Nous définissons alors notre fonction ordonnanceur appelée à chaque interruption, qui est pour l'instant vide, puisque les tâches n'ont pas encore été construites, et c'est ce à quoi nous allons maintenant nous atteler.&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|500px|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|500px]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|500px]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|500px]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
&lt;br /&gt;
===== PINOUT =====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!PIN UTILITY&lt;br /&gt;
!PIN NAME&lt;br /&gt;
|-&lt;br /&gt;
|ChipSelect&lt;br /&gt;
|PB0&lt;br /&gt;
|-&lt;br /&gt;
|CLK/SCK&lt;br /&gt;
|PB1&lt;br /&gt;
|-&lt;br /&gt;
|MOSI&lt;br /&gt;
|PB2&lt;br /&gt;
|-&lt;br /&gt;
|MISO&lt;br /&gt;
|PB3&lt;br /&gt;
|-&lt;br /&gt;
|Pin d'Interruption&lt;br /&gt;
|PB4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6610</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6610"/>
		<updated>2024-11-13T10:22:00Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== Ordonnanceur ===&lt;br /&gt;
==== SQUELETTE BASIQUE DE L'ORDONNANCEUR ====&lt;br /&gt;
&lt;br /&gt;
==== STRUCTURE DES TÂCHES ====&lt;br /&gt;
&lt;br /&gt;
==== CLIGNOTEMENT DES LEDs ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== COMMUNICATION SERIE ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Minicom .mp4|vignette|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|[[Fichier:Blinkleds.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== COMMUNICATION SPI ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:7SegmentPicture.jpg|vignette]]&lt;br /&gt;
|&lt;br /&gt;
|[[Fichier:7SegDisplay.mp4|vignette]]&lt;br /&gt;
|}&lt;br /&gt;
==== FONCTION SLEEP ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Communication Série ===&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Photos et vidéos || description&lt;br /&gt;
|+&lt;br /&gt;
|[[Fichier:Carte-mère soudée.jpg|vignette]]&lt;br /&gt;
|Le shield soudée avec juste le BootLoader dans le microP&lt;br /&gt;
|}&lt;br /&gt;
[[Fichier:CarteSD.png|vignette]]&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:CarteSD.png&amp;diff=6609</id>
		<title>Fichier:CarteSD.png</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:CarteSD.png&amp;diff=6609"/>
		<updated>2024-11-13T10:21:52Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Détection SD par l'arduino&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6558</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6558"/>
		<updated>2024-11-13T09:52:29Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== Ordonnanceur ===&lt;br /&gt;
==== INCLUDE &amp;amp; VARIABLES ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur1.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define CTC1            WGM12&lt;br /&gt;
#define PERIODE         20&lt;br /&gt;
#define NB_TASKS 2&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
&lt;br /&gt;
Task TaskList[NB_TASKS]={&lt;br /&gt;
  {task_led1, 0x600, 1},&lt;br /&gt;
  {task_led2, 0x700, 1}&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Initialisation du minuteur et PRESCALER ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_minuteur(int diviseur,long periode)&lt;br /&gt;
{&lt;br /&gt;
  TCCR1A=0;               // Le mode choisi n'utilise pas ce registre&lt;br /&gt;
  TCCR1B=(1&amp;lt;&amp;lt;CTC1);       // Réinitialisation du minuteur sur expiration&lt;br /&gt;
  switch(diviseur)&lt;br /&gt;
  {&lt;br /&gt;
    case    8: TCCR1B |= (1&amp;lt;&amp;lt;CS11); break;&lt;br /&gt;
    case   64: TCCR1B |= (1&amp;lt;&amp;lt;CS11 | 11&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
    case  256: TCCR1B |= (1&amp;lt;&amp;lt;CS12); break;&lt;br /&gt;
    case 1024: TCCR1B |= (1&amp;lt;&amp;lt;CS12 | 1&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
  }&lt;br /&gt;
// Un cycle prend 1/F_CPU secondes.&lt;br /&gt;
// Un pas de compteur prend diviseur/F_CPU secondes.&lt;br /&gt;
// Pour une periode en millisecondes, il faut (periode/1000)/(diviseur/F_CPU) pas&lt;br /&gt;
// soit (periode*F_CPU)/(1000*diviseur)&lt;br /&gt;
  OCR1A=F_CPU/1000*periode/diviseur;  // Calcul du pas&lt;br /&gt;
  TCNT1=0;                // Compteur initialisé&lt;br /&gt;
  TIMSK1=(1&amp;lt;&amp;lt;OCIE1A);     // Comparaison du compteur avec OCR1A&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ordonnanceur()&lt;br /&gt;
{&lt;br /&gt;
  currentTask = (currentTask + 1) % NB_TASKS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== ISR ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED)&lt;br /&gt;
{&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  TaskList[currentTask].SPointer = SP;&lt;br /&gt;
  ordonnanceur();&lt;br /&gt;
  SP = TaskList[currentTask].SPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
  asm volatile (&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Initialisation des taches ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_task(int n)&lt;br /&gt;
{&lt;br /&gt;
  int saveSP = SP;&lt;br /&gt;
  SP = TaskList[n].SPointer;&lt;br /&gt;
  int16_t address = (uint16_t)TaskList[n].fonction;&lt;br /&gt;
  asm volatile (&amp;quot;push %0 \n\t&amp;quot; : : &amp;quot;r&amp;quot; (address &amp;amp; 0x00ff));&lt;br /&gt;
  asm volatile (&amp;quot;push %0 \n\t&amp;quot; : : &amp;quot;r&amp;quot; ((address &amp;amp; 0xff00) &amp;gt;&amp;gt; 8));&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  TaskList[n].SPointer = SP;&lt;br /&gt;
  SP = saveSP;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== MAIN ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  init_minuteur(256,PERIODE);&lt;br /&gt;
  for(int i=1;i&amp;lt;NB_TASKS;i++) init_task(i);&lt;br /&gt;
  sei();&lt;br /&gt;
  SP = TaskList[currentTask].SPointer;&lt;br /&gt;
  TaskList[currentTask].fonction();&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== TACHE LED 1 ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void task_led1()&lt;br /&gt;
{&lt;br /&gt;
    DDRC |= 0b00000001;&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(100);&lt;br /&gt;
      PORTC ^= 0b00000001;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== TACHE LED 2 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void task_led2()&lt;br /&gt;
{&lt;br /&gt;
    DDRC |= 0b00001000;&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(173);&lt;br /&gt;
      PORTC ^= 0b00001000;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Communication Série ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=text-align:left&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void serie_init(long int vitesse){&lt;br /&gt;
UBRR0=F_CPU/(((unsigned long int)speed)&amp;lt;&amp;lt;4)-1; // configure la vitesse&lt;br /&gt;
UCSR0B=(1&amp;lt;&amp;lt;TXEN0 | 1&amp;lt;&amp;lt;RXEN0);                  // autorise l'envoi et la réception&lt;br /&gt;
UCSR0C=(1&amp;lt;&amp;lt;UCSZ01 | 1&amp;lt;&amp;lt;UCSZ00);                // 8 bits et 1 bit de stop&lt;br /&gt;
UCSR0A &amp;amp;= ~(1 &amp;lt;&amp;lt; U2X0);                        // double vitesse désactivée&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void serie_envoyer(char c){&lt;br /&gt;
loop_until_bit_is_set(UCSR0A,UDRE0);&lt;br /&gt;
UDR0=c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char serie_recevoir(void){&lt;br /&gt;
loop_until_bit_is_set(UCSR0A, RXC0);&lt;br /&gt;
return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void){&lt;br /&gt;
serie_init(9600);&lt;br /&gt;
while(1){&lt;br /&gt;
  unsigned char c=serie_recevoir();&lt;br /&gt;
  serie_envoyer(c);&lt;br /&gt;
  }&lt;br /&gt;
return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| [[Fichier:Minicom .mp4|vignette|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
[[Fichier:7SegmentPicture.jpg|vignette]]&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Carte-mère soudée.jpg|vignette]]&lt;br /&gt;
![[Fichier:7SegDisplay.mp4|vignette]]La carte-mère soudée mais pas encore programmée&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
[[Fichier:Blinkleds.mp4|vignette]]&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
[[Fichier:LEDBlinking.mp4|vignette]]&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:7SegmentPicture.jpg&amp;diff=6557</id>
		<title>Fichier:7SegmentPicture.jpg</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:7SegmentPicture.jpg&amp;diff=6557"/>
		<updated>2024-11-13T09:52:17Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Image du 7 Segment&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:7SegDisplay.mp4&amp;diff=6555</id>
		<title>Fichier:7SegDisplay.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:7SegDisplay.mp4&amp;diff=6555"/>
		<updated>2024-11-13T09:51:42Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Afficheur 7 Segments fonctionnant grâce à l'ordonnanceur.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:LEDBlinking.mp4&amp;diff=6553</id>
		<title>Fichier:LEDBlinking.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:LEDBlinking.mp4&amp;diff=6553"/>
		<updated>2024-11-13T09:50:51Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LED clignotantes à deux féquences premières entre elles&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6549</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6549"/>
		<updated>2024-11-13T09:49:27Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== Ordonnanceur ===&lt;br /&gt;
==== INCLUDE &amp;amp; VARIABLES ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur1.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define CTC1            WGM12&lt;br /&gt;
#define PERIODE         20&lt;br /&gt;
#define NB_TASKS 2&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
&lt;br /&gt;
Task TaskList[NB_TASKS]={&lt;br /&gt;
  {task_led1, 0x600, 1},&lt;br /&gt;
  {task_led2, 0x700, 1}&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Initialisation du minuteur et PRESCALER ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_minuteur(int diviseur,long periode)&lt;br /&gt;
{&lt;br /&gt;
  TCCR1A=0;               // Le mode choisi n'utilise pas ce registre&lt;br /&gt;
  TCCR1B=(1&amp;lt;&amp;lt;CTC1);       // Réinitialisation du minuteur sur expiration&lt;br /&gt;
  switch(diviseur)&lt;br /&gt;
  {&lt;br /&gt;
    case    8: TCCR1B |= (1&amp;lt;&amp;lt;CS11); break;&lt;br /&gt;
    case   64: TCCR1B |= (1&amp;lt;&amp;lt;CS11 | 11&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
    case  256: TCCR1B |= (1&amp;lt;&amp;lt;CS12); break;&lt;br /&gt;
    case 1024: TCCR1B |= (1&amp;lt;&amp;lt;CS12 | 1&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
  }&lt;br /&gt;
// Un cycle prend 1/F_CPU secondes.&lt;br /&gt;
// Un pas de compteur prend diviseur/F_CPU secondes.&lt;br /&gt;
// Pour une periode en millisecondes, il faut (periode/1000)/(diviseur/F_CPU) pas&lt;br /&gt;
// soit (periode*F_CPU)/(1000*diviseur)&lt;br /&gt;
  OCR1A=F_CPU/1000*periode/diviseur;  // Calcul du pas&lt;br /&gt;
  TCNT1=0;                // Compteur initialisé&lt;br /&gt;
  TIMSK1=(1&amp;lt;&amp;lt;OCIE1A);     // Comparaison du compteur avec OCR1A&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ordonnanceur()&lt;br /&gt;
{&lt;br /&gt;
  currentTask = (currentTask + 1) % NB_TASKS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== ISR ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED)&lt;br /&gt;
{&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  TaskList[currentTask].SPointer = SP;&lt;br /&gt;
  ordonnanceur();&lt;br /&gt;
  SP = TaskList[currentTask].SPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
  asm volatile (&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Initialisation des taches ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_task(int n)&lt;br /&gt;
{&lt;br /&gt;
  int saveSP = SP;&lt;br /&gt;
  SP = TaskList[n].SPointer;&lt;br /&gt;
  int16_t address = (uint16_t)TaskList[n].fonction;&lt;br /&gt;
  asm volatile (&amp;quot;push %0 \n\t&amp;quot; : : &amp;quot;r&amp;quot; (address &amp;amp; 0x00ff));&lt;br /&gt;
  asm volatile (&amp;quot;push %0 \n\t&amp;quot; : : &amp;quot;r&amp;quot; ((address &amp;amp; 0xff00) &amp;gt;&amp;gt; 8));&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  TaskList[n].SPointer = SP;&lt;br /&gt;
  SP = saveSP;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== MAIN ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  init_minuteur(256,PERIODE);&lt;br /&gt;
  for(int i=1;i&amp;lt;NB_TASKS;i++) init_task(i);&lt;br /&gt;
  sei();&lt;br /&gt;
  SP = TaskList[currentTask].SPointer;&lt;br /&gt;
  TaskList[currentTask].fonction();&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== TACHE LED 1 ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void task_led1()&lt;br /&gt;
{&lt;br /&gt;
    DDRC |= 0b00000001;&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(100);&lt;br /&gt;
      PORTC ^= 0b00000001;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== TACHE LED 2 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void task_led2()&lt;br /&gt;
{&lt;br /&gt;
    DDRC |= 0b00001000;&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(173);&lt;br /&gt;
      PORTC ^= 0b00001000;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Communication Série ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=text-align:left&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void serie_init(long int vitesse){&lt;br /&gt;
UBRR0=F_CPU/(((unsigned long int)speed)&amp;lt;&amp;lt;4)-1; // configure la vitesse&lt;br /&gt;
UCSR0B=(1&amp;lt;&amp;lt;TXEN0 | 1&amp;lt;&amp;lt;RXEN0);                  // autorise l'envoi et la réception&lt;br /&gt;
UCSR0C=(1&amp;lt;&amp;lt;UCSZ01 | 1&amp;lt;&amp;lt;UCSZ00);                // 8 bits et 1 bit de stop&lt;br /&gt;
UCSR0A &amp;amp;= ~(1 &amp;lt;&amp;lt; U2X0);                        // double vitesse désactivée&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void serie_envoyer(char c){&lt;br /&gt;
loop_until_bit_is_set(UCSR0A,UDRE0);&lt;br /&gt;
UDR0=c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char serie_recevoir(void){&lt;br /&gt;
loop_until_bit_is_set(UCSR0A, RXC0);&lt;br /&gt;
return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void){&lt;br /&gt;
serie_init(9600);&lt;br /&gt;
while(1){&lt;br /&gt;
  unsigned char c=serie_recevoir();&lt;br /&gt;
  serie_envoyer(c);&lt;br /&gt;
  }&lt;br /&gt;
return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| [[Fichier:Minicom .mp4|vignette|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
==== Shield  ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Carte-mère soudée.jpg|vignette]]&lt;br /&gt;
!La carte-mère soudée mais pas encore programmée&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte RNDIS ====&lt;br /&gt;
[[Fichier:Blinkleds.mp4|vignette]]&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Blinkleds.mp4&amp;diff=6545</id>
		<title>Fichier:Blinkleds.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Blinkleds.mp4&amp;diff=6545"/>
		<updated>2024-11-13T09:48:44Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LEDs clignotent a deux fréquences premières entre elles&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6519</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6519"/>
		<updated>2024-11-09T15:49:30Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt; Code Source et autre programmes&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
=== Ordonnanceur ===&lt;br /&gt;
==== INCLUDE &amp;amp; VARIABLES ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;quot;ordonnanceur1.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define CTC1            WGM12&lt;br /&gt;
#define PERIODE         20&lt;br /&gt;
#define NB_TASKS 2&lt;br /&gt;
int currentTask = 0;&lt;br /&gt;
&lt;br /&gt;
Task TaskList[NB_TASKS]={&lt;br /&gt;
  {task_led1, 0x600, 1},&lt;br /&gt;
  {task_led2, 0x700, 1}&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Initialisation du minuteur et PRESCALER ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_minuteur(int diviseur,long periode)&lt;br /&gt;
{&lt;br /&gt;
  TCCR1A=0;               // Le mode choisi n'utilise pas ce registre&lt;br /&gt;
  TCCR1B=(1&amp;lt;&amp;lt;CTC1);       // Réinitialisation du minuteur sur expiration&lt;br /&gt;
  switch(diviseur)&lt;br /&gt;
  {&lt;br /&gt;
    case    8: TCCR1B |= (1&amp;lt;&amp;lt;CS11); break;&lt;br /&gt;
    case   64: TCCR1B |= (1&amp;lt;&amp;lt;CS11 | 11&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
    case  256: TCCR1B |= (1&amp;lt;&amp;lt;CS12); break;&lt;br /&gt;
    case 1024: TCCR1B |= (1&amp;lt;&amp;lt;CS12 | 1&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
  }&lt;br /&gt;
// Un cycle prend 1/F_CPU secondes.&lt;br /&gt;
// Un pas de compteur prend diviseur/F_CPU secondes.&lt;br /&gt;
// Pour une periode en millisecondes, il faut (periode/1000)/(diviseur/F_CPU) pas&lt;br /&gt;
// soit (periode*F_CPU)/(1000*diviseur)&lt;br /&gt;
  OCR1A=F_CPU/1000*periode/diviseur;  // Calcul du pas&lt;br /&gt;
  TCNT1=0;                // Compteur initialisé&lt;br /&gt;
  TIMSK1=(1&amp;lt;&amp;lt;OCIE1A);     // Comparaison du compteur avec OCR1A&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ordonnanceur()&lt;br /&gt;
{&lt;br /&gt;
  currentTask = (currentTask + 1) % NB_TASKS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== ISR ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect, ISR_NAKED)&lt;br /&gt;
{&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  TaskList[currentTask].SPointer = SP;&lt;br /&gt;
  ordonnanceur();&lt;br /&gt;
  SP = TaskList[currentTask].SPointer;&lt;br /&gt;
  RESTORE_REGISTERS();&lt;br /&gt;
  asm volatile (&amp;quot;reti&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Initialisation des taches ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void init_task(int n)&lt;br /&gt;
{&lt;br /&gt;
  int saveSP = SP;&lt;br /&gt;
  SP = TaskList[n].SPointer;&lt;br /&gt;
  int16_t address = (uint16_t)TaskList[n].fonction;&lt;br /&gt;
  asm volatile (&amp;quot;push %0 \n\t&amp;quot; : : &amp;quot;r&amp;quot; (address &amp;amp; 0x00ff));&lt;br /&gt;
  asm volatile (&amp;quot;push %0 \n\t&amp;quot; : : &amp;quot;r&amp;quot; ((address &amp;amp; 0xff00) &amp;gt;&amp;gt; 8));&lt;br /&gt;
  SAVE_REGISTERS();&lt;br /&gt;
  TaskList[n].SPointer = SP;&lt;br /&gt;
  SP = saveSP;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== MAIN ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  init_minuteur(256,PERIODE);&lt;br /&gt;
  for(int i=1;i&amp;lt;NB_TASKS;i++) init_task(i);&lt;br /&gt;
  sei();&lt;br /&gt;
  SP = TaskList[currentTask].SPointer;&lt;br /&gt;
  TaskList[currentTask].fonction();&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== MACROS ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define SAVE_REGISTERS() \&lt;br /&gt;
asm volatile ( \&lt;br /&gt;
 &amp;quot;push r0 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;in r0, __SREG__ \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r0 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r1 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r2 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r3 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r4 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r5 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r6 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r7 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r8 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r9 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r10 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r11 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r12 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r13 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r14 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r15 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r16 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r17 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r18 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r19 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r20 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r21 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r22 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r23 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r24 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r25 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r26 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r27 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r28 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r29 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r30 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;push r31 \n\t&amp;quot; \&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define RESTORE_REGISTERS() \&lt;br /&gt;
asm volatile ( \&lt;br /&gt;
 &amp;quot;pop r31 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r30 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r29 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r28 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r27 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r26 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r25 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r24 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r23 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r22 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r21 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r20 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r19 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r18 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r17 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r16 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r15 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r14 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r13 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r12 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r11 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r10 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r9 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r8 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r7 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r6 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r5 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r4 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r3 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r2 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r1 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r0 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;out __SREG__, r0 \n\t&amp;quot; \&lt;br /&gt;
 &amp;quot;pop r0 \n\t&amp;quot; \&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== TACHE LED 1 ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void task_led1()&lt;br /&gt;
{&lt;br /&gt;
    DDRC |= 0b00000001;&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(100);&lt;br /&gt;
      PORTC ^= 0b00000001;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== TACHE LED 2 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void task_led2()&lt;br /&gt;
{&lt;br /&gt;
    DDRC |= 0b00001000;&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(173);&lt;br /&gt;
      PORTC ^= 0b00001000;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Makefile ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
export CC = avr-gcc&lt;br /&gt;
&lt;br /&gt;
export MCU = atmega328p&lt;br /&gt;
export TARGET_ARCH = -mmcu=$(MCU)&lt;br /&gt;
&lt;br /&gt;
export CFLAGS =  -Wall -I. -DF_CPU=16000000 -Os #-g&lt;br /&gt;
export LDFLAGS = -g $(TARGET_ARCH) -lm -Wl,--gc-sections #	-Os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TARGET = ordonnanceur1&lt;br /&gt;
#TERM = /dev/ttyUSB0&lt;br /&gt;
TERM = /dev/ttyACM0&lt;br /&gt;
CPPFLAGS = -mmcu=$(MCU)&lt;br /&gt;
PGMER = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
PGMERISP = -c arduino -b 115200 -P $(TERM)&lt;br /&gt;
ARVDUDECONF= -C /usr/local/arduino/arduino-0021/hardware/tools/avrdude.conf&lt;br /&gt;
export DUDE = /usr/bin/avrdude -F -v -p $(MCU) $(AVRDUDECONF)&lt;br /&gt;
&lt;br /&gt;
C_SRC = $(wildcard *.c)&lt;br /&gt;
OBJS = $(C_SRC:.c=.o)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
all: $(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
ass:$(C_SRC)&lt;br /&gt;
	$(CC) -S $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f *.o&lt;br /&gt;
&lt;br /&gt;
%.o:%.c&lt;br /&gt;
	$(CC) -c $(CPPFLAGS) $(CFLAGS) $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(TARGET).elf: $(OBJS)&lt;br /&gt;
	$(CC) $(LDFLAGS) -o $@ $(OBJS)&lt;br /&gt;
&lt;br /&gt;
$(TARGET).hex: $(TARGET).elf&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex&lt;br /&gt;
	avr-objcopy -j .eeprom --set-section-flags=.eeprom=&amp;quot;alloc,load&amp;quot; --change-section-lma .eeprom=0 -O ihex $(TARGET).elf eeprom.hex&lt;br /&gt;
&lt;br /&gt;
upload: $(TARGET).hex&lt;br /&gt;
	stty -F $(TERM) hupcl # reset&lt;br /&gt;
	$(DUDE) $(PGMER) -U flash:w:$(TARGET).hex&lt;br /&gt;
#	$(DUDE) $(PGMERISP) -U flash:w:$(TARGET).hex&lt;br /&gt;
&lt;br /&gt;
size: $(TARGET).elf&lt;br /&gt;
	avr-size --format=avr --mcu=$(MCU) $(TARGET).elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Communication Série ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=text-align:left&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void serie_init(long int vitesse){&lt;br /&gt;
UBRR0=F_CPU/(((unsigned long int)speed)&amp;lt;&amp;lt;4)-1; // configure la vitesse&lt;br /&gt;
UCSR0B=(1&amp;lt;&amp;lt;TXEN0 | 1&amp;lt;&amp;lt;RXEN0);                  // autorise l'envoi et la réception&lt;br /&gt;
UCSR0C=(1&amp;lt;&amp;lt;UCSZ01 | 1&amp;lt;&amp;lt;UCSZ00);                // 8 bits et 1 bit de stop&lt;br /&gt;
UCSR0A &amp;amp;= ~(1 &amp;lt;&amp;lt; U2X0);                        // double vitesse désactivée&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void serie_envoyer(char c){&lt;br /&gt;
loop_until_bit_is_set(UCSR0A,UDRE0);&lt;br /&gt;
UDR0=c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char serie_recevoir(void){&lt;br /&gt;
loop_until_bit_is_set(UCSR0A, RXC0);&lt;br /&gt;
return UDR0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void){&lt;br /&gt;
serie_init(9600);&lt;br /&gt;
while(1){&lt;br /&gt;
  unsigned char c=serie_recevoir();&lt;br /&gt;
  serie_envoyer(c);&lt;br /&gt;
  }&lt;br /&gt;
return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| [[Fichier:Minicom .mp4|vignette|Minicom démontrant le bon fonctionnement du code à gauche]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;div class=&amp;quot;mcwiki-header&amp;quot; style=&amp;quot;border-radius: 40px; padding: 15px; font-weight: bold; color: #FFFFFF; text-align: center; font-size: 80%; background: #ED254E; vertical-align: top; width: 98%;&amp;quot;&amp;gt;Schéma, PCB &amp;amp; KiCAD&amp;lt;/div&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
==== Carte soudée ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
![[Fichier:Carte-mère soudée.jpg|vignette]]&lt;br /&gt;
!La carte-mère soudée mais pas encore programmée&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Carte mère soudée ====&lt;br /&gt;
&lt;br /&gt;
==== Carte Fille réseau RNDIS soudée ====&lt;br /&gt;
&lt;br /&gt;
== NOTES ==&lt;br /&gt;
deux ports USB pour la carte fille : un pour la connexion en ISP/Réseau à l'ordinateur et un pour la programmation. Possibilité de faire une connexion SPI avec un câble RJ45&lt;br /&gt;
&lt;br /&gt;
== Ressources &amp;amp;  Sources ==&lt;br /&gt;
&lt;br /&gt;
===== Lien Git : https://gitea.plil.fr/rboursau/S8-Pico-Binome-7 =====&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6100</id>
		<title>SE4Binome2024-7</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE4Binome2024-7&amp;diff=6100"/>
		<updated>2024-10-02T13:31:05Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define CTC1            WGM12           // Meilleur nom pour le bit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;define PERIODE         1000&lt;br /&gt;
&lt;br /&gt;
void init_minuteur(int diviseur,long periode){&lt;br /&gt;
&lt;br /&gt;
TCCR1A=0;               // Le mode choisi n'utilise pas ce registre&lt;br /&gt;
&lt;br /&gt;
TCCR1B=(1&amp;lt;&amp;lt;CTC1);       // Réinitialisation du minuteur sur expiration&lt;br /&gt;
&lt;br /&gt;
switch(diviseur){&lt;br /&gt;
&lt;br /&gt;
  case    8: TCCR1B |= (1&amp;lt;&amp;lt;CS11); break;&lt;br /&gt;
&lt;br /&gt;
  case   64: TCCR1B |= (1&amp;lt;&amp;lt;CS11 | 11&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
&lt;br /&gt;
  case  256: TCCR1B |= (1&amp;lt;&amp;lt;CS12); break;&lt;br /&gt;
&lt;br /&gt;
  case 1024: TCCR1B |= (1&amp;lt;&amp;lt;CS12 | 1&amp;lt;&amp;lt;CS10); break;&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
OCR1A=F_CPU/1000*periode/diviseur;&lt;br /&gt;
&lt;br /&gt;
TCNT1=0;&lt;br /&gt;
&lt;br /&gt;
TIMSK1=(1&amp;lt;&amp;lt;OCIE1A);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER1_COMPA_vect){&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void){&lt;br /&gt;
&lt;br /&gt;
DDRC &amp;amp;= 0b000000001;              // Chenillard sur 4 LED&lt;br /&gt;
&lt;br /&gt;
PORTB ^= ~0b00000001;            // LED éteintes&lt;br /&gt;
&lt;br /&gt;
init_minuteur(256,PERIODE);&lt;br /&gt;
&lt;br /&gt;
sei();&lt;br /&gt;
&lt;br /&gt;
while(1);&lt;br /&gt;
&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Pico_SE4_2024/2025&amp;diff=6022</id>
		<title>Pico SE4 2024/2025</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Pico_SE4_2024/2025&amp;diff=6022"/>
		<updated>2024-09-23T10:22:07Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Objectif =&lt;br /&gt;
&lt;br /&gt;
Voir le sujet dans le menu englobant.&lt;br /&gt;
&lt;br /&gt;
= Organisation du travail =&lt;br /&gt;
&lt;br /&gt;
Vous déposerez votre travail dans un projet GIT sur le serveur [https://gitea.plil.fr Gitea de la platforme informatique].&lt;br /&gt;
&lt;br /&gt;
= Bouclier ordonnanceur =&lt;br /&gt;
&lt;br /&gt;
Vous devez réaliser le bouclier Arduino Uno pour l'ordonnancement et pour que chaque binôme puisse simuler une carte mère.&lt;br /&gt;
&lt;br /&gt;
A la base le bouclier permet de connecter 5 périphériques SPI via des connecteurs IDC HE10 8 contacts. A noter qu'en plus des lignes du bus SPI, une ligne réinitialisation et une ligne interruption sont acheminées de et vers les périphériques SPI.&lt;br /&gt;
&lt;br /&gt;
Le bouclier comprend aussi une mémoire. Vous devez prévoir une carte micro-SD via un connecteur Molex 10431 mais aussi une puce mémoire AT45DB641E. Les deux mémoires doivent être prévues sur le circuit imprimé, vous déciderez plus tard laquelle sera soudée. Vu que l'Arduino fonctionne en 5V et les mémoires en 3,3V un releveur de niveau est nécessaire. La puce a utiliser est la 74LV125.&lt;br /&gt;
&lt;br /&gt;
[[File:2024_picoshield_schematic.pdf|thumb|left|500px|Schéma PicoShield]]&lt;br /&gt;
&lt;br /&gt;
[[File:2024_picoshield_footprints.png|thumb|right|500px|Empreintes]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Réalisations des élèves =&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Numéro du binôme !! Numéro du groupe !! Elèves !! Page &lt;br /&gt;
|-&lt;br /&gt;
| Binôme 1&lt;br /&gt;
| Groupe 1&lt;br /&gt;
| Lilian GREVIN &amp;amp; Sarah DEPARIS&lt;br /&gt;
| [[SE4Binome2024-1|Binôme 1 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 2&lt;br /&gt;
| Groupe 1&lt;br /&gt;
| Kaoutar EL BACHIRI &amp;amp; Maxime BARRET&lt;br /&gt;
| [[SE4Binome2024-2|Binôme 2 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 3&lt;br /&gt;
| Groupe 1&lt;br /&gt;
| DETREZ Victorien &amp;amp; CART Benjamin&lt;br /&gt;
| [[SE4Binome2024-3|Binôme 3 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 4&lt;br /&gt;
| Groupe 1&lt;br /&gt;
|WACQUET Justin &amp;amp; TEPELI Ibrahim&lt;br /&gt;
| [[SE4Binome2024-4|Binôme 4 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 5&lt;br /&gt;
| Groupe 2&lt;br /&gt;
| Augustin DJADJA-AVONYO &amp;amp; Kévan TOURON&lt;br /&gt;
| [[SE4Binome2024-5|Binôme 5 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 6&lt;br /&gt;
| Groupe 2&lt;br /&gt;
| Yassine YAHIANI &amp;amp; Abdel ZONGO&lt;br /&gt;
| [[SE4Binome2024-6|Binôme 6 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 7&lt;br /&gt;
| Groupe 2&lt;br /&gt;
| Rémi BOURSAULT &amp;amp; Prénom NOM&lt;br /&gt;
| [[SE4Binome2024-7|Binôme 7 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 8&lt;br /&gt;
| Groupe 2&lt;br /&gt;
| Louis BONNINGRE &amp;amp; Prénom NOM&lt;br /&gt;
| [[SE4Binome2024-8|Binôme 8 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
| Binôme 9&lt;br /&gt;
| Groupe 3&lt;br /&gt;
| Camille CARIAT &amp;amp; Agathe HOUDUSSE&lt;br /&gt;
| [[SE4Binome2024-9|Binôme 9 2024/2025]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5979</id>
		<title>SE3 PSE Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5979"/>
		<updated>2024-06-15T14:13:35Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Ajout des Diodes Électro-Luminescentes (la classe) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet système embarqué de Rémi BOURSAULT et Antoine LECOMTE =&lt;br /&gt;
L'objectif de ce module est de modéliser une manette, puis de la rendre fonctionnelle, pour jouer au jeu Space Invaders qui est codé dans le même module.&lt;br /&gt;
&lt;br /&gt;
Les professeurs avaient préalablement fourni un dossier contenant un modèle de PCB incomplet et différents fichiers &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; pour nous aider à démarrer. &lt;br /&gt;
&lt;br /&gt;
== Création de Manette ==&lt;br /&gt;
La création de la manette commence par la création sur kicad d'un PCB. Nous avons pris celui fourni sur le wiki du projet qui possède tous les composants mais non routés.   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Modélisation PCB ==&lt;br /&gt;
A partir du fichier KiCad fourni, nous avons modélisé un PCB déstiné à être utilisé comme manette. Nous avons donc ajouté des boutons, replacé ces derniers. Grâce au schéma nous avons sélectionné des PIN pour connecter les boutons, ainsi que des LEDs et des résistances que nous avons aussi ajouté.    &lt;br /&gt;
&lt;br /&gt;
Voyant que nous avions encore de l'espace de libre sur notre carte, étant un peu ambitieux et voyant que le procédé n'est pas bien compliqué, nous décidons alors de rajouter quelque boutons et quelques LEDs en plus pour que notre manette soit un peu plus crédibles.  &lt;br /&gt;
&lt;br /&gt;
Il nous a alors fallu rajouté  les dit composants sur notre schéma pour que ceux-ci soient rajoutés automatiquement sur le PCB lors de l'utilisation de la fonction &amp;quot;Update PCB with changes made to schematic&amp;quot;                        &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Image boutons.png|Schéma des boutons&lt;br /&gt;
File:Connecteur ISP.png|Schéma du connecteur ISP&lt;br /&gt;
File:Schéma des LEDs.png|Schéma des LEDs&lt;br /&gt;
File:Connecteur USB.png|Schéma du connecteur USB&lt;br /&gt;
File:Microcontroleur.png|Schéma du microcontrôleur&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Plan pcb.jpg|thumb|center|500px| Ajout du plan de masse et des via]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le routage effectué, nous avons ajouté un plan de masse et ajouté une grande quantité de via à différent endroits de la carte pour que la masse soit bien connectée et éviter les fuites thermiques de certains composants. &lt;br /&gt;
&lt;br /&gt;
Ensuite le fichier a été envoyé pour impression en Chine, grâce au fichier Gerber généré par KiCad que vous pouvez retrouvez sur notre GIT:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Vue3d.jpg|thumb|center|500px| Rendu 3D final de la carte]]&lt;br /&gt;
&lt;br /&gt;
== Programmateur AVR ==&lt;br /&gt;
En attendant de recevoir les cartes, nous nous sommes initiés à la programmation d'un périphérique USB, nous avons utilisé les bibliothèques LUFA et LibUSB pour utilisé un programmateur AVR. Il nous a fallu également modifier des fichiers Descripteurs, en &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; qui permettent, une fois téléversés dans le programmateur, d'être détecter par le PC et que celui-ci  reconnaisse les fonctionnalités du périphérique (InPoint et EndPoint). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Soudure de la carte ==&lt;br /&gt;
Une fois la carte reçu, nous avons pris tous nos composants et avons commencé à les souder. N'ayant que très peu d'expérience dans ce domaine ce n'était pas quelque chose de facile au début. Il nous a fallu un peu d'aide et pas mal de flux pour comprendre comment faire des soudures propres. Une fois le coup de main pris, la fin du montage de la carte était assez rapide. Nous avons donc pu passer à la programmation pour la reconnaissance par l'ordinateur de notre carte.&lt;br /&gt;
[[Fichier:Carte vierge.jpg|thumb|left|500px| Carte vierge]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Carte soudée manette.jpg|thumb|center|500px| Carte avec tous les composants soudés]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois la carte soudée il a fallu pouvoir la configurer pour par la suite pouvoir la programmer. Pour se faire il faut lui injecter un bootloader via son programmateur ISP. Ce qui passe la manette en mode DFU et elle est alors reconnu par l'ordinateur.&lt;br /&gt;
[[Fichier:Manette reconnu en mode dfu.png|thumb|center|500px| Manette reconnu en mode DFU]]&lt;br /&gt;
&lt;br /&gt;
== LUFA de la manette ==&lt;br /&gt;
Après avoir reçu les cartes et terminé de souder les composants nous avons pu attaquer la programmation du microcontrôleur pour pouvoir utiliser la manette. Nous avons procédés étape par étape, en commençant par allumé les LEDs puis réceptionner les signaux des boutons pour finir par fusionner les deux pour avoir une manette fonctionnelle.&lt;br /&gt;
&lt;br /&gt;
=== LEDs ===&lt;br /&gt;
Pour éviter un décalage sur le portF il faut désactiver le JTAG en copiant deux fois cette ligne au niveau de la déclaration des pins : &amp;lt;code&amp;gt;MCUCR |= (1&amp;lt;&amp;lt;JTD);&amp;lt;/code&amp;gt;cela permet d'accéder aux LEDs sur les pins PF4 et PF5.&lt;br /&gt;
Une fois la configuration terminé, nous avons testé nos LEDs avec la fonction suivante : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void blink_leds(void){&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ce qui nous donne ce résultat [[Fichier:Vidéo clignotement LEDs.mp4|center|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également fait la fonction EtatLED dans le fichier USBFinal.c (fichier servant à détecter et communiquer avec la manette par le port USB) qui se trouve dans le dossier libUSB, qui sert à allumer ou éteindre la LED de son choix.&lt;br /&gt;
&lt;br /&gt;
Celle ci utilise le device, handle et configuration descriptor qui ont été définies dans des fonctions précédentes, disponible sur le git dans usbfinal.c ,grandement inspirées de ce qui est donné sur le site de Monsieur Redon, qui sélectionnent la manette et le config descriptor. On aurait dû également sélectionner l'interface dans cette fonction précédente plutôt que de le faire dans EtatLED mais nous nous en sommes rendus compte trop tard.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void EtatLED(int etat, int numLED, libusb_device *manette, libusb_device_handle *handle, struct libusb_config_descriptor *configdesc)&lt;br /&gt;
{&lt;br /&gt;
    int indint=0;/* e.g. première interface */&lt;br /&gt;
    int indalt=0; /* e.g. première alternative */&lt;br /&gt;
    int interface=1; //;configdesc-&amp;gt;interface[indint].altsetting[indalt].bInterfaceNumber;&lt;br /&gt;
    int status=libusb_claim_interface(handle,interface);&lt;br /&gt;
&lt;br /&gt;
    if(status!=0)&lt;br /&gt;
    {&lt;br /&gt;
            printf(&amp;quot;bij\n&amp;quot;);&lt;br /&gt;
        perror(&amp;quot;libusb_claim_interface&amp;quot;);&lt;br /&gt;
        exit(-1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;Interface %d claimed\n&amp;quot;,interface);&lt;br /&gt;
&lt;br /&gt;
    unsigned char data = 0;&lt;br /&gt;
    if(etat){&lt;br /&gt;
       switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x01;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x02;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x03;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x04;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x11;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x12;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x13;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x14;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Exemple d'appel dans le main &amp;lt;code&amp;gt;EtatLED(0,1, manette, handle, configdesc);&amp;lt;/code&amp;gt;. La fonction va éteindre la LED 1.&lt;br /&gt;
&lt;br /&gt;
=== Boutons ===&lt;br /&gt;
Les LEDs étant configuré il est temps de passer à l'utilisation des boutons. Le principe reste globalement le même, on modifie les fichiers descriptors et minimal selon nos besoins. Pour nous faciliter un peu la tâche nous avons pris les fichiers déjà existant pour des joysticks. Nous allons considérer que si un de nos boutons est appuyé cela reviendra au même que si un joystick est dirigée au maximum dans une direction donnée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La modification majeure à faire pour que nos boutons soit détecté était sur la fonction &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool GetNextReport(USB_JoystickReport_Data_t* const ReportData)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t PrevJoyStatus    = 0;&lt;br /&gt;
  static uint8_t PrevButtonStatus = 0;&lt;br /&gt;
  bool           InputChanged     = false;&lt;br /&gt;
&lt;br /&gt;
  /* Clear the report contents */&lt;br /&gt;
  memset(ReportData, 0, sizeof(USB_JoystickReport_Data_t));&lt;br /&gt;
&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINB&amp;gt;&amp;gt;PB4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 1);&lt;br /&gt;
  }&lt;br /&gt;
  /* Return whether the new report is different to the previous report or not */&lt;br /&gt;
  return InputChanged;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cela nous permet de dire que si un bouton que nous avons configuré est enfoncé, on met la direction X ou Y à plus ou moins 100. Nous les avons tester avec jstest-gtk qui permet de visualiser les directions et appuis d'un joystick, tous nos boutons sont reconnus et configurés comme nous le souhaitons.&lt;br /&gt;
&lt;br /&gt;
=== Regroupement des deux ===&lt;br /&gt;
Pour avoir une manette fonctionnelle à 100% la dernière étape est de fusionner les différents fichiers pour les LEDs et pour les boutons pour que tout soit utilisable en même temps. Pour cela il a fallu modifié le fichier Descritors.c, dans la fonction&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor&amp;lt;/code&amp;gt;il faut indiquer le nombre d'interface à deux, puis rajouter une interface. Dans notre cas étant parti du fichier des boutons nous avons rajouté celle des LEDs. Une fois ceci fais, il ne restait plus qu'à rajouter les fonctions de chaque partie dans un même fichier .c et nous avons fini la configuration et la programmation de notre manette.&lt;br /&gt;
&lt;br /&gt;
== Implémentation dans le Space Invaders ==&lt;br /&gt;
&lt;br /&gt;
=== Ajout des boutons ===&lt;br /&gt;
On va dans cette partie utiliser tout ce qui a été implémenté pour rendre la manette utilisable avec le jeu Space Invaders codé en parallèle. &lt;br /&gt;
&lt;br /&gt;
D'abord, on a, grâce au tutoriel fourni, ajouté l'utilisation des boutons avec la librairie SDL. Il nous a suffit de créer une fonction qui d'abord initialise notre manette en détectant le nombre de Joystick que celle ci possède. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:JoystickInit.png|thumb|center|700px|Initialisation de la manette]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite, on a créé une fonction handleControllerReformed qui pendant que le jeu tourne vient récupérer les valeurs des axes et des boutons de la manette en utilisant le pointeur initialisé par JoystickInitialisation.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-21-04.png|thumb|center|700px|Fonction récupérant la valeur des axes et des boutons]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintenant que nous avons les valeurs des boutons et des axes à chaque instant, nous pouvons les utiliser comme input dans nos programmes pour, par exemple, les déplacements du vaisseau et le lancer de missiles. La valeur des axes est toujours maximale car notre joystick est en fait 4 boutons interprétés comme un Joystick, c'est pour cela qu'on vient comparer leurs valeurs aux valeurs maximales envoyés par un Joystick (32767 et -32768). Les boutons sont des booléens qui retournent 1 quand ils sont à l'état bas et 0 à l'état haut.[[Fichier:Screenshot from 2024-06-10 22-21-55.png|thumb|left|700px|Fonction permettant de faire se déplacer le joueur au clavier ou à la manette]]&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-22-34.png|thumb|right|700px|Fonction permettant d'ajouter un missile si l'on appuie sur e ou sur un bouton.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vous pouvez voir le résultat de l'implémentation totale des boutons :&lt;br /&gt;
[[Fichier:TestBoutons.mp4|thumb|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Ajout des Diodes Électro-Luminescentes (la classe) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour utiliser les LEDs, c'est légèrement plus compliqué. Le code que nous avions fait précédemment dans usbfinal.c, notamment EtatLED, va nous être utile. On va transformer ce fichier usbfinal.c en une librairie dynamique en .so. Grâce à ça, on va pouvoir réutiliser la fonction EtatLED dans notre programme pour le jeu et l'appeler directement dans des fonctions.&lt;br /&gt;
&lt;br /&gt;
Pour faire cela, on va compiler en utilisant des options particulières pour gcc. La commande est &amp;lt;code&amp;gt;gcc -fPIC -shared -o libusbfinal.so usbfinal.c -lusb-1.0&amp;lt;/code&amp;gt;. L'option &amp;lt;code&amp;gt;-fPIC&amp;lt;/code&amp;gt; permet de génerer du code indépendant de l'emplacement mémoire auquel il est chargé. L'option &amp;lt;code&amp;gt;-shared&amp;lt;/code&amp;gt; permet de génerer une bibliotèque partager plutôt qu'un executable, et &amp;lt;code&amp;gt;-lusb-1.0&amp;lt;/code&amp;gt; permet d'indiquer au compilateur qu'on utilise libusb 1.0 dans ce fichier C.&lt;br /&gt;
&lt;br /&gt;
On se retrouve alors avec un fichier .so disponible dans le git (dans les fichiers LUFA, répértoire libUSB, le chemin précis est spécifié dans le readme).&lt;br /&gt;
&lt;br /&gt;
Ce fichier est la bibliothèque dynamique qu'on peut appeler dans notre makefile.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-19-26.png|thumb|center|700px|Makefile pour le jeu, adapté à l'utilisation de la manette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit qu'il faut rajouter dans l'éxecution la commande &amp;lt;code&amp;gt;export LD_LIBRARY_PATH=./src/include:$$LD_LIBRARY_PATH &amp;amp;&amp;amp; ./$(TARGET) &amp;lt;/code&amp;gt;(nécessaire pour indiquer l'emplacement des bibliothèques dynamiques), et éxecuter la commande sudo make run pour lancer le jeu (libusb nécessite d'avoir les droits administrateurs).&lt;br /&gt;
&lt;br /&gt;
On peut alors dans le fichier source C spaceinvaders ajouter le fichier d'en-tête associé à la librairie usbfinal.h également disponible dans le git, et utiliser toutes les fonctions crééés dans usbfinal.c. On a finalement créé une fonction displayLivesLEDs qui vient alumer le nombre de LEDs correspondant au nombre de vies restantes, qu'on appelle à chaque fois que le joueur est touché pour éviter des appels inutiles (tant que l'on ne fait rien, les LEDs gardent leur état).&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-18-12.png|thumb|center|500px|Fonction de gestion des LEDs]]&lt;br /&gt;
&lt;br /&gt;
Le résultat final avec implémentation des LEDs :&lt;br /&gt;
&lt;br /&gt;
[[Fichier:TestLEDs.mp4|thumb|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut également vous fournir le .zip du jeu spaceinvaders avec l'implémentation de la manette si besoin (trop volumineux pour le wiki) en nous contactant par mail (remi.boursault@polytech-lille.net).&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
On a fini.&lt;br /&gt;
&lt;br /&gt;
Plus sérieusement, la manette possède les fonctionnalités attendues. Les fichiers LUFA permettent de faire comprendre à un PC qu'elle est constitué d'un Joystick, de deux boutons et de 4 LEDs. On a également réussi à utiliser tous ces éléments dans notre jeu comme il était demandé.&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous pouvez consulter le [https://archives.plil.fr/rboursau/S6-manette.git dépôt Git] des projet. Celui-ci contient le fichier gerber de la manette crée sur KiCad. Les fichiers utilisé pour le programmateur AVR sont dans le dossier projet-info-manette/ProgrammateurAVR. Et les fichiers utilisé pour la manette sont eux dans le dossier projet-info-manette/fichier_lufa_libusb.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:TestBoutons.mp4&amp;diff=5978</id>
		<title>Fichier:TestBoutons.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:TestBoutons.mp4&amp;diff=5978"/>
		<updated>2024-06-15T14:12:19Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;dd&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5977</id>
		<title>SE3 PSE Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5977"/>
		<updated>2024-06-15T14:08:00Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Ajout des Diodes Électro-Luminescentes (la classe) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet système embarqué de Rémi BOURSAULT et Antoine LECOMTE =&lt;br /&gt;
L'objectif de ce module est de modéliser une manette, puis de la rendre fonctionnelle, pour jouer au jeu Space Invaders qui est codé dans le même module.&lt;br /&gt;
&lt;br /&gt;
Les professeurs avaient préalablement fourni un dossier contenant un modèle de PCB incomplet et différents fichiers &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; pour nous aider à démarrer. &lt;br /&gt;
&lt;br /&gt;
== Création de Manette ==&lt;br /&gt;
La création de la manette commence par la création sur kicad d'un PCB. Nous avons pris celui fourni sur le wiki du projet qui possède tous les composants mais non routés.   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Modélisation PCB ==&lt;br /&gt;
A partir du fichier KiCad fourni, nous avons modélisé un PCB déstiné à être utilisé comme manette. Nous avons donc ajouté des boutons, replacé ces derniers. Grâce au schéma nous avons sélectionné des PIN pour connecter les boutons, ainsi que des LEDs et des résistances que nous avons aussi ajouté.    &lt;br /&gt;
&lt;br /&gt;
Voyant que nous avions encore de l'espace de libre sur notre carte, étant un peu ambitieux et voyant que le procédé n'est pas bien compliqué, nous décidons alors de rajouter quelque boutons et quelques LEDs en plus pour que notre manette soit un peu plus crédibles.  &lt;br /&gt;
&lt;br /&gt;
Il nous a alors fallu rajouté  les dit composants sur notre schéma pour que ceux-ci soient rajoutés automatiquement sur le PCB lors de l'utilisation de la fonction &amp;quot;Update PCB with changes made to schematic&amp;quot;                        &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Image boutons.png|Schéma des boutons&lt;br /&gt;
File:Connecteur ISP.png|Schéma du connecteur ISP&lt;br /&gt;
File:Schéma des LEDs.png|Schéma des LEDs&lt;br /&gt;
File:Connecteur USB.png|Schéma du connecteur USB&lt;br /&gt;
File:Microcontroleur.png|Schéma du microcontrôleur&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Plan pcb.jpg|thumb|center|500px| Ajout du plan de masse et des via]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le routage effectué, nous avons ajouté un plan de masse et ajouté une grande quantité de via à différent endroits de la carte pour que la masse soit bien connectée et éviter les fuites thermiques de certains composants. &lt;br /&gt;
&lt;br /&gt;
Ensuite le fichier a été envoyé pour impression en Chine, grâce au fichier Gerber généré par KiCad que vous pouvez retrouvez sur notre GIT:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Vue3d.jpg|thumb|center|500px| Rendu 3D final de la carte]]&lt;br /&gt;
&lt;br /&gt;
== Programmateur AVR ==&lt;br /&gt;
En attendant de recevoir les cartes, nous nous sommes initiés à la programmation d'un périphérique USB, nous avons utilisé les bibliothèques LUFA et LibUSB pour utilisé un programmateur AVR. Il nous a fallu également modifier des fichiers Descripteurs, en &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; qui permettent, une fois téléversés dans le programmateur, d'être détecter par le PC et que celui-ci  reconnaisse les fonctionnalités du périphérique (InPoint et EndPoint). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Soudure de la carte ==&lt;br /&gt;
Une fois la carte reçu, nous avons pris tous nos composants et avons commencé à les souder. N'ayant que très peu d'expérience dans ce domaine ce n'était pas quelque chose de facile au début. Il nous a fallu un peu d'aide et pas mal de flux pour comprendre comment faire des soudures propres. Une fois le coup de main pris, la fin du montage de la carte était assez rapide. Nous avons donc pu passer à la programmation pour la reconnaissance par l'ordinateur de notre carte.&lt;br /&gt;
[[Fichier:Carte vierge.jpg|thumb|left|500px| Carte vierge]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Carte soudée manette.jpg|thumb|center|500px| Carte avec tous les composants soudés]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois la carte soudée il a fallu pouvoir la configurer pour par la suite pouvoir la programmer. Pour se faire il faut lui injecter un bootloader via son programmateur ISP. Ce qui passe la manette en mode DFU et elle est alors reconnu par l'ordinateur.&lt;br /&gt;
[[Fichier:Manette reconnu en mode dfu.png|thumb|center|500px| Manette reconnu en mode DFU]]&lt;br /&gt;
&lt;br /&gt;
== LUFA de la manette ==&lt;br /&gt;
Après avoir reçu les cartes et terminé de souder les composants nous avons pu attaquer la programmation du microcontrôleur pour pouvoir utiliser la manette. Nous avons procédés étape par étape, en commençant par allumé les LEDs puis réceptionner les signaux des boutons pour finir par fusionner les deux pour avoir une manette fonctionnelle.&lt;br /&gt;
&lt;br /&gt;
=== LEDs ===&lt;br /&gt;
Pour éviter un décalage sur le portF il faut désactiver le JTAG en copiant deux fois cette ligne au niveau de la déclaration des pins : &amp;lt;code&amp;gt;MCUCR |= (1&amp;lt;&amp;lt;JTD);&amp;lt;/code&amp;gt;cela permet d'accéder aux LEDs sur les pins PF4 et PF5.&lt;br /&gt;
Une fois la configuration terminé, nous avons testé nos LEDs avec la fonction suivante : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void blink_leds(void){&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ce qui nous donne ce résultat [[Fichier:Vidéo clignotement LEDs.mp4|center|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également fait la fonction EtatLED dans le fichier USBFinal.c (fichier servant à détecter et communiquer avec la manette par le port USB) qui se trouve dans le dossier libUSB, qui sert à allumer ou éteindre la LED de son choix.&lt;br /&gt;
&lt;br /&gt;
Celle ci utilise le device, handle et configuration descriptor qui ont été définies dans des fonctions précédentes, disponible sur le git dans usbfinal.c ,grandement inspirées de ce qui est donné sur le site de Monsieur Redon, qui sélectionnent la manette et le config descriptor. On aurait dû également sélectionner l'interface dans cette fonction précédente plutôt que de le faire dans EtatLED mais nous nous en sommes rendus compte trop tard.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void EtatLED(int etat, int numLED, libusb_device *manette, libusb_device_handle *handle, struct libusb_config_descriptor *configdesc)&lt;br /&gt;
{&lt;br /&gt;
    int indint=0;/* e.g. première interface */&lt;br /&gt;
    int indalt=0; /* e.g. première alternative */&lt;br /&gt;
    int interface=1; //;configdesc-&amp;gt;interface[indint].altsetting[indalt].bInterfaceNumber;&lt;br /&gt;
    int status=libusb_claim_interface(handle,interface);&lt;br /&gt;
&lt;br /&gt;
    if(status!=0)&lt;br /&gt;
    {&lt;br /&gt;
            printf(&amp;quot;bij\n&amp;quot;);&lt;br /&gt;
        perror(&amp;quot;libusb_claim_interface&amp;quot;);&lt;br /&gt;
        exit(-1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;Interface %d claimed\n&amp;quot;,interface);&lt;br /&gt;
&lt;br /&gt;
    unsigned char data = 0;&lt;br /&gt;
    if(etat){&lt;br /&gt;
       switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x01;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x02;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x03;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x04;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x11;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x12;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x13;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x14;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Exemple d'appel dans le main &amp;lt;code&amp;gt;EtatLED(0,1, manette, handle, configdesc);&amp;lt;/code&amp;gt;. La fonction va éteindre la LED 1.&lt;br /&gt;
&lt;br /&gt;
=== Boutons ===&lt;br /&gt;
Les LEDs étant configuré il est temps de passer à l'utilisation des boutons. Le principe reste globalement le même, on modifie les fichiers descriptors et minimal selon nos besoins. Pour nous faciliter un peu la tâche nous avons pris les fichiers déjà existant pour des joysticks. Nous allons considérer que si un de nos boutons est appuyé cela reviendra au même que si un joystick est dirigée au maximum dans une direction donnée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La modification majeure à faire pour que nos boutons soit détecté était sur la fonction &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool GetNextReport(USB_JoystickReport_Data_t* const ReportData)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t PrevJoyStatus    = 0;&lt;br /&gt;
  static uint8_t PrevButtonStatus = 0;&lt;br /&gt;
  bool           InputChanged     = false;&lt;br /&gt;
&lt;br /&gt;
  /* Clear the report contents */&lt;br /&gt;
  memset(ReportData, 0, sizeof(USB_JoystickReport_Data_t));&lt;br /&gt;
&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINB&amp;gt;&amp;gt;PB4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 1);&lt;br /&gt;
  }&lt;br /&gt;
  /* Return whether the new report is different to the previous report or not */&lt;br /&gt;
  return InputChanged;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cela nous permet de dire que si un bouton que nous avons configuré est enfoncé, on met la direction X ou Y à plus ou moins 100. Nous les avons tester avec jstest-gtk qui permet de visualiser les directions et appuis d'un joystick, tous nos boutons sont reconnus et configurés comme nous le souhaitons.&lt;br /&gt;
&lt;br /&gt;
=== Regroupement des deux ===&lt;br /&gt;
Pour avoir une manette fonctionnelle à 100% la dernière étape est de fusionner les différents fichiers pour les LEDs et pour les boutons pour que tout soit utilisable en même temps. Pour cela il a fallu modifié le fichier Descritors.c, dans la fonction&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor&amp;lt;/code&amp;gt;il faut indiquer le nombre d'interface à deux, puis rajouter une interface. Dans notre cas étant parti du fichier des boutons nous avons rajouté celle des LEDs. Une fois ceci fais, il ne restait plus qu'à rajouter les fonctions de chaque partie dans un même fichier .c et nous avons fini la configuration et la programmation de notre manette.&lt;br /&gt;
&lt;br /&gt;
== Implémentation dans le Space Invaders ==&lt;br /&gt;
&lt;br /&gt;
=== Ajout des boutons ===&lt;br /&gt;
On va dans cette partie utiliser tout ce qui a été implémenté pour rendre la manette utilisable avec le jeu Space Invaders codé en parallèle. &lt;br /&gt;
&lt;br /&gt;
D'abord, on a, grâce au tutoriel fourni, ajouté l'utilisation des boutons avec la librairie SDL. Il nous a suffit de créer une fonction qui d'abord initialise notre manette en détectant le nombre de Joystick que celle ci possède. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:JoystickInit.png|thumb|center|700px|Initialisation de la manette]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite, on a créé une fonction handleControllerReformed qui pendant que le jeu tourne vient récupérer les valeurs des axes et des boutons de la manette en utilisant le pointeur initialisé par JoystickInitialisation.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-21-04.png|thumb|center|700px|Fonction récupérant la valeur des axes et des boutons]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintenant que nous avons les valeurs des boutons et des axes à chaque instant, nous pouvons les utiliser comme input dans nos programmes pour, par exemple, les déplacements du vaisseau et le lancer de missiles. La valeur des axes est toujours maximale car notre joystick est en fait 4 boutons interprétés comme un Joystick, c'est pour cela qu'on vient comparer leurs valeurs aux valeurs maximales envoyés par un Joystick (32767 et -32768). Les boutons sont des booléens qui retournent 1 quand ils sont à l'état bas et 0 à l'état haut.[[Fichier:Screenshot from 2024-06-10 22-21-55.png|thumb|left|700px|Fonction permettant de faire se déplacer le joueur au clavier ou à la manette]]&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-22-34.png|thumb|right|700px|Fonction permettant d'ajouter un missile si l'on appuie sur e ou sur un bouton.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vous pouvez voir le résultat de l'implémentation totale des boutons en cliquant [https://www.youtube.com/watch?v=TwVJ7-ZU8_8&amp;amp;ab_channel=R%C3%A9mi sur ce lien].&lt;br /&gt;
&lt;br /&gt;
=== Ajout des Diodes Électro-Luminescentes (la classe) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour utiliser les LEDs, c'est légèrement plus compliqué. Le code que nous avions fait précédemment dans usbfinal.c, notamment EtatLED, va nous être utile. On va transformer ce fichier usbfinal.c en une librairie dynamique en .so. Grâce à ça, on va pouvoir réutiliser la fonction EtatLED dans notre programme pour le jeu et l'appeler directement dans des fonctions.&lt;br /&gt;
&lt;br /&gt;
Pour faire cela, on va compiler en utilisant des options particulières pour gcc. La commande est &amp;lt;code&amp;gt;gcc -fPIC -shared -o libusbfinal.so usbfinal.c -lusb-1.0&amp;lt;/code&amp;gt;. L'option &amp;lt;code&amp;gt;-fPIC&amp;lt;/code&amp;gt; permet de génerer du code indépendant de l'emplacement mémoire auquel il est chargé. L'option &amp;lt;code&amp;gt;-shared&amp;lt;/code&amp;gt; permet de génerer une bibliotèque partager plutôt qu'un executable, et &amp;lt;code&amp;gt;-lusb-1.0&amp;lt;/code&amp;gt; permet d'indiquer au compilateur qu'on utilise libusb 1.0 dans ce fichier C.&lt;br /&gt;
&lt;br /&gt;
On se retrouve alors avec un fichier .so disponible dans le git (dans les fichiers LUFA, répértoire libUSB, le chemin précis est spécifié dans le readme).&lt;br /&gt;
&lt;br /&gt;
Ce fichier est la bibliothèque dynamique qu'on peut appeler dans notre makefile.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-19-26.png|thumb|center|700px|Makefile pour le jeu, adapté à l'utilisation de la manette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit qu'il faut rajouter dans l'éxecution la commande &amp;lt;code&amp;gt;export LD_LIBRARY_PATH=./src/include:$$LD_LIBRARY_PATH &amp;amp;&amp;amp; ./$(TARGET) &amp;lt;/code&amp;gt;(nécessaire pour indiquer l'emplacement des bibliothèques dynamiques), et éxecuter la commande sudo make run pour lancer le jeu (libusb nécessite d'avoir les droits administrateurs).&lt;br /&gt;
&lt;br /&gt;
On peut alors dans le fichier source C spaceinvaders ajouter le fichier d'en-tête associé à la librairie usbfinal.h également disponible dans le git, et utiliser toutes les fonctions crééés dans usbfinal.c. On a finalement créé une fonction displayLivesLEDs qui vient alumer le nombre de LEDs correspondant au nombre de vies restantes, qu'on appelle à chaque fois que le joueur est touché pour éviter des appels inutiles (tant que l'on ne fait rien, les LEDs gardent leur état).&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-18-12.png|thumb|center|500px|Fonction de gestion des LEDs]]&lt;br /&gt;
&lt;br /&gt;
Le résultat final avec implémentation des LEDs :&lt;br /&gt;
&lt;br /&gt;
[[Fichier:TestLEDs.mp4|thumb|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut également vous fournir le .zip du jeu spaceinvaders avec l'implémentation de la manette si besoin (trop volumineux pour le wiki) en nous contactant par mail (remi.boursault@polytech-lille.net).&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
On a fini.&lt;br /&gt;
&lt;br /&gt;
Plus sérieusement, la manette possède les fonctionnalités attendues. Les fichiers LUFA permettent de faire comprendre à un PC qu'elle est constitué d'un Joystick, de deux boutons et de 4 LEDs. On a également réussi à utiliser tous ces éléments dans notre jeu comme il était demandé.&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous pouvez consulter le [https://archives.plil.fr/rboursau/S6-manette.git dépôt Git] des projet. Celui-ci contient le fichier gerber de la manette crée sur KiCad. Les fichiers utilisé pour le programmateur AVR sont dans le dossier projet-info-manette/ProgrammateurAVR. Et les fichiers utilisé pour la manette sont eux dans le dossier projet-info-manette/fichier_lufa_libusb.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5976</id>
		<title>SE3 PSE Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5976"/>
		<updated>2024-06-15T14:07:43Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : /* Ajout des Diodes Électro-Luminescentes (la classe) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet système embarqué de Rémi BOURSAULT et Antoine LECOMTE =&lt;br /&gt;
L'objectif de ce module est de modéliser une manette, puis de la rendre fonctionnelle, pour jouer au jeu Space Invaders qui est codé dans le même module.&lt;br /&gt;
&lt;br /&gt;
Les professeurs avaient préalablement fourni un dossier contenant un modèle de PCB incomplet et différents fichiers &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; pour nous aider à démarrer. &lt;br /&gt;
&lt;br /&gt;
== Création de Manette ==&lt;br /&gt;
La création de la manette commence par la création sur kicad d'un PCB. Nous avons pris celui fourni sur le wiki du projet qui possède tous les composants mais non routés.   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Modélisation PCB ==&lt;br /&gt;
A partir du fichier KiCad fourni, nous avons modélisé un PCB déstiné à être utilisé comme manette. Nous avons donc ajouté des boutons, replacé ces derniers. Grâce au schéma nous avons sélectionné des PIN pour connecter les boutons, ainsi que des LEDs et des résistances que nous avons aussi ajouté.    &lt;br /&gt;
&lt;br /&gt;
Voyant que nous avions encore de l'espace de libre sur notre carte, étant un peu ambitieux et voyant que le procédé n'est pas bien compliqué, nous décidons alors de rajouter quelque boutons et quelques LEDs en plus pour que notre manette soit un peu plus crédibles.  &lt;br /&gt;
&lt;br /&gt;
Il nous a alors fallu rajouté  les dit composants sur notre schéma pour que ceux-ci soient rajoutés automatiquement sur le PCB lors de l'utilisation de la fonction &amp;quot;Update PCB with changes made to schematic&amp;quot;                        &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Image boutons.png|Schéma des boutons&lt;br /&gt;
File:Connecteur ISP.png|Schéma du connecteur ISP&lt;br /&gt;
File:Schéma des LEDs.png|Schéma des LEDs&lt;br /&gt;
File:Connecteur USB.png|Schéma du connecteur USB&lt;br /&gt;
File:Microcontroleur.png|Schéma du microcontrôleur&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Plan pcb.jpg|thumb|center|500px| Ajout du plan de masse et des via]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le routage effectué, nous avons ajouté un plan de masse et ajouté une grande quantité de via à différent endroits de la carte pour que la masse soit bien connectée et éviter les fuites thermiques de certains composants. &lt;br /&gt;
&lt;br /&gt;
Ensuite le fichier a été envoyé pour impression en Chine, grâce au fichier Gerber généré par KiCad que vous pouvez retrouvez sur notre GIT:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Vue3d.jpg|thumb|center|500px| Rendu 3D final de la carte]]&lt;br /&gt;
&lt;br /&gt;
== Programmateur AVR ==&lt;br /&gt;
En attendant de recevoir les cartes, nous nous sommes initiés à la programmation d'un périphérique USB, nous avons utilisé les bibliothèques LUFA et LibUSB pour utilisé un programmateur AVR. Il nous a fallu également modifier des fichiers Descripteurs, en &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; qui permettent, une fois téléversés dans le programmateur, d'être détecter par le PC et que celui-ci  reconnaisse les fonctionnalités du périphérique (InPoint et EndPoint). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Soudure de la carte ==&lt;br /&gt;
Une fois la carte reçu, nous avons pris tous nos composants et avons commencé à les souder. N'ayant que très peu d'expérience dans ce domaine ce n'était pas quelque chose de facile au début. Il nous a fallu un peu d'aide et pas mal de flux pour comprendre comment faire des soudures propres. Une fois le coup de main pris, la fin du montage de la carte était assez rapide. Nous avons donc pu passer à la programmation pour la reconnaissance par l'ordinateur de notre carte.&lt;br /&gt;
[[Fichier:Carte vierge.jpg|thumb|left|500px| Carte vierge]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Carte soudée manette.jpg|thumb|center|500px| Carte avec tous les composants soudés]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois la carte soudée il a fallu pouvoir la configurer pour par la suite pouvoir la programmer. Pour se faire il faut lui injecter un bootloader via son programmateur ISP. Ce qui passe la manette en mode DFU et elle est alors reconnu par l'ordinateur.&lt;br /&gt;
[[Fichier:Manette reconnu en mode dfu.png|thumb|center|500px| Manette reconnu en mode DFU]]&lt;br /&gt;
&lt;br /&gt;
== LUFA de la manette ==&lt;br /&gt;
Après avoir reçu les cartes et terminé de souder les composants nous avons pu attaquer la programmation du microcontrôleur pour pouvoir utiliser la manette. Nous avons procédés étape par étape, en commençant par allumé les LEDs puis réceptionner les signaux des boutons pour finir par fusionner les deux pour avoir une manette fonctionnelle.&lt;br /&gt;
&lt;br /&gt;
=== LEDs ===&lt;br /&gt;
Pour éviter un décalage sur le portF il faut désactiver le JTAG en copiant deux fois cette ligne au niveau de la déclaration des pins : &amp;lt;code&amp;gt;MCUCR |= (1&amp;lt;&amp;lt;JTD);&amp;lt;/code&amp;gt;cela permet d'accéder aux LEDs sur les pins PF4 et PF5.&lt;br /&gt;
Une fois la configuration terminé, nous avons testé nos LEDs avec la fonction suivante : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void blink_leds(void){&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ce qui nous donne ce résultat [[Fichier:Vidéo clignotement LEDs.mp4|center|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également fait la fonction EtatLED dans le fichier USBFinal.c (fichier servant à détecter et communiquer avec la manette par le port USB) qui se trouve dans le dossier libUSB, qui sert à allumer ou éteindre la LED de son choix.&lt;br /&gt;
&lt;br /&gt;
Celle ci utilise le device, handle et configuration descriptor qui ont été définies dans des fonctions précédentes, disponible sur le git dans usbfinal.c ,grandement inspirées de ce qui est donné sur le site de Monsieur Redon, qui sélectionnent la manette et le config descriptor. On aurait dû également sélectionner l'interface dans cette fonction précédente plutôt que de le faire dans EtatLED mais nous nous en sommes rendus compte trop tard.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void EtatLED(int etat, int numLED, libusb_device *manette, libusb_device_handle *handle, struct libusb_config_descriptor *configdesc)&lt;br /&gt;
{&lt;br /&gt;
    int indint=0;/* e.g. première interface */&lt;br /&gt;
    int indalt=0; /* e.g. première alternative */&lt;br /&gt;
    int interface=1; //;configdesc-&amp;gt;interface[indint].altsetting[indalt].bInterfaceNumber;&lt;br /&gt;
    int status=libusb_claim_interface(handle,interface);&lt;br /&gt;
&lt;br /&gt;
    if(status!=0)&lt;br /&gt;
    {&lt;br /&gt;
            printf(&amp;quot;bij\n&amp;quot;);&lt;br /&gt;
        perror(&amp;quot;libusb_claim_interface&amp;quot;);&lt;br /&gt;
        exit(-1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;Interface %d claimed\n&amp;quot;,interface);&lt;br /&gt;
&lt;br /&gt;
    unsigned char data = 0;&lt;br /&gt;
    if(etat){&lt;br /&gt;
       switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x01;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x02;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x03;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x04;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x11;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x12;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x13;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x14;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Exemple d'appel dans le main &amp;lt;code&amp;gt;EtatLED(0,1, manette, handle, configdesc);&amp;lt;/code&amp;gt;. La fonction va éteindre la LED 1.&lt;br /&gt;
&lt;br /&gt;
=== Boutons ===&lt;br /&gt;
Les LEDs étant configuré il est temps de passer à l'utilisation des boutons. Le principe reste globalement le même, on modifie les fichiers descriptors et minimal selon nos besoins. Pour nous faciliter un peu la tâche nous avons pris les fichiers déjà existant pour des joysticks. Nous allons considérer que si un de nos boutons est appuyé cela reviendra au même que si un joystick est dirigée au maximum dans une direction donnée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La modification majeure à faire pour que nos boutons soit détecté était sur la fonction &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool GetNextReport(USB_JoystickReport_Data_t* const ReportData)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t PrevJoyStatus    = 0;&lt;br /&gt;
  static uint8_t PrevButtonStatus = 0;&lt;br /&gt;
  bool           InputChanged     = false;&lt;br /&gt;
&lt;br /&gt;
  /* Clear the report contents */&lt;br /&gt;
  memset(ReportData, 0, sizeof(USB_JoystickReport_Data_t));&lt;br /&gt;
&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINB&amp;gt;&amp;gt;PB4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 1);&lt;br /&gt;
  }&lt;br /&gt;
  /* Return whether the new report is different to the previous report or not */&lt;br /&gt;
  return InputChanged;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cela nous permet de dire que si un bouton que nous avons configuré est enfoncé, on met la direction X ou Y à plus ou moins 100. Nous les avons tester avec jstest-gtk qui permet de visualiser les directions et appuis d'un joystick, tous nos boutons sont reconnus et configurés comme nous le souhaitons.&lt;br /&gt;
&lt;br /&gt;
=== Regroupement des deux ===&lt;br /&gt;
Pour avoir une manette fonctionnelle à 100% la dernière étape est de fusionner les différents fichiers pour les LEDs et pour les boutons pour que tout soit utilisable en même temps. Pour cela il a fallu modifié le fichier Descritors.c, dans la fonction&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor&amp;lt;/code&amp;gt;il faut indiquer le nombre d'interface à deux, puis rajouter une interface. Dans notre cas étant parti du fichier des boutons nous avons rajouté celle des LEDs. Une fois ceci fais, il ne restait plus qu'à rajouter les fonctions de chaque partie dans un même fichier .c et nous avons fini la configuration et la programmation de notre manette.&lt;br /&gt;
&lt;br /&gt;
== Implémentation dans le Space Invaders ==&lt;br /&gt;
&lt;br /&gt;
=== Ajout des boutons ===&lt;br /&gt;
On va dans cette partie utiliser tout ce qui a été implémenté pour rendre la manette utilisable avec le jeu Space Invaders codé en parallèle. &lt;br /&gt;
&lt;br /&gt;
D'abord, on a, grâce au tutoriel fourni, ajouté l'utilisation des boutons avec la librairie SDL. Il nous a suffit de créer une fonction qui d'abord initialise notre manette en détectant le nombre de Joystick que celle ci possède. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:JoystickInit.png|thumb|center|700px|Initialisation de la manette]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite, on a créé une fonction handleControllerReformed qui pendant que le jeu tourne vient récupérer les valeurs des axes et des boutons de la manette en utilisant le pointeur initialisé par JoystickInitialisation.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-21-04.png|thumb|center|700px|Fonction récupérant la valeur des axes et des boutons]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintenant que nous avons les valeurs des boutons et des axes à chaque instant, nous pouvons les utiliser comme input dans nos programmes pour, par exemple, les déplacements du vaisseau et le lancer de missiles. La valeur des axes est toujours maximale car notre joystick est en fait 4 boutons interprétés comme un Joystick, c'est pour cela qu'on vient comparer leurs valeurs aux valeurs maximales envoyés par un Joystick (32767 et -32768). Les boutons sont des booléens qui retournent 1 quand ils sont à l'état bas et 0 à l'état haut.[[Fichier:Screenshot from 2024-06-10 22-21-55.png|thumb|left|700px|Fonction permettant de faire se déplacer le joueur au clavier ou à la manette]]&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-22-34.png|thumb|right|700px|Fonction permettant d'ajouter un missile si l'on appuie sur e ou sur un bouton.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vous pouvez voir le résultat de l'implémentation totale des boutons en cliquant [https://www.youtube.com/watch?v=TwVJ7-ZU8_8&amp;amp;ab_channel=R%C3%A9mi sur ce lien].&lt;br /&gt;
&lt;br /&gt;
=== Ajout des Diodes Électro-Luminescentes (la classe) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour utiliser les LEDs, c'est légèrement plus compliqué. Le code que nous avions fait précédemment dans usbfinal.c, notamment EtatLED, va nous être utile. On va transformer ce fichier usbfinal.c en une librairie dynamique en .so. Grâce à ça, on va pouvoir réutiliser la fonction EtatLED dans notre programme pour le jeu et l'appeler directement dans des fonctions.&lt;br /&gt;
&lt;br /&gt;
Pour faire cela, on va compiler en utilisant des options particulières pour gcc. La commande est &amp;lt;code&amp;gt;gcc -fPIC -shared -o libusbfinal.so usbfinal.c -lusb-1.0&amp;lt;/code&amp;gt;. L'option &amp;lt;code&amp;gt;-fPIC&amp;lt;/code&amp;gt; permet de génerer du code indépendant de l'emplacement mémoire auquel il est chargé. L'option &amp;lt;code&amp;gt;-shared&amp;lt;/code&amp;gt; permet de génerer une bibliotèque partager plutôt qu'un executable, et &amp;lt;code&amp;gt;-lusb-1.0&amp;lt;/code&amp;gt; permet d'indiquer au compilateur qu'on utilise libusb 1.0 dans ce fichier C.&lt;br /&gt;
&lt;br /&gt;
On se retrouve alors avec un fichier .so disponible dans le git (dans les fichiers LUFA, répértoire libUSB, le chemin précis est spécifié dans le readme).&lt;br /&gt;
&lt;br /&gt;
Ce fichier est la bibliothèque dynamique qu'on peut appeler dans notre makefile.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-19-26.png|thumb|center|700px|Makefile pour le jeu, adapté à l'utilisation de la manette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit qu'il faut rajouter dans l'éxecution la commande &amp;lt;code&amp;gt;export LD_LIBRARY_PATH=./src/include:$$LD_LIBRARY_PATH &amp;amp;&amp;amp; ./$(TARGET) &amp;lt;/code&amp;gt;(nécessaire pour indiquer l'emplacement des bibliothèques dynamiques), et éxecuter la commande sudo make run pour lancer le jeu (libusb nécessite d'avoir les droits administrateurs).&lt;br /&gt;
&lt;br /&gt;
On peut alors dans le fichier source C spaceinvaders ajouter le fichier d'en-tête associé à la librairie usbfinal.h également disponible dans le git, et utiliser toutes les fonctions crééés dans usbfinal.c. On a finalement créé une fonction displayLivesLEDs qui vient alumer le nombre de LEDs correspondant au nombre de vies restantes, qu'on appelle à chaque fois que le joueur est touché pour éviter des appels inutiles (tant que l'on ne fait rien, les LEDs gardent leur état).&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-18-12.png|thumb|center|500px|Fonction de gestion des LEDs]]&lt;br /&gt;
&lt;br /&gt;
Le résultat final avec implémentation des LEDs :&lt;br /&gt;
&lt;br /&gt;
[[Fichier:TestLEDs.mp4|thumb|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut également vous fournir le .zip du jeu spaceinvaders avec l'implémentation de la manette si besoin (trop volumineux pour le wiki) en nous contactant par mail (remi.boursault@polytech-lille.net).&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
On a fini.&lt;br /&gt;
&lt;br /&gt;
Plus sérieusement, la manette possède les fonctionnalités attendues. Les fichiers LUFA permettent de faire comprendre à un PC qu'elle est constitué d'un Joystick, de deux boutons et de 4 LEDs. On a également réussi à utiliser tous ces éléments dans notre jeu comme il était demandé.&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous pouvez consulter le [https://archives.plil.fr/rboursau/S6-manette.git dépôt Git] des projet. Celui-ci contient le fichier gerber de la manette crée sur KiCad. Les fichiers utilisé pour le programmateur AVR sont dans le dossier projet-info-manette/ProgrammateurAVR. Et les fichiers utilisé pour la manette sont eux dans le dossier projet-info-manette/fichier_lufa_libusb.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5975</id>
		<title>SE3 PSE Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5975"/>
		<updated>2024-06-15T14:07:22Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet système embarqué de Rémi BOURSAULT et Antoine LECOMTE =&lt;br /&gt;
L'objectif de ce module est de modéliser une manette, puis de la rendre fonctionnelle, pour jouer au jeu Space Invaders qui est codé dans le même module.&lt;br /&gt;
&lt;br /&gt;
Les professeurs avaient préalablement fourni un dossier contenant un modèle de PCB incomplet et différents fichiers &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; pour nous aider à démarrer. &lt;br /&gt;
&lt;br /&gt;
== Création de Manette ==&lt;br /&gt;
La création de la manette commence par la création sur kicad d'un PCB. Nous avons pris celui fourni sur le wiki du projet qui possède tous les composants mais non routés.   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Modélisation PCB ==&lt;br /&gt;
A partir du fichier KiCad fourni, nous avons modélisé un PCB déstiné à être utilisé comme manette. Nous avons donc ajouté des boutons, replacé ces derniers. Grâce au schéma nous avons sélectionné des PIN pour connecter les boutons, ainsi que des LEDs et des résistances que nous avons aussi ajouté.    &lt;br /&gt;
&lt;br /&gt;
Voyant que nous avions encore de l'espace de libre sur notre carte, étant un peu ambitieux et voyant que le procédé n'est pas bien compliqué, nous décidons alors de rajouter quelque boutons et quelques LEDs en plus pour que notre manette soit un peu plus crédibles.  &lt;br /&gt;
&lt;br /&gt;
Il nous a alors fallu rajouté  les dit composants sur notre schéma pour que ceux-ci soient rajoutés automatiquement sur le PCB lors de l'utilisation de la fonction &amp;quot;Update PCB with changes made to schematic&amp;quot;                        &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Image boutons.png|Schéma des boutons&lt;br /&gt;
File:Connecteur ISP.png|Schéma du connecteur ISP&lt;br /&gt;
File:Schéma des LEDs.png|Schéma des LEDs&lt;br /&gt;
File:Connecteur USB.png|Schéma du connecteur USB&lt;br /&gt;
File:Microcontroleur.png|Schéma du microcontrôleur&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Plan pcb.jpg|thumb|center|500px| Ajout du plan de masse et des via]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le routage effectué, nous avons ajouté un plan de masse et ajouté une grande quantité de via à différent endroits de la carte pour que la masse soit bien connectée et éviter les fuites thermiques de certains composants. &lt;br /&gt;
&lt;br /&gt;
Ensuite le fichier a été envoyé pour impression en Chine, grâce au fichier Gerber généré par KiCad que vous pouvez retrouvez sur notre GIT:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Vue3d.jpg|thumb|center|500px| Rendu 3D final de la carte]]&lt;br /&gt;
&lt;br /&gt;
== Programmateur AVR ==&lt;br /&gt;
En attendant de recevoir les cartes, nous nous sommes initiés à la programmation d'un périphérique USB, nous avons utilisé les bibliothèques LUFA et LibUSB pour utilisé un programmateur AVR. Il nous a fallu également modifier des fichiers Descripteurs, en &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; qui permettent, une fois téléversés dans le programmateur, d'être détecter par le PC et que celui-ci  reconnaisse les fonctionnalités du périphérique (InPoint et EndPoint). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Soudure de la carte ==&lt;br /&gt;
Une fois la carte reçu, nous avons pris tous nos composants et avons commencé à les souder. N'ayant que très peu d'expérience dans ce domaine ce n'était pas quelque chose de facile au début. Il nous a fallu un peu d'aide et pas mal de flux pour comprendre comment faire des soudures propres. Une fois le coup de main pris, la fin du montage de la carte était assez rapide. Nous avons donc pu passer à la programmation pour la reconnaissance par l'ordinateur de notre carte.&lt;br /&gt;
[[Fichier:Carte vierge.jpg|thumb|left|500px| Carte vierge]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Carte soudée manette.jpg|thumb|center|500px| Carte avec tous les composants soudés]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois la carte soudée il a fallu pouvoir la configurer pour par la suite pouvoir la programmer. Pour se faire il faut lui injecter un bootloader via son programmateur ISP. Ce qui passe la manette en mode DFU et elle est alors reconnu par l'ordinateur.&lt;br /&gt;
[[Fichier:Manette reconnu en mode dfu.png|thumb|center|500px| Manette reconnu en mode DFU]]&lt;br /&gt;
&lt;br /&gt;
== LUFA de la manette ==&lt;br /&gt;
Après avoir reçu les cartes et terminé de souder les composants nous avons pu attaquer la programmation du microcontrôleur pour pouvoir utiliser la manette. Nous avons procédés étape par étape, en commençant par allumé les LEDs puis réceptionner les signaux des boutons pour finir par fusionner les deux pour avoir une manette fonctionnelle.&lt;br /&gt;
&lt;br /&gt;
=== LEDs ===&lt;br /&gt;
Pour éviter un décalage sur le portF il faut désactiver le JTAG en copiant deux fois cette ligne au niveau de la déclaration des pins : &amp;lt;code&amp;gt;MCUCR |= (1&amp;lt;&amp;lt;JTD);&amp;lt;/code&amp;gt;cela permet d'accéder aux LEDs sur les pins PF4 et PF5.&lt;br /&gt;
Une fois la configuration terminé, nous avons testé nos LEDs avec la fonction suivante : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void blink_leds(void){&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ce qui nous donne ce résultat [[Fichier:Vidéo clignotement LEDs.mp4|center|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également fait la fonction EtatLED dans le fichier USBFinal.c (fichier servant à détecter et communiquer avec la manette par le port USB) qui se trouve dans le dossier libUSB, qui sert à allumer ou éteindre la LED de son choix.&lt;br /&gt;
&lt;br /&gt;
Celle ci utilise le device, handle et configuration descriptor qui ont été définies dans des fonctions précédentes, disponible sur le git dans usbfinal.c ,grandement inspirées de ce qui est donné sur le site de Monsieur Redon, qui sélectionnent la manette et le config descriptor. On aurait dû également sélectionner l'interface dans cette fonction précédente plutôt que de le faire dans EtatLED mais nous nous en sommes rendus compte trop tard.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void EtatLED(int etat, int numLED, libusb_device *manette, libusb_device_handle *handle, struct libusb_config_descriptor *configdesc)&lt;br /&gt;
{&lt;br /&gt;
    int indint=0;/* e.g. première interface */&lt;br /&gt;
    int indalt=0; /* e.g. première alternative */&lt;br /&gt;
    int interface=1; //;configdesc-&amp;gt;interface[indint].altsetting[indalt].bInterfaceNumber;&lt;br /&gt;
    int status=libusb_claim_interface(handle,interface);&lt;br /&gt;
&lt;br /&gt;
    if(status!=0)&lt;br /&gt;
    {&lt;br /&gt;
            printf(&amp;quot;bij\n&amp;quot;);&lt;br /&gt;
        perror(&amp;quot;libusb_claim_interface&amp;quot;);&lt;br /&gt;
        exit(-1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;Interface %d claimed\n&amp;quot;,interface);&lt;br /&gt;
&lt;br /&gt;
    unsigned char data = 0;&lt;br /&gt;
    if(etat){&lt;br /&gt;
       switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x01;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x02;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x03;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x04;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x11;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x12;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x13;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x14;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Exemple d'appel dans le main &amp;lt;code&amp;gt;EtatLED(0,1, manette, handle, configdesc);&amp;lt;/code&amp;gt;. La fonction va éteindre la LED 1.&lt;br /&gt;
&lt;br /&gt;
=== Boutons ===&lt;br /&gt;
Les LEDs étant configuré il est temps de passer à l'utilisation des boutons. Le principe reste globalement le même, on modifie les fichiers descriptors et minimal selon nos besoins. Pour nous faciliter un peu la tâche nous avons pris les fichiers déjà existant pour des joysticks. Nous allons considérer que si un de nos boutons est appuyé cela reviendra au même que si un joystick est dirigée au maximum dans une direction donnée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La modification majeure à faire pour que nos boutons soit détecté était sur la fonction &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool GetNextReport(USB_JoystickReport_Data_t* const ReportData)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t PrevJoyStatus    = 0;&lt;br /&gt;
  static uint8_t PrevButtonStatus = 0;&lt;br /&gt;
  bool           InputChanged     = false;&lt;br /&gt;
&lt;br /&gt;
  /* Clear the report contents */&lt;br /&gt;
  memset(ReportData, 0, sizeof(USB_JoystickReport_Data_t));&lt;br /&gt;
&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINB&amp;gt;&amp;gt;PB4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 1);&lt;br /&gt;
  }&lt;br /&gt;
  /* Return whether the new report is different to the previous report or not */&lt;br /&gt;
  return InputChanged;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cela nous permet de dire que si un bouton que nous avons configuré est enfoncé, on met la direction X ou Y à plus ou moins 100. Nous les avons tester avec jstest-gtk qui permet de visualiser les directions et appuis d'un joystick, tous nos boutons sont reconnus et configurés comme nous le souhaitons.&lt;br /&gt;
&lt;br /&gt;
=== Regroupement des deux ===&lt;br /&gt;
Pour avoir une manette fonctionnelle à 100% la dernière étape est de fusionner les différents fichiers pour les LEDs et pour les boutons pour que tout soit utilisable en même temps. Pour cela il a fallu modifié le fichier Descritors.c, dans la fonction&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor&amp;lt;/code&amp;gt;il faut indiquer le nombre d'interface à deux, puis rajouter une interface. Dans notre cas étant parti du fichier des boutons nous avons rajouté celle des LEDs. Une fois ceci fais, il ne restait plus qu'à rajouter les fonctions de chaque partie dans un même fichier .c et nous avons fini la configuration et la programmation de notre manette.&lt;br /&gt;
&lt;br /&gt;
== Implémentation dans le Space Invaders ==&lt;br /&gt;
&lt;br /&gt;
=== Ajout des boutons ===&lt;br /&gt;
On va dans cette partie utiliser tout ce qui a été implémenté pour rendre la manette utilisable avec le jeu Space Invaders codé en parallèle. &lt;br /&gt;
&lt;br /&gt;
D'abord, on a, grâce au tutoriel fourni, ajouté l'utilisation des boutons avec la librairie SDL. Il nous a suffit de créer une fonction qui d'abord initialise notre manette en détectant le nombre de Joystick que celle ci possède. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:JoystickInit.png|thumb|center|700px|Initialisation de la manette]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite, on a créé une fonction handleControllerReformed qui pendant que le jeu tourne vient récupérer les valeurs des axes et des boutons de la manette en utilisant le pointeur initialisé par JoystickInitialisation.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-21-04.png|thumb|center|700px|Fonction récupérant la valeur des axes et des boutons]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintenant que nous avons les valeurs des boutons et des axes à chaque instant, nous pouvons les utiliser comme input dans nos programmes pour, par exemple, les déplacements du vaisseau et le lancer de missiles. La valeur des axes est toujours maximale car notre joystick est en fait 4 boutons interprétés comme un Joystick, c'est pour cela qu'on vient comparer leurs valeurs aux valeurs maximales envoyés par un Joystick (32767 et -32768). Les boutons sont des booléens qui retournent 1 quand ils sont à l'état bas et 0 à l'état haut.[[Fichier:Screenshot from 2024-06-10 22-21-55.png|thumb|left|700px|Fonction permettant de faire se déplacer le joueur au clavier ou à la manette]]&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-22-34.png|thumb|right|700px|Fonction permettant d'ajouter un missile si l'on appuie sur e ou sur un bouton.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vous pouvez voir le résultat de l'implémentation totale des boutons en cliquant [https://www.youtube.com/watch?v=TwVJ7-ZU8_8&amp;amp;ab_channel=R%C3%A9mi sur ce lien].&lt;br /&gt;
&lt;br /&gt;
=== Ajout des Diodes Électro-Luminescentes (la classe) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour utiliser les LEDs, c'est légèrement plus compliqué. Le code que nous avions fait précédemment dans usbfinal.c, notamment EtatLED, va nous être utile. On va transformer ce fichier usbfinal.c en une librairie dynamique en .so. Grâce à ça, on va pouvoir réutiliser la fonction EtatLED dans notre programme pour le jeu et l'appeler directement dans des fonctions.&lt;br /&gt;
&lt;br /&gt;
Pour faire cela, on va compiler en utilisant des options particulières pour gcc. La commande est &amp;lt;code&amp;gt;gcc -fPIC -shared -o libusbfinal.so usbfinal.c -lusb-1.0&amp;lt;/code&amp;gt;. L'option &amp;lt;code&amp;gt;-fPIC&amp;lt;/code&amp;gt; permet de génerer du code indépendant de l'emplacement mémoire auquel il est chargé. L'option &amp;lt;code&amp;gt;-shared&amp;lt;/code&amp;gt; permet de génerer une bibliotèque partager plutôt qu'un executable, et &amp;lt;code&amp;gt;-lusb-1.0&amp;lt;/code&amp;gt; permet d'indiquer au compilateur qu'on utilise libusb 1.0 dans ce fichier C.&lt;br /&gt;
&lt;br /&gt;
On se retrouve alors avec un fichier .so disponible dans le git (dans les fichiers LUFA, répértoire libUSB, le chemin précis est spécifié dans le readme).&lt;br /&gt;
&lt;br /&gt;
Ce fichier est la bibliothèque dynamique qu'on peut appeler dans notre makefile.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-19-26.png|thumb|center|700px|Makefile pour le jeu, adapté à l'utilisation de la manette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit qu'il faut rajouter dans l'éxecution la commande &amp;lt;code&amp;gt;export LD_LIBRARY_PATH=./src/include:$$LD_LIBRARY_PATH &amp;amp;&amp;amp; ./$(TARGET) &amp;lt;/code&amp;gt;(nécessaire pour indiquer l'emplacement des bibliothèques dynamiques), et éxecuter la commande sudo make run pour lancer le jeu (libusb nécessite d'avoir les droits administrateurs).&lt;br /&gt;
&lt;br /&gt;
On peut alors dans le fichier source C spaceinvaders ajouter le fichier d'en-tête associé à la librairie usbfinal.h également disponible dans le git, et utiliser toutes les fonctions crééés dans usbfinal.c. On a finalement créé une fonction displayLivesLEDs qui vient alumer le nombre de LEDs correspondant au nombre de vies restantes, qu'on appelle à chaque fois que le joueur est touché pour éviter des appels inutiles (tant que l'on ne fait rien, les LEDs gardent leur état).&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-18-12.png|thumb|center|500px|Fonction de gestion des LEDs]]&lt;br /&gt;
&lt;br /&gt;
Le résultat final avec implémentation des LEDs :&lt;br /&gt;
&lt;br /&gt;
[[Fichier:TestLEDs.mp4|thumb|center|700px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut également vous fournir le .zip du jeu spaceinvaders avec l'implémentation de la manette si besoin (trop volumineux pour le wiki) en nous contactant par mail (remi.boursault@polytech-lille.net).&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
On a fini.&lt;br /&gt;
&lt;br /&gt;
Plus sérieusement, la manette possède les fonctionnalités attendues. Les fichiers LUFA permettent de faire comprendre à un PC qu'elle est constitué d'un Joystick, de deux boutons et de 4 LEDs. On a également réussi à utiliser tous ces éléments dans notre jeu comme il était demandé.&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous pouvez consulter le [https://archives.plil.fr/rboursau/S6-manette.git dépôt Git] des projet. Celui-ci contient le fichier gerber de la manette crée sur KiCad. Les fichiers utilisé pour le programmateur AVR sont dans le dossier projet-info-manette/ProgrammateurAVR. Et les fichiers utilisé pour la manette sont eux dans le dossier projet-info-manette/fichier_lufa_libusb.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5974</id>
		<title>SE3 PSE Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5974"/>
		<updated>2024-06-15T14:06:44Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet système embarqué de Rémi BOURSAULT et Antoine LECOMTE =&lt;br /&gt;
L'objectif de ce module est de modéliser une manette, puis de la rendre fonctionnelle, pour jouer au jeu Space Invaders qui est codé dans le même module.&lt;br /&gt;
&lt;br /&gt;
Les professeurs avaient préalablement fourni un dossier contenant un modèle de PCB incomplet et différents fichiers &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; pour nous aider à démarrer. &lt;br /&gt;
&lt;br /&gt;
== Création de Manette ==&lt;br /&gt;
La création de la manette commence par la création sur kicad d'un PCB. Nous avons pris celui fourni sur le wiki du projet qui possède tous les composants mais non routés.   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Modélisation PCB ==&lt;br /&gt;
A partir du fichier KiCad fourni, nous avons modélisé un PCB déstiné à être utilisé comme manette. Nous avons donc ajouté des boutons, replacé ces derniers. Grâce au schéma nous avons sélectionné des PIN pour connecter les boutons, ainsi que des LEDs et des résistances que nous avons aussi ajouté.    &lt;br /&gt;
&lt;br /&gt;
Voyant que nous avions encore de l'espace de libre sur notre carte, étant un peu ambitieux et voyant que le procédé n'est pas bien compliqué, nous décidons alors de rajouter quelque boutons et quelques LEDs en plus pour que notre manette soit un peu plus crédibles.  &lt;br /&gt;
&lt;br /&gt;
Il nous a alors fallu rajouté  les dit composants sur notre schéma pour que ceux-ci soient rajoutés automatiquement sur le PCB lors de l'utilisation de la fonction &amp;quot;Update PCB with changes made to schematic&amp;quot;                        &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Image boutons.png|Schéma des boutons&lt;br /&gt;
File:Connecteur ISP.png|Schéma du connecteur ISP&lt;br /&gt;
File:Schéma des LEDs.png|Schéma des LEDs&lt;br /&gt;
File:Connecteur USB.png|Schéma du connecteur USB&lt;br /&gt;
File:Microcontroleur.png|Schéma du microcontrôleur&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Plan pcb.jpg|thumb|center|500px| Ajout du plan de masse et des via]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le routage effectué, nous avons ajouté un plan de masse et ajouté une grande quantité de via à différent endroits de la carte pour que la masse soit bien connectée et éviter les fuites thermiques de certains composants. &lt;br /&gt;
&lt;br /&gt;
Ensuite le fichier a été envoyé pour impression en Chine, grâce au fichier Gerber généré par KiCad que vous pouvez retrouvez sur notre GIT:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Vue3d.jpg|thumb|center|500px| Rendu 3D final de la carte]]&lt;br /&gt;
&lt;br /&gt;
== Programmateur AVR ==&lt;br /&gt;
En attendant de recevoir les cartes, nous nous sommes initiés à la programmation d'un périphérique USB, nous avons utilisé les bibliothèques LUFA et LibUSB pour utilisé un programmateur AVR. Il nous a fallu également modifier des fichiers Descripteurs, en &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; qui permettent, une fois téléversés dans le programmateur, d'être détecter par le PC et que celui-ci  reconnaisse les fonctionnalités du périphérique (InPoint et EndPoint). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Soudure de la carte ==&lt;br /&gt;
Une fois la carte reçu, nous avons pris tous nos composants et avons commencé à les souder. N'ayant que très peu d'expérience dans ce domaine ce n'était pas quelque chose de facile au début. Il nous a fallu un peu d'aide et pas mal de flux pour comprendre comment faire des soudures propres. Une fois le coup de main pris, la fin du montage de la carte était assez rapide. Nous avons donc pu passer à la programmation pour la reconnaissance par l'ordinateur de notre carte.&lt;br /&gt;
[[Fichier:Carte vierge.jpg|thumb|left|500px| Carte vierge]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Carte soudée manette.jpg|thumb|center|500px| Carte avec tous les composants soudés]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois la carte soudée il a fallu pouvoir la configurer pour par la suite pouvoir la programmer. Pour se faire il faut lui injecter un bootloader via son programmateur ISP. Ce qui passe la manette en mode DFU et elle est alors reconnu par l'ordinateur.&lt;br /&gt;
[[Fichier:Manette reconnu en mode dfu.png|thumb|center|500px| Manette reconnu en mode DFU]]&lt;br /&gt;
&lt;br /&gt;
== LUFA de la manette ==&lt;br /&gt;
Après avoir reçu les cartes et terminé de souder les composants nous avons pu attaquer la programmation du microcontrôleur pour pouvoir utiliser la manette. Nous avons procédés étape par étape, en commençant par allumé les LEDs puis réceptionner les signaux des boutons pour finir par fusionner les deux pour avoir une manette fonctionnelle.&lt;br /&gt;
&lt;br /&gt;
=== LEDs ===&lt;br /&gt;
Pour éviter un décalage sur le portF il faut désactiver le JTAG en copiant deux fois cette ligne au niveau de la déclaration des pins : &amp;lt;code&amp;gt;MCUCR |= (1&amp;lt;&amp;lt;JTD);&amp;lt;/code&amp;gt;cela permet d'accéder aux LEDs sur les pins PF4 et PF5.&lt;br /&gt;
Une fois la configuration terminé, nous avons testé nos LEDs avec la fonction suivante : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void blink_leds(void){&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ce qui nous donne ce résultat [[Fichier:Vidéo clignotement LEDs.mp4|center|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également fait la fonction EtatLED dans le fichier USBFinal.c (fichier servant à détecter et communiquer avec la manette par le port USB) qui se trouve dans le dossier libUSB, qui sert à allumer ou éteindre la LED de son choix.&lt;br /&gt;
&lt;br /&gt;
Celle ci utilise le device, handle et configuration descriptor qui ont été définies dans des fonctions précédentes, disponible sur le git dans usbfinal.c ,grandement inspirées de ce qui est donné sur le site de Monsieur Redon, qui sélectionnent la manette et le config descriptor. On aurait dû également sélectionner l'interface dans cette fonction précédente plutôt que de le faire dans EtatLED mais nous nous en sommes rendus compte trop tard.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void EtatLED(int etat, int numLED, libusb_device *manette, libusb_device_handle *handle, struct libusb_config_descriptor *configdesc)&lt;br /&gt;
{&lt;br /&gt;
    int indint=0;/* e.g. première interface */&lt;br /&gt;
    int indalt=0; /* e.g. première alternative */&lt;br /&gt;
    int interface=1; //;configdesc-&amp;gt;interface[indint].altsetting[indalt].bInterfaceNumber;&lt;br /&gt;
    int status=libusb_claim_interface(handle,interface);&lt;br /&gt;
&lt;br /&gt;
    if(status!=0)&lt;br /&gt;
    {&lt;br /&gt;
            printf(&amp;quot;bij\n&amp;quot;);&lt;br /&gt;
        perror(&amp;quot;libusb_claim_interface&amp;quot;);&lt;br /&gt;
        exit(-1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;Interface %d claimed\n&amp;quot;,interface);&lt;br /&gt;
&lt;br /&gt;
    unsigned char data = 0;&lt;br /&gt;
    if(etat){&lt;br /&gt;
       switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x01;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x02;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x03;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x04;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x11;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x12;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x13;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x14;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Exemple d'appel dans le main &amp;lt;code&amp;gt;EtatLED(0,1, manette, handle, configdesc);&amp;lt;/code&amp;gt;. La fonction va éteindre la LED 1.&lt;br /&gt;
&lt;br /&gt;
=== Boutons ===&lt;br /&gt;
Les LEDs étant configuré il est temps de passer à l'utilisation des boutons. Le principe reste globalement le même, on modifie les fichiers descriptors et minimal selon nos besoins. Pour nous faciliter un peu la tâche nous avons pris les fichiers déjà existant pour des joysticks. Nous allons considérer que si un de nos boutons est appuyé cela reviendra au même que si un joystick est dirigée au maximum dans une direction donnée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La modification majeure à faire pour que nos boutons soit détecté était sur la fonction &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool GetNextReport(USB_JoystickReport_Data_t* const ReportData)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t PrevJoyStatus    = 0;&lt;br /&gt;
  static uint8_t PrevButtonStatus = 0;&lt;br /&gt;
  bool           InputChanged     = false;&lt;br /&gt;
&lt;br /&gt;
  /* Clear the report contents */&lt;br /&gt;
  memset(ReportData, 0, sizeof(USB_JoystickReport_Data_t));&lt;br /&gt;
&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINB&amp;gt;&amp;gt;PB4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 1);&lt;br /&gt;
  }&lt;br /&gt;
  /* Return whether the new report is different to the previous report or not */&lt;br /&gt;
  return InputChanged;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cela nous permet de dire que si un bouton que nous avons configuré est enfoncé, on met la direction X ou Y à plus ou moins 100. Nous les avons tester avec jstest-gtk qui permet de visualiser les directions et appuis d'un joystick, tous nos boutons sont reconnus et configurés comme nous le souhaitons.&lt;br /&gt;
&lt;br /&gt;
=== Regroupement des deux ===&lt;br /&gt;
Pour avoir une manette fonctionnelle à 100% la dernière étape est de fusionner les différents fichiers pour les LEDs et pour les boutons pour que tout soit utilisable en même temps. Pour cela il a fallu modifié le fichier Descritors.c, dans la fonction&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor&amp;lt;/code&amp;gt;il faut indiquer le nombre d'interface à deux, puis rajouter une interface. Dans notre cas étant parti du fichier des boutons nous avons rajouté celle des LEDs. Une fois ceci fais, il ne restait plus qu'à rajouter les fonctions de chaque partie dans un même fichier .c et nous avons fini la configuration et la programmation de notre manette.&lt;br /&gt;
&lt;br /&gt;
== Implémentation dans le Space Invaders ==&lt;br /&gt;
&lt;br /&gt;
=== Ajout des boutons ===&lt;br /&gt;
On va dans cette partie utiliser tout ce qui a été implémenté pour rendre la manette utilisable avec le jeu Space Invaders codé en parallèle. &lt;br /&gt;
&lt;br /&gt;
D'abord, on a, grâce au tutoriel fourni, ajouté l'utilisation des boutons avec la librairie SDL. Il nous a suffit de créer une fonction qui d'abord initialise notre manette en détectant le nombre de Joystick que celle ci possède. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:JoystickInit.png|thumb|center|700px|Initialisation de la manette]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite, on a créé une fonction handleControllerReformed qui pendant que le jeu tourne vient récupérer les valeurs des axes et des boutons de la manette en utilisant le pointeur initialisé par JoystickInitialisation.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-21-04.png|thumb|center|700px|Fonction récupérant la valeur des axes et des boutons]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintenant que nous avons les valeurs des boutons et des axes à chaque instant, nous pouvons les utiliser comme input dans nos programmes pour, par exemple, les déplacements du vaisseau et le lancer de missiles. La valeur des axes est toujours maximale car notre joystick est en fait 4 boutons interprétés comme un Joystick, c'est pour cela qu'on vient comparer leurs valeurs aux valeurs maximales envoyés par un Joystick (32767 et -32768). Les boutons sont des booléens qui retournent 1 quand ils sont à l'état bas et 0 à l'état haut.[[Fichier:Screenshot from 2024-06-10 22-21-55.png|thumb|left|700px|Fonction permettant de faire se déplacer le joueur au clavier ou à la manette]]&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-22-34.png|thumb|right|700px|Fonction permettant d'ajouter un missile si l'on appuie sur e ou sur un bouton.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vous pouvez voir le résultat de l'implémentation totale des boutons en cliquant [https://www.youtube.com/watch?v=TwVJ7-ZU8_8&amp;amp;ab_channel=R%C3%A9mi sur ce lien].&lt;br /&gt;
&lt;br /&gt;
=== Ajout des Diodes Électro-Luminescentes (la classe) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour utiliser les LEDs, c'est légèrement plus compliqué. Le code que nous avions fait précédemment dans usbfinal.c, notamment EtatLED, va nous être utile. On va transformer ce fichier usbfinal.c en une librairie dynamique en .so. Grâce à ça, on va pouvoir réutiliser la fonction EtatLED dans notre programme pour le jeu et l'appeler directement dans des fonctions.&lt;br /&gt;
&lt;br /&gt;
Pour faire cela, on va compiler en utilisant des options particulières pour gcc. La commande est &amp;lt;code&amp;gt;gcc -fPIC -shared -o libusbfinal.so usbfinal.c -lusb-1.0&amp;lt;/code&amp;gt;. L'option &amp;lt;code&amp;gt;-fPIC&amp;lt;/code&amp;gt; permet de génerer du code indépendant de l'emplacement mémoire auquel il est chargé. L'option &amp;lt;code&amp;gt;-shared&amp;lt;/code&amp;gt; permet de génerer une bibliotèque partager plutôt qu'un executable, et &amp;lt;code&amp;gt;-lusb-1.0&amp;lt;/code&amp;gt; permet d'indiquer au compilateur qu'on utilise libusb 1.0 dans ce fichier C.&lt;br /&gt;
&lt;br /&gt;
On se retrouve alors avec un fichier .so disponible dans le git (dans les fichiers LUFA, répértoire libUSB, le chemin précis est spécifié dans le readme).&lt;br /&gt;
&lt;br /&gt;
Ce fichier est la bibliothèque dynamique qu'on peut appeler dans notre makefile.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-19-26.png|thumb|center|700px|Makefile pour le jeu, adapté à l'utilisation de la manette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit qu'il faut rajouter dans l'éxecution la commande &amp;lt;code&amp;gt;export LD_LIBRARY_PATH=./src/include:$$LD_LIBRARY_PATH &amp;amp;&amp;amp; ./$(TARGET) &amp;lt;/code&amp;gt;(nécessaire pour indiquer l'emplacement des bibliothèques dynamiques), et éxecuter la commande sudo make run pour lancer le jeu (libusb nécessite d'avoir les droits administrateurs).&lt;br /&gt;
&lt;br /&gt;
On peut alors dans le fichier source C spaceinvaders ajouter le fichier d'en-tête associé à la librairie usbfinal.h également disponible dans le git, et utiliser toutes les fonctions crééés dans usbfinal.c. On a finalement créé une fonction displayLivesLEDs qui vient alumer le nombre de LEDs correspondant au nombre de vies restantes, qu'on appelle à chaque fois que le joueur est touché pour éviter des appels inutiles (tant que l'on ne fait rien, les LEDs gardent leur état).&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-18-12.png|thumb|center|500px|Fonction de gestion des LEDs]]&lt;br /&gt;
&lt;br /&gt;
Le résultat final avec implémentation des LEDs :&lt;br /&gt;
&lt;br /&gt;
[[Fichier:TestLEDs.mp4|thumb|center|700px|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut également vous fournir le .zip du jeu spaceinvaders avec l'implémentation de la manette si besoin (trop volumineux pour le wiki) en nous contactant par mail (remi.boursault@polytech-lille.net).&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
On a fini.&lt;br /&gt;
&lt;br /&gt;
Plus sérieusement, la manette possède les fonctionnalités attendues. Les fichiers LUFA permettent de faire comprendre à un PC qu'elle est constitué d'un Joystick, de deux boutons et de 4 LEDs. On a également réussi à utiliser tous ces éléments dans notre jeu comme il était demandé.&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous pouvez consulter le [https://archives.plil.fr/rboursau/S6-manette.git dépôt Git] des projet. Celui-ci contient le fichier gerber de la manette crée sur KiCad. Les fichiers utilisé pour le programmateur AVR sont dans le dossier projet-info-manette/ProgrammateurAVR. Et les fichiers utilisé pour la manette sont eux dans le dossier projet-info-manette/fichier_lufa_libusb.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5973</id>
		<title>SE3 PSE Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3_PSE_Binome2023-5&amp;diff=5973"/>
		<updated>2024-06-15T14:04:02Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet système embarqué de Rémi BOURSAULT et Antoine LECOMTE =&lt;br /&gt;
L'objectif de ce module est de modéliser une manette, puis de la rendre fonctionnelle, pour jouer au jeu Space Invaders qui est codé dans le même module.&lt;br /&gt;
&lt;br /&gt;
Les professeurs avaient préalablement fourni un dossier contenant un modèle de PCB incomplet et différents fichiers &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; pour nous aider à démarrer. &lt;br /&gt;
&lt;br /&gt;
== Création de Manette ==&lt;br /&gt;
La création de la manette commence par la création sur kicad d'un PCB. Nous avons pris celui fourni sur le wiki du projet qui possède tous les composants mais non routés.   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Modélisation PCB ==&lt;br /&gt;
A partir du fichier KiCad fourni, nous avons modélisé un PCB déstiné à être utilisé comme manette. Nous avons donc ajouté des boutons, replacé ces derniers. Grâce au schéma nous avons sélectionné des PIN pour connecter les boutons, ainsi que des LEDs et des résistances que nous avons aussi ajouté.    &lt;br /&gt;
&lt;br /&gt;
Voyant que nous avions encore de l'espace de libre sur notre carte, étant un peu ambitieux et voyant que le procédé n'est pas bien compliqué, nous décidons alors de rajouter quelque boutons et quelques LEDs en plus pour que notre manette soit un peu plus crédibles.  &lt;br /&gt;
&lt;br /&gt;
Il nous a alors fallu rajouté  les dit composants sur notre schéma pour que ceux-ci soient rajoutés automatiquement sur le PCB lors de l'utilisation de la fonction &amp;quot;Update PCB with changes made to schematic&amp;quot;                        &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Image boutons.png|Schéma des boutons&lt;br /&gt;
File:Connecteur ISP.png|Schéma du connecteur ISP&lt;br /&gt;
File:Schéma des LEDs.png|Schéma des LEDs&lt;br /&gt;
File:Connecteur USB.png|Schéma du connecteur USB&lt;br /&gt;
File:Microcontroleur.png|Schéma du microcontrôleur&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Plan pcb.jpg|thumb|center|500px| Ajout du plan de masse et des via]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le routage effectué, nous avons ajouté un plan de masse et ajouté une grande quantité de via à différent endroits de la carte pour que la masse soit bien connectée et éviter les fuites thermiques de certains composants. &lt;br /&gt;
&lt;br /&gt;
Ensuite le fichier a été envoyé pour impression en Chine, grâce au fichier Gerber généré par KiCad que vous pouvez retrouvez sur notre GIT:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Vue3d.jpg|thumb|center|500px| Rendu 3D final de la carte]]&lt;br /&gt;
&lt;br /&gt;
== Programmateur AVR ==&lt;br /&gt;
En attendant de recevoir les cartes, nous nous sommes initiés à la programmation d'un périphérique USB, nous avons utilisé les bibliothèques LUFA et LibUSB pour utilisé un programmateur AVR. Il nous a fallu également modifier des fichiers Descripteurs, en &amp;quot;.c&amp;quot; et &amp;quot;.h&amp;quot; qui permettent, une fois téléversés dans le programmateur, d'être détecter par le PC et que celui-ci  reconnaisse les fonctionnalités du périphérique (InPoint et EndPoint). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Soudure de la carte ==&lt;br /&gt;
Une fois la carte reçu, nous avons pris tous nos composants et avons commencé à les souder. N'ayant que très peu d'expérience dans ce domaine ce n'était pas quelque chose de facile au début. Il nous a fallu un peu d'aide et pas mal de flux pour comprendre comment faire des soudures propres. Une fois le coup de main pris, la fin du montage de la carte était assez rapide. Nous avons donc pu passer à la programmation pour la reconnaissance par l'ordinateur de notre carte.&lt;br /&gt;
[[Fichier:Carte vierge.jpg|thumb|left|500px| Carte vierge]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Carte soudée manette.jpg|thumb|center|500px| Carte avec tous les composants soudés]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois la carte soudée il a fallu pouvoir la configurer pour par la suite pouvoir la programmer. Pour se faire il faut lui injecter un bootloader via son programmateur ISP. Ce qui passe la manette en mode DFU et elle est alors reconnu par l'ordinateur.&lt;br /&gt;
[[Fichier:Manette reconnu en mode dfu.png|thumb|center|500px| Manette reconnu en mode DFU]]&lt;br /&gt;
&lt;br /&gt;
== LUFA de la manette ==&lt;br /&gt;
Après avoir reçu les cartes et terminé de souder les composants nous avons pu attaquer la programmation du microcontrôleur pour pouvoir utiliser la manette. Nous avons procédés étape par étape, en commençant par allumé les LEDs puis réceptionner les signaux des boutons pour finir par fusionner les deux pour avoir une manette fonctionnelle.&lt;br /&gt;
&lt;br /&gt;
=== LEDs ===&lt;br /&gt;
Pour éviter un décalage sur le portF il faut désactiver le JTAG en copiant deux fois cette ligne au niveau de la déclaration des pins : &amp;lt;code&amp;gt;MCUCR |= (1&amp;lt;&amp;lt;JTD);&amp;lt;/code&amp;gt;cela permet d'accéder aux LEDs sur les pins PF4 et PF5.&lt;br /&gt;
Une fois la configuration terminé, nous avons testé nos LEDs avec la fonction suivante : &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void blink_leds(void){&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF |= 0b00110011;&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
  PORTF &amp;amp;= ~0b00110011;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ce qui nous donne ce résultat [[Fichier:Vidéo clignotement LEDs.mp4|center|vignette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nous avons également fait la fonction EtatLED dans le fichier USBFinal.c (fichier servant à détecter et communiquer avec la manette par le port USB) qui se trouve dans le dossier libUSB, qui sert à allumer ou éteindre la LED de son choix.&lt;br /&gt;
&lt;br /&gt;
Celle ci utilise le device, handle et configuration descriptor qui ont été définies dans des fonctions précédentes, disponible sur le git dans usbfinal.c ,grandement inspirées de ce qui est donné sur le site de Monsieur Redon, qui sélectionnent la manette et le config descriptor. On aurait dû également sélectionner l'interface dans cette fonction précédente plutôt que de le faire dans EtatLED mais nous nous en sommes rendus compte trop tard.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void EtatLED(int etat, int numLED, libusb_device *manette, libusb_device_handle *handle, struct libusb_config_descriptor *configdesc)&lt;br /&gt;
{&lt;br /&gt;
    int indint=0;/* e.g. première interface */&lt;br /&gt;
    int indalt=0; /* e.g. première alternative */&lt;br /&gt;
    int interface=1; //;configdesc-&amp;gt;interface[indint].altsetting[indalt].bInterfaceNumber;&lt;br /&gt;
    int status=libusb_claim_interface(handle,interface);&lt;br /&gt;
&lt;br /&gt;
    if(status!=0)&lt;br /&gt;
    {&lt;br /&gt;
            printf(&amp;quot;bij\n&amp;quot;);&lt;br /&gt;
        perror(&amp;quot;libusb_claim_interface&amp;quot;);&lt;br /&gt;
        exit(-1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;Interface %d claimed\n&amp;quot;,interface);&lt;br /&gt;
&lt;br /&gt;
    unsigned char data = 0;&lt;br /&gt;
    if(etat){&lt;br /&gt;
       switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x01;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x02;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x03;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x04;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        switch (numLED){&lt;br /&gt;
        case 1:&lt;br /&gt;
            data = 0x11;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            data = 0x12;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            data = 0x13;&lt;br /&gt;
            break;&lt;br /&gt;
        case 4:&lt;br /&gt;
            data = 0x14;&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;Selectionnez un chiffre entre 1 et 4&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Exemple d'appel dans le main &amp;lt;code&amp;gt;EtatLED(0,1, manette, handle, configdesc);&amp;lt;/code&amp;gt;. La fonction va éteindre la LED 1.&lt;br /&gt;
&lt;br /&gt;
=== Boutons ===&lt;br /&gt;
Les LEDs étant configuré il est temps de passer à l'utilisation des boutons. Le principe reste globalement le même, on modifie les fichiers descriptors et minimal selon nos besoins. Pour nous faciliter un peu la tâche nous avons pris les fichiers déjà existant pour des joysticks. Nous allons considérer que si un de nos boutons est appuyé cela reviendra au même que si un joystick est dirigée au maximum dans une direction donnée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
La modification majeure à faire pour que nos boutons soit détecté était sur la fonction &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool GetNextReport(USB_JoystickReport_Data_t* const ReportData)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t PrevJoyStatus    = 0;&lt;br /&gt;
  static uint8_t PrevButtonStatus = 0;&lt;br /&gt;
  bool           InputChanged     = false;&lt;br /&gt;
&lt;br /&gt;
  /* Clear the report contents */&lt;br /&gt;
  memset(ReportData, 0, sizeof(USB_JoystickReport_Data_t));&lt;br /&gt;
&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;X =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PIND&amp;gt;&amp;gt;PD6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y = -100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINB&amp;gt;&amp;gt;PB4) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Y =  100;&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF7) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 0);&lt;br /&gt;
  }&lt;br /&gt;
  if(~(PINF&amp;gt;&amp;gt;PF6) &amp;amp; 1){&lt;br /&gt;
    ReportData-&amp;gt;Button |= (1 &amp;lt;&amp;lt; 1);&lt;br /&gt;
  }&lt;br /&gt;
  /* Return whether the new report is different to the previous report or not */&lt;br /&gt;
  return InputChanged;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Cela nous permet de dire que si un bouton que nous avons configuré est enfoncé, on met la direction X ou Y à plus ou moins 100. Nous les avons tester avec jstest-gtk qui permet de visualiser les directions et appuis d'un joystick, tous nos boutons sont reconnus et configurés comme nous le souhaitons.&lt;br /&gt;
&lt;br /&gt;
=== Regroupement des deux ===&lt;br /&gt;
Pour avoir une manette fonctionnelle à 100% la dernière étape est de fusionner les différents fichiers pour les LEDs et pour les boutons pour que tout soit utilisable en même temps. Pour cela il a fallu modifié le fichier Descritors.c, dans la fonction&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor&amp;lt;/code&amp;gt;il faut indiquer le nombre d'interface à deux, puis rajouter une interface. Dans notre cas étant parti du fichier des boutons nous avons rajouté celle des LEDs. Une fois ceci fais, il ne restait plus qu'à rajouter les fonctions de chaque partie dans un même fichier .c et nous avons fini la configuration et la programmation de notre manette.&lt;br /&gt;
&lt;br /&gt;
== Implémentation dans le Space Invaders ==&lt;br /&gt;
&lt;br /&gt;
=== Ajout des boutons ===&lt;br /&gt;
On va dans cette partie utiliser tout ce qui a été implémenté pour rendre la manette utilisable avec le jeu Space Invaders codé en parallèle. &lt;br /&gt;
&lt;br /&gt;
D'abord, on a, grâce au tutoriel fourni, ajouté l'utilisation des boutons avec la librairie SDL. Il nous a suffit de créer une fonction qui d'abord initialise notre manette en détectant le nombre de Joystick que celle ci possède. &lt;br /&gt;
&lt;br /&gt;
[[Fichier:JoystickInit.png|thumb|center|700px|Initialisation de la manette]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ensuite, on a créé une fonction handleControllerReformed qui pendant que le jeu tourne vient récupérer les valeurs des axes et des boutons de la manette en utilisant le pointeur initialisé par JoystickInitialisation.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-21-04.png|thumb|center|700px|Fonction récupérant la valeur des axes et des boutons]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintenant que nous avons les valeurs des boutons et des axes à chaque instant, nous pouvons les utiliser comme input dans nos programmes pour, par exemple, les déplacements du vaisseau et le lancer de missiles. La valeur des axes est toujours maximale car notre joystick est en fait 4 boutons interprétés comme un Joystick, c'est pour cela qu'on vient comparer leurs valeurs aux valeurs maximales envoyés par un Joystick (32767 et -32768). Les boutons sont des booléens qui retournent 1 quand ils sont à l'état bas et 0 à l'état haut.[[Fichier:Screenshot from 2024-06-10 22-21-55.png|thumb|left|700px|Fonction permettant de faire se déplacer le joueur au clavier ou à la manette]]&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-22-34.png|thumb|right|700px|Fonction permettant d'ajouter un missile si l'on appuie sur e ou sur un bouton.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
[[Fichier:TestLEDs.mp4|vignette]]&lt;br /&gt;
&amp;lt;p style=&amp;quot;clear: both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vous pouvez voir le résultat de l'implémentation totale des boutons en cliquant [https://www.youtube.com/watch?v=TwVJ7-ZU8_8&amp;amp;ab_channel=R%C3%A9mi sur ce lien].&lt;br /&gt;
&lt;br /&gt;
=== Ajout des Diodes Électro-Luminescentes (la classe) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour utiliser les LEDs, c'est légèrement plus compliqué. Le code que nous avions fait précédemment dans usbfinal.c, notamment EtatLED, va nous être utile. On va transformer ce fichier usbfinal.c en une librairie dynamique en .so. Grâce à ça, on va pouvoir réutiliser la fonction EtatLED dans notre programme pour le jeu et l'appeler directement dans des fonctions.&lt;br /&gt;
&lt;br /&gt;
Pour faire cela, on va compiler en utilisant des options particulières pour gcc. La commande est &amp;lt;code&amp;gt;gcc -fPIC -shared -o libusbfinal.so usbfinal.c -lusb-1.0&amp;lt;/code&amp;gt;. L'option &amp;lt;code&amp;gt;-fPIC&amp;lt;/code&amp;gt; permet de génerer du code indépendant de l'emplacement mémoire auquel il est chargé. L'option &amp;lt;code&amp;gt;-shared&amp;lt;/code&amp;gt; permet de génerer une bibliotèque partager plutôt qu'un executable, et &amp;lt;code&amp;gt;-lusb-1.0&amp;lt;/code&amp;gt; permet d'indiquer au compilateur qu'on utilise libusb 1.0 dans ce fichier C.&lt;br /&gt;
&lt;br /&gt;
On se retrouve alors avec un fichier .so disponible dans le git (dans les fichiers LUFA, répértoire libUSB, le chemin précis est spécifié dans le readme).&lt;br /&gt;
&lt;br /&gt;
Ce fichier est la bibliothèque dynamique qu'on peut appeler dans notre makefile.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-19-26.png|thumb|center|700px|Makefile pour le jeu, adapté à l'utilisation de la manette]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On voit qu'il faut rajouter dans l'éxecution la commande &amp;lt;code&amp;gt;export LD_LIBRARY_PATH=./src/include:$$LD_LIBRARY_PATH &amp;amp;&amp;amp; ./$(TARGET) &amp;lt;/code&amp;gt;(nécessaire pour indiquer l'emplacement des bibliothèques dynamiques), et éxecuter la commande sudo make run pour lancer le jeu (libusb nécessite d'avoir les droits administrateurs).&lt;br /&gt;
&lt;br /&gt;
On peut alors dans le fichier source C spaceinvaders ajouter le fichier d'en-tête associé à la librairie usbfinal.h également disponible dans le git, et utiliser toutes les fonctions crééés dans usbfinal.c. On a finalement créé une fonction displayLivesLEDs qui vient alumer le nombre de LEDs correspondant au nombre de vies restantes, qu'on appelle à chaque fois que le joueur est touché pour éviter des appels inutiles (tant que l'on ne fait rien, les LEDs gardent leur état).&lt;br /&gt;
[[Fichier:Screenshot from 2024-06-10 22-18-12.png|thumb|center|500px|Fonction de gestion des LEDs]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le résultat final avec implémentation des LEDs est disponible [https://www.youtube.com/watch?v=zt6Xmwq_HsA&amp;amp;ab_channel=R%C3%A9mi sur ce lien.]&lt;br /&gt;
&lt;br /&gt;
On peut également vous fournir le .zip du jeu spaceinvaders avec l'implémentation de la manette si besoin (trop volumineux pour le wiki) en nous contactant par mail (remi.boursault@polytech-lille.net).&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
On a fini.&lt;br /&gt;
&lt;br /&gt;
Plus sérieusement, la manette possède les fonctionnalités attendues. Les fichiers LUFA permettent de faire comprendre à un PC qu'elle est constitué d'un Joystick, de deux boutons et de 4 LEDs. On a également réussi à utiliser tous ces éléments dans notre jeu comme il était demandé.&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous pouvez consulter le [https://archives.plil.fr/rboursau/S6-manette.git dépôt Git] des projet. Celui-ci contient le fichier gerber de la manette crée sur KiCad. Les fichiers utilisé pour le programmateur AVR sont dans le dossier projet-info-manette/ProgrammateurAVR. Et les fichiers utilisé pour la manette sont eux dans le dossier projet-info-manette/fichier_lufa_libusb.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:TestLEDs.mp4&amp;diff=5972</id>
		<title>Fichier:TestLEDs.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:TestLEDs.mp4&amp;diff=5972"/>
		<updated>2024-06-15T14:03:36Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Coucouuu_%E2%80%90_R%C3%A9alis%C3%A9e_avec_Clipchamp.mp4&amp;diff=5971</id>
		<title>Fichier:Coucouuu ‐ Réalisée avec Clipchamp.mp4</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=Fichier:Coucouuu_%E2%80%90_R%C3%A9alis%C3%A9e_avec_Clipchamp.mp4&amp;diff=5971"/>
		<updated>2024-06-15T13:58:39Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;a&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Binome2023-5&amp;diff=5643</id>
		<title>SE3Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Binome2023-5&amp;diff=5643"/>
		<updated>2024-06-12T15:35:54Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet Groupe 5 : Voiture Programmable =&lt;br /&gt;
Cette page est consacrée à la présentation du projet SE3 réalisé par BOURSAULT Rémi &amp;amp; LECOMTE Antoine, qui est porté sur la création d'une voiture contrôlé par programmation.&lt;br /&gt;
&lt;br /&gt;
== Présentation : ==&lt;br /&gt;
&lt;br /&gt;
=== Objectifs ===&lt;br /&gt;
Les objectifs du projets sont : &lt;br /&gt;
&lt;br /&gt;
* Concevoir une voiture fonctionnant sur batterie, avec un ATMega.&lt;br /&gt;
* Intégrer des clignotants fonctionnels.&lt;br /&gt;
* Permettre à la voiture d'avancer ou reculer, toujours en utilisant l'ATMega.&lt;br /&gt;
* Ajout d'un gyrophare.&lt;br /&gt;
&lt;br /&gt;
=== Ressources ===&lt;br /&gt;
Pour atteindre ces différents objectifs, nous aurons besoins des ressources suivantes :&lt;br /&gt;
&lt;br /&gt;
* Un microcontrôleur ATMega16U4/ATMega32U4&lt;br /&gt;
* Des LEDS&lt;br /&gt;
* Deux moteurs à courant continu&lt;br /&gt;
* Un châssis &lt;br /&gt;
* Des dipôles classiques&lt;br /&gt;
* Un gyrophare&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après avoir choisi notre projet et nos différents objectifs pour ce projet, nous avons commencé à travailler sur le schéma de montage sur KiCAD afin de pouvoir ensuite faire le PCB et le routage qui sera envoyé à une usine pour imprimer nos cartes. Les fonctions et composants principaux de notre projet sont les suivants.&lt;br /&gt;
&lt;br /&gt;
== Fonction sur le schéma : ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Microcontrôleur ===&lt;br /&gt;
La partie indispensable de tous montages électroniques, le cerveau de la voiture. Sans le microcontrôleur rien dans ce projet n'est possible, il permet de gérer les informations. qui sont transmises dans toutes la carte. C'est un microcontrôleur ATMega32u4, ce n'est pas le plus puissant, mais pour la taille du projet que nous faisons c'est bien assez. Il possède assez d'entrées/sorties pour ce que nous voulons en faire.[[Fichier:Microcontroleu.png||thumb|left|300px|Schéma du microcontrôleur]]&lt;br /&gt;
[[Fichier:Pin configuration atmega.png||thumb|center|300px|Configuration des pins sur l'atmega]]&lt;br /&gt;
&lt;br /&gt;
=== Commande Moteur ===&lt;br /&gt;
Ce bloc est celui qui actionne les moteurs et leur dit quand s'arrêter. Celui-ci est composé d'un TB6612FNG, un driver pour moteur qui ne sert qu'à ça. Il prend sept signaux en entrée trois pour gérer chaque moteur et un signal de standby. Nous avons utilisé la datasheet pour savoir comment le schématiser et quels composants utilisé pour que celui-ci fonctionne dans des conditions optimales. &lt;br /&gt;
&lt;br /&gt;
Il est nécessaire d'ajouter ce composant, car les signaux de sortie délivrés par l'ATMega sont trop faibles pour alimenter un moteur. Ce driver vient, à partir d'une tension d'alimentation Vcc amplifier le signal de commande délivré par l'ATMega pour que les moteurs aient l'alimentation adéquate. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet TB6612.png||thumb|left|300px|PCB routé de la carte]]&lt;br /&gt;
[[Fichier:Commande Moteur.png||thumb|center|300px|Schéma du bloc de commande des moteurs]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Chargeur LI-PO ===&lt;br /&gt;
Comme notre voiture fonctionnera sur batterie, il faut un moyen de la recharger, ceci se faisant par l'intermédiaire d'un port USB, il faut un bloc pour adapter le courant que reçoit l'USB pour que cela corresponde aux caractéristiques de notre batterie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Chargeur lipo.png||thumb|center|300px|Schéma du chargeur]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Utilisation de la Batterie ===&lt;br /&gt;
Ce bloc permet de séparer l'alimentation en USB et sur batterie. Lorsqu'un cavalier est présent sur J6, VBAT est directement relié à Vcc et ainsi la batterie sert d'alimentation pour  la carte. Il est impératif de débrancher l'USB avant de mettre le cavalier, pour éviter tout court-circuit. Lorsqu'on enlève le cavalier, on peut brancher l'USB et la carte fonctionne en filaire.[[Fichier:Utilisation baterie.png||thumb|center|300px|Schéma du switch batterie]]&lt;br /&gt;
=== Connecteur USB ===&lt;br /&gt;
On dispose de deux connecteurs USB, un pour recharger la batterie, et un autre pour transmettre les données, qui servira à implanter un programme dans notre microcontrôleur.[[Fichier:Connecteur USBs.png||thumb|center|300px|Schéma des connecteur USB]]&lt;br /&gt;
=== Connecteur ISP ===&lt;br /&gt;
Pour charger le bootloader sur le microcontrôleur si besoin.[[Fichier:Connecteur ISP.png||thumb|center|300px|Schéma du connecteur ISP]]&lt;br /&gt;
=== Diodes Électroluminescentes ===&lt;br /&gt;
[[Fichier:LED voiture.png||thumb|center|300px|Schéma des LEDS]]&lt;br /&gt;
&lt;br /&gt;
== PCB et routage : ==&lt;br /&gt;
Après avoir fini toutes nos fonctions et le schéma, nous nous sommes attaqué au PCB de la carte, il nous a fallu quelque essais pour obtenir une disposition qui nous convienne et qui facilite le routage.[[Fichier:Vue3D.png||thumb|left|300px|Vue 3D de la carte]]&lt;br /&gt;
[[Fichier:PCB voiture.png||thumb|center|300px|PCB routé de la carte]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Brasage des composants ==&lt;br /&gt;
Une fois les cartes reçues, nous nous sommes attaqués à la brasure des différents composants. Nous avons commencé par le microcontrôleur, le cristal et les résistances et capacités associées. Dès le départ nous avons eu des problèmes avec le brasage, des pattes soudées entre elles, le cristal mal posé. Mais notre plus gros problème a été l'allocation des pins du microcontrôleur. Nous ne le savions pas au moment de la création de notre PCB et nous n'y avons pas fait attention, mais des pins sont spécialement attribuées au MISO, MOSI et SOCKET. Pins que nous devons utilisés pour envoyer le bootloader sur la microcontrôleur via le programmateur ISP (en orange sur le schéma).  Nous avons donc dû improviser une solution. Nous avons coupé les pistes et relié par des fils les broches de l'ISP aux pins correspondantes. À cause de cette erreur nos LEDs D2 et D3 ne sont plus utilisables.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Atmega pin mosi.png|thumb|center|800x|Pins de l'atmega utilisés pour l'ISP]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois ce problème réglé, nous avons pu passer à la suite, la gestion des moteurs. Pour ce faire il n'y a pas eu autant de problèmes que sur la partie microcontrôleur, nous avons simplement soudé le contrôleur de moteurs, un TB6612FNG, puis les broches sur lesquelles nous allons venir brancher les moteurs, et enfin les fils directement sur les broches des moteurs. Nous avons également utilisé du plastique thermo rétractable pour que les fils soient protégés et restent soudés aux moteurs durant toute la durée du projet. Les moteurs étant prêt à l'emploi, nous les avons testé pour savoir si tout était fonctionnelle. Une fois confirmé, ayant en tête le contexte écologique actuel, nous avons pris des bouchons de bouteilles comme roues et mis des élastiques autour pour avoir de la traction lors de nos différents essais.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Moteur et roues.jpg|thumb|center|300px|Moteurs et roues]]&lt;br /&gt;
&lt;br /&gt;
Pour tester toute notre carte, nous avons créé un code sous le nom de helloworld.c qui rempli différente fonctions (disponible sur le GIT), allumer ou faire clignoter les LEDs en fonction du déplacement de la voiture et, bien évidemment, déplacer la voiture. La voiture réagissant comme nous le souhaitons, nous décidons donc de passer à l'étape suivante, le fonctionnement sur batterie. Ce n'est pas la partie la plus compliquée, il suffit de braser les broches permettant d'accueillir la batterie puis celles destinées au cavalier. Une fois le tout installé sur notre carte, nous la testons et tout fonctionne normalement. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#define __DELAY_BACKWARD_COMPATIBLE__&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define PWM1 PB7&lt;br /&gt;
#define PWM2 PD0&lt;br /&gt;
#define AIN1 PF6&lt;br /&gt;
#define AIN2 PF7&lt;br /&gt;
#define BIN1 PF4&lt;br /&gt;
#define BIN2 PF1&lt;br /&gt;
#define STBY PF5&lt;br /&gt;
#define D3 PB0&lt;br /&gt;
#define D2 PE6&lt;br /&gt;
#define D6 PC6&lt;br /&gt;
#define D7 PC7&lt;br /&gt;
#define D8 PD4&lt;br /&gt;
#define D9 PD6&lt;br /&gt;
#define START PF0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void blink_droit()&lt;br /&gt;
{&lt;br /&gt;
    for(int i = 0; i&amp;lt;50; i++)&lt;br /&gt;
    {&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;D3);&lt;br /&gt;
        PORTC |= (1&amp;lt;&amp;lt; D7);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
        PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;D3);&lt;br /&gt;
        PORTC &amp;amp;= ~(1&amp;lt;&amp;lt; D7);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void blink_gauche()&lt;br /&gt;
{&lt;br /&gt;
    for(int i = 0; i&amp;lt;50; i++)&lt;br /&gt;
    {&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;D8);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;D8);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void avancer(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN1);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN2);&lt;br /&gt;
    PORTE |= (1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTE &amp;amp;= ~(1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reculer(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN2);&lt;br /&gt;
    PORTC |= (1&amp;lt;&amp;lt; D6);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN1);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2) | (1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTC &amp;amp;= ~(1&amp;lt;&amp;lt; D6);&lt;br /&gt;
    PORTD &amp;amp;= ~(1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void gauche(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN2);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN2);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
   // blink_gauche();&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void droite(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN1);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN1);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
   // blink_droit();&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    uint16_t time = 4000, half = 1200, full = 2400;&lt;br /&gt;
    CLKSEL0 = 0b00010101;&lt;br /&gt;
    CLKSEL1 = 0b00001111;&lt;br /&gt;
    CLKPR = 0b10000000;&lt;br /&gt;
    CLKPR = 0;&lt;br /&gt;
&lt;br /&gt;
    MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
    MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    DDRB |= (1&amp;lt;&amp;lt; PWM1) | (1&amp;lt;&amp;lt; PB0);&lt;br /&gt;
    DDRF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt;BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    DDRF &amp;amp;= ~(1&amp;lt;&amp;lt;START);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt;START);&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt; PWM2) | (1&amp;lt;&amp;lt; D8) | (1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    DDRE |= (1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    DDRC |= (1&amp;lt;&amp;lt; D6) | (1&amp;lt;&amp;lt; D7);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        if(~(PINF&amp;gt;&amp;gt;START) &amp;amp; 1)&lt;br /&gt;
        {&lt;br /&gt;
            avancer(time);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            reculer(time);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            gauche(half);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            droite(full);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:LED allumée sur batterie.jpg|thumb|left|150px|LED allumée en fonctionnement sur batterie]]&lt;br /&gt;
[[Fichier:Voiture appui bouton.mp4|thumb|center|Voiture fonctionnant sur batterie]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Comme il nous reste encore un peu de temps, nous décidons de nous attaquer à la dernière partie, la recharge de la batterie. Comme pour la partie de fonctionnement sur batterie, il n'y a aucune partie de code, il suffit de souder des composants en plus sur la carte pour que la nouvelle fonctionnalité soit active. Donc nous nous y sommes mis et encore une fois il n'y a aucun problème.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Voiture déchargée.mp4|thumb|left|500px|Voiture déchargée]]&lt;br /&gt;
[[Fichier:Voiture en charge.mp4|thumb|center|500px|Voiture en charge]]&lt;br /&gt;
[[Fichier:Voiture après recharge.mp4|thumb|center|500px|Voiture après recharge]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
Lors de ce projet, nous avons pu  acquérir des compétences de base dans la Conception Assistée par Ordinateur et nous avons appris beaucoup sur la manière dont un système embarqué fonctionne précisément, notamment sur le fonctionnement nomade. Grâce à nos erreurs concernant l'attribution de certains PINs (les PINs MISO MOSI et SLK, et la non utilisation de port adaptés au PWM pour le réglage de la puissance délivrée par les moteurs), nous avons compris l'extrême importance de porter la plus grande attention à la CAO, pour éviter d'avoir de gros problèmes par la suite. Nous avons tout de même réussi à atteindre les objectifs fixés dans les temps, et l'intégralité des modules présents sur la carte ont bien été intégrés sur le prototype.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous trouverez dans le lien [https://archives.plil.fr/rboursau/premier-systeme-embarque.git GIT] de notre projet, tous les fichiers de création de la carte se trouvent dans le dossier projet_voiture, le code envoyé pour tester la voiture dans le dossier cod.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Binome2023-5&amp;diff=5642</id>
		<title>SE3Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Binome2023-5&amp;diff=5642"/>
		<updated>2024-06-12T15:34:11Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet Groupe 5 : Voiture Programmable =&lt;br /&gt;
Cette page est consacrée à la présentation du projet SE3 réalisé par BOURSAULT Rémi &amp;amp; LECOMTE Antoine, qui est porté sur la création d'une voiture contrôlé par programmation.&lt;br /&gt;
&lt;br /&gt;
== Présentation : ==&lt;br /&gt;
&lt;br /&gt;
=== Objectifs ===&lt;br /&gt;
Les objectifs du projets sont : &lt;br /&gt;
&lt;br /&gt;
* Concevoir une voiture fonctionnant sur batterie, avec un ATMega.&lt;br /&gt;
* Intégrer des clignotants fonctionnels.&lt;br /&gt;
* Permettre à la voiture d'avancer ou reculer, toujours en utilisant l'ATMega.&lt;br /&gt;
* Ajout d'un gyrophare.&lt;br /&gt;
&lt;br /&gt;
=== Ressources ===&lt;br /&gt;
Pour atteindre ces différents objectifs, nous aurons besoins des ressources suivantes :&lt;br /&gt;
&lt;br /&gt;
* Un microcontrôleur ATMega16U4/ATMega32U4&lt;br /&gt;
* Des LEDS&lt;br /&gt;
* Deux moteurs à courant continu&lt;br /&gt;
* Un châssis &lt;br /&gt;
* Des dipôles classiques&lt;br /&gt;
* Un gyrophare&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après avoir choisi notre projet et nos différents objectifs pour ce projet, nous avons commencé à travailler sur le schéma de montage sur KiCAD afin de pouvoir ensuite faire le PCB et le routage qui sera envoyé à une usine pour imprimer nos cartes. Les fonctions et composants principaux de notre projet sont les suivants.&lt;br /&gt;
&lt;br /&gt;
== Fonction sur le schéma : ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Microcontrôleur ===&lt;br /&gt;
La partie indispensable de tous montages électroniques, le cerveau de la voiture. Sans le microcontrôleur rien dans ce projet n'est possible, il permet de gérer les informations. qui sont transmises dans toutes la carte. C'est un microcontrôleur ATMega32u4, ce n'est pas le plus puissant, mais pour la taille du projet que nous faisons c'est bien assez. Il possède assez d'entrées/sorties pour ce que nous voulons en faire.[[Fichier:Microcontroleu.png||thumb|left|300px|Schéma du microcontrôleur]]&lt;br /&gt;
[[Fichier:Pin configuration atmega.png||thumb|center|300px|Configuration des pins sur l'atmega]]&lt;br /&gt;
&lt;br /&gt;
=== Commande Moteur ===&lt;br /&gt;
Ce bloc est celui qui actionne les moteurs et leur dit quand s'arrêter. Celui-ci est composé d'un TB6612FNG, un driver pour moteur qui ne sert qu'à ça. Il prend sept signaux en entrée trois pour gérer chaque moteur et un signal de standby. Nous avons utilisé la datasheet pour savoir comment le schématiser et quels composants utilisé pour que celui-ci fonctionne dans des conditions optimales. &lt;br /&gt;
&lt;br /&gt;
Il est nécessaire d'ajouter ce composant, car les signaux de sortie délivrés par l'ATMega sont trop faibles pour alimenter un moteur. Ce driver vient, à partir d'une tension d'alimentation Vcc amplifier le signal de commande délivré par l'ATMega pour que les moteurs aient l'alimentation adéquate. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet TB6612.png||thumb|left|300px|PCB routé de la carte]]&lt;br /&gt;
[[Fichier:Commande Moteur.png||thumb|center|300px|Schéma du bloc de commande des moteurs]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Chargeur LI-PO ===&lt;br /&gt;
Comme notre voiture fonctionnera sur batterie, il faut un moyen de la recharger, ceci se faisant par l'intermédiaire d'un port USB, il faut un bloc pour adapter le courant que reçoit l'USB pour que cela corresponde aux caractéristiques de notre batterie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Chargeur lipo.png||thumb|center|300px|Schéma du chargeur]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Utilisation de la Batterie ===&lt;br /&gt;
Ce bloc permet de séparer l'alimentation en USB et sur batterie. Lorsqu'un cavalier est présent sur J6, VBAT est directement relié à Vcc et ainsi la batterie sert d'alimentation pour  la carte. Il est impératif de débrancher l'USB avant de mettre le cavalier, pour éviter tout court-circuit. Lorsqu'on enlève le cavalier, on peut brancher l'USB et la carte fonctionne en filaire.[[Fichier:Utilisation baterie.png||thumb|center|300px|Schéma du switch batterie]]&lt;br /&gt;
=== Connecteur USB ===&lt;br /&gt;
On dispose de deux connecteurs USB, un pour recharger la batterie, et un autre pour transmettre les données, qui servira à implanter un programme dans notre microcontrôleur.[[Fichier:Connecteur USBs.png||thumb|center|300px|Schéma des connecteur USB]]&lt;br /&gt;
=== Connecteur ISP ===&lt;br /&gt;
Pour charger le bootloader sur le microcontrôleur si besoin.[[Fichier:Connecteur ISP.png||thumb|center|300px|Schéma du connecteur ISP]]&lt;br /&gt;
=== Diodes Électroluminescentes ===&lt;br /&gt;
[[Fichier:LED voiture.png||thumb|center|300px|Schéma des LEDS]]&lt;br /&gt;
&lt;br /&gt;
== PCB et routage : ==&lt;br /&gt;
Après avoir fini toutes nos fonctions et le schéma, nous nous sommes attaqué au PCB de la carte, il nous a fallu quelque essais pour obtenir une disposition qui nous convienne et qui facilite le routage.[[Fichier:Vue3D.png||thumb|left|300px|Vue 3D de la carte]]&lt;br /&gt;
[[Fichier:PCB voiture.png||thumb|center|300px|PCB routé de la carte]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Brasage des composants ==&lt;br /&gt;
Une fois les cartes reçues, nous nous sommes attaqués à la brasure des différents composants. Nous avons commencé par le microcontrôleur, le cristal et les résistances et capacités associées. Dès le départ nous avons eu des problèmes avec le brasage, des pattes soudées entre elles, le cristal mal posé. Mais notre plus gros problème a été l'allocation des pins du microcontrôleur. Nous ne le savions pas au moment de la création de notre PCB et nous n'y avons pas fait attention, mais des pins sont spécialement attribuées au MISO, MOSI et SOCKET. Pins que nous devons utilisés pour envoyer le bootloader sur la microcontrôleur via le programmateur ISP (en orange sur le schéma).  Nous avons donc dû improviser une solution. Nous avons coupé les pistes et relié par des fils les broches de l'ISP aux pins correspondantes. À cause de cette erreur nos LEDs D2 et D3 ne sont plus utilisables.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Atmega pin mosi.png|thumb|center|800x|Pins de l'atmega utilisés pour l'ISP]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois ce problème réglé, nous avons pu passer à la suite, la gestion des moteurs. Pour ce faire il n'y a pas eu autant de problèmes que sur la partie microcontrôleur, nous avons simplement soudé le contrôleur de moteurs, un TB6612FNG, puis les broches sur lesquelles nous allons venir brancher les moteurs, et enfin les fils directement sur les broches des moteurs. Nous avons également utilisé du plastique thermo rétractable pour que les fils soient protégés et restent soudés aux moteurs durant toute la durée du projet. Les moteurs étant prêt à l'emploi, nous les avons testé pour savoir si tout était fonctionnelle. Une fois confirmé, ayant en tête le contexte écologique actuel, nous avons pris des bouchons de bouteilles comme roues et mis des élastiques autour pour avoir de la traction lors de nos différents essais.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Moteur et roues.jpg|thumb|center|300px|Moteurs et roues]]&lt;br /&gt;
&lt;br /&gt;
Pour tester toute notre carte, nous avons créé un code sous le nom de helloworld.c qui rempli différente fonctions (disponible sur le GIT), allumer ou faire clignoter les LEDs en fonction du déplacement de la voiture et, bien évidemment, déplacer la voiture. La voiture réagissant comme nous le souhaitons, nous décidons donc de passer à l'étape suivante, le fonctionnement sur batterie. Ce n'est pas la partie la plus compliquée, il suffit de braser les broches permettant d'accueillir la batterie puis celles destinées au cavalier. Une fois le tout installé sur notre carte, nous la testons et tout fonctionne normalement. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#define __DELAY_BACKWARD_COMPATIBLE__&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define PWM1 PB7&lt;br /&gt;
#define PWM2 PD0&lt;br /&gt;
#define AIN1 PF6&lt;br /&gt;
#define AIN2 PF7&lt;br /&gt;
#define BIN1 PF4&lt;br /&gt;
#define BIN2 PF1&lt;br /&gt;
#define STBY PF5&lt;br /&gt;
#define D3 PB0&lt;br /&gt;
#define D2 PE6&lt;br /&gt;
#define D6 PC6&lt;br /&gt;
#define D7 PC7&lt;br /&gt;
#define D8 PD4&lt;br /&gt;
#define D9 PD6&lt;br /&gt;
#define START PF0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void blink_droit()&lt;br /&gt;
{&lt;br /&gt;
    for(int i = 0; i&amp;lt;50; i++)&lt;br /&gt;
    {&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;D3);&lt;br /&gt;
        PORTC |= (1&amp;lt;&amp;lt; D7);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
        PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;D3);&lt;br /&gt;
        PORTC &amp;amp;= ~(1&amp;lt;&amp;lt; D7);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void blink_gauche()&lt;br /&gt;
{&lt;br /&gt;
    for(int i = 0; i&amp;lt;50; i++)&lt;br /&gt;
    {&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;D8);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;D8);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void avancer(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN1);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN2);&lt;br /&gt;
    PORTE |= (1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTE &amp;amp;= ~(1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reculer(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN2);&lt;br /&gt;
    PORTC |= (1&amp;lt;&amp;lt; D6);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN1);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2) | (1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTC &amp;amp;= ~(1&amp;lt;&amp;lt; D6);&lt;br /&gt;
    PORTD &amp;amp;= ~(1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void gauche(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN2);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN2);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
   // blink_gauche();&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void droite(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN1);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN1);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
   // blink_droit();&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    uint16_t time = 4000, half = 1200, full = 2400;&lt;br /&gt;
    CLKSEL0 = 0b00010101;&lt;br /&gt;
    CLKSEL1 = 0b00001111;&lt;br /&gt;
    CLKPR = 0b10000000;&lt;br /&gt;
    CLKPR = 0;&lt;br /&gt;
&lt;br /&gt;
    MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
    MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    DDRB |= (1&amp;lt;&amp;lt; PWM1) | (1&amp;lt;&amp;lt; PB0);&lt;br /&gt;
    DDRF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt;BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    DDRF &amp;amp;= ~(1&amp;lt;&amp;lt;START);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt;START);&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt; PWM2) | (1&amp;lt;&amp;lt; D8) | (1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    DDRE |= (1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    DDRC |= (1&amp;lt;&amp;lt; D6) | (1&amp;lt;&amp;lt; D7);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        if(~(PINF&amp;gt;&amp;gt;START) &amp;amp; 1)&lt;br /&gt;
        {&lt;br /&gt;
            avancer(time);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            reculer(time);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            gauche(half);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            droite(full);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:LED allumée sur batterie.jpg|thumb|left|150px|LED allumée en fonctionnement sur batterie]]&lt;br /&gt;
[[Fichier:Voiture appui bouton.mp4|thumb|center|Voiture fonctionnant sur batterie]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Comme il nous reste encore un peu de temps, nous décidons de nous attaquer à la dernière partie, la recharge de la batterie. Comme pour la partie de fonctionnement sur batterie, il n'y a aucune partie de code, il suffit de souder des composants en plus sur la carte pour que la nouvelle fonctionnalité soit active. Donc nous nous y sommes mis et encore une fois il n'y a aucun problème.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Voiture déchargée.mp4|thumb|left|500px|Voiture déchargée]]&lt;br /&gt;
[[Fichier:Voiture en charge.mp4|thumb|center|500px|Voiture en charge]]&lt;br /&gt;
[[Fichier:Voiture après recharge.mp4|thumb|left|500px|Voiture après recharge]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
Lors de ce projet, nous avons pu  acquérir des compétences de base dans la Conception Assistée par Ordinateur et nous avons appris beaucoup sur la manière dont un système embarqué fonctionne précisément, notamment sur le fonctionnement nomade. Grâce à nos erreurs concernant l'attribution de certains PINs (les PINs MISO MOSI et SLK, et la non utilisation de port adaptés au PWM pour le réglage de la puissance délivrée par les moteurs), nous avons compris l'extrême importance de porter la plus grande attention à la CAO, pour éviter d'avoir de gros problèmes par la suite. Nous avons tout de même réussi à atteindre les objectifs fixés dans les temps, et l'intégralité des modules présents sur la carte ont bien été intégrés sur le prototype.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous trouverez dans le lien [https://archives.plil.fr/rboursau/premier-systeme-embarque.git GIT] de notre projet, tous les fichiers de création de la carte se trouvent dans le dossier projet_voiture, le code envoyé pour tester la voiture dans le dossier cod.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
	<entry>
		<id>https://projets-se.plil.fr/mediawiki/index.php?title=SE3Binome2023-5&amp;diff=5641</id>
		<title>SE3Binome2023-5</title>
		<link rel="alternate" type="text/html" href="https://projets-se.plil.fr/mediawiki/index.php?title=SE3Binome2023-5&amp;diff=5641"/>
		<updated>2024-06-12T15:34:00Z</updated>

		<summary type="html">&lt;p&gt;Rboursau : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projet Groupe 5 : Voiture Programmable =&lt;br /&gt;
Cette page est consacrée à la présentation du projet SE3 réalisé par BOURSAULT Rémi &amp;amp; LECOMTE Antoine, qui est porté sur la création d'une voiture contrôlé par programmation.&lt;br /&gt;
&lt;br /&gt;
== Présentation : ==&lt;br /&gt;
&lt;br /&gt;
=== Objectifs ===&lt;br /&gt;
Les objectifs du projets sont : &lt;br /&gt;
&lt;br /&gt;
* Concevoir une voiture fonctionnant sur batterie, avec un ATMega.&lt;br /&gt;
* Intégrer des clignotants fonctionnels.&lt;br /&gt;
* Permettre à la voiture d'avancer ou reculer, toujours en utilisant l'ATMega.&lt;br /&gt;
* Ajout d'un gyrophare.&lt;br /&gt;
&lt;br /&gt;
=== Ressources ===&lt;br /&gt;
Pour atteindre ces différents objectifs, nous aurons besoins des ressources suivantes :&lt;br /&gt;
&lt;br /&gt;
* Un microcontrôleur ATMega16U4/ATMega32U4&lt;br /&gt;
* Des LEDS&lt;br /&gt;
* Deux moteurs à courant continu&lt;br /&gt;
* Un châssis &lt;br /&gt;
* Des dipôles classiques&lt;br /&gt;
* Un gyrophare&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Après avoir choisi notre projet et nos différents objectifs pour ce projet, nous avons commencé à travailler sur le schéma de montage sur KiCAD afin de pouvoir ensuite faire le PCB et le routage qui sera envoyé à une usine pour imprimer nos cartes. Les fonctions et composants principaux de notre projet sont les suivants.&lt;br /&gt;
&lt;br /&gt;
== Fonction sur le schéma : ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Microcontrôleur ===&lt;br /&gt;
La partie indispensable de tous montages électroniques, le cerveau de la voiture. Sans le microcontrôleur rien dans ce projet n'est possible, il permet de gérer les informations. qui sont transmises dans toutes la carte. C'est un microcontrôleur ATMega32u4, ce n'est pas le plus puissant, mais pour la taille du projet que nous faisons c'est bien assez. Il possède assez d'entrées/sorties pour ce que nous voulons en faire.[[Fichier:Microcontroleu.png||thumb|left|300px|Schéma du microcontrôleur]]&lt;br /&gt;
[[Fichier:Pin configuration atmega.png||thumb|center|300px|Configuration des pins sur l'atmega]]&lt;br /&gt;
&lt;br /&gt;
=== Commande Moteur ===&lt;br /&gt;
Ce bloc est celui qui actionne les moteurs et leur dit quand s'arrêter. Celui-ci est composé d'un TB6612FNG, un driver pour moteur qui ne sert qu'à ça. Il prend sept signaux en entrée trois pour gérer chaque moteur et un signal de standby. Nous avons utilisé la datasheet pour savoir comment le schématiser et quels composants utilisé pour que celui-ci fonctionne dans des conditions optimales. &lt;br /&gt;
&lt;br /&gt;
Il est nécessaire d'ajouter ce composant, car les signaux de sortie délivrés par l'ATMega sont trop faibles pour alimenter un moteur. Ce driver vient, à partir d'une tension d'alimentation Vcc amplifier le signal de commande délivré par l'ATMega pour que les moteurs aient l'alimentation adéquate. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Datasheet TB6612.png||thumb|left|300px|PCB routé de la carte]]&lt;br /&gt;
[[Fichier:Commande Moteur.png||thumb|center|300px|Schéma du bloc de commande des moteurs]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Chargeur LI-PO ===&lt;br /&gt;
Comme notre voiture fonctionnera sur batterie, il faut un moyen de la recharger, ceci se faisant par l'intermédiaire d'un port USB, il faut un bloc pour adapter le courant que reçoit l'USB pour que cela corresponde aux caractéristiques de notre batterie.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Chargeur lipo.png||thumb|center|300px|Schéma du chargeur]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Utilisation de la Batterie ===&lt;br /&gt;
Ce bloc permet de séparer l'alimentation en USB et sur batterie. Lorsqu'un cavalier est présent sur J6, VBAT est directement relié à Vcc et ainsi la batterie sert d'alimentation pour  la carte. Il est impératif de débrancher l'USB avant de mettre le cavalier, pour éviter tout court-circuit. Lorsqu'on enlève le cavalier, on peut brancher l'USB et la carte fonctionne en filaire.[[Fichier:Utilisation baterie.png||thumb|center|300px|Schéma du switch batterie]]&lt;br /&gt;
=== Connecteur USB ===&lt;br /&gt;
On dispose de deux connecteurs USB, un pour recharger la batterie, et un autre pour transmettre les données, qui servira à implanter un programme dans notre microcontrôleur.[[Fichier:Connecteur USBs.png||thumb|center|300px|Schéma des connecteur USB]]&lt;br /&gt;
=== Connecteur ISP ===&lt;br /&gt;
Pour charger le bootloader sur le microcontrôleur si besoin.[[Fichier:Connecteur ISP.png||thumb|center|300px|Schéma du connecteur ISP]]&lt;br /&gt;
=== Diodes Électroluminescentes ===&lt;br /&gt;
[[Fichier:LED voiture.png||thumb|center|300px|Schéma des LEDS]]&lt;br /&gt;
&lt;br /&gt;
== PCB et routage : ==&lt;br /&gt;
Après avoir fini toutes nos fonctions et le schéma, nous nous sommes attaqué au PCB de la carte, il nous a fallu quelque essais pour obtenir une disposition qui nous convienne et qui facilite le routage.[[Fichier:Vue3D.png||thumb|left|300px|Vue 3D de la carte]]&lt;br /&gt;
[[Fichier:PCB voiture.png||thumb|center|300px|PCB routé de la carte]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Brasage des composants ==&lt;br /&gt;
Une fois les cartes reçues, nous nous sommes attaqués à la brasure des différents composants. Nous avons commencé par le microcontrôleur, le cristal et les résistances et capacités associées. Dès le départ nous avons eu des problèmes avec le brasage, des pattes soudées entre elles, le cristal mal posé. Mais notre plus gros problème a été l'allocation des pins du microcontrôleur. Nous ne le savions pas au moment de la création de notre PCB et nous n'y avons pas fait attention, mais des pins sont spécialement attribuées au MISO, MOSI et SOCKET. Pins que nous devons utilisés pour envoyer le bootloader sur la microcontrôleur via le programmateur ISP (en orange sur le schéma).  Nous avons donc dû improviser une solution. Nous avons coupé les pistes et relié par des fils les broches de l'ISP aux pins correspondantes. À cause de cette erreur nos LEDs D2 et D3 ne sont plus utilisables.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Atmega pin mosi.png|thumb|center|800x|Pins de l'atmega utilisés pour l'ISP]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois ce problème réglé, nous avons pu passer à la suite, la gestion des moteurs. Pour ce faire il n'y a pas eu autant de problèmes que sur la partie microcontrôleur, nous avons simplement soudé le contrôleur de moteurs, un TB6612FNG, puis les broches sur lesquelles nous allons venir brancher les moteurs, et enfin les fils directement sur les broches des moteurs. Nous avons également utilisé du plastique thermo rétractable pour que les fils soient protégés et restent soudés aux moteurs durant toute la durée du projet. Les moteurs étant prêt à l'emploi, nous les avons testé pour savoir si tout était fonctionnelle. Une fois confirmé, ayant en tête le contexte écologique actuel, nous avons pris des bouchons de bouteilles comme roues et mis des élastiques autour pour avoir de la traction lors de nos différents essais.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Moteur et roues.jpg|thumb|center|300px|Moteurs et roues]]&lt;br /&gt;
&lt;br /&gt;
Pour tester toute notre carte, nous avons créé un code sous le nom de helloworld.c qui rempli différente fonctions (disponible sur le GIT), allumer ou faire clignoter les LEDs en fonction du déplacement de la voiture et, bien évidemment, déplacer la voiture. La voiture réagissant comme nous le souhaitons, nous décidons donc de passer à l'étape suivante, le fonctionnement sur batterie. Ce n'est pas la partie la plus compliquée, il suffit de braser les broches permettant d'accueillir la batterie puis celles destinées au cavalier. Une fois le tout installé sur notre carte, nous la testons et tout fonctionne normalement. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#define __DELAY_BACKWARD_COMPATIBLE__&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define PWM1 PB7&lt;br /&gt;
#define PWM2 PD0&lt;br /&gt;
#define AIN1 PF6&lt;br /&gt;
#define AIN2 PF7&lt;br /&gt;
#define BIN1 PF4&lt;br /&gt;
#define BIN2 PF1&lt;br /&gt;
#define STBY PF5&lt;br /&gt;
#define D3 PB0&lt;br /&gt;
#define D2 PE6&lt;br /&gt;
#define D6 PC6&lt;br /&gt;
#define D7 PC7&lt;br /&gt;
#define D8 PD4&lt;br /&gt;
#define D9 PD6&lt;br /&gt;
#define START PF0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void blink_droit()&lt;br /&gt;
{&lt;br /&gt;
    for(int i = 0; i&amp;lt;50; i++)&lt;br /&gt;
    {&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;D3);&lt;br /&gt;
        PORTC |= (1&amp;lt;&amp;lt; D7);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
        PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;D3);&lt;br /&gt;
        PORTC &amp;amp;= ~(1&amp;lt;&amp;lt; D7);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void blink_gauche()&lt;br /&gt;
{&lt;br /&gt;
    for(int i = 0; i&amp;lt;50; i++)&lt;br /&gt;
    {&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;D8);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;D8);&lt;br /&gt;
        _delay_ms(200);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void avancer(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN1);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN2);&lt;br /&gt;
    PORTE |= (1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTE &amp;amp;= ~(1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reculer(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN2);&lt;br /&gt;
    PORTC |= (1&amp;lt;&amp;lt; D6);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN1);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2) | (1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTC &amp;amp;= ~(1&amp;lt;&amp;lt; D6);&lt;br /&gt;
    PORTD &amp;amp;= ~(1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void gauche(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN2);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN2);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
   // blink_gauche();&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void droite(int time)&lt;br /&gt;
{&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt; PWM1);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; AIN1);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; BIN1);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PWM2);&lt;br /&gt;
   // blink_droit();&lt;br /&gt;
    _delay_ms(time);&lt;br /&gt;
    PORTF &amp;amp;= ~(1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    uint16_t time = 4000, half = 1200, full = 2400;&lt;br /&gt;
    CLKSEL0 = 0b00010101;&lt;br /&gt;
    CLKSEL1 = 0b00001111;&lt;br /&gt;
    CLKPR = 0b10000000;&lt;br /&gt;
    CLKPR = 0;&lt;br /&gt;
&lt;br /&gt;
    MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
    MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    DDRB |= (1&amp;lt;&amp;lt; PWM1) | (1&amp;lt;&amp;lt; PB0);&lt;br /&gt;
    DDRF |= (1&amp;lt;&amp;lt; AIN1) | (1&amp;lt;&amp;lt; AIN2) | (1&amp;lt;&amp;lt; BIN1) | (1&amp;lt;&amp;lt;BIN2) | (1&amp;lt;&amp;lt; STBY);&lt;br /&gt;
    DDRF &amp;amp;= ~(1&amp;lt;&amp;lt;START);&lt;br /&gt;
    PORTF |= (1&amp;lt;&amp;lt;START);&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt; PWM2) | (1&amp;lt;&amp;lt; D8) | (1&amp;lt;&amp;lt; D9);&lt;br /&gt;
    DDRE |= (1&amp;lt;&amp;lt; D2);&lt;br /&gt;
    DDRC |= (1&amp;lt;&amp;lt; D6) | (1&amp;lt;&amp;lt; D7);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        if(~(PINF&amp;gt;&amp;gt;START) &amp;amp; 1)&lt;br /&gt;
        {&lt;br /&gt;
            avancer(time);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            reculer(time);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            gauche(half);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
            droite(full);&lt;br /&gt;
            _delay_ms(1000);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[Fichier:LED allumée sur batterie.jpg|thumb|left|150px|LED allumée en fonctionnement sur batterie]]&lt;br /&gt;
[[Fichier:Voiture appui bouton.mp4|thumb|center|Voiture fonctionnant sur batterie]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Comme il nous reste encore un peu de temps, nous décidons de nous attaquer à la dernière partie, la recharge de la batterie. Comme pour la partie de fonctionnement sur batterie, il n'y a aucune partie de code, il suffit de souder des composants en plus sur la carte pour que la nouvelle fonctionnalité soit active. Donc nous nous y sommes mis et encore une fois il n'y a aucun problème.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Voiture déchargée.mp4|thumb|left|500px|Voiture déchargée]]&lt;br /&gt;
[[Fichier:Voiture en charge.mp4|thumb|center|500px|Voiture en charge]]&lt;br /&gt;
[[Fichier:Voiture après recharge.mp4|thumb|left|500px|Voiture après recharge]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== CONCLUSION ==&lt;br /&gt;
Lors de ce projet, nous avons pu  acquérir des compétences de base dans la Conception Assistée par Ordinateur et nous avons appris beaucoup sur la manière dont un système embarqué fonctionne précisément, notamment sur le fonctionnement nomade. Grâce à nos erreurs concernant l'attribution de certains PINs (les PINs MISO MOSI et SLK, et la non utilisation de port adaptés au PWM pour le réglage de la puissance délivrée par les moteurs), nous avons compris l'extrême importance de porter la plus grande attention à la CAO, pour éviter d'avoir de gros problèmes par la suite. Nous avons tout de même réussi à atteindre les objectifs fixés dans les temps, et l'intégralité des modules présents sur la carte ont bien été intégrés sur le prototype.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== GIT ==&lt;br /&gt;
Vous trouverez dans le lien [https://archives.plil.fr/rboursau/premier-systeme-embarque.git GIT] de notre projet, tous les fichiers de création de la carte se trouvent dans le dossier projet_voiture, le code envoyé pour tester la voiture dans le dossier cod.&lt;/div&gt;</summary>
		<author><name>Rboursau</name></author>
	</entry>
</feed>