lundi 12 janvier 2015

    
   
 

Un lecteur / enregistreur de données sur carte SD pour port USB

 
Ce petit montage permet d’écrire des données sur une mémoire Secure Digital et de les relire, tout cela à partir du port USB d’un PC. Voyons comment cet appareil a été développé sur les plans matériel et logiciel, grâce à la mise en oeuvre d’un microcontrôleur doté d’un Universal Serial Bus.


Depuis quelques temps, on trouve dans le commerce, en tant que dispositifs autonomes ou fournis avec les APN, des appareils de lecture/écriture sur SDCard à interface USB ou, plus rarement, IEEE1394. Comme il s’agit d’appareils économiques, nous vous proposons ici une autoconstruction qui vous reviendra le même prix qu’un achat commercial, mais qui vous procurera la satisfaction d’une initiation à cette technologie sophistiquée.

Notre réalisation
Cet article vous propose en effet le schéma électrique et la réalisation pratique d’un tel lecteur/enregistreur de données sur carte SD pour port USB ; lisez-le, car c’est une bonne occasion d’expérimenter les concepts théoriques de la SD-Card (et que vous devez commencer à connaître puisque nous avons déjà publié maints montages utilisant une SD comme mémoire de données). Notre objectif est cette fois de réaliser un petit circuit qui permette d’écrire et de lire les blocs d’une SD en utilisant une interface USB. Le résultat final a été atteint grâce à la mise en oeuvre d’un PIC18F2550, entouré de quelques rares composants coomplémentaires, mais doté d’un remarquable programme résident. Sans parler du programme en Delphi tournant sur le PC. Il s’agit, une fois encore, d’une expérimentation didactique…mais dont les possibilités d’évolution sont multiples puisque, d’un côté nous intégrons un PIC à une énorme mémoire et que de l’autre nous l’interfaçons avec un système de communication rapide et efficace comme l’USB.
Ce sera une manière utile et passionnante de saisir sur le vif tout ce qu’on a acquis précédemment en théorie, ou même pour certains d’apprendre très simplement de façon concrète tout ce qu’une SD peut faire.

