Index

carte

 

 

 

Le circuit de la carte est très simple à graver. Je conseil néanmoins de bien placer un support 40 broches afin de pouvoir aisément reprogrammer la puce si nécessaire.

L'étape qui se complique un peu plus est la programmation de la puce. Pour ceux qui ont l'habitude d'utiliser IC-Prog, sachez que je ne suis jamais arrivé à programmer un 18f4550 avec. Pour celà, je conseil le programme WinPic800 que vous pourrez trouver dans la rubrique téléchargement.

Il est impératif de respecter la configuration ci-dessous pour garantir le bon fonctionnement, après avoir charger le fichier hex dans le programme, allez dans l'onglet « setting » et configurez comme ci :

 

winpic

 

Les drivers de la carte se trouvent avec le reste des documents relatifs à cette carte mis à disposition dans la rubrique download.

 

Quelques petits tests effectués sur la carte :

Test de rapidité : Pour effectuer ce test, j'ai tout simplement placé deux timers dans le programme, un premier qui appel la fonction en continu, l'autre qui active le premier timer pendant 1 seconde et le désactive pendant 1 seconde. Les résultats sont que la carte envoie et reçoit en moyenne 10 packet par seconde. Soit peut exécuter environ 300 instructions par seconde. Pour certaine application, ce n'est pas rapide du tout, pour d'autre application c'est plus qu'acceptable.

Test de l'acquisition ADC : Le but de ce test est de vérifier la qualité de l'acquisition analogique de la carte dans la configuration par défaut. Ce test semble peut-être un peu stupide, mais connaissant le résultat je peux affirmer le contraire.
J'ai effectué le test sur deux sources de tensions différentes, la première mon alimentation de laboratoire réglée sur 3.47 Volt, résultat après 64 mesures en continues :

 

347

Le résultat est accablant, soit mon alimentation n'est absolument pas stable, soit le convertisseur analogique est vraiment un jouet.

Pour en avoir le cœur net, j'ai effectué un deuxième test sur une pile, mesurée au multimètre à 1.287V, le résultat :

1287

Il est évident que devant un pareil résultat, on ne peut que supposer un problème. Il ne s'agit en fait pas du convertisseur qui serait un jouet mais de la tension de référence qui n'est pas stable. En effet, comme référence par défaut, le pic utilise la tension de l'USB, si maintenant on refait ces test en configurant la puce pour qu'elle prenne un référentiel extérieur stable… Les mesures sont parfaites. Si vous souhaiter utiliser le convertisseur de façon précise, il est préférable d'imposer une tension de référence externe, soit de filtrer parfaitement votre tension d'alimentation. Pour celà vous pouvez placer des capacités de l'ordre de 220µF entre le 5V et la masse, des capacités de 100nF au plus près des broches d'alimentation et une self en série sur le 5Volt de manière à éliminer les parasites.

 

Pouvoir contrôler des processus extérieurs via une carte USB était un projet qui me bottait, je me suis donc lancé dans une première carte permettant de contrôler 16 entrées/sorties, et 4 entrées analogiques. Je préfère préciser tout de suite, qu'il s'agit d'un premier développement en USB, et que le terme USB ne signifie pas que la carte est ultra rapide.

Le port USB étant un port particulier, il ne peut pas être utilisé comme le port parallèle, il est nécessaire d'avoir une puce qui puisse traiter la communication, recevoir les packets et en renvoyer. La première étape était donc de trouver une puce… Ma recherche s'est directement orientée vers Microchip® car je possède un programmateur. La puce retenue est un 18f4550 très simple à mettre en œuvre. En effet 4 capacités, 1 quartz, 3 résistances suffisent pour la faire fonctionner.

 

schema18f4550 2

 

Au centre du schéma, nous retrouvons le PIC18F4550, celui-ci est cadencé par un quartz à 20MHz. Nous avons aussi câblé sur ce schéma 4 entrées analogiques: AN0, AN1, AN2 et AN3. Sur les broches RC0 et RC1 nous plaçons deux leds qui nous servirons pour notre application. Sur la broche RC2 un switch qui servira aussi pour cet exemple. Le port B et D restent inutilisés pour notre article mais nous plaçons la possibilité d'étendre les fonctionnalités de la carte en installant des borniers.

