Le circuit électronique

Le circuit électronique

Ce circuit est directement déduit des informations fournies par Microchip. Le connecteur ICSP permet de programmer le PIC sur place, sans même le débrancher du port USB ! La résistance de pull-up R1 permet de maintenir /MCLR (Master Clear Reset, i.e. reset du PIC) à 1. Si vous ne souhaitez pas disposer de la fonction de reset, vous pouvez vous passer de cette résistance, mais dans ce cas, il faudra positionner la directive de configuration MCLRE (MCLR Enable) à OFF ci-dessous.

La réalisation de ce circuit sur plaque à trous a déjà été présentée :

Montage sur plaque à trous

Le programme

J'ai récupéré les programmes de démonstration de Microchip (voir aussi la documentation), et en particulier le firmware CDC, émulation de port série. L'idée est de créer un périphérique de classe Communication Device. Ainsi, un tel périphérique est pris en charge sur l'ordinateur par un driver générique. Et réciproquement sur le PIC, en reprenant le firmware de démonstration de Microchip, tout se passe comme si on utilisait un UART. Ce n'est donc pas très compliqué...

J'ai repris tous les fichiers du firmware de démonstration, et effectué les modifications suivantes.

Configuration du PIC

Le firmware de Microchip est prévu pour PIC18F4450. Il s'agit d'adapter les directives de configuration. J'utilise les directives suivantes pour le 18F2455 (plus de détails à venir au fil des expérimentations) :

#pragma config USBDIV = 2, CPUDIV = OSC1_PLL2, PLLDIV = 5
#pragma config FOSC = HSPLL_HS, FCMEM = OFF, IESO = OFF
#pragma config VREGEN = ON, PWRT = OFF, BOR = ON, BORV = 21
#pragma config WDT = OFF, WDTPS = 32768
#pragma config CCP2MX = ON, PBADEN = OFF, LPT1OSC = OFF, MCLRE = ON
#pragma config STVREN = ON, LVP = OFF, XINST = OFF

J'ai ajouté ces lignes au début de main.c.

Adaptation du firmware à notre montage

Section ajoutée le 2 décembre 2007, grâce au retour d'Hervé.

Le firmware de Microchip est capable de désactiver l'USB lorsque le câble est débranché. La détection du branchement se fait via un port d'I/O, à relier au +5V de l'USB. Or par défaut, la détection se fait sur l'un des ports où se trouvent nos LED : il nous faut donc obligatoirement désactiver ce mécanisme. Sa configuration se fait dans autofiles/usbcfg.h. Par défaut, il est activé (#define USE_USB_BUS_SENSE_IO). Il suffit de changer le #define en #undef. Voir aussi la documentation de Microchip (lien ci-dessus).

Programme applicatif

Ce n'est pas le tout de reprendre le code de chez Microchip, il faut quand-même écrire le code correspondant à notre application ! C'est le rôle du fichier user.c :

/** I N C L U D E S **********************************************************/
#include <p18cxxx.h>
#include <usart.h>
#include "system\typedefs.h"

#include "system\usb\usb.h"

#include "user\user.h"

/** V A R I A B L E S ********************************************************/
#pragma udata

char input_buffer[64];
char output_buffer[64];

/** P R I V A T E  P R O T O T Y P E S ***************************************/
void InitializeUSART(void);

/** D E C L A R A T I O N S **************************************************/
#pragma code
void UserInit(void)
{
    LATA = 0;		// clear data latches
    TRISA = 0;		// direction: output pins

    InitializeUSART();   
}//end UserInit

void InitializeUSART(void)
{
    TRISCbits.TRISC7=1; // RX
    TRISCbits.TRISC6=0; // TX
    SPBRG = 0x71;
    SPBRGH = 0x02;      // 0x0271 for 48MHz -> 19200 baud
    TXSTA = 0x24;       // TX enable BRGH=1
    RCSTA = 0x90;       // continuous RX
    BAUDCON = 0x08;     // BRG16 = 1
}//end InitializeUSART


void ProcessIO(void)
{   
	static unsigned int cnt = 0;
	char str[32];
	char input_buffer[10];
	static int blink = 1;

	if( (usb_device_state < CONFIGURED_STATE) || (UCONbits.SUSPND==1) ) return;

	// User Application USB tasks

	cnt++;
	if(blink) { // blink the LED
		if(cnt == 32768) PORTA = 1;
		if(cnt == 0) PORTA = 2;
	}

    if(getsUSBUSART(input_buffer,1)) // if a byte has been received from the computer
    {
        if(input_buffer[0] == 'r') { // r (red) -> light the LED on RA0
			blink = 0;
			PORTA = 1;
			if(mUSBUSARTIsTxTrfReady())	putrsUSBUSART("\rRED.\r\n");
			return;
		}

		if(input_buffer[0] == 'g') { // g (green)  -> light the LED on RA1
			blink = 0;
			PORTA = 2;
			
			if(mUSBUSARTIsTxTrfReady())	putrsUSBUSART("\rGREEN.\r\n");

			return;
		}

        if(input_buffer[0] == 'b') { // b (blink) -> blink the two LEDs alternatively
			blink = 1;
			if(mUSBUSARTIsTxTrfReady())	putrsUSBUSART("\rBLINKING.\r\n");
			return;
		}

        if(mUSBUSARTIsTxTrfReady())		
			putrsUSBUSART("\rNot understood (b|g|r).\r\n");
	}

}//end ProcessIO

/** EOF user.c ***************************************************************/

Utilisation

Après réalisation du circuit et programmation, tout devrait fonctionner facilement. Voici ce qui se passe sous Windows lorsqu'on connecte notre périphérique tout neuf :

  • Windows affiche la boîte de dialogue de détection d'un nouveau périphérique. Pourquoi ne reconnaît-il pas sa classe directement, je ne le sais pas ! Il faut donc lui indiquer quel driver utiliser, et pour cela, lui indiquer un fichier INF tout simple fourni par Microchip (mchpcdc.inf) ;
  • un port COM supplémentaire est créé ;
  • on peut alors s'y connecter, par exemple avec l'Hyperterminal : taper r pour allumer la LED rouge, g pour allumer la LED verte ou b pour faire clignoter les LED.

Conclusion

Voici donc un exemple complet de création d'un périphérique USB avec le PIC18F2455. Il reste un bug étrange, qui survient parfois : il arrive que les chaînes en mémoire soient légèrement corrompues. Peut-être un problème de débordement de mémoire quelque-part... Quelques idées pour la suite :

  • dans l'immédiat, faire fonctionner la chose sous Linux (edit : fait) ;
  • réécrire tout le firmware, pour me passer complètement de code copyrighté Microchip ;
  • faire fonctionner le mode « classe » (pour éviter d'avoir à spécifier un driver) (edit : rien à faire) ;
  • implémenter mon propre protocole, et donc le driver correspondant sur l'OS.

J'ai d'ores et déjà réutilisé cette conception de base (élaborée pendant mes loisirs) pour mon travail (donc je n'en partage pas les résultats) afin de réaliser un décodage de trames infrarouges directement sur le PIC.

NB : j'ai copié-collé le programme dans mon système de blog, en faisant à la volée quelques modifications. Il est possible qu'il y ait quelques erreurs : si c'est le cas, merci de me le signaler. Indiquez-moi également si les indications sont trop « rapides ».