MVVM, USB et HID

Sous ce titre énigmatique se cache un petit projet pédagogique mettant en œuvre un ensemble de technologies. De l'utilisation d'une sonde de température DS1820 communiquant avec un micro-contrôleur PIC18F2550 selon le protocole 1-Wire, puis de la communication USB selon le protocole HID (Human Interface Device) afin de notifier un programme développé en C# sous Visual Studio 2010 en appliquant le pattern MVVM

Sous ce titre énigmatique, je vais vous décrire un petit projet, parfaitement inutile, mais pédagogique (il faut bien trouver une raison) mettant en œuvre un ensemble de technologie.

  • Une jolie sonde de température, de type DS1820, discute en utilisant le protocole 1-Wire avec un micro-contrôleur PIC 18F2550.
  • Ce petit micro-contrôleur comporte une interface USB permettant d'implémenter le protocole HID. Il peut donc communiquer avec un Micro Ordinateur sans avoir à utiliser un driver spécial.
  • Enfin, ultime étape de ce projet, afficher la température en utilisant un petit programme écrit en C# sous Visual Studio 2010 (Personne n'est parfait :-) .


Pour corser un peu la dernière étape, nous allons utiliser le pattern MVVM, en utilisant le framework fournit par Mr Laurent Bugnion : MVVM Light ToolKit

Circuit électronique

Schéma de base pour le micro-contrôleur

Avant de faire du logiciel, il faut faire un minimum de Hardware. Le schéma de la carte est très simple et reprends les spécifications de base des différents datasheet. Le circuit est alimenté en 5 Volts au travers de la connexion USB.
On utilise un quartz de 20MHz avec 2 petites capacités de 22pF. Le circuit de Reset est également classique, une résistance de tirage et une capacité.
Il faut ajouter une capacité sur la broche Vusb du micro-contrôleur. Il semblerait qu'il faille un minimum de 220nF alors que la documentation indique 100 nF
On ajoute également la très classique capacité de 100nF sur l'alimentation permettant de filtrer tous les petits parasites.

Interface de sonde

La connexion de la sonde est on ne peut plus simple, il suffit de l'alimenter et de relier la borne de données à un des ports du micro-contrôleur. Il faut juste ajouter une résistance de tirage haut de 4,7 KOhms.

Schéma du montage

schema.PNG

le circuit imprimé fortement agrandi :

circuite.PNG

le résultat final :

temperature1.jpg

une autre vue :

temperature2.jpg

Les schémas et routage de ce circuit ont été réalisé en utilisant le logiciel Kicad J'ai ensuite simplement imprimé le typon sur une imprimante laser, insoler et graver le circuit avec une solution d'eau, d'acide chlorhydrique et d'eau oxygénée à 110°

Le code du micro-contrôleur, utilisation du stack USB HID et mise en oeuvre du protocole One Wire

Environnement de développement

En utilisant des PICs, je pense que le plus simple est d'utiliser les outils mis gracieusement à disposition par Microchip. J'utilise donc l'IDE MPLAB ainsi que le compilateur C18 dernière version.
Partant du principe qu'il est inutile de tout refaire, je me suis basé d'un exemple fourni dans le stack USB.

Traitement du OneWire

La communication avec un composant OneWire n'est pas compliqué. Tout d'abord, quelques #define afin de faciliter la lecture du code et permettre également de l'adapter plus rapidement :
Définition du port utilisé pour communiquer avec le composant

#define OneWireOut LATBbits.LATB2
#define OneWireIn PORTBbits.RB2
#define OneWireControl TRISBbits.TRISB2

Avant d'interroger le composant, il faut d'abord vérifier qu'il soit présent. Cette opération est réalisée par cette petite fonction :

//Fonction de reset et test de presence, return, 1 si un device est présent sur la ligne
unsigned char ResetAndPresence(void)
{
unsigned char byPresent = 0;
//Port en sortie
OneWireControl = 0;
//On force un 0 en sortie
OneWireOut = 0;
//Attente du délai minimum
Wait480us();
//On force le port à 1
OneWireOut = 1;
//On repasse le port en entrée
OneWireControl = 1;
//Attente du temps minimum (entre 15 et 60 us pour la réponse), On attend 70us
Wait70us();
if(OneWireIn == 0)
{
byPresent = 1;
}
Wait410us();
OneWireControl = 0;
OneWireOut = 1;
return byPresent;
}

Comme on peut facilement le constater, cette fonction retourne 1 si le composant est présent, sinon, elle retourne 0.
Les différentes fonctions d'attentes sont disponible dans le fichier source

Il y a également plusieurs petites fonctions de base :

Ecrire un 1 sur le port de communication

void Writebit1(void)
{
//On force un 0 en sortie
OneWireOut = 0;
//Attente du délai minimum
Wait10us();
OneWireOut = 1;
Wait60us();
}

Ecrire un 1 sur le port de communication