Les capacités C1 et C2 sont de 22pf. Pour R5, R3, R4, R7 nous placerons simplement des résistances de 1k. Attention de ne pas se tromper pour le connecteur USB. Il s'agit d'un connecteur femelle de Type B. Pour la connecter au PC, j'utilise un cordon d'imprimante A vers B, le même que les cordons HP.

 

Le Firmware

On peut trouver sur le site de microchip des exemples de programmes pour le 18f4550. Il existe trois méthodes pour mettre en œuvre cette puce USB, soit d'utiliser le driver HID (Human interface device, qui est le protocole utilisé dans tout ce qui est souris, clavier, gamepad), soit un driver qui simule le port Com qui rend alors facilement toutes les applications qui fonctionnaient sur port série convertissable en USB sans devoir changer le programme qui l'utilise, soit un driver de Microchip mpusbapi.dll. L'option retenue est cette dernière.

Pour communiquer avec l'ordinateur, la puce échange avec celui-ci un packet d'octet. L'idée pour cette première carte n'est pas d'envoyer un packet par instruction mais d'envoyer une série d'instructions en même temps. Une instruction est par exemple de changer l'état d'un des port, de lancer une conversion analogique vers numérique, de modifier les paramètre des convertisseur, d'effectuer une pause d'autant de nano secondes.

Je me suis donc basé sur un programme d'exemple de microchip pour écrire un premier code. Pour écrire ce code il est impératif d'avoir MPLab muni du compilateur C18 disponible gratuitement pour les étudiants.

L'ordinateur envoie un packet de 64octets au 18f4550, chaque octet est soit une instruction (les codes de celles-ci plus bas) soit le ou les paramètres de ces instructions. Le premier octet est toujours une instruction, les octets suivants ses paramètres, le dernier paramètre est suivi de l'instruction suivante.

 

Les instructions
0 : rien
1 à 5 suivit d'un paramètre : attribue le paramètre au registre TRIS[A..E]
6 à 10 suivit d'un paramètre : attribue le paramètre au registre PORT[A..E]
11 à 15 suivit d'un paramètre : attribue le paramètre au registre LAT[A..E]
16 à 20 suivit d'un paramètre : ajoute le paramètre au registre TRIS[A..E]
21 à 25 suivit d'un paramètre : ajoute le paramètre au registre PORT[A..E]
26 à 30 suivit d'un paramètre : ajoute le paramètre au registre LAT[A..E]
31 à 35 suivit d'un paramètre : retire le paramètre au registre TRIS[A..E]
36 à 40 suivit d'un paramètre : retire le paramètre au registre PORT[A..E]
41 à 45 suivit d'un paramètre : retire le paramètre au registre LAT[A..E]

 

50 suivit de trois paramètre : conversion analogique vers numérique sur l'entrée donnée par le paramètre 1, le paramètre 2 recevra le registre ADRSH et le paramètre 3 recevra ADRSL. La configuration de la conversion est :

 

/* 12 TAD FOSC/32 */
ADCON2=42;
/* AN0-15, VREF */
ADCON1 = 0b00001011;
/* Turn on the ADC */
ADCON0bits.ADON = 1;
ADCON0bits.CHS3 = 0;
ADCON0bits.CHS2 = 0;

 

51 suivit de trois paramètres : conversion analogique vers numérique sur l'entrée donnée par le paramètre 1, le paramètre 2 recevra le registre ADRSH et le paramètre 3 recevra ADRSL. La configuration de la conversion aura pu être configurée préalablement par l'utilisateur, a noter que cette configuration restera enregistrée tant que la puce sera sous tension et que l'instruction 50 ne sera pas appelée.

 

52 à 54 suivit d'un paramètre : attribue le paramètre au registre ADCON[0..2]

