jeudi 18 octobre 2018

Chapitre 49 : Baremetal Raspberry : écrire un fichier sur la carte SD


Dans ce projet, nous allons réaliser l’écriture d’un fichier sur la carte SD mais auparavant, nous allons reprendre la routine de lecture vue précédemment pour prendre en compte la présence de plusieurs clusters.
Dans un système de fichier  FAT32, un fichier est décomposé en clusters qui sont un ensemble de secteurs (blocs). Sur ma carte, un secteur contient 512 octets et un cluster 64 secteurs soit 32768 caractères. L’entrée du fichier dans le répertoire donne le N° du premier cluster. Si le fichier à une taille supérieure à 32768 caractères, les clusters supplémentaires nécessaires sont indiqués dans la table d’allocation des fichiers (FAT) se trouvant après les informations de la partition. J’ai eu un peu de mal à trouver cette table car il faut ajouter un nombre de secteurs réservés à l’adresse de la partition pour avoir l’emplacement du début de la table. Sur ma carte il y a 256 secteurs réservés mais ce chiffre est en hexadécimal et cette valeur bizarre m’a trompé plusieurs fois. Bref sur ma carte la table d’allocation commence au 2256h secteurs. Et le N° du cluster suivant est stocké sur 4 octets à la position indiqué par le N° du cluster précédant : c’est clair !!!
Prenons un exemple : un fichier a besoin de 2 clusters et le numéro du premier cluster stocké dans l’entrée est 5. Pour trouver le N° du 2iéme cluster il faut aller le chercher dans la table à la position 4* 5 soit 20 octets sur le secteur 2256h. Et on trouvera par exemple le N° du cluster 8 et il faudra aller chercher le suivant à la position 4 * 8 soit 32 octets sur le secteur 2256h. Et là on trouvera la valeur 0FFFFFFFh qui indique la fin de cette chaine. Un cluster libre est renseigné à 0.
 Ensuite avec chaque N° de cluster trouvé, il faut accéder aux données du fichier en calculant l’adresse comme suit :
Enlever 2 au N° du cluster trouvé (par exemple 5 -2 = 3), le multiplier par le nombre de secteurs par cluster (40h) et ajouter le tout à l’adresse du répertoire (sur ma carte c’est le secteur 4000) donc les données de ce cluster commence au secteur 4000h + (40h *3) = 40C0h et celui du cluster 8 au secteur 4000h + (40h *6) = 4180h.
Donc la routine de lecture précédente est modifiée pour balayer la chaine des clusters et lire les données de chaque cluster trouvé.
De plus j’en profite pour modifier la déclaration du buffer de lecture. En effet celui-ci sera déclaré par le module appelant et son adresse passée à la routine de lecture. Ainsi il sera possible de lire plusieurs fichiers. Vous remarquerez que contrairement à beaucoup de langage, nous n’avons pas de fonctions d’ouverture et de fermeture, et que nous lisons la totalité du fichier (il aurait été possible d’envisager une lecture bloc par bloc ou cluster par cluster).
Passons à l’écriture : Nous commençons par ajouter dans le module carteSd1.s une routine d’écriture d’un ou plusieurs blocs. Elle est pratiquement identique à la routine de lecture sauf le test du code de disponibilité de la carte et l’envoi des données du buffer vers la carte.
Dans le module repertoire.s, nous ajoutons les routines nécessaires à l’écriture. C’est compliqué car il y a plusieurs cas à gérer : le fichier existe déjà et dans ce cas si la nouvelle taille est supérieur à l’ancienne, il faut vérifier que cette taille ne nécessite pas un ou plusieurs clusters supplémentaires. Si c’est le cas, il faut parcourir la chaine des adresses des clusters pour trouver le dernier puis trouver des clusters vides et les ajouter à la chaine.
Attention : je n’ai pas programmé le cas où la taille est plus petite et qu’il faut libérer des clusters qui ne sont plus utilisés.
Ensuite, dans tous les cas, il faut écrire les données du buffer d’écriture dans les clusters et mettre à jour l’entrée du fichier dans le répertoire avec la nouvelle taille.
Si le fichier n’existe pas sur la carte, il faut tout créer : chercher un cluster vide dans la table d’allocation, allouer autant de clusters que nécessaire, écrire les données du buffer dans les clusters puis créer une entrée complète dans le répertoire principal.
Attention, je n’ai pas programmé l’écriture dans un sous répertoire !!! ni le retour arrière sur des données déjà écrites en cas d’erreur d’écriture d’une entité.
La création de l’entrée pose un petit problème : en effet il faut renseigner la date et l’heure de création et le timbre (timestamp) or le Raspberry ne comporte pas d’horloge interne alimentée par une pile pour conserver une date et une heure et nous n’avons pas d’accès à internet dans ce contexte baremetal pour y récupérer la date et heure Internet. Donc j’ai pris pour option de prendre la date et l’heure de l’entrée précédente !!!!!. Si vous continuez à développer dans ce contexte et si vous voulez avoir une date et une heure correcte, il vous faudra ajouter au Raspberry une carte horloge externe alimentée en permanence.
Je dois vous avouer que ces routines m’ont donné du mal à écrire et à mettre au point. Elles ne sont donc pas bien écrites et il faudrait les améliorer. L’utilisation de nombreux registres compliquent la compréhension de la programmation : il y a donc du travail à faire !!!
Pour tester tout cela, j’ai modifié le module kernel.s pour avoir des commandes plus évoluées comme :
                Lire nomdefichier  ou
                Ecrire nomdefichier1 nomdefichier2    (en fait c’est copier le fichier 1 dans le fichier 2)
                Ou  ecrire nomdefichier1 nomdefichier2   taille   pour tester les changements de taille.
J’ai aussi modifié la routine uart_send (dans le module mini_uart.s) pour vérifier les caractères envoyés car j’ai de temps en temps un redémarrage de window10 avec une erreur du driver prolific !!. Car je pensais que le problème venait de l’envoi de caractères non imprimables. Hélas cette correction n’est pas efficace !!!

Voilà voilà le projet complet.

Aucun commentaire:

Enregistrer un commentaire