« SE4Binome2024-8 » : différence entre les versions
Aucun résumé des modifications |
|||
(2 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 2 : | Ligne 2 : | ||
Lors de ce projet nous allons réaliser un pico-ordinateur qui comporte plusieurs fonctionnalités qui sont implémentées grâce à des cartes filles que l'on vient brancher sur une carte mère. | Lors de ce projet nous allons réaliser un pico-ordinateur qui comporte plusieurs fonctionnalités qui sont implémentées grâce à des cartes filles que l'on vient brancher sur une carte mère. | ||
[https://gitea.plil.fr/lbonning/pico-se4_binome8 GIT du projet] | [https://gitea.plil.fr/lbonning/pico-se4_binome8 GIT du projet] | ||
Ligne 220 : | Ligne 219 : | ||
Après avoir confirmé que nous pouvons lire le bloc 0 nous avons essayé de lire les blocs suivants, or nous obtenions une erreur <code>Address out of range</code>. Le problème vient du fait qu'on utilise une carte SD standard (pas SDHC) et qu'il faut multiplier le numéro de bloc par 512. En effet les cartes SDHC et SDXC sont block-addressed, le bloc 0 est à l'adresse 0, le bloc 1 à l'adresse 1 mais les cartes SD (comme la notre) sont bytes-addressed, le bloc 0 est à l'adresse 0, le bloc 1 à l'adresse 512... Nous avons donc créé une fonction intermédiaire qui multiplie le numero de bloc voulu par 512 (décalage de 9 bits) pour obtenir l'adresse du bloc. | Après avoir confirmé que nous pouvons lire le bloc 0 nous avons essayé de lire les blocs suivants, or nous obtenions une erreur <code>Address out of range</code>. Le problème vient du fait qu'on utilise une carte SD standard (pas SDHC) et qu'il faut multiplier le numéro de bloc par 512. En effet les cartes SDHC et SDXC sont block-addressed, le bloc 0 est à l'adresse 0, le bloc 1 à l'adresse 1 mais les cartes SD (comme la notre) sont bytes-addressed, le bloc 0 est à l'adresse 0, le bloc 1 à l'adresse 512... Nous avons donc créé une fonction intermédiaire qui multiplie le numero de bloc voulu par 512 (décalage de 9 bits) pour obtenir l'adresse du bloc. | ||
Nous avons aussi eu des problèmes avec les fonctions de lecture et écriture car nous avions déclaré <code>block</code> comme un int, sauf que sur un Atmega un int est codé sur 2 octets ce qui nous à vite posé des problèmes d'overflow. Ce problème a été résolu en déclarant <code>block</code> comme un <code>uint32_t</code>. | |||
<syntaxhighlight lang="c" line="1" start="1"> | <syntaxhighlight lang="c" line="1" start="1"> | ||
void lecture_block(uint32_t block){ | void lecture_block(uint32_t block){ | ||
Ligne 240 : | Ligne 241 : | ||
[[File:Pico2024_B8_schema_sysfich.png|thumb|center|800px|Schéma de notre système de fichiers]] | [[File:Pico2024_B8_schema_sysfich.png|thumb|center|800px|Schéma de notre système de fichiers]] | ||
<br style="clear: both;" /> | <br style="clear: both;" /> | ||
Nous avons utilisé deux blocs de la carte comme superblocs (table of content) qui contiennent des "descripteurs" de fichiers et pointent vers le premier bloc au sont stockées les données (4 blocs consécutifs). Nous avons choisi de que nos fichiers | Nous avons utilisé deux blocs de la carte comme superblocs (table of content) qui contiennent des "descripteurs" de fichiers et pointent vers le premier bloc au sont stockées les données (4 blocs consécutifs). Nous avons choisi de que nos fichiers ont un nom de 14 caractères maximum, ainsi notre structure Fichier à une taille de 16 octets et on peut en mettre 32 par superbloc (32*16 = 512), deux superblocs suffiront donc pour accéder à 64 fichiers. En tout notre système de fichiers utilise (seulement) 132096 octets sur la carte SD (4*64*512 + 2*512). | ||
<syntaxhighlight lang="c" line="1" start="1"> | <syntaxhighlight lang="c" line="1" start="1"> | ||
struct Fichier{ | struct Fichier{ | ||
Ligne 249 : | Ligne 250 : | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Nous avons | Nous avons commencé par implémenter la fonction <code>format()</code> qui efface tous les blocs de données et remplit les superblocs de structures Fichier disponibles, avec le starting_block adéquat et un nom vide. | ||
<syntaxhighlight lang="c" line="1" start="1"> | <syntaxhighlight lang="c" line="1" start="1"> | ||
Ligne 275 : | Ligne 276 : | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Pour la commande LS() on parcours les deux superblocs et on envoie sur le port série | Pour la commande LS() on parcours les deux superblocs et on envoie sur le port série les noms des fichiers présents sur la carte SD. | ||
<syntaxhighlight lang="c" line="1" start="1"> | <syntaxhighlight lang="c" line="1" start="1"> | ||
void LS(){ | void LS(){ | ||
Ligne 297 : | Ligne 298 : | ||
Nous avons ensuite implémenté les fonctions suivantes: | Nous avons ensuite implémenté les fonctions suivantes: | ||
* APPEND() : crée un fichier sur la carte SD, si il existe déjà les données passées en paramètre sont ajoutées à la fin du fichier existant | * APPEND() : crée un fichier sur la carte SD, si il existe déjà les données passées en paramètre sont ajoutées à la fin du fichier existant. | ||
* READ() : renvoie sur le port série le contenu du fichier demandé | * READ() : renvoie sur le port série le contenu du fichier demandé. | ||
* REMOVE() : supprime le fichier demandé, on "vide" son descripteur dans le superbloc puis on rempli les 4 blocs de données de 0x00 | * REMOVE() : supprime le fichier demandé, on "vide" son descripteur dans le superbloc puis on rempli les 4 blocs de données de 0x00. | ||
* RENAME() : permet de renommer un fichier, on remplace simplement sont nom dans son descripteur (pas de modification des blocs de données) | * RENAME() : permet de renommer un fichier, on remplace simplement sont nom dans son descripteur (pas de modification des blocs de données). | ||
* COPY() : copie d'un fichier source vers un nouveau fichier destination | * COPY() : copie d'un fichier source vers un nouveau fichier destination. | ||
* QUOTADISK() : donne le nombre d'emplacements de fichiers disponibles et le nombre total. | |||
Toutes ces fonctions et les fonctions intermédiaires sont dans le fichier <code>SysFich/sys_fichier.c</code> dans notre | Toutes ces fonctions et les fonctions intermédiaires sont dans le fichier <code>SysFich/sys_fichier.c</code> dans notre [https://gitea.plil.fr/lbonning/pico-se4_binome8 dépôt GIT] | ||
. | |||
Pour finir nous avons créé une interface en série pour envoyer les commandes à la carte mère et recevoir le contenu des fichiers. | Pour finir nous avons créé une interface en série pour envoyer les commandes à la carte mère et recevoir le contenu des fichiers. | ||
Ligne 309 : | Ligne 312 : | ||
<br style="clear: both;" /> | <br style="clear: both;" /> | ||
Sur la vidéo on peut bien constater que la carte SD crée bien les fichiers comme on le demande, on arrive à les lire (<code>63 6F 75 63 6F 75 20 63 27 65 73 74 20 6C 6F 75 69 73</code> en hexa donne bien le message <code>coucou c'est louis</code>), à | Sur la vidéo on peut bien constater que la carte SD crée bien les fichiers comme on le demande, on arrive à les lire (<code>63 6F 75 63 6F 75 20 63 27 65 73 74 20 6C 6F 75 69 73</code> en hexa donne bien le message <code>coucou c'est louis</code>), à concatener des données à la fin d'un fichier, de lire les différents fichiers, de les copier et également les supprimer à volonté. | ||
=Conclusion= | =Conclusion= | ||
Ce projet était très intéressant et très complet sur beaucoup d' | Ce projet était très intéressant et très complet sur beaucoup d'aspects différents de la formation, que ce soit pour la conception des cartes, la partie programmation avec l'ordonnanceur et la partie système de fichiers. Bien que le commencement fut très satisfaisant et que notre progression était plutôt rapide au début du projet, nous avons eu pas mal de soucis pour la partie gestion des fichiers sur la carte SD qui nous ont ralenti. Que ce soit les recherches pour comprendre le fonctionnement d'une carte SD ou encore réussir à communiquer avec par SPI, notre avancement a été ralenti pendant plusieurs séances. Nous n'avons malheureusement pas pu finir ce projet totalement, nous aurions par la suite dû gérer les connexions des différentes cartes filles. C'est à dire que celles-ci puissent utiliser le système de fichiers pour pouvoir enregistrer et lire sur la carte SD (envoie de commandes par SPI plutôt que UART). |
Version actuelle datée du 23 janvier 2025 à 08:44
Wiki du projet Pico BONNINGRE Louis LECOMTE Antoine.
Lors de ce projet nous allons réaliser un pico-ordinateur qui comporte plusieurs fonctionnalités qui sont implémentées grâce à des cartes filles que l'on vient brancher sur une carte mère.
PicoShield
Pour débuter cet ordinateur, nous allons tester les fonctionnalités de base grâce à un shield que nous connectons à une carte arduino.
Modification Shield
Modification du PCB du shield, ajout d'une puce mémoire dans le cas ou la connexion à la carte SD ne fonctionne pas.
Assemblage et test du Shield
Nous avons soudé les composants sur le shield puis testé les leds et un afficheur 7 segments connecté (en ISP) sur un des ports pour carte fille
Test de la carte SD
Nous avons testé le connecteur de carte SD, à l'aide de la bibliothèque arduino 'SD.h'. La carte est bien reconnue, nous pouvons voir la taille mémoire de la carte et les fichiers présents dessus.
Nous avons testé toutes les fonctionnalités de base avec le PicoShield, nous pouvons donc passer à la suite.
Ordonnanceur
Nous avons commencé par réaliser un ordonnanceur simple pour effectuer deux taches simples (clignotement de deux LEDs). Comme visible sur la vidéo, la LED bleue et la LED orange clignotent bien à des fréquences différentes, la LED rouge clignote à chaque appel de l'ordonnanceur.
Pour notre ordonnanceur il est important d'initialiser les taches, notamment leur pointeur de pile.
void init_task(int t){
uint16_t oldSP = SP;
SP = Taches[t].stack;
uint16_t adresse = (uint16_t)Taches[t].start;
asm volatile("push %0" : : "r" (adresse & 0x00ff));
asm volatile("push %0" : : "r" ((adresse & 0xff00) >> 8));
portSAVE_Registers();
Taches[t].stack = SP;
SP = oldSP;
}
A chaque interruption de l'ISR (toutes les 20ms) on sauvegarde le contexte de la tache en cours, l'ordonnanceur passe à la tache suivante et on restaure le contexte de cette tache.
ISR(TIMER1_COMPA_vect, ISR_NAKED){
/* Sauvegarde du contexte de la tâche interrompue */
portSAVE_Registers();
Taches[courant].stack = SP;
/* Appel à l'ordonnanceur */
ordonnanceur();
/* Récupération du contexte de la tâche ré-activée */
SP = Taches[courant].stack;
portRESTORE_Registers();
asm volatile("reti");
}
Tâche série
Une fois l’ordonnanceur capable de lancer deux tâches en parallèle, nous nous sommes attaqués à la communication série de la carte par USB. Pour ce faire, nous utilisons l’application minicom coté PC qui permet une connexion série. Il faut initialiser les paramètres de la connexion, notre carte renvoie le message reçu dans le terminal.
Ci joint les deux taches responsables de l'envoi et de la reception de données par le protocole série, serial_buffer
est une variable globale utilisée pour stocker les messages reçus et à envoyer. Les fonctions Serial_Transmit()
et Serial_Receive()
sont déclarées dans pico_serial.h
et proviennent de la documentation AVR.
void SerialWrite(){
while (1){
Serial_Transmit('\r\n');
Serial_Transmit(serial_buffer);
}
}
void SerialRead(){
while(1){
serial_buffer = Serial_Receive();
}
}
Lors de ce test notre atmega envoi "LOULOUTOINE" en boucle sur le port série.
Tâche 7 segments
Nous ajoutons maintenant une tâche permettant de communiquer avec l'écran 7 segments par la communication SPI (toujours en parallèle des autres tâches). Voici par exemple deux des fonctions SPI, il faut préalablement initialiser la communication SPI avec les registres SPI.
void SPI_send(uint8_t data) {
DISPLAY_PORT &= ~(1<<DISPLAY_PIN); //On met le pin CS de l'écran à l'état bas pour l'activer
SPDR = data;
while (!(SPSR & (1 << SPIF)));
DISPLAY_PORT |= (1<<DISPLAY_PIN); // On relache l'écran
}
void clearDisplaySPI() {
SPI_send(0x76); // Commande pour effacer l'écran et remettre le curseur a gauche
}
Pour afficher un caractère sur l'écran il suffit donc d'appeler SPI_send()
en passant en paramètre le caractère à envoyer.
Etat endormi
Une fois que les tâches ont été correctement séparées, il a fallu ajuster notre ordonnanceur pour garantir que les délais de chaque tâche soient respectés. En effet, avec notre méthode actuelle qui repose sur l'utilisation de la fonction _delays_ms()
à chaque ajout de tâche, le temps total d'exécution augmente de manière cumulative. Par exemple, avec deux tâches, un délai de 200 ms prendrait 400 ms, et avec trois tâches 600 ms. Pour résoudre ce problème, nous avons décidé d'ajouter un état à chaque tâche, permettant de les "endormir" lorsqu'elles ne sont pas appelées, et de les "réveiller" lorsqu'elles le sont. Grâce à ce système, chaque fois qu'une tâche endormie est exécutée, on décrémente son timer de 20 millisecondes à chaque cycle. Ainsi chaque tâche respecte son délai sans interférer avec l'exécution des autres tâches.
void Sleeping(){
for (int i = 0; i < NB_TASKS;i++){
if (Taches[i].etat.sit == ENDORMI) Taches[i].etat.time.tempsendormi -= 20;
if (Taches[i].etat.time.tempsendormi <= 0) {
Taches[i].etat.time.tempsendormi = 0;
Taches[i].etat.sit = ACTIF;
}
}
}
Notre nouvel ordonnanceur qui appelle Sleeping()
void ordonnanceur(){
PORTC ^= 0x01; // On fait clignoter une des led (J1) à chaque fois que l'ordonnanceur est appelé
Sleeping();
do{
courant ++;
if (courant == NB_TASKS) courant = 0;
} while (Taches[courant].etat.sit == ENDORMI);
}
Cette fonction endors la tâche pendant la durée t (en millisecondes). On remet ensuite le timer à 0 et on lance l'interruption manuellement.
void makeSleep(int t){
Taches[courant].etat.sit = ENDORMI;
Taches[courant].etat.time.tempsendormi = t;
TCNT1 = 0;
TIMER1_COMPA_vect();
}
Voici notre struture modifiée pour ajouter l'état endormi On utilise une union pour representer le temps d'endormissement.
#define ENDORMI 1
#define ACTIF 0
typedef union{
int tempsendormi;
} Time;
typedef struct{
int situation; //1 pour endormi, 0 pour éveillé
Time time;
} Etat;
typedef struct task{
void (*start)(void);
uint16_t stack;
Etat etat;
} task;
Carte mère
Parmi notre groupe, nous sommes le binôme en charge de la carte mère.
Nous avons décidé d'utiliser un Atmega 328p qui sera programmé avec avr-dude
pour cela notre carte mère comportera également un Atmega8u2 qui servira de programmateur et d'interface serie/USB.
Nous avons prévu une alimentation par un port USB C, ayant connecté les pins CC1 et CC2 à des resistances de 5.1k nous obtenons 5v et 3A max. Il sera donc simple d'alimenter notre carte avec un câble USB, chargeur de téléphone ou alimentation de Raspberry Pi par exemple et d'obtenir jusqu'à 15w ce qui est largement suffisant pour notre carte mère et les cartes filles des autres binômes.
Notre PCB est terminé, voici le schéma et un rendu du PCB final.
Les fichiers GERBER sont disponibles dans notre dépot GIT
Nous avons reçu notre PCB, pour commencer nous assemblons les composants minimums pour faire clignoter une LED avec le 328p.
En programmant par le port ISP avec une Arduino as ISP on arrive à faire clignoter la LED reliée sur PC1 du 328p. On soude ensuite le minimum pour tester l'AtMega8u2, on arrive à faire clignoter deux led reliées a ses broches.
Nous installons maintenant le firmware dans le 8u2 et le bootloader dans le 328p afin de pouvoir programmer le 328p en utilisant la connexion USB du 8u2.
Pour cela il faut d'abord effacer le 8u2 avec les commandes suivantes : avrdude -c stk500v1 -p atmega8u2 -P /dev/ttyACM0 -b 19200 -t
, Il faut ensuite envoyer la commande erase
et quit
On peux maintenant modifier les fuses pour notre crystal externe à 16MHz : avrdude -c stk500v1 -p atmega8u2 -P /dev/ttyACM0 -b 19200 -U lfuse:w:0xde:m -U hfuse:w:0xd9:m -U efuse:w:0xf4:m
Puis flasher le firmware 8u2 provenant d'une Arduino UNO : avrdude -c stk500v1 -p atmega8u2 -P /dev/ttyACM0 -b 19200 -U flash:w:8u2_firmware.hex
Pour la programmation du Bootloader du 328p on utilise directement la fonction "burn bootloader" de l'IDE Arduino.
Malheureusement notre carte n'est pas reconnue par l'ordinateur avec la commande lsusb
. Avec dmesg
nous voyons qu'un périphérique USB est détecté mais avec des erreurs, nous essayons de ressouder notre connecteur USB-C à l'air chaud et vérifions au multimètre que les pins sont bien soudés mais nous avons toujours les mêmes erreurs. Après vérification de notre schéma une erreur s'est glissée lors du commit 181625dc05 fait par monsieur Redon, les pins D+ et D- sont inversés. Nous modifions notre carte pour corriger cette inversion. Succès !! notre carte est maintenant bien visible en USB.
Après soudure des connecteurs HE10, des leds et de leurs résistances correspondantes, nous avons téleversé notre code pour l'ordonnanceur. Les leds clignotent bien et l'écran 7 segments connecté à la PicoMother fonctionne parfaitement et simultanément.
Nous testons également que la carte SD est détectable par notre carte mère et c'est bien le cas.
Système de fichier
Nous passons maintenant à la dernière partie du projet, le système de fichiers. Pour cela nous commençons par essayer de lire et d'écrire un secteur de la carte SD (en mode SPI une carte SD et accessible par blocs (secteurs) de 512 octets), nous utilisons la bibliothèque suivante pour faciliter la gestion de la carte SD :Bibliothèque SD. En modifiant cette bibliothèque on arrive à lire et écrire un secteur de la carte SD. Sur cette photo on lit le secteur 0 de la carte SD, 0x55AA à la fin du secteur signifie que la carte est actuellement formatée avec un système de fichier MBR.
Après avoir confirmé que nous pouvons lire le bloc 0 nous avons essayé de lire les blocs suivants, or nous obtenions une erreur Address out of range
. Le problème vient du fait qu'on utilise une carte SD standard (pas SDHC) et qu'il faut multiplier le numéro de bloc par 512. En effet les cartes SDHC et SDXC sont block-addressed, le bloc 0 est à l'adresse 0, le bloc 1 à l'adresse 1 mais les cartes SD (comme la notre) sont bytes-addressed, le bloc 0 est à l'adresse 0, le bloc 1 à l'adresse 512... Nous avons donc créé une fonction intermédiaire qui multiplie le numero de bloc voulu par 512 (décalage de 9 bits) pour obtenir l'adresse du bloc.
Nous avons aussi eu des problèmes avec les fonctions de lecture et écriture car nous avions déclaré block
comme un int, sauf que sur un Atmega un int est codé sur 2 octets ce qui nous à vite posé des problèmes d'overflow. Ce problème a été résolu en déclarant block
comme un uint32_t
.
void lecture_block(uint32_t block){
uint8_t res, token;
uint32_t addr = block << 9; //Multiplication par 512
// Lecture du bloc (secteur)
res = SD_readSingleBlock(addr, buffer, &token); //res reçoit un octet décrivant l'état de la carte SD (pas utilisé dans notre code)
// Impression en cas d'erreur
if(!(token & 0xF0)){
UART_pputs("Erreur :\r\n");
SD_printDataErrToken(token);
}
}
Comme on ne peut lire ou écrire qu'un bloc complet, nous avons implémenté un buffer d'une taille de 512 octets que l'on modifie puis écris dans un bloc de la carte SD ou qui reçoit un bloc complet de celle-ci, ce buffer utilise un quart de la mémoire vive de notre atmega (2ko sur un atmega328p).
Une fois les fonctions de lecture et d'écriture de blocs sur la carte SD adaptées à notre carte mère et carte SD nous avons commencé à créer notre pico système de fichiers.
Nous avons utilisé deux blocs de la carte comme superblocs (table of content) qui contiennent des "descripteurs" de fichiers et pointent vers le premier bloc au sont stockées les données (4 blocs consécutifs). Nous avons choisi de que nos fichiers ont un nom de 14 caractères maximum, ainsi notre structure Fichier à une taille de 16 octets et on peut en mettre 32 par superbloc (32*16 = 512), deux superblocs suffiront donc pour accéder à 64 fichiers. En tout notre système de fichiers utilise (seulement) 132096 octets sur la carte SD (4*64*512 + 2*512).
struct Fichier{
uint8_t available;
uint8_t starting_block;
char name[FILE_NAME];
}typedef Fichier; //Taille 16 octets
Nous avons commencé par implémenter la fonction format()
qui efface tous les blocs de données et remplit les superblocs de structures Fichier disponibles, avec le starting_block adéquat et un nom vide.
void FORMAT(){
UART_pputs("Formatage...\r\n");
//On rempli le buffer de 0x00
clear_buffer();
for(int i = 0; i < TOC_BLOCKS + (BLOCK_PAR_FILE * MAX_FILE); i++){
ecriture_block(i); //on remplit tous les blocs de données et la TOC avec les 0x00
}
Fichier fichier; //Création d'une struct Fichier
fichier.available = 0x01; //Le fichier est disponible
memset(fichier.name, 0x00, FILE_NAME); //nom vide (charactere 00 en hex)
//On écrit la TOC dans le buffer (on place des structs Fichier avec available -> 0x01)
for (int toc = 0; toc < TOC_BLOCKS; toc++) {
for (int j = 0; j < MAX_FILE; j++) {
fichier.starting_block = TOC_BLOCKS + (j * BLOCK_PAR_FILE) + (toc * FILE_PAR_TOC);
int start_index = j * sizeof(Fichier);
memcpy(&buffer[start_index], &fichier, sizeof(Fichier)); // Copie de la structure
}
ecriture_block(toc); // Écriture du buffer dans le superbloc correspondant
}
UART_pputs("Formatage terminé\r\n");
}
Pour la commande LS() on parcours les deux superblocs et on envoie sur le port série les noms des fichiers présents sur la carte SD.
void LS(){
UART_pputs("Fichier(s) sur la carte SD :\r\n");
for (int toc = 0; toc < TOC_BLOCKS; toc++){ // On boucle sur les differents blocs de TOC
lecture_block(toc);
for (int i = 0; i < FILE_PAR_TOC; i++){
// Création d'un struct fichier
Fichier fichier;
// On rempli Fichier avec les infos du buffer
memcpy(&fichier, &buffer[i * sizeof(Fichier)], sizeof(Fichier));
// Fichier non dispo donc un fichier est stocké sur la carte
if (fichier.available == 0x00) {
UART_puts(fichier.name); //Affichage du nom du fichier trouvé
UART_pputs("\r\n");
}
}
}
}
Nous avons ensuite implémenté les fonctions suivantes:
- APPEND() : crée un fichier sur la carte SD, si il existe déjà les données passées en paramètre sont ajoutées à la fin du fichier existant.
- READ() : renvoie sur le port série le contenu du fichier demandé.
- REMOVE() : supprime le fichier demandé, on "vide" son descripteur dans le superbloc puis on rempli les 4 blocs de données de 0x00.
- RENAME() : permet de renommer un fichier, on remplace simplement sont nom dans son descripteur (pas de modification des blocs de données).
- COPY() : copie d'un fichier source vers un nouveau fichier destination.
- QUOTADISK() : donne le nombre d'emplacements de fichiers disponibles et le nombre total.
Toutes ces fonctions et les fonctions intermédiaires sont dans le fichier SysFich/sys_fichier.c
dans notre dépôt GIT
.
Pour finir nous avons créé une interface en série pour envoyer les commandes à la carte mère et recevoir le contenu des fichiers.
Sur la vidéo on peut bien constater que la carte SD crée bien les fichiers comme on le demande, on arrive à les lire (63 6F 75 63 6F 75 20 63 27 65 73 74 20 6C 6F 75 69 73
en hexa donne bien le message coucou c'est louis
), à concatener des données à la fin d'un fichier, de lire les différents fichiers, de les copier et également les supprimer à volonté.
Conclusion
Ce projet était très intéressant et très complet sur beaucoup d'aspects différents de la formation, que ce soit pour la conception des cartes, la partie programmation avec l'ordonnanceur et la partie système de fichiers. Bien que le commencement fut très satisfaisant et que notre progression était plutôt rapide au début du projet, nous avons eu pas mal de soucis pour la partie gestion des fichiers sur la carte SD qui nous ont ralenti. Que ce soit les recherches pour comprendre le fonctionnement d'une carte SD ou encore réussir à communiquer avec par SPI, notre avancement a été ralenti pendant plusieurs séances. Nous n'avons malheureusement pas pu finir ce projet totalement, nous aurions par la suite dû gérer les connexions des différentes cartes filles. C'est à dire que celles-ci puissent utiliser le système de fichiers pour pouvoir enregistrer et lire sur la carte SD (envoie de commandes par SPI plutôt que UART).