60 suivit d'un paramètre : effectue une pause de 10 * le paramètre * 200ns.
61 suivit d'un paramètre : effectue une pause de 100 * le paramètre * 200ns.
62 suivit d'un paramètre : effectue une pause de 1000 * le paramètre * 200ns.

101 à 105 suivit d'un paramètre : attribue le registre TRIS[A..E] au paramètre
106 à 110 suivit d'un paramètre : attribue le registre PORT[A..E] au paramètre
111 à 115 suivit d'un paramètre : attribue le registre LAT[A..E] au paramètre

 

Logiciel

logiciel

 

Point de vue du logiciel pour la contrôler, j'ai préparer un petit exemple en Delphi. Le programme permet d'envoyer 20 octets à la carte, on peut envoyer 64 octets, mais pour cela fallait que j'ajoute beaucoup de spinedit, et franchement, ça devenait long ;).

La fonction la plus intéressante de ce programme est :

 

SendReceivePacket(@send_buf,64,@receive_buf,RecvLength,100,100)

 

Cette fonction envoie en premier paramètre l'adresse d'un tableau de 64 éléments qui contient les octets à envoyer, le deuxième paramètre contient le nombre d'octets à envoyer, attention ne pas changer ces paramètres, le firmware est fait pour recevoir 64 octet et en renvoyer 64. Le troisième paramètre est l'adresse du tableau qui va recevoir les données de la carte, le cinquième est le nombre de données qui doivent être reçues, soit 64. Et les deux derniers sont les délais maximum que l'on veut bien attendre pour envoyer et pour recevoir. Ici les délais sont de 100ms, si vous décidez d'utiliser beaucoup de fois des fonctions pauses il est préférable de monter ce délai à 1000ms.

 

Une fois les informations envoyées, le résultat apparaît à l'écran dans le MemoText.

 

On peut aussi remarquer dans le programme, cette ligne-ci :

 

Const vid_pid : PCHAR = 'vid_04d8&pid_000b' + #0;


Il s'agit d'une ligne très importante, tous les périphériques USB possèdent deux numéros, le Vendor ID et le Product ID, ils permettent de différencier les différents produits USB, attribuer à chaque carte son driver, que chaque programme communique avec le bon périphérique sans mélanger les informations. Dans notre cas, c'est 04d8 et 000b, nous y reviendrons prochainement lors du montage de la carte.

 

Les codes sources du programme et du firmware sont disponibles en téléchargement, il m'est très compliqué d'en expliquer l'intégralité.

 

Le logiciel windows :

 

L'exemple de client que nous allons écrire sera en Delphi. Il existe des versions gratuite de Delphi disponible sur http://www.codegear.com.

