Mon projet n° 1 : récepteur GPS

J'ai décidé de réaliser la même chose que ça

 

Qu'est ce bidule ?

Réponse : C'est un GPS Prevent Navirad. D'après le fabricant, le GPS Navirad vous alerte par des messages vocaux à l'approche des zones dangereuses matérialisées par la présence de radars automatiques. Le GPS Navirad vous incite à être prudent lorsque vous entrez dans une zone présentant un risque permanent ou aléatoire. Les zones à risque - les zones de contrôles mobiles, les virages dangereux, les zones d’accumulation d’accidents - pourront être ajoutées par les utilisateurs dans la base de données du GPS Navirad et partagées via Internet

Pour réaliser ce projet, il faut du matériel et du logiciel :

J'ai attendu logntemps avant de me lancer car au niveau des outils de développement, je n'avais pas grand chose :

Le problème quand on veut programmer le PIC, c'est qu'il faut l'enlever de la platine d'essai, le mettre sur le programmateur et ensuite le remettre sur sa platine. Quand on a fait la va-et-vient plusieurs fois, on est un peu dégouté.

Et puis, en lisant le numéro 292 de Electronique Pratique je suis tombé sur un article de JMandon page 64 "Découvrir le PIC 16F88 et MikroBasik"

Grâce à JMandon, j'ai trouvé la solution à mon problème ; j'installe un logiciel "résidant" dans mon PIC, il s'appelle Bootloader. Chaque fois que j'applique un reset sur le microcontroleur, celui-ci va d'abord regarder si on tente d'écrire un programme sur sa broche RX, si oui alors il efface et réécrit le nouveau programme, sinon il execute le programme.


Etape 1 : les outils de développement

Il me fallait une petite platine supportant le PIC. Le support de CI est à wrapper donc je peux l'enficher sur ma platine d'essais.

Un minimum de composants (1 régulateur et 2 condensateurs)
Le Bouton Poussoir (repos - fermeture) sert seulement à la programmation du PIC

Le circuit a été réalisé avec le logiciel de CAO LochMaster.

Pour programmer le PIC, il faut réaliser une platine d'interface qui se connecte sur le port série d'un PC via la RS232.

Des montages à base de MAX232 foisonnent sur Internet (le mien est ultra conventionnel), mais attention dans l'article de JMandon j'ai trouvé une erreur les I/O RS232 et les I/O TTL sont inversés.

à gauche du -12/+12V (RS232) à droite du 0/5V (TTL)
Le connecteur à gauche sert à brancher un cordon de 4 fils jusqu'au PIC (0V, TX, RX, 5V)

Avec ces 2 platines, il est possible de reprogrammer "in situ" le PIC sans avoir à le déplacer de la platine d'essais, un premier pas ... n'est ce pas.

Mais tout d'abord, il faut installer le bootloader (Bloader-F88-Internal8MHz.HEX) dans le PIC, il y restera une fois pour toute.

On l'installe avec le PIC-01 et l'application (icprog.exe) très connu des utilisateurs de PIC.

Voilà, le PIC est prêt à accueillir votre programme.

Nous avons vu la partie matérielle, passons maintenant au logiciel.

J'utilise pour cela un logiciel de développement MPLAB IDE v7.20 de la société MICROCHIP, il est gratuit (FREE), autant en profiter. Avec cet outil je peux écrire mes programmes comme dans un éditeur de texte, il lance le compilateur CC5X et je peux même faire du debug, etc.