Le schéma électrique
Le circuit dont la figure 1 donne le schéma électrique est décidément fort simple ! Le seul problème que nous ayons rencontré concerne la réalisation d’un système qui puisse gérer au mieux la communication avec le PIC : celui-ci travaille, on le sait, avec des signaux en 0/5 V, alors que la SD nécessite des tensions comprises entre 0 et 3,6 V. Nous l’avons résolu une fois de plus (ce problème en effet n’est pas nouveau) de manière fort élégante : pour les lignes allant du PIC à la carte on utilise une diode schottky et une résistance de tirage.
Ainsi, quand le microcontrôleur présente à la SD le un logique, la diode est bloquée et la tension sur la broche de la carte est celle due à la résistance de tirage, soit 3,3 V (même si le PIC fournit 5 V). Quand le micro engendre un niveau logique bas, la diode conduit, ce qui porte à environ zéro volt également la broche de la carte. En ce qui concerne la connexion dans le sens inverse, de la SD vers le PIC, la chose est légèrement différente : pour rendre la traduction des niveaux logiques simple tout en restant efficace et précise, nous avons utilisé un “buffer/line driver” (pilote de ligne/tampon) en technologie HCT ; il s’agit d’un circuit intégré très économique mais d’une efficacité totale.
Nous en avons utilisé la version la plus courante, le 74HCT125 ; pour en habiliter les sorties, quatre broches OE1 à OE4 (Output Enable) sont utilisées : le signal d’entrée est présenté en sortie quand la ligne OE est au zéro logique. Comme nous voulons que le passage E vers S se fasse le plus rapidement possible, nous avons relié les broches OE directement à la masse. Par conséquent, chaque pilote de ligne est toujours actif. Les lignes d’entrée sont pleinement compatibles avec les signaux provenant des cartes SD, car les circuits intégrés basés sur la logique ACT/HCT acceptent en entrées des niveaux inférieurs aux TTL (par exemple 0/3V) et présentent en sortie des niveaux 0/5 V ; quand ils sont alimentés en 5 V, ils “voient” un niveau de 3 V comme si c’était un niveau TTL 5 V normal et fournissent en sortie 5 V, tension idéale pour commander directement les lignes d’entrée du PIC18F2550. La tension d’alimentation pour le PIC est prise directement sur le port USB (rappelons que ce standard prévoit la fourniture de 5 V 500 mA aux périphériques connectés). Le régulateur LM1086-CT-3.3, à partir de ce 5 V, engendre une tension de 3,3 V, suffisante pour faire fonctionner la SD.
Pour mettre en évidence les opérations que le micro doit accomplir, nous avons doté la platine de trois LED de couleurs différentes : la jaune indique que le circuit est bien alimenté ; la rouge qu’une opération de lecture ou d’écriture est en cours et la verte que la platine est prête à élaborer une commande envoyée par l’hôte. Si les deux LED rouge et verte sont allumées ensemble, c’est qu’une condition d’erreur a été détectée et qu’un transfert de données depuis ou vers la SD s’est produit (le “reset” du système doit par conséquent avoir lieu).
Vous vous êtes sans doute aperçus qu’aucune mémoire temporaire FRAM ou EEPROM n’est présente dans le circuit ; c’est parce que nous nous servons de la rapidité du bus US (Universal Serial Bus) pour envoyer directement à l’hôte les valeurs lues sur la carte ; de même, durant l’écriture, nous faisons en sorte que l’hôte envoie directement à la SD les données que nous voulons y inscrire. L’universalité de ce bus série (Universal Serial Bus) nous permet donc d’éviter d’alourdir le circuit avec un composant supplémentaire.
En outre, afin de rendre le système suffisamment efficace, nous aurions dû utiliser un “buffer” rapide, puisque sur une EEPROM traditionnelle chaque octet nécessite une pause de stabilisation de 10 ms. La lecture/écriture de données sur SD ne peut se faire que par blocs d’au moins 512 octets : par conséquent pour chaque opération de lecture/écriture le PIC attend l’arrivée des 512 octets, avec lesquels il forme un paquet qu’il envoie séquentiellement à la SD (ce qui crée de ce fait un interfaçage direct entre le bus SPI et le bus USB. La communication avec la SD se fait en mode SPI, au moyen de deux lignes de données série (une en entrée et une en sortie) synchronisées par un signal d’horloge et une ligne de “Chip Select” utile, dans le cas où l’on connecterait plusieurs cartes en même temps, pour indiquer avec laquelle on veut communiquer.

Figure 1 : Schéma électrique du lecteur/enregistreur de données sur SD-Card en USB.

Figure 2a : Schéma d’implantation des composants du lecteur/enregistreur de données sur SD-Card en USB.

Figure 2b : Dessin, à l’échelle 1, du circuit imprimé du lecteur/enregistreur de données sur SD-Card en USB.

Figure 3 : Photo d’un des prototypes du lecteur/enregistreur de données sur SDCard en USB.

Liste des composants
R1 ...... 4,7 k
R2 ...... 4,7 k
R3 ...... 4,7 k
R4 ...... 1,5 k
R5 ...... 1 k
R6 ...... 1 k
R7 ...... 1 k
C1 ...... 15 pF céramique
C2 ...... 15 pF céramique
C3 ...... 100 nF multicouche
C4 ...... 220 μF 25 V électrolytique
C5 ...... 100 nF multicouche
C6 ...... 220 μF 25 V électrolytique
C7 ...... 220 nF multicouche
D1 ...... BAT85
D2 ...... BAT85
D3 ...... BAT85
LD1 ..... LED 3 mm verte
LD2 ..... LED 3 mm rouge
LD3 ..... LED 3 mm jaune
U1 ...... PIC18F2550-ET611
U2 ...... LM1086-3.3
U3 ...... 74HC125
Q1 ...... quartz 20 MHz
SD1 ..... lecteur pour carte SD

Divers :
1 support 2 x 14
1 support 2 x 7
1 connecteur USB-B
Sauf spécification contraire, toutes les résistances sont des 1/4 W à 5 %.


Le programme résident
La complexité de ce projet vient, non pas du matériel, mais du programme résident et du “front-end” rédigé en Delphi. Pour le programme résident, nous avons utilisé le PICBasic afin de rendre plus simples les explications que nous vous devons ! Si vous avez suivi un cours de PICBasic, cela vous paraîtra même simpliste et constituera un exercice vous permettant de mettre en pratique la théorie acquise.
Commençons par analyser les parties les plus remarquables du dispositif, soit le fichier SDUSBdsc.asm. Pour ceux qui n’ont aucune idée de ce qu’est un descripteur, il faut préciser que le PIC18F2550 sera reconnu par le PC comme un périphérique HID (Human Interface Device) et que pour pouvoir communiquer de manière correcte au moyen de l’interface USB, il est nécessaire que l’ordinateur l’identifie comme tel. Le descripteur est une structure fournissant à l’hôte toutes les informations générales sur le dispositif et sur ses modes de fonctionnement.
Rappelons qu’en effet, sur le bus USB, c’est toujours l’ordinateur qui commande.
De plus, le descripteur établit quels et combien sont les ports de communication utilisables (les fameux “end-points”) : il est donc fondamental pour que le logiciel puisse envoyer les données au PIC et en recevoir de lui.
Dans le “Listing” 1 nous voyons les points les plus importants : nous établissons la taille (en octets) du “buffer” pour l’Endpoint0 (pour les dispositifs à faible vitesse la seule valeur est 8), la dimension du tableau (“array”) gardant la trace des configurations alternatives de chaque interface (rappelons qu’un dispositif peut avoir plusieurs interfaces, chacune avec plusieurs configurations, ou “settings”, que l’hôte peut sélectionner), le nombre maximum de “end-points” utilisables dans le projet (un seulement et on ne doit pas prendre en compte l’Endpoint0), le nombre de configurations et d’interfaces du dispositif, le mode de gestion des “buffers” associés à chaque “end-point”, la configuration du registre UCFG (USB Configuration Register) et la classe d’appartenance du dispositif (HID).
En particulier, en ce qui concerne le registre UCFG, nous avons décidé d’utiliser le transpondeur interne de la puce et une configuration de type “Low-Speed” (vitesse lente). Dans le tableau définissant les paramètres de la classe, nous avons précisé l’utilisation de l’Endpoint1 IN/OUT, avec un “buffer” à 8 octets (“Listing” 2).
Dans le descripteur Interface nous précisons le nombre de “end-points” utilisés (l’Endpoint0 ne comptant pas, car c’est celui de service). En outre, nous utiliserons deux ports de communication, un en sortie et l’autre en entrée (“Listing” 3).
Dans le descripteur des “end-points” (“Listing” 4) nous établissons que celui d’émission aura une longueur maximale de 8 bits (1 octet) et celui de réception de 5 octets. En effet, nous avons établi que le logiciel envoie au PIC des commandes de type CODAGE + 4 PARAMETRES (nous nous sommes inspirés de la structure définie dans les spécifications de Secure Digital pour les CMD17 et CMD24, soit les commandes de lecture/écriture qui sont suivies de l’adresse à 32 bits du bloc à lire/écrire. De l’autre côté, la carte répondra à travers des séquences d’octets. L’intervalle de “polling” (demande de la part de l’ordinateur) est fixé à 10 ms, ce qui est plus que suffisant pour les buts que nous nous sommes fixés.
Dans le descripteur Report (“Listing” 5) nous avons utilisé une structure hiérarchique. Les définitions des champs d’entrée et de sortie, dimensionnées en fonction de ce que nous avons dit plus haut, sont mises en évidence. On le voit, il s’agit de structures à 8 bits pour lesquelles on a fixé des limites logiques ordinaires (0-255).
Passons vite sur les Usage.
En ce qui concerne le descripteur String, nous avons utilisé les valeurs décrites dans le “Listing” 6. Ces valeurs sont reportées dans un panneau du logiciel dès que le dispositif achève le processus d’énumération.
Pour les néophytes, précisons que ce processus sert à faire en sorte que l’hôte (“host”) et le périphérique (“device”) fassent connaissance et puissent ensuite instaurer un canal de communication. Rappelons aussi que le nom du fichier contenant les descripteurs doit être inséré dans le fichier usbdesc.asm afin que le compilateur PBP l’inclue correctement dans la structure du .HEX que nous insèrerons ensuite dans la mémoire du PIC.
Nous avons entre autres utilisé, pour la compilation correcte du projet logiciel, un fichier p18f2550.inc dûment modifié, prévoyant une division de la fréquence d’oscillation en entrée égale à 5 et une division de la fréquence d’horloge du système égale à 4.
Ainsi, nous pouvons monter dans le circuit un quartz de 20 MHz et maintenir (grâce à l’activation d’un PLL) une horloge interne de 24 MHz. Pour la programmation du PIC nous utilisons le Melabs Programmer de microEngineering Labs et maintenons les configurations visible dans la fenêtre de dialogue de la figure 4.
Ceci dit, le moment est venu de passer au code proprement dit. Jetons un coup d’oeil au “Listing” 7, lequel comporte des déclarations : pour la communication avec la carte, nous utilisons le mode SPI comprenant un sous ensemble de commandes du protocole SD standard et c’est le plus simple à implémenter. Il n’utilise en effet que trois lignes pour communiquer : une pour l’horloge (SCK), une pour les données entrantes (SDI) et une autre pour les données sortantes (SDO) ; en plus, bien sûr, du signal CS (Chip Select) servant surtout à entrer en mode SPI et pour établir le commencement et la fin d’une transaction.
Nous avons en outre précisé les broches utilisées pour l’allumage et l’extinction des deux LED de signalisation.
Enfin vient la séquence des variables nécessaires pour le processus.
La paire IND0, IND1 sert à établir l’adresse du bloc à lire/écrire et compose la séquence des 32 bits envoyés par l’hôte comme liste de paramètres.
Le vecteur CMD, constitué de 5 octets, contient la séquence CODAGE + 4 PARAMETRES.
Le choix est dû à ce qui a été établi dans le descripteur report.
Comme le montre le “Listing” 8, le code d’initialisation est fort simple. Le PORTA est mis tout en sortie car nous n’utilisons que deux lignes pour commander les LED ; dans le PORTB on a prévu la broche RB2 comme ligne d’entrée pour les données provenant de la carte. L’extinction des deux LED est effectuée et on passe au code de mise en route du dispositif d’abord et de la SD ensuite.
Initialement la procédure d’énumération du dispositif est réclamée et une pause de stabilisation d’une demi seconde a lieu. A la fin de la procédure, le PIC est reconnu par le système d’exploitation de l’ordinateur et il est prêt à entrer en communication avec le logiciel que nous utiliserons pour la lecture/écriture de la carte. A la fin de cette procédure nous lançons une phase d’initialisation de la SD qui nous permettra d’abord de passer en mode SPI, puis d’établir les limites dimensionnelles des blocs de lecture/écriture. Pour résumer : nous pouvons distinguer deux phases fondamentales, RESET CARD (CMD0) et INITIALISATION (CMD1).
Dans le “Listing” 9 vous voyez comment se présente le code correspondant.
Notez que le “reset” est précédé d’une séquence de cycles d’horloge “à vide” (ou “Dummy Clock”, horloge fictive) comme le veulent les spécifications Secure Digital.
En particulier, le CMD0 est envoyé en maintenant la ligne CS à 0 : cette procédure permet d’entrer en mode SPI.
Une fois entrés, on lance l’initialisation du dispositif et on attend que la SD ait terminé cette phase (on vérifie l’arrivée de la réponse qu’elle envoie).
Rappelons qu’en mode SPI la carte est en mesure d’envoyer exclusivement deux types de réponse, une à 8 bits (RISP1) et une à 16 bits (RISP2), qui ont chacune une structure particulière de signalisation des erreurs. Notez que dans ces séquences un délai (“time-out”) a été prévu afin d’éviter qu’en cas d’erreur le système ne se bloque.
Dans ce cas, à travers un compteur (CONTA1), on établit la limite de répétition au delà de laquelle on suppose que la carte refuse de répondre ou que l’initialisation a échoué.La phase de définitions du nombre d’octets qui composeront le bloc de lecture/écriture de la SD est sautée, car on se sert de la longueur standard de 512 octets (“Listing” 10). Quand cette première procédure est terminée, nous allumons la LED verte (pour signaler que le dispositif est initialisé et qu’il est prêt à recevoir des commandes du logiciel tournant dans l’ordinateur).
Nous entrons en particulier dans une boucle d’attente sur le port USB, à travers lequel nous nous attendons à recevoir une séquence de 5 octets, dont le premier contiendra les codes de la commande à exécuter.
Dans le “Listing” 11 on voit clairement la séquence logique utilisée pour établir quelles instructions exécuter en fonction du premier octet reçu. Nous avons intentionnellement maintenu le codage utilisé dans la description du protocole SPI pour SD : la 17 lance la lecture (dans le protocole on utilise la CMD17) et la 24 l’écriture (CMD24). Si une quelconque autre commande arrive, elle est écartée et nous retournons à la lecture d’une nouvelle séquence de 5 octets. Notez que la fonction USBService, qui gère le module d’interface USB, est régulièrement appelée. Le code résultant, en effet, n’est plus basé sur la gestion des signaux d’interruption (“interrupt”), mais exclusivement sur une interrogation continue des registres associés au port devant identifier les diverses opérations à accomplir.
En ce qui concerne la procédure de lecture, nous avons convenu de recevoir l’adresse du bloc à lire dans les 4 derniers octets de la séquence de 5.
Nous lançons alors la commande de lecture en transférant les 512 octets sur le bus USB à travers l’instruction USBOUT. Les instructions résultantes sont visibles dans le “Listing” 12. Nous allumons la LED rouge pour signaler que l’élaboration de la commande est en cours. La CMD17 est envoyée à la SD en lui passant comme paramètres les 4 derniers octets reçus de l’ordinateur.
Quand la commande est acceptée, la carte envoie une séquence de bits particulière nommée “Start-Block”, après quoi l’émission commence en une séquence de 512 octets ; à chaque octet correspond immédiatement un envoi. A la fin, à travers la CMD13, le registre de Status de la carte est lu, afin de vérifier si l’opération a réussi ou si une erreur s’est produite.
Ensuite, un saut (“jump”) est fait jusqu’à l’étiquette AVVIOP où est localisé le code qui s’occupera d’attendre une nouvelle commande de l’hôte. La LED rouge est éteinte, la verte rallumée pour signaler à l’usager la possibilité d’envoyer une nouvelle demande de lecture/écriture. Et nous arrivons à l’étiquette SCRIVI contenant le code de l’écriture d’un bloc de 512 octets sur SD (“Listing” 13).
Nous allumons la LED rouge et nous valorisons adéquatement IND0 et IND1 avec l’adresse du secteur à écrire. A travers la CMD24 nous initialisons la transaction d’écriture et envoyons le “Start Block” (“Listing” 14).
L’hôte (“host”) nous envoie 104 paquets de 5 octets chacun : le premier se compose de l’identifiant de la commande à exécuter suivi de l’adresse du secteur correspondant. Les 103 restants sont constitués des valeurs à écrire sur la carte. Le dernier paquet a 3 octets de fermeture et donc, comme le montre le “listing”, nous ne déposons que les deux premiers dans la carte. Nous concluons donc l’opération d’écriture (“Listing” 15).
Signalons l’opération de AND logique pour l’extraction dudit “Data Response Token” envoyé pour chaque bloc de données écrit.
Cette séquence de bits devient très importante dans le cas d’opérations comme l’écriture de plusieurs blocs contigus (ici nous ne l’utiliserons pas). Si, en effet, un erreur se produit, le PIC doit bloquer la transaction à travers une CMD12.
Nous ne faisons pas autre chose que signaler l’erreur en allumant ensemble les LED rouge et verte (étiquettes ERRORE, ERRORE2) et en réinitialisant la carte. Ainsi, le circuit ne se bloque pas et retourne au cycle d’attente de la commande de la part de l’hôte. Comme pour l’opération de lecture, dans ce cas également nous utilisons la CMD13 pour connaître le Status de la SD. A la fin nous revenons à l’étiquette AVVIOP pour attendre la prochaine séquence de commande à travers l’USB.

"Listing" 1.

; **********************************************************************
; TABLEAU PARAMETRES GENERAUX
; **********************************************************************

#define EP0_BUFF_SIZE 8
#define MAX_NUM_INT 1
#define MAX_EP_NUMBER 1
#define NUM_CONFIGURATIONS 1
#define NUM_INTERFACES 1

#define MODE_PP _PPBM0
#define UCFG_VAL _TRINT|MODE_PP ; Low-Speed

#define USB_USE_HID

zzz L02 p38 zzz


"Listing" 2.

; **************************************************
; TABLEAU PARAMETRES CLASSES HID
; **************************************************

#define HID_INTF_ID 0x00
#define HID_UEP UEP1
#define HID_BD_OUT ep1Bo
#define HID_INT_OUT_EP_SIZE 8
#define HID_BD_IN ep1Bi
#define HID_INT_IN_EP_SIZE 8
#define HID_NUM_OF_DSC 1


"Listing" 3.

; **************************************************
; TABLEAU 3 DESCRIPTEUR INTERFACE
; **************************************************
Interface1
retlw (HIDDescriptor1-Interface1)/2 ; bLength
retlw DSC_INTF ; bDescriptorType
retlw 0x00 ; bInterfaceNumber
retlw 0x00 ; bAlternateSetting
retlw 0x02 ; bNumEndpoints
retlw 0x03 ; bInterfaceClass
retlw 0x01 ; bInterfaceSubClass
retlw 0x02 ; bInterface Protocol
retlw 0x05 ; iInterface


"Listing" 4.

; ******************************************************************
; TABLEAU 5 DESCRIPTEUR «END-POINTS»
; ******************************************************************
Endpoint1
retlw (Endpoint2-Endpoint1)/2 ; bLength
retlw DSC_EP ; bDescriptorType
retlw 0x81 ; bEndpointAddress
retlw 0x03 ; bmAttributes
retlw 0x01 ; wMaxPacketSize (low-b)
retlw 0x00 ; wMaxPacketSize (high-b)
retlw 0x0A ; bInterval
Endpoint2
retlw (EndConfig1-Endpoint2)/2 ; bLength
retlw DSC_EP ; bDescriptorType
retlw 0x01 ; bEndpointAddress
retlw 0x03 ; bmAttributes
retlw 0x05 ; wMaxPacketSize (low-b)
retlw 0x00 ; wMaxPacketSize (high-b)
retlw 0x0A ; bInterval
EndConfig1


"Listing" 5.

ReportDescriptor
retlw 0x06 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x01 ; Usage Page (low-b) (“Vendor Defined Page 1”)
retlw 0xFF ; Usage Page (high-b) (“Vendor Defined Page 1”)
retlw 0x09 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x01 ; Usage (“Vendor Defined Usage 1”)
retlw 0xA1 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x01 ; Collection (“Application”)
retlw 0x09 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x02 ; Usage (“Vendor Defined Usage 2”)
retlw 0xA1 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x00 ; Collection (“Physical”)
retlw 0x06 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x02 ; Usage Page (low-b) (“Vendor Defined Page 2”)
retlw 0xFF ; Usage Page (high-b) (“Vendor Defined Page 2”)
retlw 0x09 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x03 ; Usage (“Vendor Defined Usage 3”)
retlw 0x09 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x04 ; Usage (“Vendor Defined Usage 4”)
retlw 0x15 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x00 ; Logical Minimum (0)
retlw 0x26 ; Octet de préfixe (bTag,bType,bSize)
retlw 0xFF ; Logical Maximum (low-b) (255)
retlw 0x00 ; Logical Maximum (high-b)
retlw 0x75 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x08 ; Report Size (8 bits)
retlw 0x95 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x01 ; Report Count (1 campo dati)
retlw 0x81 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x02 ; Input (Data, Var, Abs)
retlw 0x09 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x05 ; Usage (“Vendor Defined Usage 5”)
retlw 0x15 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x00 ; Logical Minimum (0)
retlw 0x26 ; Octet de préfixe (bTag,bType,bSize)
retlw 0xFF ; Logical Maximum (low-b) (255)
retlw 0x00 ; Logical Maximum (high-b)
retlw 0x75 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x08 ; Report Size (8 bits)
retlw 0x95 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x05 ; Report Count (5 campi dati)
retlw 0x91 ; Octet de préfixe (bTag,bType,bSize)
retlw 0x02 ; Output (Data, Var, Abs)
retlw 0xC0 ; End Collection (“Physical”)
retlw 0xC0 ; End Collection (“Application”)
end_ReportDescriptor


"Listing" 6.

DEVICE NAME: Lecture/Ecriture SDCARD
MANIFACTURER: Microchip
SERIAL NUMBER: TAU333
CONFIGURATION: CFG1
INTERFACE: EP1/INOUT


"Listing" 7.

‘*********************************
‘*Connexion avec la SDCard
‘*********************************
SCK var PORTB.6 ‘CLOCK-CARD PIN5 (27 PIC)
SDI var PORTB.5 ‘DONNEES-ENTREE-CARD PIN2 (26 PIC)
SS var PORTB.4 ‘SELECTION-CARD PIN1 (25 PIC)
SDO var PORTB.2 ‘DONNEES-SORTIE-CARD PIN7 (23 PIC)
‘********
‘* LED
‘********
ROSSO var PORTA.3 ‘LED ROUGE
VERDE var PORTA.5 ‘LED VERTE
‘***************
‘* Application
‘***************
RISP1 var byte ‘REPONSE TYPE R1 PAR CARD (8bit)
RISP2 var word ‘REPONSE TYPE R2 PAR CARD (16bit)
IND1 var word ‘ADRESSE SDCARD WORD HAUTE BIT 16-31
IND0 var word ‘ADRESSE SDCARD WORD BASSE BIT 0-15
CAR var byte ‘CARACTERE A ECRIRE
CONTA VAR BYTE ‘compteur octets reçus
CMD VAR BYTE[5] ‘Séquence Commande+IND0+IND1
CONTA1 var word ‘COMPTEUR
CONTA2 var word ‘COMPTEUR
CTL var byte ‘OCTET DE CONTROLE FRAM
QX var byte ‘ANALYSE 4 BITS Réponse Données


"Listing" 8.

ADCON1 = 001111 ‘PIN RA Numériques
TRISA = 000000 ‘ PORTA en sortie
TRISB = 000100 ‘ RB6=CLOCK RB5=VERSO CARD
‘ RB4=SELEC CARD RB2=PAR CARD
PORTA = 0 ‘ Reset LED
ROSSO = 1
VERDE = 0

USBInit ‘ Processus d’énumération à la fin le dispositif ‘ entre dans l’état Configuré

Pause 500 ‘ Attente


"Listing" 9.

RESET:
SS=1
FOR CONTA1 = 1 TO 10
SHIFTOUT SDI,SCK,MSBFIRST,[$FF] ‘Envoie cycles d’horloge à vide
NEXT CONTA1
SS=0
PAUSE 50
‘********************************
‘* CMD0 maintenant SS à 0
‘********************************
SHIFTOUT SDI, SCK, MSBFIRST, [$40,$00,$00,$00,$00,$95] ‘Envoie CMD0
SHIFTIN SDO, SCK, MSBPRE, [RISP1] ‘Lis réponse R1 à partir de la Card
CONTA1 = 0
WHILE RISP1 <> 1
SHIFTIN SDO, SCK, MSBPRE, [RISP1] ‘Lit réponse R1 à partir de la Card
CONTA1 = CONTA1 + 1
IF CONTA1 >= 255 THEN ‘Time-Out écoulé, il sort
GOTO ERRORE
ENDIF
WEND
SS=1
PAUSE 50
SS=0


"Listing" 10.

‘********************************************************
‘* Envoie à répétition CMD1 jusqu’à réponse = 0
‘********************************************************
CONTA1 = 0
RISP1 = 1
WHILE RISP1 <> 0
SS=1
SHIFTOUT SDI,SCK,MSBFIRST,[$FF]
SHIFTIN SDO,SCK,MSBPRE,[RISP1]
SS=0
PAUSE 50
SHIFTOUT SDI,SCK,MSBFIRST,[$41,$00,$00,$00,$00,$FF,$FF] ‘Envoie CMD1
SHIFTIN SDO,SCK,MSBPRE,[RISP1]
CONTA1 = CONTA1 + 1
IF CONTA1 >= 255 THEN ‘Time-Out écoulé, il sort
GOTO ERRORE
ENDIF
WEND


"Listing" 11.

AVVIOP:
ROSSO = 0 ‘Extinction LED Rouge
VERDE = 1 ‘Allumage LED Verte
RICEVI:
USBService
CONTA = 5
USBIN 1,CMD,CONTA,RICEVI ‘Reçoit séquence commande
USBService
IF CMD[0] = 17 THEN ‘CMD17 = Lecture Secteur
GOTO LEGGI
ENDIF
IF CMD[0] = 24 THEN ‘CMD24 = Ecriture Secteur
GOTO SCRIVI
ENDIF
GOTO AVVIOP


"Listing" 12.

LEGGI:
ROSSO = 1 ‘Allumage LED Rouge
VERDE = 0 ‘Extinction LED Verte
IND0.BYTE1 = CMD[1] ‘Valorisation Adresse à lire
IND0.BYTE0 = CMD[2]
IND1.BYTE1 = CMD[3]
IND1.BYTE0 = CMD[4]

Initialisation de l’adresse du bloc à lire sur la carte à travers les 4 derniers octets de la séquence de 5.
‘* LECTURE 512 OCTETS SUR SDCARD SS=1 SHIFTOUT SDI,SCK,MSBFIRST,[$FF] SHIFTIN SDO,SCK,MSBPRE,[RISP1] SS=0 SHIFTOUT SDI,SCK,MSBFIRST,[$51,IND1.BYTE1,IND1.BYTE0,IND0.BYTE1,IND0.BYTE0,$FF]
Adresse du bloc à lire passé à la carte comme paramètre de CMD17.
‘Envoie CMD17 SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA1 = 0 WHILE RISP1<> 0 SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA1 = CONTA1 + 1 IF CONTA1 >= 255 THEN ‘Time-Out écoulé, il sort GOTO ERRORE ENDIF WEND ‘Réception START BLOCK SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA1 = 0 WHILE RISP1<> $FE
Réception du Start-Block 11111110.
SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA1 = CONTA1 + 1 IF CONTA1 >= 255 THEN ‘Time-Out écoulé, il sort GOTO ERRORE ENDIF WEND ‘Réception BLOC DONNEES FOR CONTA2= 0 TO 511 SHIFTIN SDO,SCK,MSBPRE,[CAR] INVIA: USBService USBOUT 1,CAR,1,INVIA ‘Envoie par USB
Réception de 1 octet et écriture sur FRAM.
NEXT CONTA2 ‘Réception CRC SHIFTIN SDO,SCK,MSBPRE,[RISP1] SHIFTIN SDO,SCK,MSBPRE,[RISP1] ‘Dès que la carte a fini de lire vérifie l’état de la carte ‘Envoie le CMD13 SS=1 SHIFTOUT SDI,SCK,MSBFIRST,[$FF] SHIFTIN SDO,SCK,MSBPRE,[RISP1] SS=0 SHIFTOUT SDI,SCK,MSBFIRST,[$4D,$00,$00,$00,$00,$FF]
Réception du statut de la carte à travers la réponse à 16 bits.
‘Reçois le status à 16 bits réponse format 2 SHIFTIN SDO,SCK,MSBPRE,[RISP2\16] CONTA2=0 WHILE RISP2.BYTE0 <> 0 SHIFTIN SDO,SCK,MSBPRE,[RISP2\16] CONTA2 = CONTA2 + 1 IF CONTA2 >= 255 THEN GOTO ERRORE2 ENDIF WEND GOTO AVVIOP "Listing" 13. SCRIVI: ROSSO = 1 ‘Allumage LED Rouge VERDE = 0 ‘Extinction LED Verte IND0.BYTE1 = CMD[1] ‘Valorisation Adresse à écrire IND0.BYTE0 = CMD[2] IND1.BYTE1 = CMD[3] IND1.BYTE0 = CMD[4] ‘------------------------------------------------------------ ‘* ECRITURE 512 OCTETS SUR SDCARD ‘------------------------------------------------------------ USBService SS=1 SHIFTOUT SDI,SCK,MSBFIRST,[$FF] SHIFTIN SDO,SCK,MSBPRE,[RISP1] ‘Envoie CMD24 écriture bloc sur SDCard SS=0 SHIFTOUT SDI,SCK,MSBFIRST,[$58,IND1.BYTE1,IND1.BYTE0,IND0.BYTE1,IND0.BYTE0,$FF]
Passage de l’adresse du bloc à écrire.
SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA2 = 0 WHILE RISP1 <> 0
Vérifie si la carte est prête à recevoir des données.
SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA2 = CONTA2 + 1 IF CONTA2 > 10000 THEN GOTO ERRORE ENDIF WEND ‘Envoie Start Block %11111110=$FE SHIFTOUT SDI,SCK,MSBFIRST,[$FE] "Listing" 14. FOR CONTA1 = 1 TO 102 RX5: USBService CONTA = 5 USBIN 1,CMD,CONTA,RX5
Réception bloc 5 octets.
USBService ‘Commence Ecriture sur SDCARD FOR CONTA = 0 TO 4 CAR = CMD[CONTA] SHIFTOUT SDI,SCK,MSBFIRST,[CAR]
Ecriture 5 derniers octets reçus.
NEXT CONTA NEXT CONTA1 RX6: USBService CONTA = 5 USBIN 1,CMD,CONTA,RX6 USBService CAR = CMD[0] SHIFTOUT SDI,SCK,MSBFIRST,[CAR] CAR = CMD[1] SHIFTOUT SDI,SCK,MSBFIRST,[CAR]
Fermeture du secteur.
"Listing" 15. SHIFTOUT SDI,SCK,MSBFIRST,[$FF,$FF] SHIFTIN SDO,SCK,MSBPRE,[RISP1] QX = 000000 QX = RISP1 & $0F
4 derniers bits réponse de la commande d’écriture : 0101 DONNEES ACCEPTEES 1011 DONNEES REFUSEES ERREUR CRC 1101 DONNEES REFUSEES ERREUR ECRITURE
IF QX <> 000101 THEN GOTO ERRORE ENDIF ‘Reçois le bit busy de la carte pendant l’écriture SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA2=0 WHILE RISP1 = 0 SHIFTIN SDO,SCK,MSBPRE,[RISP1] CONTA2 = CONTA2 + 1 IF CONTA2 >= 255 THEN GOTO ERRORE ENDIF WEND ‘Envoie le CMD13 SS=1 SHIFTOUT SDI,SCK,MSBFIRST,[$FF] SHIFTIN SDO,SCK,MSBPRE,[RISP1] SS=0 SHIFTOUT SDI,SCK,MSBFIRST,[$4D,$00,$00,$00,$00,$FF] SHIFTIN SDO,SCK,MSBPRE,[RISP2\16]
Réception status de la carte.
CONTA2=0 WHILE RISP2.BYTE0 <> 0 SHIFTIN SDO,SCK,MSBPRE,[RISP2\16] CONTA2 = CONTA2 + 1 IF CONTA2 >= 255 THEN GOTO ERRORE2 ENDIF WEND GoTo AVVIOP


Le logiciel pour PC
Voyons maintenant le logiciel que nous ferons tourner sur l’ordinateur pour commander la lecture/écriture de la SD.
Il a été écrit en Delphi et utilise principalement l’objet TJvHidDevice-Controller : il s’agit d’un composant conçu lors d’un projet du JEDI (Joint Endeavour of Delphi Innovators), c’est-à-dire une communauté internationale d’un millier de développeurs Delphi, dont le but est de diffuser cet environnement et de son frère cadet Kylix. L’interface du programme est très simple à utiliser et elle permet d’expérimenter immédiatement les fonctions que nous avons introduites dans le programme.
L’écran principal (figure 5) visualise trois panneaux : l’un contient les données concernant l’USB, un autre permet de choisir la dimension de la carte et le troisième comporte les poussoirs nécessaires aux fonctions de lecture/écriture. Le tout est complété par une simple grille de 512 cellules divisée en 32 lignes de 16 éléments.
Il est ainsi possible de contrôler et de modifier les valeurs. Afin d’éviter les erreurs, on a prévu une procédure de contrôle de la congruence des données insérées, dès qu’une lecture/écriture ou une export/importation est lancée.
En cas d’insertion d’une valeur non admissible (incongrue), un message indiquant le numéro de la ligne et celui de la colonne (soit les coordonnées de la cellule fautive) est visualisé.
Si nous relions le circuit au port USB (la SD étant déjà insérée dans le lecteur) à la suite du processus d’énumération, les champs identifiants sont chargés dans ce panneau (valeur insérées dans le descripteur String).
Dans la liste des dispositifs apparaît le mot lecture/écriture SD-Card. Le Status de la communication est mis à jour (Relié ou Débranché) et si nous regardons notre platine, nous voyons que la LED jaune s’allume la première (la tension d’alimentation arrive bien), suivie de la LED verte (la carte a dépassé la phase de “reset”, l’entrée en mode SPI et le processus d’initialisation). Notez que si on relie le circuit sans SD dans le lecteur, les LED verte et rouge s’allumeront ensemble (pour signaler l’erreur).
Après cette première phase, on peut se positionner sur le panneau Paramètres Card pour sélectionner à travers les deux petites flèches la capacité de la carte.
Ainsi, la limite maximale des secteurs accessibles est recalculée. Le nombre se réfère à la quantité de sections de 512 octets utilisables pour stocker les données.
On peut alors se positionner sur le panneau Procedure. Ici nous insérons le nombre de secteurs que nous voulons élaborer ; naturellement, la modification de ces champs est liée à la limite dimensionnelle de la carte que nous venons d’établir. Au moyen des poussoirs du panneau, nous pouvons lancer 5 fonctions fondamentales (nous les analysons ci-dessous).
Leggi : un clic sur ce poussoir lance une lecture du secteur que nous avons précisé dans le champ (Blocco Let/Scr) ; l’avancement de la procédure est contrôlable au moyen de la fenêtre d’applet rouge (voir figure 5) ; au fur et à mesure de l’arrivée de la séquence d’octets au PC, les valeurs sont insérées dans la grille et sont directement visibles et modifiables.
Scrivi : lance une information d’écriture du secteur que nous avons précisé dans le champ (Blocco Let/Scr) et charge directement les données de la grille.
Cancella : vide la grille en insérant la valeur FFh dans toutes les cellules.
Importa : charge les données dans la grille à partir d’un fichier externe.
Esporta : envoie les données de la grille vers un fichier externe. En ce qui concerne les deux dernières fonctions, songez que les fichiers utilisés ont une extension de type .sdc, ce ne sont toutefois que des fichiers texte.
Leur structure est des plus simples : à chaque ligne correspond une valeur à insérer dans la grille.
La séquence est produite en lisant chaque ligne de gauche à droite et de haut en bas, comme on le ferait en lisant une feuille de papier. Avec un clic sur le poussoir Importa on ouvre une fenêtre de dialogue, comme le montre la figure 6.
Il est donc possible de sélectionner le fichier et de le charger par un clic sur Apri. Un contrôle des données insérées est effectué pour signaler éventuellement une ligne erronée. La procédure d’exportation fonctionne de manière essentiellement complémentaire.
Là encore, une fenêtre de dialogue s’ouvre, dans laquelle on peut insérer le nom du fichier que l’on veut créer.
Après quoi, avec un clic sur le poussoir Salva, la grille est transférée dans le fichier sélectionné.

Figure 4 : Pour la programmation du PIC nous utilisons le Melabs Programmer de MicroEngineering Labs, en maintenant la configuration visible dans cette fenêtre de dialogue.

Figure 5 : L’écran principal du programme présente trois panneaux.

Figure 6 : Un clic sur le poussoir Importer ouvre cette fenêtre de dialogue.

La réalisation pratique
La réalisation pratique de ce lecteur/enregistreur de données sur SD-Card en USB est des plus simples et des plus rapides (toute la complexité de l’appareil venant du programme résident et du logiciel pour PC). La platine est constituée d’un petit circuit imprimé simple face, dont la figure 2b donne le dessin à l’échelle 1. Fabriquez-le au moyen de la méthode dite de la “pellicule bleue” et, quand vous l’avez devant vous, gravé, percé, étamé, commencez par insérer les deux supports de circuits intégrés et les deux “straps” (sous R3 et entre C4 et C6). Vérifiez attentivement vos soudures (ni court-circuit entre pistes ou pastilles ni soudure froide collée). Insérez et soudez ensuite tous les composants (comme le montrent les figures 2a et 3), en poursuivant par les résistances, condensateurs, diodes schottky, LED, quartz (debout) et régulateur (debout sans dissipateur et semelle métallique tournée vers l’extérieur de la platine) et en terminant par le “périphériques” : à savoir le connecteur USB-B pour circuit imprimé. Attention à l’orientation des composants polarisés : circuits intégrés (repère-détrompeurs en U bien orientés vers le bas, mais insérez-les à la toute fin), diodes, LED, régulateur et électrolytiques.
N’oubliez pas de souder aussi les languettes de blindage du connecteur USB-B. Vérifiez bien toutes les polarités et (encore une fois) la qualité des soudures.
Voilà pour la face “ composants”.
Retournez la platine et soudez très minutieusement (directement sur les pistes du “côté cuivre”) le lecteur de carte (ou porte-SD) SD1. Insérez les circuits intégrés. Le microcontrôleur est disponible déjà programmé en usine (voir nos annonceurs). Vérifiez tout encore une fois.
Vous pouvez maintenant installer la platine dans un boîtier plastique de dimensions appropriées : le couvercle sera percé de 3 trous pour le passage des LED ; l’un des petits côtés d’un évidement carré pour le connecteur USB et l’autre petit côté d’une fente pour insérer la SD-Card. L’alimentation en 5 V se fait donc par le port USB de l’ordinateur. Pour relier la platine à l’ordinateur, utilisez un banal câble USB.

Conclusion
Quoique fort simple matériellement, cet appareil permet de se familiariser avec ce nouveau support de mémoire et de réaliser des expérimentations formatrices avant d’aborder des arguments plus complexes : par exemple, ce circuit vous aidera à anlyser les secteurs clés du formatage FAT16 comme le “boot-sector” et la “root-directory” (voir les précédents articles consacrés à des montages mettant en oeuvre une SD-Card).
Mais en dehors de cet aspect didactique (qui, il est vrai, nous a une fois de plus guidés), ce lecteur/enregistreur de données USB vous sera utile pour acheminer vers un ordinateur des données écrites par des appareils de tous types dans une carte SD ; ou bien pour transférer dans cette même SD des fichiers présents dans le PC.
 

Aucun commentaire:

Enregistrer un commentaire