Pour nous simplifier la tâche, nous allons utiliser une DLL écrite par Mecanique (http://www.mecanique.co.uk). Dans le dossier téléchargeable à la fin de l'article, vous trouverez cette DLL. Celle-ci était fournir avec le logiciel EasyHID, les copyright de ce dernier nous empêchant de le distribuer, nous ne vous transmettrons que la DLL et le code source du programme que nous avons réalisé.

 

Le logiciel - Exploration du code

Premièrement nous avons quatre constantes importantes :

BufferInSize = 64;
BufferOutSize = 64;
VENDOR_ID = 1240;
PRODUCT_ID = 0005;

Nous nous rappellerons que lorsque nous avons travaillé le firmware de la carte, nous avons brièvement parlé du descripteur, dans lequel nous configurons le VID (Vendor ID) PID (product ID). Ces deux valeurs identifiant auprès de l'ordinateur le périphérique branché. Dans le client qui va communiquer avec la carte, ces deux valeurs doivent se retrouver. Nous avons aussi la taille du buffer d'entrée et de sortie que nous avons configuré à 64 octets.

Nous sautons volontairement dans le code de nombreuses lignes de codes propres à la création des objets et la déclaration des procédures qui est automatique générée par delphi pour en arriver à l'envoie des informations. Pour ça nous avons un petit commentaire bien sympathique que nous allons analyser :

(*
if you want to write some data to the USB device, then
try using something like this...
// first element is report ID, set to 0...
FBufferOut[0] := $0;
// fill the buffer with some data...
for index := 1 to BufferOutSize do
FBufferOut[index] := index;
DevHandle := GetHandle(VendorID, ProductID);
Write(DevHandle,@FBufferOut);
*)

Le programme nous trouvons un tableau utilisable dans toute l'application de 65 éléments qui s'appelle FBufferOut. C'est dans ce tableau que l'on place les données à envoyer. Pourquoi 65 ??? Parce que l'élément 0 du tableau c'est le reportID, il devra toujours est mis à 0 comme indiqué dans le commentaire. Nous pouvons ensuite mettre dans les case 1 à 64 toutes nos données.

Une fois les données prêtes, nous devons créer la communication avec la carte. La première étape est de dire au programme à quelle adresse il doit envoyer son paquet. Pour ça DevHandler, variable de type cardinal, se voit attribuer une valeur, et cette valeur sera donnée par la fonction GetHandle (VendorID, ProductID). C'est là que l'on remarque l'importance de ces deux nombres. En effet lorsque l'on connecte la carte à l'ordinateur, celui-ci lui octroit un « Handle » et pour retrouver cet « Handle » et pouvoir communiquer avec la carte, il faut demander à l'ordinateur quel valeur il a attribué à la carte possédant tel et tel VID et PID. Il nous suffit plus que d'envoyer le tout par la fonction Write(DevHandle, @FBufferOut) . « @ » Car nous envoyons à la fonction la référence du tableau contenant les informations afin que celle-ci récupére chaque élément en mémoire.

La lecture de donnée se passe de façon différente. Nous avons dans le code du programme, une fonction préécrite :

 

function TForm1.USBEvent(var Msg: TMessage): Boolean;

 

Cette fonction réagit à chaque événement qui se produit sur le port USB. Si l'on connecte ou déconnecte une carte… Si elle envoie des données… C'est-à-dire que chaque fois qu'il va y avoir un échange entre une carte et l'ordinateur, cette fonction va réagir automatiquement. Et dans cette fonction nous avons justement :

 

NOTIFY_READ :
begin
DevHandle := Msg.LParam; // handle of HID device in this message
if (GetVendorID(DevHandle) = VENDOR_ID) and (GetProductID(DevHandle) = PRODUCT_ID) then begin
// read the data - remember that first byte is report ID...
Read(DevHandle,@FBufferIn);
// process data here...
end;

 

Une fois que la carte va créer un évènement en voulant envoyer quelque chose, le logiciel va détecter qu'il s'agit de donnée à recevoir, il va alors entrer dans cette partie de procédure NOTIFY_READ.

Première étape il va regardé le « Handle » de la carte qui envoie une donnée, et va comparé celui-ci au « Handle » de notre carte, s'il s'agit du bon, il va lire les données envoyées par la carte. Nous pouvons alors placer notre code à la suite de cette instruction Read(DevHandle, @FBufferIn). Toutes les données reçues se trouveront dans le tableau FBufferIn.

 

Le Logiciel - Réalisation de notre interface

 

interface

 

 

Pour cette opération, nous aurons besoins de :

3 Labels
1 Gauge
2 Boutons
2 SpinEdits
1 Memo

Placé comme sur l'image ci-dessus.

La gauge nous donnera en pourcent la valeur de la tension par rapport 5V, le bouton "mesurer" demandera une mesure, le premier label nous affichera la valeur mesurée, le bouton "Clignoter" enverra un paquet avec le nombre de clignotement et la période de ceux-ci. Et les deux spinedits permettront de configurer ces deux derniers paramètres.

Pour demander une mesure, nous devons simplement envoyer un paquet où l'élément 1 du paquet vaut 0 :

 

// mise à 0 du buffer
FBufferOut[1] := $0; // on met le buffer[0] a 0

// Attribution de la carte usb
DevHandle := GetHandle(VENDOR_ID , PRODUCT_ID);
Write(DevHandle,@FBufferOut);

 

Pour faire clignoter la led, nous devons placer 1 dans l'élément 1 du buffer, placer le nombre de fois dans l'élément 2 et la pause entre chaque changement d'état dans l'élément 3

 

// mise à 0 du buffer
FBufferOut [1]:= 1 ;
FBufferOut [2]:= spinedit1.value ; // N° de fois
FBufferOut [3]:= spinedit2.value ; // la pause

// Attribution de la carte usb
DevHandle := GetHandle(VENDOR_ID , PRODUCT_ID);
Write(DevHandle,@FBufferOut);

 

Petit conseil pour éviter les erreurs, dans les paramètres du spinedit, placez que celui-ci ne peut pas prendre de valeur supérieur à 255.

Maintenant la réception des données, comme nous l'avons vu précédemment, lorsque la carte va nous envoyer quelque chose, le programme va directement réagir, nous allons donc créer une nouvelle fonction « Refresh » qui sera appelée par la fonction USBEvent lorsque celle-ci réagira pour un envoie de donnée :

 

NOTIFY_READ :
begin
DevHandle := Msg.LParam; // handle of HID device in this message
if (GetVendorID(DevHandle) = VENDOR_ID) and (GetProductID(DevHandle) = PRODUCT_ID) thenbegin
// read the data - remember that first byte is report ID...
Read(DevHandle,@FBufferIn);
Refresh ; // On appelle alors la fonction refresh !
// process data here...
end;

 

La fonction "refresh" va premièrement regarder ce qui lui est envoyé, la carte enverra en élément 1 du paquet '255' s'il s'agit d'une mesure, le seul cas qui nous intéresse. S'il s'agit bien de 255, l'élément 2 possédera les 8 bits de poids fort et l'élément 3 les deux bits de poids faible restant de la conversion analogique (qui s'effectue sur 10 bits, ouf on a assez de doigts). Par une petite opération mathématique, nous reconstituons une seule valeur binaire regroupant les deux parties du message. Et nous affichons le résultat :

 