Ensuite pour télécharger votre programme (nécessairement avec l'extension .HEX), on utilise l'application (Screamer-v14.exe).

A chaque RESET du PIC, celui-ci charge le nouveau programme dans sa mémoire. Vous voulez le modifier, que cela ne tienne, vous le recompilez et refaites un RESET (facile n'est ce pas !).


Etape 2 : Le GPS

J'ai acheté un récepteur GPS chez PEARL à 69,90€. Il s'agit en fait du modèle NL-203P fabriqué par Navilock.de.

Les données sortant de ce GPS exploitent le protocole NMEA 0813. Les principales informations transmises au format NMEA sont lisibles sur le site commlinx.com

Ce modèle de GPS a une sortie série et un connecteur PS/2. Grâce à l'aide du fabricant j'ai récupéré le brochage du connecteur et surtout la tension d'alimentation, c'est du 5V.

Je voulais pouvoir connecter ce récepteur sur un PC ne disposant pas de port série. Pour cela, j'ai acheté un module de conversion USB-CONV1N à 27€ chez LEXTRONIC.

Le module de conversion est sur la gauche
Le récepteur à côté de sa platine

Le schéma logique, ci-dessous, a été réalisé avec le logiciel de CAO sPlan

N'essayer pas de brancher directement le GPS sur le PIC, ça ne marchera pas. J'ai essayé plusieurs choses mais ce qui a fonctionné du 1er coup, c'est le transceiver SN75176.


Etape 3 : L'affichage, le plus simple mais le plus long ...

J'ai vraiment passé du temps sur cette étape.

Au début, je faisais tous mes programmes avec le MikroPascal, très simple d'utilisation. On peut réaliser en quelques mn un programme qui marche.

Je n'arrivais rien à afficher sur mon afficheur LCD 2 * 16 digits alors que j'ai tout essayé, oui vraiment tout, le mode 4 bits, le mode 8 bits, le pas-à-pas et là je peux vous dire que sans outil de développement c'est vraiment la galère.

Vous avez par exemple l'ICD-2 de MICROCHIP : , il permet de faire de la simulation directement sur le PIC, c'est mieux à mon avis que de la simulation logicielle.

N'ayant pas cet outil, j'ai créé une routine en C qui envoie sur un hyperterminal les 8 bits d'un mot. C'est pratique pour afficher le contenu d'un registre : ainsi si on veut afficher le PORTA, j'aurai quelque chose comme ceci à l'écran 0000.0101, mais c'est pas TOP.

Il fallait bien que je me débrouille avec mes 2 petites cartes.

Finalement, j'ai trouvé la cause, mon PIC a dû prendre un coup dans les dents et il était HS le pauvre. Impossible de le deviner, le chenillard fonctionnait bien, le PIC se reprogrammait sans problème, etc.. Je ne ne sais toujours pas ce qui a lâché dans sa circuiterie !

Heureusement j'en avais commandé 2 à 3,44€ chez FARNELL, impossible de trouver le PIC 16F88 dans les magasins parisiens, ils sont toujours à vendre le 16F84, les pauvres vendeurs.

Le montage final qui marche !!!!

et le résultat

J'affiche l'heure UTS ...
et mes coordonnées géographiques (je suis à Paris)

 

Le programme en C


//******************************************************************************
// microcontroller : P16F88
//
// Project: GPS_version1.c
// Ce projet fonctionne avec un PIC 16F88
//
// 21/08/05	Le projet a pour but de capturer une trame GPS et 
// 			l'afficher sur un afficheur LCD
// 
// Internal Oscillator 8 Mhz, No WDT, No MCLR, PWRT  

/* PORTB */
/* 13   12   11   10   9    8    7    6  pin PIC  	*/
/* 7    6    5    4    3    2    1    0  PORTB		*/ 
/*                                                  */
/* -    -    (TX) -    RS   RX   R/W  E  DATA 		*/
/*                     4         5    6  pin LCD  	*/

/* PORTA */
/* 16   15   4    3    2    1    18   17 pin PIC  	*/
/* 7    6    5    4    3    2    1    0  PORTA		*/
/*                                                  */
/*           X         d7   d6   d5   d4 DATA 		*/
/*                     14             11 pin LCD  	*/

/* exemple de trame GPS */
/* $GPRMC,175430.476,A,4849.3897,N,00222.1923,E ... = 17h54mn30s 48°49.3897 2°22.1923 */

#pragma chip PIC16F88
#pragma config = 0x03F10

#pragma origin 4

#pragma bit E @ PORTB.0 ;
#pragma bit RW @ PORTB.1 ; 
#pragma bit RS @ PORTB.3 ;
#pragma bit L @ PORTA.4 ;

/****************************************************/
// PROCEDURES
void wait_ms(uns16 tm);
char UART_getc(void);
void UART_printc(char caractere);
void LCD_enable(void);
void LCD_busy (void);
void LCD_out8C(unsigned char code);
void LCD_out4C(unsigned char code);
void LCD_out4D(unsigned char data);
void LCD_dsp(unsigned char *str,  unsigned int  dline);
void ioinit(void);

/****************************************************/

void main()
{	
// VARIABLES
 	char str1[16], str2[16]; // tableau alphanumérique
	char *l1 = str1, *l2 = str2; // pointeur vers les tableaux
	char TIME[6]; // longueur de la trame GPS
	char LAT[11];
	char LON[12];
	int	j,k=0;
	char command; 
/****************************************************/
// HORLOGE DU PIC
	OSCCON = 0b.0111.0000; // Setup internal oscillator for 8MHz
    while( OSCCON.2==0 ); // Wait for frequency to stabilize
    
/****************************************************/
// INHIBITION FONCTIONS INUTILES    
    ANSEL = 0b.0000.0000; // all digital
	CMCON = 0b.0000.0111; // disable comparator module            
	
/****************************************************/	
// PORTA et PORTB
	PORTA = 0b.0000.0000;
	TRISA = 0b.0000.0000; // 0 = Output, 1 = Input  		
   	PORTB = 0b.0000.0000;			 
	TRISB = 0b.0000.0100; // 0 = Output, 1 = Input RB2 - Incoming Data RX

/****************************************************/
// UART
	TXSTA = 0b.0010.0100; // 8-bit asych mode, high speed uart enabled
	RCSTA = 0b.1001.0000; // SPEN = 1 et CREN = 1
	SPBRG = 104 ; // UART 9600 bauds pour 4800 mettre 104
	
/****************************************************/
// MESSAGES  
    str1[0] ='i';str1[1] ='n';str1[2] ='i';str1[3] ='t'; str1[4] ='i';str1[5] ='a';str1[6] ='l';str1[7] ='i';
	str1[8] ='s';str1[9] ='a';str1[10]='t';str1[11]='i'; str1[12]='o';str1[13]='n';str1[14]=' ';str1[15]=' ';
	
	str2[0] ='G';str2[1] ='P';str2[2] ='S';str2[3] =' '; str2[4] =' ';str2[5] =' ';str2[6] =' ';str2[7] =' ';
	str2[8] =' ';str2[9] =' ';str2[10]=' ';str2[11]=' '; str2[12]=' ';str2[13]=' ';str2[14]=' ';str2[15]=' ';

/****************************************************/
	ioinit();
		
	LCD_dsp( l1 , 1 );
	LCD_dsp( l2 , 2 );

	/*UART_printc(13);  //CR
  	UART_printc(10);  //LF */
    	
	wait_ms(1000);
		
	while (1)
	{

	while (UART_getc() != '$');
	
	command = UART_getc();   
	if (command == 'G') 
		{
		 command = UART_getc();   
		 if (command == 'P') 
			 {
			 command = UART_getc();   
		 	 if (command == 'R') 
			 	 {
	  		 	 command = UART_getc();   
		 	 	 if (command == 'M') 
			         {
	    			 command = UART_getc();   
		 	 	     if (command == 'C') 
			             {
					 /* $GPRMC,175430.476,A,4849.3897,N,00222.1923,E ... = 17h54mn30s 48°49.3897 2°22.1923 */
					 /* TIME   012345 */
					 /* LAT                 01234567890 */
					 /* LON	  	                        012345678901 */
				             command = UART_getc(); 
				             j=0;
							 do
							 {
  	    					 	command = UART_getc(); 
  	    					 	TIME[j] = command;
							 } while( ++j<6 ); // lit l'heure = 6 caractères
	
							 j=0;
							 do
							 {
  	    					 	command = UART_getc(); 
							 } while( ++j<7 ); // on saute ce qui est inutile = 7 caractères
							 
							 j=0;
							 do
							 {
  	    					 	command = UART_getc(); 
  	    					 	LAT[j] = command;
							 } while( ++j<11 ); // lit la LAT
							 
							 command = UART_getc(); // on saute le ','
							 
							 j=0;
							 do
							 {
  	    					 	command = UART_getc(); 
  	    					 	LON[j] = command;
							 } while( ++j<12 ); // lit la LON
							 
							 // affichage de l'heure et des coordonnées
							 
							 wait_ms(1000);
							 			 
							 if (TIME [4] == '5') // alt 124 pour écrire ||
							 {
							 	L = 1;
							 	LCD_out4C(0x01); // clear display
							 	LCD_out4C(0x02); 
							 	command = TIME[0];
  							 	LCD_out4D (command);
  							 	command = TIME[1];
  							 	LCD_out4D (command);
  							 	LCD_out4D ('h');
  							 	command = TIME[2];
  							 	LCD_out4D (command);
  							 	command = TIME[3];
  							 	LCD_out4D (command);
  							 	LCD_out4D ('m');
  							 	command = TIME[4];
  							 	LCD_out4D (command);
  							 	command = TIME[5];
  							 	LCD_out4D (command);
  							 	LCD_out4D ('s');
  							 }
							 else
							 {					 							 
							 	L = 0;
							 	LCD_out4C(0x02);    					 	 			 
   							 	LCD_out4D ('L');
   							 	LCD_out4D ('A');
   							 	LCD_out4D ('T');
   							 	LCD_out4D (':');
   							 	   							 	
   							 	for( j=0; j<2; j++ )
	    					 	{ 
									command = LAT[j];
  							 		LCD_out4D (command);	
   							 	}
   							 	LCD_out4D ('d');
   							 	for( j=2; j<9; j++ )
	    					 	{ 
									command = LAT[j];
  							 		LCD_out4D (command);	
   							 	}
   							 	command = LAT[10];
   							 	LCD_out4D (command);
   							   							 
   							 	LCD_out4C(0xC0); 
   							 	LCD_out4D ('L');
   							 	LCD_out4D ('O');
   							 	LCD_out4D ('N');
   							 	LCD_out4D (':');
   							 
   							 	for( j=0; j<3; j++ )
	    					 	{ 
									command = LON[j];
  							 		LCD_out4D (command);	
   							 	}
   							 	LCD_out4D ('d');
   							 	for( j=3; j<10; j++ )
	    					 	{ 
									command = LON[j];
  							 		LCD_out4D (command);	
   							 	}
   							 	command = LON[11];
   							 	LCD_out4D (command);
   							 }
		                 }
		             }
		         }
		     }
		}
    } // fin du while (1)	
}// fin du main

/***   Ecriture d'un caractere dans l'UART   ***/
void UART_printc(char caractere) 
{
	TXREG = caractere; // envoi du caractere
	while (TXIF == 0); // PIR1.4 buffer de transmission vide ?	
}

/***   Lecture d'un caractère dans l'UART   ***/
//Speed is controller by UART setup registers
unsigned char UART_getc(void)
{
	CREN=1; // RCSTA.4 autorise la réception en continue
    while(RCIF == 0); // PIR1.5 1 = buffer de réception plein ?
    CREN = 0; 
    return(RCREG);    
}

/* envoi d'une instruction mode 8 bits */
void LCD_out8C(unsigned char code)
{	
	RS = 0; RW = 0 ; // Instruction Write operation   
	wait_ms(1);
	 
	PORTA = code; // output 8 bits		        

	LCD_enable();
	
	RS = 1 ;RW = 0;	
	wait_ms(5);					
}


/* envoi d'une instruction mode 4 bits */
void LCD_out4C(unsigned char code)
{	
	RS = 0; RW = 0 ; // Instruction Write operation  octet poids fort 
	wait_ms(1);
	 
	PORTA = (code >> 4) & 0x0F; // output 4 bits		        

	LCD_enable();
	
	RS = 1 ;RW = 0;
	
	wait_ms(5);	
	
	RS = 0; RW = 0 ; // Instruction Write operation  octet poids faible  
	wait_ms(1);
	 
	PORTA = code & 0x0F; // output 4 bits		        

	LCD_enable();
	
	RS = 1 ;RW = 0;	
	wait_ms(5);					
}


/* envoi d'une donnée mode 4 bits */
void LCD_out4D(unsigned char data)
{		
	RS = 1 ;RW = 0 ; // Data Write operation  octet poids fort       
	wait_ms(1);
	
	PORTA = (data >> 4) & 0x0F; // output demi octet		        
	
	LCD_enable();
	
	RS = 1;RW = 0;
	
	wait_ms(5);	
	
	RS = 1 ;RW = 0 ; // Data Write operation  octet poids faible       
	wait_ms(1);
	
	PORTA = data & 0x0F ; // output	demi octet	        
	
	LCD_enable();
	
	RS = 1;RW = 0;	
	wait_ms(5);				
}

/* LCD control routine -----*/
void LCD_dsp(unsigned char *str,  unsigned int  dline)
{	
int	i;
			
	if( dline == 1 )
		{	
		LCD_out4C(0x02); // Return home 1st line DDRAM : 00h 01h ...		
	  	}
 	  	else
	  	{	
		LCD_out4C(0xC0); // Set DDRAM Address 0x80 + 2nd line : 40h 41h ... 		
	  	}
	
	wait_ms(1);
	
   for( i=0; i<16; i++ )
     	{ // affichage d'une ligne du message    
		LCD_out4D(*str++);
     	}
}

/* i/o initialize -----*/
void ioinit(void)
{
    /* Début initialisation  */
	wait_ms(30); // tempo de 30ms		

	LCD_out8C(0x03); // function set 1	
	wait_ms(1);

	LCD_out8C(0x03); // function set 2
	wait_ms(1);

	LCD_out8C(0x03); // function set 3	
	wait_ms(1);

	LCD_out8C(0x02); // function set 3	
	wait_ms(1);
	
	LCD_out4C(0x28); // system set mode 4 bits + 2 lignes + 5x7 dots	
	wait_ms(1);

	LCD_out4C(0x0F); // display on + cursor on  + blink 
	wait_ms(1);

	LCD_out4C(0x01); // clear display
	wait_ms(2);
	
	LCD_out4C(0x06); // entry mode set : increment move to the right
	wait_ms(5);
	/* Fin initialisation   */
}


/* ENABLE */
void LCD_enable(void)
{
	E = 1; 
	wait_ms(5);
	E = 0;
	wait_ms(5);
}

/*** Tempo ***/
// tm=1 correspond à 1ms pour Q=4Mz 
void wait_ms(uns16 tm)
{
uns16 t1, t2;

	t1 = tm;
	do{
	t2 = 90;	
	while( --t2!=0 );
	}
	while( --t1!=0 );
}

  

 


Etape 4: Le détecteur d'approche