ERREUR 404

Mise en oeuvre d'un host USB

Présentation :

L'article qui suit a pour but de présenter la mise en oeuvre d'un HOST USB FULL SPEED qui contrôle des périphériques MASS STORAGE (clés USB, appareils photos, disques durs..). Je vous conseille, avant de vous lancer dans la lecture de l'article, de lire les rappels indispensables que je propose à la page cours USB.

Beaucoup de monde utilise les clés USB pour transporter des données, et parfois certains électroniciens aimeraient pouvoir les utiliser pour donner à leurs montages une utilisation simple et conviviale. C'est pour répondre à cette demande que j'ai décidé de publier cet article. Le code que je propose mérite des améliorations que je suis prêt à prendre en compte si on me les soumet. Cependant, le code que je donne est fonctionnel sur une majorité de clés USB (celles que j'ai pu tester).

Je donne aussi les logs USB réalisés à l'aide d'un TRACKER110 de la société Ellisys. Même si vous ne possédez pas de TRACKER, vous pouvez visualiser le fichier log à l'aide du logiciel gratuit téléchargeable sur le site du constructeur. Cette stack USB utilise moins de 9Ko (4500 instructions assembleur 16 bits) de mémoire programme et moins de 540 octets de ram,ce qui lui permets d'être intégrée même dans les petits microcontrôleurs

Le hardware

Avant de se lancer dans la programmation, il faut commencer par réaliser une carte fonctionnelle qui contient le nécessaire pour réaliser un host USB; un microcontrôleur et un SIE. Un SIE (Serial Engine Interface) est l'interface matérielle entre le microcontrôleur et le bus USB. Cette interface gère toute la partie matérielle du bus : adaptateur de niveau, décodage, bit stuffing, stockage dans les buffers. Ce composant est appelé plus généralement PHY (comme physique).

Ici, j'ai décidé de mettre en oeuvre un composant Cypress : le SL811HS. Ce composant est un host/device qui peut travailler en FULL SPEED ou en LOW SPEED. Ici, nous ne travaillerons qu'en FULL SPEED ; les clés LOW SPEED seront rejetées. Les clés actuelles travaillent en FULL SPEED et pour la majorité en HIGH SPEED. A noter que les clés que l'on appelle courament USB2.0 sont généralement des clés travaillant en HIGH SPEED : ces clés seront supportées et travailleront à vitesse réduite (12 Mbits).


sl811

Ce composant est directement connecté au microcontrôleur à l'aide d'une interface parallèle 8 bits et quelques signaux de contrôle. Son alimentation doit être une alimentation en 3.3V ce qui implique l'utilisation d'un régulateur, ici réalisé avec un transistor et une diode zener( Q3 et D4). Notons au passage que le microcontrôleur est lui alimenté en 5V, mais il n'y a pas de problème en ce qui concerne la connexion au SL811 car celui-ci est "5V tolérant".

On retrouve un port série sur la carte qui permet de "débugger au printf()" (technique très appréciée à l'ENIB...).

Le port USB de type A reçoit les 2 fils de donnée du bus USB et son alimentation. L'alimentation de celui-ci est permanente, ce qui implique que le périphérique sera mis sous tension dès son branchement. De plus, il n'y aura pas de contrôle de surintensité. Au passage, on remarque la présence des résistances R12 et R13 montées en "pull down" sur les lignes de donnée ; la norme USB impose l'utilisation de ces deux résistances pour un host.

Voici la partie principale du schéma :

schéma

Les fonctions d'interface avec le SL811

Il faut à présent se pencher sur la partie logicielle de notre host. Dans un premier temps, il faut implémenter un certain nombre de fonctions qui permettront de communiquer avec le SIE. La communication avec le SIE est réalisée sur un bus de donnée 8 bits bidirectionnel. Cinq signaux sont nécessaires pour le contrôle des communications :

Au passage, il est nécessaire de remarquer que l'on n'utilise pas la communication DMA. Ainsi, il faudra laisser le signal NDREQ non connecté et forcer la signal NDACK à Vcc. On peut remarquer que la pin d'interruption du SL811 est connectée sur RB0. Cependant, elle ne sera pas utilisée dans le programme. Pour réaliser un host, cette pin ne nous sera pas utile mais j'ai préféré la connecter.

Le SL811 possède de nombreux registres 8 bits. L'écriture/lecture se réalise en deux étapes principales : on commence par écrire sur le port de donnée l'adresse du registre à lire/écrire, puis on effectue une écriture/lecture du registre. Il faudra bien faire attention à positionner le port de donnée en sortie lors de l'écriture de l'adresse ou d'une donnée en entrée lors de la lecture d'une donnée.

Exemple d'une lecture d'un registre :

lecture
Exemple d'une écriture d'un registre :

ecriture

Le SL811 permet aussi d'écrire/lire des registres en mode "burst", c'est à dire d'écrire/lire les octets les uns à la suite des autres sans avoir à spécifier l'adresse à chaque fois. Les fonctions sl811_write_buf(..) et sl811_read_buf(..) utilisent ce mode burst.

Vous pouvez télécharger la documentation du SL811 dans la rubrique "Télécharger les documentations" à la fin de l'article.

On peut considérer que l'interface microcontrôleur/SL811 s'arrête ici. A partir de maintenant, les fonctions sont des fonctions spécifiques à l'USB.

Les fonctions USB host

Il s'agit maintenant de créer des fonctions qui réalisent l'énumération et le contrôle des périphériques MASS STORAGE. (énumération : voir cours).

Comme nous l'avons vu, la première vérification à faire lors d'un branchement d'un périphérique est de détecter sa vitesse. Pour cela, on détecte un positionnement de la ligne data+ ou data- au niveau haut. Notre host ne permet pas de contrôler les clés LOW SPEED pour des raisons de simplicité. Si on branche un périphérique LOW SPEED, le programme sort de suite en échec. Si par contre, le périphérique est un périphérique FULL SPEED (ou HIGH SPEED) comme attendu, après une temporisation (100ms) permettant une stabilisation de l'alimentation, on envoie une commande RESET_USB. Cette commande a pour effet de positionner les deux lignes data+ et data- au niveau bas pendant 20ms, le périphérique sait ainsi qu'il doit s'initialiser (reset usb définit dans la norme).

La première requête USB à envoyer est, comme nous avons pu le voir, une commande de changement d'adresse (SET_ADDRESS). Si l'on regarde cette fonction, on s'aperçoit que cette requête commence par envoyer un paquet SETUP avec les 8 octets correspondants à la requête SET_ADDRESS. Ensuite, la fonction envoie un paquet IN auquel le périphérique répond un paquet DATA vide pour dire que tout c'est bien passé.

La deuxième requête que l'on trouve est la requête qui va permettre de vérifier qu'il s'agit bien d'un périphérique MASS_STORAGE. Cette requête, vous l'avez deviné, c'est GET_CONFIGURATION. Remarquez que la commande GET_CONFIGURATION a été écrite pour récupérer plusieurs descripteurs à la fois : on récupère ici 32 octets. On récupère en fait 4 descripteurs : le premier est le descripteur de CONFIGURATION, le second est le descripteur d'INTERFACE et les deux autres sont des descripteurs d'ENDPOINT. J'ai repéré les champs qui nous intéressent en les mettant en gras. Les octets que l'on récupère sont organisés ainsi :


OFFSET Description
0 bLength = 0x09
1 bDescriptorType = 0x02
2 to 3 wTotalLength = 0x0020
4 bNumInterface
5 bConfigurationValue
6 iConfiguration
7 bmAttributes. Reserved
8 bMaxPower (x2 to have mA)
9 bLength = 0x09
10 bDescriptorType = 0x04
11 bInterfaceNumber
12 bAlternateSetting
13 bNumEndpoints
14 bInterfaceClass = 0x08
15 bInterfaceSubClass = 0x05
16 bInterfaceProtocol = 0x50
17 iInterface
18 bLength = 0x09
19 bDescriptorType = 0x05
20 bEndpointAddress
21 bmAttributes. TransferType
22 to 23 wMaxPacketSize
24 bInterval
25 bLength = 0x09
26 bDescriptorType = 0x05
27 bEndpointAddress
28 bmAttributes.TransferType
29 to 30 wMaxPacketSize
31 bInterval

Le host va contrôler la classe du périphérique. Ici, on vérifie qu'il s'agit de la classe MASS_STORAGE qui est repérée par la constante 0x08. Le host va ensuite lire le numéro des ENPOINTs qu'il doit utiliser pour envoyer et recevoir les données vers/depuis le périphérique de stockage de masse.

Les fonctions MASS_STORAGE

L'énumération du périphérique étant réalisé (le minimum nécessaire), il est l'heure d'implémenter les fonctions MASS_STORAGE. Il y a de nombreuses fonctions MASS_STORAGE dans la norme "Bulk Only" : ici, je me contenterai sur les fonctions minimales au contrôle du périphérique :

Ces fonctions utilisent uniquement le transfert bulk ; pour faire simple, ces fonctions envoient les données dans des paquets OUT et reçoivent les données du périphérique dans des paquets IN.

Les fonctions sont des fonctions SCSI placées dans des paquets USB. Toutes les fonctions sont basées sur le même principe : envoi d'une commande dans un paquet CBW, envoi ou réception des données, et réception d'un paquet CSW pour récupérer le statut de la commande (voir cours).

Je ne commenterai pas plus en détail ces fonctions. Sachez que vous pouvez trouver toute la documentation nécessaire dans la rubrique télécharger à la fin de cet article.

Réalisation pratique

Tous les fichiers nécessaires sont disponibles au téléchargement : le PCB est un PCB simple face à ce qui permettra une réalisation plus aisée pour les amateurs qui se lanceraient dans l'aventure USB. Vous pouvez retrouvez la méthode de réalisation de circuits imprimés sur ma page Astuces.


Téléchargement de fichiers :
Liste des composants :
">
Nom Valeur Nom Valeur
R1 1K C1 220u
R2 1K C2 1u
R2 1K C2 1u
R3 100 C3 100n
R4 10K C4 100n
R5 470 C5 100n
R6 470 C6 100n
R7 470 C7 100n
R8 22 C8 100n
R9 22 C9 100n
R10 10K C10 100n
R11 10K C11 22p
R12 15K C12 22p
R13 15K C13 100n
R14 1M C15 22p
R15 100 C16 22p
IC1 PIC18F452 D1 AN4004
IC2 SL811HS D2 1N4148
IC3 7805 D3 1N4148
IC4 74AC14N D4 zener 3.9V
Q1 quartz 16MHz RS232 Sub D femelle 9 broches
Q2 quartz 12MHz SUPPLY Bornier 2 plots 5.08
Q3 BC337 USB Connecteur USB type A

Implantation des composants :

implantation
Première version de la carte : (le pcb a légèrement changé sur la nouvelle version)

photo PCB
Mise au point :

Je vous conseille d'utiliser un support pour le PIC. Le premier test qui est à faire est de contrôler les tensions d'alimentation. Pour cela, utilisez un bloc secteur 9 ou 12 V et connectez le sur le bornier "Supply". Contrôlez la tension 5V à la sortie du régulateur et le 3.3V sur l'émetteur du transistor Q3. Si ces tests sont concluants, vous pouvez insérer le PIC et le SL811 dans leur support respectif.


Téléchargement du code source

J'ai décidé de fournir les sources du firmware en version complète. Il sera diffusé sous licence GPL. Le programme est livré sans garantie. Je vous encourage à l'améliorer et à me soumettre vos améliorations que je rajouterai aux fichiers téléchargeables. Le code que je fournis est fonctionnel si l'on souhaite effectuer une écriture ou une lecture sur une clé USB. Il se peut que certaines clés ne fonctionnent pas avec ce code : en effet, malgré les normes USB et Mass Storage, on trouve toujours des fabriquants qui s'éloignent plus ou moins de ces normes. J'espère qu'avec cet article je vous aurai donné envie de vous lancer dans le monde de l'USB.

Le code fourni lit le secteur 0, il incrémente l'octet 508, et réécrit le secteur modifié.

Si j'ai choisi l'octet 508 du secteur, c'est en fait pour ne pas altérer un octet du secteur 0 qui est un octet nécessaire au système de fichier (FAT ou autre). Evidemment, cette carte ne gère pas le système de fichier : la carte permet d'écrire ou de lire la mémoire flash secteur par secteur mais ne permet pas d'écrire un fichier ou un dossier...

Pour optenir les fichiers sources et fichiers hex, veuillez cliquer sur l'icône ci-dessous :

Si vous apportez des modifications à ce code, merci de me les communiquer afin que je puisse les redistribuer à mon tour. Mon but, en distribuant ce code source libre, est d'aider les amateurs à se lancer dans l'aventure USB. Aussi, j'aimerai que ce code ne reste pas statique et que chacun puisse apporter ses améliorations et en fasse profiter la communauté. C'est le principe du logiciel libre. J'espère que vous jouerez le jeu...

Mettre en oeuvre le host

Comme vous l'avez vu dans la partie matérielle, la carte host possède un port série RS232 qui va permettre de débugger plus facilement. Vous n'êtes pas obligé d'utiliser le port série pour faire fonctionner le montage, celui-ci servira simplement à afficher les données lues. Cependant, je vous conseille dans un premier temps de programmer le microcontrôleur avec le programme d'origine et de regarder ce qu'il affiche sur le port série : ça permet de vérifier le bon fonctionnement.

Pour cela, utilisez un logiciel terminal série (ex : hyper terminal sous windows) et connectez votre carte sur le port série de l'ordinateur. Il faudra régler la configuration du port : 1 bit de start, 8 bits de données, 1 bits de stop, vitesse 115200 bits/s. Une animation est disponible ci-dessous, cliquez sur l'image pour démarrer l'animation.


animation

La led verte s'allume dès que le programme démarre. La led orange s'allume lorsque la clé est connectée, elle s'éteint dès que l'écriture est terminée.

Le code host utilise moins de 9Ko de memoire programme et moins de 540 octets de ram ce qui permet de l'intégrer facilement même dans les plus simples des systèmes embarqués. Comme je l'ai mentionné plus haut, cette stack USB ne gère pas le système de fichier. Pour cela, vous pouvez trouver de nombreuses informations utiles en cliquant sur ce lien : www.oryxmp3.com.

L'implémenation d'un système de fichier en plus de la stack USB complique le programme. Cependant, sans la gestion de système de fichier, on ne peut imaginer écrire des données qui puissent être relues sur un PC ! Cette implémentation est assez simple mais occupe très vite beaucoup de mémoire programme. Cependant, contrairement à l'implémentation d'une stack USB, on trouve très facilement de la documentation et des exemples sur internet.

Téléchargement des documentations

Vous pouvez télécharger le fichier log de l'exemple précédent. Vous pouvez ouvrir ce fichier avec le logiciel gratuit téléchargeable sur le site d'Ellisys.

La norme USB est téléchargeable sur le site www.usb.org

Mes autres pages USB