Le micro-controleur PIC16F877

Ce micro-controleur est destiné à coordonner les différentes parties du robot, entre capteurs, sonar, moteur, jack et autres systèmes embarqués.
Dans ce contexte le robot construit utilisera un moteur pas à pas, changeant de trajectoire en fonction des capteurs sans anticiper à l'avance la direction à suivre.
Reste encore à programmer le Pic pour qu'il agisse de manière autonome.

 

Définitions des variables :


Le pic 16F877 dispose de 3 ports de 8bits (B, C et D), d'un port de 5 bits (A) et d'un port de 3 bits (E). Soit en tout 32 entrées/sorties. Toutes les bornes de tous les ports peuvent être configurées en entrées ou en sorties logiques, mais pour les autres utilisations (PWM, liaison série, entrée analogique, etc..) seules certaines broches sont utilisables.


Pour les capteurs nous utiliserons les entrées :
RA0 pour le capteur Gauche
RA1 pour le capteur Droit
RA3 pour le capteur Central

RB0 pour une Led Verte
RB1 pour une Led Rouge
RB2 pour un bouton d'arrêt d'urgence
RB3, RB6 et RB7 pour la connexion vers l'ICD

RC1 pour la vitesse du moteur Gauche
RC2 pour la vitesse du moteur Droit
RC3 et RC4 pour le sonar

RD0 pour la fiche jack
RD1 pour le sens du moteur Gauche
RD2 pour le sens du moteur Droit
RD3 pour le Enable (Autorisation de fonctionnement des moteurs)
RD4 à RD7 pour les switchs (réglages mode de course)

Toutes ces bornes sont contenues dans la librairie <pic.h>

 

Les Capteurs :


Les capteurs situés à l'avant du robot permettent de déterminer par une tension compris entre 0 et 5 Volts le taux de luminosité de la surface réfléchie. Ces capteurs optiques nous permettront donc de déterminer si oui ou non le robot est sur la piste. Pour ce faire on affectera à partir d'un certain seuil la valeur 0 indiquant la sorties de piste (fond noir ou bleu ) et 1 sinon (fond blanc de la piste).
Pour mesurer ce seuil : 2 méthodes.

-La première consiste à relever les tensions lors d'un fond noir, lord d'un fond blanc et de faire la moyenne des deux. Le résultat ainsi obtenu se voit transformer en décimal (voire Héxadécimal) : seuil en décimal = (Vmoy x 255 ) / 5
- L'utilisation d'un programme, affichant les valeurs décimales des tensions converties lues sur les Capteurs. Il suffit ensuite de prendre la moyenne entre la valeur sur fond blanc et celle sur fond noir.


Les tournants :

Pour négocier les tournants et suivre la piste on utilise 3 capteurs. On dispose ainsi de 8 états possibles :
Capteur :
G C D
0 0 0 ' Arrivée à une intersection (règle de priorité)
0 0 1 ' Le robot est hors piste, diminution forte du moteur gauche pour ramener le robot sur la ligne
0 1 0 ' Le robot est sur la piste les moteurs sont à pleine vitesse
0 1 1 ' Le robot est hors piste, diminution moyenne du moteur gauche pour ramener le robot sur la ligne
1 0 0 ' Le robot est hors piste, diminution forte du moteur droit pour ramener le robot sur la ligne
1 0 1 ' Impossibilité de calcul
1 1 0 ' Le robot est hors piste, diminution moyenne du moteur droit pour ramener le robot sur la ligne
1 1 1 ' Robot hors piste, tentative de repositionnement par marche arrière en gardant la même vitesse pour chaque roues

Il faut donc diminuer la vitesse par un double palier.

 

Le rapport cyclique :

r= rayon de la roue = constante
a= rapport cyclique
v1= vitesse de la roue gauche
v2= vitesse de la roue droite
w1= vitesse angulaire de la roue gauche =2 ¶ v1
w2= vitesse angulaire de la roue droite =2 ¶ v2
or v2 = a v1
d'où w2= 2 ¶ a v1
t= temps mis par la roue pour faire une distance d pour la roue gauche et d+e pour la roue droite.
t=d/rw1=(d+e)/rw2
x²=(y-230/2)² + d² = (y-230/2)² + (rw1t)²
cos alpha= (y-230/2) / x
e= 230 alpha = cos^-1 [(y-230/2)/x]

Pour v1 fixé : W1 = Constante
Connaissant la distance parcourue : d = constante
d'où t = constante
w2=(d+e) / r t = (cos^-1 [(y-230/2)/x] + e) / (r t)

v2= w2 / ( 2 ¶ ) = (cos^-1 [(y-230/2)/x] + e) / (2 ¶ r t)

a = v2 / v1


a2 sera donc le rapport cyclique fixé pour le un rayon y de 500mm
et a1 le rapport cyclique fixé pour le rayon y de 1500 mm

avec a1< a2.

 

 

Exemple d'algorithme avec son programme :

Algo :