J'aurais pu factoriser un peu ces 2 fonctions d'écriture, mais vu le code, je n'y ai pas trouvé trop grand intérêt. De plus la place disponible sur ce micro-contrôleur étant plus que surdimensionné pour ce projet, j'ai préféré faire des fonctions basiques.

void Writebit0(void)
{
//On force un 0 en sortie
OneWireOut = 0;
//Attente du délai minimum
Wait60us();
OneWireOut = 1;
Wait10us();
}

Lire 1 bit le port de communication

unsigned char ReadBit(void)
{
unsigned char ucRet = 0;
//On force un zero pendant un minimum de 1us
OneWireOut = 0;
Wait2us();
//On repasse en entrée
OneWireControl = 1;
Wait2us();
Wait2us();
Wait2us();
if(OneWireIn == 1) ucRet = 0x01;
//Attente du minimum syndical et reprise du bus
Wait50us();
Wait10us();
OneWireControl = 0;
OneWireOut = 1;
return ucRet;
}

Du coup, les fonctions de plus haut niveau sont relativement simples.

Ecriture d'un Byte sur le port.

Cela se résume à une simple histoire de rotation
void WriteByte(unsigned char Data)
{
unsigned char byBoucle = 0;
for(byBoucle = 0; byBoucle < 8; byBoucle++)
{
if((Data & 0x01) == 0x01) Writebit1();
else Writebit0();
Data >>= 1;
}
}

Lecture d'un Byte

Là encore, une simple histoire de rotation

unsigned char ReadByte(void)
{
unsigned char byBoucle = 0;
unsigned char ucRet = 0x00;
for(byBoucle = 0; byBoucle < 8; byBoucle++)
{
ucRet >>= 1;
if(ReadBit() == 0x01) ucRet |= 0x80;
}
return ucRet;
}

Gestion de la communication USB

Développement de l'interface graphique sous Visual Studio 2010 en MVVM

Pour des besoins professionnels, je me suis mis à développer en C# en utilisant le pattern MVVM (Model-Vue- VueModel). Ce projet professionnel étant un peu compliqué, je me suis dit qu'utiliser ce même pattern pour le développement de cet interface constituerait un excellent exercice.
Le choix du toolkit MVVM a été relativement simple, il existe Prism de Microsoft et un Framework beaucoup plus accessible fournit par Mr Laurent Bugnion : MVVM Light Toolkit. Ce Framework a d'ailleurs été choisi par la société dans laquelle je travaille pour développer un nouveau projet
L'installation est simple, il suffit de lire et de suivre le mode d'emploi. La création du projet C# WPF se fait simplement en sélectionnant le template adéquat.

Dans ce nouveau projet, on crée le dossier Model, puis commence le premier problème, discuter en USB avec la sonde de température. Après avoir fouiné un peu sur le net, et après plusieurs essais, j'ai décidé d'utiliser la Libusb-win32, projet qui me semble être le mieux abouti.
Les exemples sont simples et permettent rapidement de prendre en main cette librairie.
La classe de communication avec le PIC18F2550 ne pose donc pas gros problème et j'obtiens rapidement la mesure de la température.

Un problème plus conceptuel se pose alors. Le PIC8F2550 envoie de façon périodique les informations. Ce périphérique est aussi un périphérique USB, et peut donc être retiré ou inséré alors que l'ordinateur est en fonction. Il faut donc que le Modèle puisse avertir le "Vue-Modèle". Par principe, je n'aime pas les timers pour effectuer des interrogations périodiques.
Me revoilà donc reparti à la recherche de renseignements sur ce pattern et je trouve la solution en lisant ce blog. Il suffit de télécharger le fichier zip et de lire le document et de consulter les différents exemples. Ils sont très simple et formateur.
Selon ce pattern, et en utilisant les fonctionnalités du Framework, il est tout à fait possible de communiquer entres les différentes parties en envoyant des messages. Concernant la détection d'insertion ou de retrait de la sonde de température, il aurait été très simple d'implémenter la fonctionnalité dans le "Vue-Modèle", mais cela entrainerait une liaison forte alors que l'implémentation de cette détection dans le modèle et l'envoi d'un message en utilisant les fonctionnalités du Framework n'entraine pas de forte liaison et permet en plus de notifier le "Vue-Modèle" de la réception d'un nouveau message de température. IL n'y aura donc pas de pooling pour récupérer les données.

La vue est on ne peut plus simple. Les informations essentielles sont "Bindées" et l'affichage se met à jour automatiquement.
Une petite zone de texte permet de vérifier que le Binding fonctionne correctement, un autre champ indique si une sonde de température est connectée.
La température est affichée directement et une petite étoile tourne de 10° à réception d'un message.

Le code source de cette application est disponible ici.

Le résultat final :

interface.png

Je suis désolé, j'ai fait les schémas électronique, le routage, la gravure du circuit, la codage du micro-contrôleur, le logiciel sous Visual Studio 2010 en C#, j'en ai profité pour apprendre et mettre en oeuvre le ToolKit MVVM Light, mais définitivement, je ne serais jamais un graphiste ou un designeur.

Haut de page