function TForm1.Refresh(): integer;
var i : integer;
var valeur : integer;
begin
if (FBufferIn[1]=255) then begin
valeur := FBufferIn[2]*4 + round(FBufferIn[3]/64);
gauge1.Progress :=round(100*valeur/1024);
label3.caption :='La valeur est : '+floattostrf(5 * valeur/1024,ffFixed,3,2)+' Volt';
end;
result := 0;
end;

 

Installation du tout :

proginaction

 

Il nous reste plus qu'à monter le tout, nous laisserons le soin à l'utilisateur de monter la carte par ses propres moyens, sachant que schéma et pcb sont mis à disposition. Pour la programmation du 18f4550, le programmateur de pic disponible sur ce site fonctionne très bien avec le 18f4550 en utilisant le logiciel WinPic800. Il ne suffit plus que de charger le firmware avec ce programme, toutes les options se configureront automatiquement.

Pour le logiciel, voici un screen de l'application en fonctionnement, vous remarquerez que la fonction « mesurer » est identique à l'action de pousser sur le bouton. Et que la gauge évolue en fonction de ce qui se trouve sur l'entrée analogique 0. Ceux muni d'un chronomètre pourront aussi vérifié le bon fonctionnement du clignotement.

detect

 

Autres informations et liens

Dès à présent, il vous est possible de réaliser vos propres interface usb, celle-ci est compatible avec toutes les versions de Windows supérieur où égale à 98. N'oubliez par de munir votre programme du fichier mcHID.dll.

Pour ceux qui voudrait en savoir plus sur les PID/VID et tout le reste :

http://www.abcelectronique.com/acquier/USB.html

Pour ceux qui voudrait utiliser autre chose que la DLL de mencanique, voici un composant pour Delphi permettant de communiquer avec les interface HID :

ftp://ftp.delphi-jedi.org/api/hid.zip

Pour ceux qui veulent en savoir plus sur le 18f4550 :

http://www.microchip.com

L'ensemble des fichiers nécessaire et suffisant à la réalisation de l'exemple de cet article est disponible directement ici :

Télécharger le dossier relatif à la carte d'interface USB HID

 


« Précédent Suivant