Les variables :
MG = 0 moteur gauche
MD = 1 moteur droit
capg entier non signé 0 ou 1
capd entier non signé 0 ou 1
capc entier non signé 0 ou 1
n entier non signé de 0 à 7 correspondant à la mise bout à bout de capg, capd et capc
seuilg entier non signé compris entre 0 et 255
seuild entier non signé compris entre 0 et 255
seuilc entier non signé compris entre 0 et 255
delta_v1 entier non signé compris entre 0 et 100 coresspondant à a1
delta_v2 entier non signé compris entre 0 et 100 coresspondant à a2
vitesse entier non signé de 0 à 100 correspondant à la vitesse maximum par exemple

les fonctions :
waitRAT pour une boucle d'attente
InitCan pour initialiser le convertisseur analogique numérique
ReadCan8bit pour renvoyer la tension reçue, sur le capteur demandé en paramètre
InitPwm pour initialiser le PWM
SetDutyCycle permet de commander la vitesse des moteurs grâce à 2 paramètres : le numéro du moteur (0 pour le gauche et 1 pour le droit) et la vitesse voulue en pourcentage

Le programme principal :
TRISB0 <-0
TRISB1 <-0
TRISD3 <-0
TRISD2 <-0
TRISD1 <-0
RD1 <-0
RD2 <-0
RD3 <-0
Appel des fonctions InitCan et InitPwm
seuilg <-on donne la valeur du seuil calculée
seuild <-on donne la valeur du seuil calculée
seuilc <-on donne la valeur du seuil calculée
vitesse <-100
delta_v1 <-a1
delta_v2 <-a2
faire :
Si : ReadCan8bit(0) < seuilg
alors : capg <-1
Sinon : capg <-0
finsi

Si : ReadCan8bit(1) < seuild
alors : capd <-1
Sinon : capd <-0
finsi
Si : ReadCan8bit(3) < seuilc
alors : capc <-1
Sinon : capc <-0
finsi

n <- (capg x 4 + capc x 2 + capd)

Pour n :

Dans le cas où n = 0 2 5 7 le robot va tout droit :
SetDutyCycle(MG,vitesse)
SetDutyCycle(MD,vitesse)
RB0<-0 on allume la led droite
RB1<-0 on allume la led gauche
break
Dans le cas où n=1 : le robot diminue fortemment la vitesse de sa roue gauche
SetDutyCycle(MG,vitesse - delta_v2)
SetDutyCycle(MD,vitesse)
RB0<-0 on allume la led droite
RB1<-1 on éteind la led gauche
break
Dans le cas où n=3 : le robot diminue moyennement la vitesse de sa roue gauche
SetDutyCycle(MG,vitesse - delta_v1)
SetDutyCycle(MD,vitesse)
RB0<-0 on allume la led droite
RB1<-1 on éteind la led gauche
break
Dans le cas où n=4 : le robot diminue fortement la vitesse de sa roue droite
SetDutyCycle(MG,vitesse)
SetDutyCycle(MD,vitesse- delta_v2)
RB0<-1 on éteind la led droite
RB1<-0 on allume la led gauche
break
Dans le cas où n=6 : le robot diminue moyennement la vitesse de sa roue droite
SetDutyCycle(MG,vitesse);
SetDutyCycle(MD,vitesse- delta_v1);
RB0<-1 on éteind la led droite
RB1<-0 on allume la led gauche
break
fin de pour
faire tant que 1 différent de 0
fin du programme principal
suivi des fonctions.

 

PROG :

#include<pic.h>

#define MG 0
#define MD 1

void waitRAT(void);
void InitCan(void);
unsigned char ReadCan8bit(unsigned char );
void InitPwm(void);
void SetDutyCycle(unsigned char ,unsigned char );

char capg,capd,capc; /* capteur numerisé gauche droite centre */
char seuilg,seuild,seuilc; /* seuil des capteurs */
char n; /* combinaision des capteurs */
char vitesse ;
char delta_v1 ;
char delta_v2 ;

void main(void)
{
TRISB0=0; /*déclaration du bit 0 du portB en sortie*/
TRISB1=0; /*déclaration du bit 1 du portB en sortie*/
TRISD1=0; /*déclaration du bit 1 du portD en sortie*/
TRISD2=0; /*déclaration du bit 2 du portD en sortie*/
TRISD3=0; /*déclaration du bit 3 du portD en sortie*/
RD1=0; /* marche avant moteur gauche */
RD2=0; /* marche avant moteur droit */
RD3=1; /* autorise le demarrage des moteurs */

InitCan(); /*Initialisation du CAN*/
InitPwm(); /*Initialisation du PWM*/
seuilg = 179 ; /*Initialisation du seuil gauche*/
seuild = 156 ; /*Initialisation du seuil droit*/
seuilc = 164 ; /*Initialisation du seuil central*/
vitesse = 100 ; /*Initialisation de la vitesse par défaut*/
delta_v1 = 10 ; /*Rapport cyclique 1*/
delta_v2 = 20 ; /*Rapport cyclique 2*/
do{
/* lire les 3 capteurs */
/* fabriquer une variable comprise entre 0 et 7 */
if(ReadCan8bit(0)<seuilg) /* numerisation */
{capg=1;}
else {capg=0;}
if(ReadCan8bit(1)<seuild) /* numerisation */
{capd=1;}
else {capd=0;}
if(ReadCan8bit(3)<seuilc) /* numerisation */
{capc=1;}
else {capc=0;}

n=(capg*4+capc*2+capd); /* 0 000
1 001
2 010
3 011
4 100
5 101
6 110
7 111 */
switch(n)
{
case 0 :
case 7 :
case 5 :
case 2 :
SetDutyCycle(MG,vitesse);
SetDutyCycle(MD,vitesse);
RB0=0;
RB1=0;
break;
case 1 :
SetDutyCycle(MG,vitesse - delta_v2);
SetDutyCycle(MD,vitesse);
RB0=0;
RB1=1;
break;
case 6 :
SetDutyCycle(MG,vitesse);
SetDutyCycle(MD,vitesse - delta_v1);
RB0=1;
RB1=0;
break;
case 3 :
SetDutyCycle(MG,vitesse - delta_v1);
SetDutyCycle(MD,vitesse);
RB0=0;
RB1=1;
break;
case 4 :
SetDutyCycle(MG,vitesse);
SetDutyCycle(MD,vitesse - delta_v2);
RB0=1;
RB1=0;
break;
}}
while(1);
}/*main */

/*Les fonctions*/
void InitCan(void)
{
ADCON1 = 0x02 ; /*Port E digital , Port A analogique , justification gauche*/
ADCON0 = 0x41 ; /*Tad = 8*Tosc = 8 * 0.25µs = 2µs > 1.6µs , ADON ;*/
ADIF = 0 ;
}


unsigned char ReadCan8bit(unsigned char ch)
{
ADCON0 &= 0xC7 ; /* RAZ CHSx */
/* Sélection du canal */
if ( ch & 1 )
{CHS0 = 1 ;}
if ( ch & 2 )
{CHS1 = 1 ;}
if ( ch & 4 )
{CHS2 = 1 ;}
waitRAT(); /* Attendre le temps d'acquisition */
ADGO = 1 ; /* Démarre la conversion */
/* Attend la fin de conversion */
while (ADGO && ADON) ; /* Par sécurité on teste aussi ADON */
/* car sinon on rentre dans une boucle infinie */
return ADRESH ;
}

/********************************************************************************************
* Required acquisition time for A/D module form Max. Heise 2002
* p. 115 equation 11-1 Tacq=19,72,e-6
* seconds, approx. 2,e-5 seconds.
* One instruction is executed in one
* cycle, one cycle at 4MHz takes 2,5,e-7
* seconds. Thus we must idle for 80
* instructions
* Formula for this loop
* Inst(I)=1+1+( (I-1)*(1+2) )+2+2=6+3I-3=3I+3
* +2 Inst.Cycles for the call of this function
* => Inst(I)=3I+5
* Inst(I) must be 80
* 80=3I+5
* => I=25
* Timing critical, so this is still in assembler.
********************************************************************************************/

char counter ;

void waitRAT(void) {

#asm
I EQU 50 ; Note this is actually twice the required value of I, just to be sure
movlw I ; 1 Inst.Cycle
movwf _counter ; 1 Inst.Cycle
WaitRAT_Loop
decfsz _counter,f ; 1 Inst.Cycle if I!=0, 2 Inst.Cycles if I==0
goto WaitRAT_Loop ; 2 Inst.Cycles
#endasm}

/****************************************************************
/* Fonction : InitPwm() *
/* Cette fonction initialise les deux modules CCP1 et CCP2 en *
/* mode pwm pour une fréquence de 10kHz avec un quartz de 4Mhz *
/* les deux rapports cycliques sont mis à 0 *
/****************************************************************/

void InitPwm(void)
{

PR2 = 99 ;
CCPR1L = 0 ;
CCP1CON = 0x0C ; /* PWM mode , bit 5 et 4 à 0 */
TRISC2 = 0 ;
CCPR2L = 0 ;
TRISC1 = 0 ;
CCP2CON = 0x0C;
T2CON = 0x04 ; /* TMR2ON prescaler = 1 */
}
/****************************************************************
/* Fonction : SetDutyCycle() *
/* Cette fonction programme le rapport cyclique DutyCycle sur *
/* le canal ch (0 ou 1) *
/* Le rapport est fourni sous la forme 0<DutyCycle<100 *
/* pour 0% < r <100% *
/* Si le canal n'est pas 0 ni 1 les deux signaux sont mis à 0 *
/* Le rapport est limité à 100% *
/****************************************************************/

void SetDutyCycle(unsigned char ch, unsigned char DutyCycle)
{

if ( DutyCycle > 100)
{DutyCycle = 100 ;}
DutyCycle = ( (unsigned int) PR2 * (unsigned int)DutyCycle )/100 ;
if ( ch == 0 )
{CCPR1L = DutyCycle ;
return ;}
if ( ch == 1 )
{CCPR2L = DutyCycle ;
return ;}
CCPR1L = 0 ;
CCPR2L = 0 ;
return ;
}

CECI n'est bien sur pas le programme définitf de quoique ce soit, et constitue juste une manière de programmer !