Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
fr:examples:can:wiper:tp3 [2010/03/03 15:12] – créée sdeniaudfr:examples:can:wiper:tp3 [2020/07/20 09:00] (current) – external edit 127.0.0.1
Line 1: Line 1:
-====== TP3 - Exemple ======+====== TP3 - Exemple ====== 
 +===== Sujet: ===== 
 +**Faire battre le balai de l'essuie-glace** 
 + 
 +==== Objectifs : ==== 
 + 
 +  * Définir les différentes trames interrogatives ou de commande à faire transiter par le réseau CAN en fonction d'une action souhaitée. 
 + 
 +  * Commander un actionneur électrique (moteur à courant continu), dans les deux sens de rotation par l'intermédaire d'un pré-actionneur commandable par réseau CAN. 
 + 
 +  * Acquérir l'état de capteurs TOR (fins de course) acessibles par réseau CAN et en déduire les actions à mener pour satisfaire un cahier des charges imposé. 
 + 
 +==== Cahier des charges : ==== 
 + 
 +Après avoir fait tourner le moteur dans le sens positif (déplacement à droite) jusqu'à atteindre l'action sur le fin de course droit, l'essuie glace réalise le cycle continu suivant: 
 + 
 +  * déplacement à gauche jusqu'à action sur le fin de course gauche 
 +  * déplacement à droite jusqu'à action sur le fin de course droite 
 +  * etc 
 + 
 + 
 +===== Eléments de solution ===== 
 +==== Analyse ==== 
 + 
 +**Principe:** 
 +Le module d'interface CAN mis en œuvre dans ce TP est le module repéré "Asservissement"
 +Ce module permet la commande d'un moteur à courant continu 24V/ 1A, dans les deux sens de rotation, en mode "PWM" (modulation de largeur d'impulsion). Cette possibilité a été expérimentée dans le TP5. 
 +Il permet d'acquérir 3 entrées TOR sur lesquelles on pourra connecter les capteurs de fin de courses: 
 + → fcd  (fin de course droit) relié à l'entrée GP5 du contrôleur CAN  
 + → fcg  (fin de course gauche) relié à l'entrée GP6 du contrôleur CAN 
 + → fs  (fin de surcource) relié à l'entrée GP7 du contrôleur CAN 
 +Le cycle demandé conduit au diagramme des états suivant: 
 + 
 +{{  :fr:examples:can:wiper:diag_etats.png  |}} 
 + 
 +**Configurations du module "Asservissement" pour l'utilisation envisagée:** 
 +Comme dans l'exemple n°2, il faut envoyer un certain nombre de "Trames" afin de configurer le module "Asservissement" 
 + 
 +**Trame n°1**   →  pour définir les entrées et sorties du module "Asservissement" 
 +Il faut initialiser le registre GPDDR ("Data Direction Register") -> Idem TP Exemple n°2 
 + 
 +**Trame n°2**   → pour initialiser la sortie GP2 en sortie PWM1 (commande  du moteur dans le sens positif) → Idem TP Exemple n°2 
 + 
 +**Trame n°3**   →  pour définir la fréquence de la  sortie PWM1: → Idem TP Exemple n°2 
 + 
 +**Trame n°4**   →  pour définir le rapport cyclique de la sortie PWM1 (module de la commande donc de la vitesse du moteur) → Idem TP Exemple n°2 
 + 
 +**Trame n°5**   → pour initialiser la sortie GP3 en sortie PWM2 (commande  du moteur dans le sens négatif) 
 +D'après la notice technique du circuit MCP25050 (pages 30 à 32), la génération du signal PWM2 se fait à partie du "Timer2" et la fréquence de ce signal est choisie grâce au registre "T2CON" d'adresse 06H  (page 15 Doc MCP25050). 
 +<code c> 
 +bit 7 =1  TMR2ON Validation du "Timer 2" 
 +bits 5:4 seront mis à 0 pour avoir un coefficient de division de fréquence égal à 1 ("TMR2 prescaler value" = 1) 
 +T_IM_Asservissement.data[0]=0x22; // Adresse du registre T1CON en écriture (doc MCP25050 p15) 06H + décalage = 06H + 1CH = 22H 
 +T_IM_Asservissement.data[1]=0xB3; // Masque sur le registre (doc MCP25050 p32)  
 +T_IM_Asservissement.data[2]=0x80; // Valeur à charger dans le registre adressé 
 +</code> 
 +Suite à ces définitions, il faudra envoyer la trame puis attendre la réponse de type "Ack"  
 + 
 +**Trame n°6**   →  pour définir la fréquence de la  sortie PWM2: 
 +Cette fréquence dépend de la valeur chargée dans le registre "PR2" 
 +<code c> 
 +T_IM_Asservissement.data[0]=0x24; // adresse du registre PR2 en écriture (doc MCP25050 p15) 08H + décalage = 08H + 1CH = 24H 
 +T_IM_Asservissement.data[1]=0xFF; // Masque sur le registre (doc MCP25050 p32)  
 +T_IM_Asservissement.data[2]=0xFF; // On chargera 255 dans le registre 
 +</code> 
 +La fréquence du quartz implanté sur la carte "asservissement" étant égale à 16Mhz, la fréquence du signal PWM2 sera donc égale à :     FPWM = 16.106/(4.256) = 15,6 KHz 
 +Ce qui est une fréquence correcte pour piloter un moteur en PWM (fréquence sensiblement inaudible). 
 +Suite à ces définitions, il faudra envoyer la trame puis attendre la réponse de type "Ack"
 + 
 +**Trame n°7**   →  pour définir le rapport cyclique de la sortie PWM2 (module de la commande donc de la vitesse du moteur) 
 +<code c> 
 +T_IM_Asservissement.data[0]=0x25; //adresse du registre PWM2DCH en écriture (doc MCP25050 p15) 0AH + décalage = 0AH + 1CH = 26H 
 +T_IM_Asservissement.data[1]=0xFF; //masque sur le registre (doc MCP25050 p33) 
 +T_IM_Asservissement.data[2]=0x00; // Commande = 0 (0xFF=255 pour la commande Maxi)  
 +</code> 
 +Suite à ces définitions, il faudra envoyer la trame puis attendre la réponse de type "Ack"
 + 
 +**Acquisition de l'état des fins de course:** 
 + 
 +Définition de la trame interrogative permettant de connaître l'état des fins de course: 
 +Dans ce cas, la trame envoyée par le contrôleur CAN (Circuit SJA1000 sur carte CAN_PC104) sera vue par le récepteur (circuit MCP25050 sur module) comme une IRM ''Information Request Message'', avec la fonction ''Read register'' (voir documentation  technique du MPC25025 pages 22).  
 + 
 +D’après le tableau donné page 22 de la notice du MCP25050, l’identificateur lui-même contiendra l’adresse du registre lu. Cette adresse est placée sur les bits  ID15 à ID8 de l’identificateur en mode étendu (bits qui seront réceptionnés et placés dans le registre RXBEID8). Le registre concerné est GPPIN d’adresse ''1Eh'' (voir documentation  technique du MPC25025 pages 37). 
 +D’autre part, les trois bits de poids faibles de l’identificateur en mode étendu devront être positionnés à 1. 
 +L’identificateur défini dans le chapitre 1 devra donc être complété comme suit: 
 +{{  :fr:examples:can:wiper:acquisition.png?600  |}} 
 + 
 +→ Définition de variables structurées sous le modèle  "Trame": 
 +<code c> 
 +Trame T_IRM_Acquisition_FC; // Trame destinée à l’interrogation du module asservissement pour acquérir Fins de Courses. 
 +</code> 
 +Remarque:  La variable structurée ''T_IRM_Acquisition_FC'' comportera 5 octets utiles seulement, 1octet pour trame_info et  4 octets pour l'identificateur en mode étendu (qui comprendra l'adresse du registre concerné par la lecture. 
 + 
 +→ Accès et définition des différents éléments de la variable structurée ''Lecture_FC''  
 +<code c> 
 +  T_IRM_Acquisition_FC.trame_info.registre=0x00;  //On initialise tous les bits à 0 
 +  T_IRM_Acquisition_FC.trame_info.champ.extend=1; //On travaille en mode étendu 
 +  T_IRM_Acquisition_FC.trame_info.champ.dlc=0x01; //Il y aura 1 octet de données demandé 
 +  T_IRM_Acquisition _FC.ident.extend.identificateur.ident=0x00841E07;  
 +</code> 
 +Suite à ces définitions, il faudra  
 +  * envoyer la trame par la fonction ''Ecrire_Trame(Acquisition _FC)'' 
 +  * puis attendre la réponse de type "OM"  en utilisant la fonction  ''Lire_Trame(&T_Recue)'' 
 + 
 +D'après la définition des identificateurs donnée en Annexe, une trame de réponse à une IRM a le même identificateur que la trame interrogative qui en a été à l'origine.  
 +Vu du module (du MCP25050), la réponse à un "IRM" (Information Request Message) est un "OM" (Output Message).  
 +La différence avec la trame interrogative origine est que cette trame réponse comporte le paramètre ''value'' (au rang 0 de la partie "data" de la trame de réponse). Ce paramètre est l'image des entrées. On récupère donc l'état des différents capteurs. 
 + 
 +**Définition de variables structurées images de l'état des fins de course** 
 + 
 +La trame reçue en réponse à cette trame interrogative comportera en data[0], l'état des fins de course. On recopie cette donnée reçue dans une variable image. 
 +union byte_bits FC; 
 +<code c> 
 +#define Etats_FC FC.valeur // Pour l'ensemble des états fins de courses 
 +#define fs FC.bit.b7  // Pour fin de surcourse 
 +#define fcg FC.bit.b6 // Pour fin de course gauche 
 +#define fcd FC.bit.b5 // Pour fin de course droit 
 +</code> 
 + 
 +Afin de pouvoir détecter les changements d'état des capteurs, on mémorise l'état dans une deuxième variable structurée -> Etat_mémorisé  
 +<code c> 
 +union byte_bits FC_Mem; // Pour fins de courses mémorisés 
 +#define Etats_FC_Mem FC_Mem.valeur  // Pour l'ensemble des états mémorisés 
 +</code> 
 +Si l'état d'une variable acquise est différente de sa valeur mémorisée, c'est qu'il y a eu un changement d'état. On en déduit alors les actions à mener. 
 +  
 + 
 +==== Organigramme: ==== 
 + 
 +{{  :fr:examples:can:wiper:orga_3.png  |}} 
 + 
 +==== Programme en "C" ==== 
 + 
 +<code c> 
 +/************************************************************************************ 
 +*       TPs sur  EID210 / Réseau CAN - VMD  (Véhicule Multiplexé Didactique)  
 +************************************************************************************* 
 +*       APPLICATION: Commande Essuie-glace à distance 
 +************************************************************************************* 
 +*   TP Exemple n°3:   Battement essuie glace avant  
 +*------------------------------------------------------------------------------------   
 +*   CAHIER DES CHARGES :        
 +*  *********************        
 +*  On souhaite que l'essuie glace réalise le cycle suivant: 
 +* - rotation droite jusqu'à atteindre le fin de course droit   
 +* - arrêt sur fin de course droit et démarrage en rotation gauche 
 +* - rotation gauche jusqu'à atteindre le fin de course gauche   
 +* - arrêt sur fin de course gauche et démarrage en rotation droite 
 +* etc ... 
 +*  La structure de programme sera de type "boucle de scrutation".  
 +*  On affichera à l'écran, dans quel état on est. 
 +*------------------------------------------------------------------------------------- 
 +*  NOM du FICHIER :  TP_Exemple 3.C          
 +* *****************  
 +**************************************************************************************/ 
 + 
 +// Déclaration des fichiers d'inclusion 
 +#include <stdio.h> 
 +#include"Structures_Donnees.h" 
 +#include"cpu_reg.h" 
 +#include "eid210_reg.h" 
 +#include "CAN_vmd.h" 
 +#include "Aton_can.h" 
 + 
 +// Déclaration des variables  
 +// Pour les Indicateurs divers (variables binaires) 
 +union byte_bits Indicateurs,FC,FC_Mem; // Structures de bits 
 +#define I_Sens_Rotation Indicateurs.bit.b0  
 +#define I_Attente_Reponse_IRM Indicateurs.bit.b1  
 +#define I_Message_Pb_Affiche Indicateurs.bit.b2  
 +// Pour les fins de course 
 +#define Etat_FC FC.valeur // Pour l'ensemble des fins de course 
 +#define fs FC.bit.b7 // Pour fin de surcourse 
 +#define fcg FC.bit.b6 // Pour fin de course gauche 
 +#define fcd FC.bit.b5 // Pour fin de course droit 
 +#define Etat_FC_Mem FC_Mem.valeur // Pour la mémorisation des fins de courses 
 +// Déclarations des diverses trames de communication 
 +Trame Trame_Recue; // Pour la trame qui vient d'etre reçue par le controleur 
 +#define Ident_Trame_Recue Trame_Recue.ident.extend.identificateur.ident 
 +// Trmes de type "IM" (Input Message -> trame de commande) 
 +Trame T_IM_Asservissement; // Pour la commande du moteur 
 +Trame T_IRM_Acquisition_FC; // Pour l'acquisition de l'état des fins de courses EG  
 +// Déclaration constantes 
 +#define Module_Vitesse 100 
 + 
 +//====================== 
 +// FONCTION PRINCIPALE 
 +//====================== 
 +main() 
 +
 +//  INITIALISATIONS 
 +//------------------ 
 +// Déclaration de variables locales à la fonction principale 
 +int Cptr_Affichage=0,Cptr_TimeOut; 
 + 
 +Init_Aton_CAN(); 
 +clsscr(); 
 +// Trame de type "IM" (trame de commande): Données d'identification 
 +T_IM_Asservissement.trame_info.registre=0x00; 
 +T_IM_Asservissement.trame_info.champ.extend=1; 
 +T_IM_Asservissement.trame_info.champ.dlc=0x03; 
 +T_IM_Asservissement.trame_info.champ.rtr=0; 
 +T_IM_Asservissement.ident.extend.identificateur.ident=Ident_T_IM_Asservissement; 
 +// Pour définir des Entrées/Sorties 
 +T_IM_Asservissement.data[0]=0x1F; // Adresse du registre GPDDR  (direction de E/S) 
 + // doc MCP25050 Page 16  
 +T_IM_Asservissement.data[1]=0xEF; // Masque -> Bit 7 non concerné  
 +T_IM_Asservissement.data[2]=0xE3; // Valeur ->  1 si Entrée et 0 si Sortie 
 + // GP7=fs Entrée; GP6=fcg Entree; GP5=fcd Entrée;  GP4=ValidIP Sortie;  
 + // GP3=PWM2 Sortie; GP2=PWM1 Sortie; GP1=AN1 Entrée; GP0=AN0 Entrée;  
 +I_Message_Pb_Affiche=0; 
 +do {Ecrire_Trame(T_IM_Asservissement); // C'est la première trame envoyée 
 +   Cptr_TimeOut=0; 
 + do{Cptr_TimeOut++;}while((Lire_Trame(&Trame_Recue)==0)&&(Cptr_TimeOut<2000)); 
 + if(Cptr_TimeOut==2000) 
 + { gotoxy(2,12); 
 +   printf(" Pas de reponse a la trame de commande en initialisation \n"); 
 +   printf(" Couper et/ou  remettre    alimentation 12 V  \n"); 
 +   do{}while(Lire_Trame(&Trame_Recue)==0); // On attend les trames  "On Bus" 
 +              for(Cptr_TimeOut=0;Cptr_TimeOut<100000;Cptr_TimeOut++); 
 +              Ecrire_Trame(T_IM_Commodo_EG);  // Renvoyer trame IM sur réseau CAN 
 +   Cptr_TimeOut=0; 
 +   do{Cptr_TimeOut++;}while((Lire_Trame(&Trame_Recue)==0)&&(Cptr_TimeOut<2000)); 
 +   if(Cptr_TimeOut==2000) 
 + { gotoxy(2,12); 
 +     printf(" Pas de reponse a la trame de commande en initialisation \n"); 
 +   printf("  Modifier le programme et recommencer  \n"); 
 +         do{}while(1); // On reste bloqué, on ne continue pas 
 + }  
 +
 +// Pour mettre à 0 les sorties  
 +T_IM_Asservissement.data[0]=0x1E; // Adresse du registre GPLAT  (Registre I/O) 
 +T_IM_Asservissement.data[1]=0x1C; // Masque -> sorties GP4,3,2 sont consernées 
 +T_IM_Asservissement.data[2]=0x00; // Valeur ->  les 3 sorties à 0 
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour définir sortie GP2 en PWM1   
 +T_IM_Asservissement.data[0]=0x21; // Adresse du registre T1CON 
 +T_IM_Asservissement.data[1]=0xB3; // Masque -> seuls bit 7;5;4;1;0 consernés 
 +T_IM_Asservissement.data[2]=0x80; // Valeur ->  TMR1ON=1; Prescaler1=1 
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour définir fréquence signal sortie PWM1   
 +T_IM_Asservissement.data[0]=0x23; // Adresse du registre PR1 
 +T_IM_Asservissement.data[1]=0xFF; // Masque -> tous les bits sont consernés 
 +T_IM_Asservissement.data[2]=0xFF; // Valeur ->  PR1=255 
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour définir sortie GP3 en PWM2   
 +T_IM_Asservissement.data[0]=0x22; // Adresse du registre T2CON 
 +T_IM_Asservissement.data[1]=0xB3; // Masque -> seuls bit 7;5;4;1;0 consernés 
 +T_IM_Asservissement.data[2]=0x80; // Valeur ->  TMR2ON=1; Prescaler2=1 
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour définir fréquence signal sortie PWM2   
 +T_IM_Asservissement.data[0]=0x24; // Adresse du registre PR2 
 +T_IM_Asservissement.data[1]=0xFF; // Masque -> tous les bits sont consernés 
 +T_IM_Asservissement.data[2]=0xFF; // Valeur ->  PR2=255 
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour initialiser rapport cyclique PWM1  
 +T_IM_Asservissement.data[0]=0x25; // Adresse du registre PWM1DC 
 +T_IM_Asservissement.data[1]=0xFF; // Masque -> tous les bits sont consernés 
 +T_IM_Asservissement.data[2]=Module_Vitesse; // Valeur ->  PWM1DC  
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour initialiser rapport cyclique PWM2 à 0 
 +T_IM_Asservissement.data[0]=0x26; // Adresse du registre PWM2DC 
 +T_IM_Asservissement.data[1]=0xFF; // Masque -> tous les bits sont consernés 
 +T_IM_Asservissement.data[2]=0; // Valeur ->  PWM2DC=0  
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Pour Valider le circuit de puissance 
 +T_IM_Asservissement.data[0]=0x1E; // Adresse du registre GPLAT  (Registre I/O) 
 +T_IM_Asservissement.data[1]=0x10; // Masque -> sortie GP4 (ValidIP) est conserné 
 +T_IM_Asservissement.data[2]=0x10; // Valeur ->  ValidIP=1 
 +Ecrire_Trame(T_IM_Asservissement); 
 +do{}while(Lire_Trame(&Trame_Recue)==0); // Attendre réponse 
 +// Masque pour les commandes IM futures 
 +T_IM_Asservissement.data[1]=0xFF; // Masque -> tous les bits sont consernés 
 +// Pour acquérir l'état des fin de course 
 +// Trame de type "IRM" (trame interrogative): Données d'identification 
 +T_IRM_Acquisition_FC.trame_info.registre=0x00; 
 +T_IRM_Acquisition_FC.trame_info.champ.extend=1; 
 +T_IRM_Acquisition_FC.trame_info.champ.dlc=1; 
 +T_IRM_Acquisition_FC.trame_info.champ.rtr=1; 
 +T_IRM_Acquisition_FC.ident.extend.identificateur.ident=Ident_T_IRM1_Asservissement; 
 + Ecrire_Trame(T_IRM_Acquisition_FC); // Envoi trame pour acquérir états fins de course 
 +do{}while(Lire_Trame(&Trame_Recue)==0); //On attend la réponse 
 + Etat_FC=~Trame_Recue.data[0]; // On récupére l'état des fin de course 
 + Etat_FC_Mem=Etat_FC; // On le mémorise 
 +// Initialisation des variables diverses 
 + I_Sens_Rotation=0; 
 + 
 +// Pour afficher titre 
 +gotoxy(1,6); 
 +printf(" ****************************************************************** \n"); 
 +printf("  TPs sur Reseau CAN  Application: Commande Essui-glace a distance  \n"); 
 +printf(" ------------------------------------------------------------------ \n"); 
 +printf("   TP Exemple n°3    Battement du BALAI D'ESSUI GLACE               \n"); 
 +printf(" ******************************************************************* \n"); 
 + 
 +// BOUCLE PRINCIPALE 
 +//******************* 
 +while(1) 
 +
 + // Pour "Acquérir l'état des fins de course" 
 + Ecrire_Trame(T_IRM_Acquisition_FC); // Envoi trame pour acquérir états fins de course 
 + Cptr_TimeOut=0; 
 + do{Cptr_TimeOut++;}while((Lire_Trame(&Trame_Recue)==0)&&(Cptr_TimeOut<10000)); 
 + if(Cptr_TimeOut==10000) 
 + {clsscr(),gotoxy(2,10); 
 + printf(" Pas de reponse a la trame interrogative pour fins de courses \n"); 
 + printf("  Il faut recharger et relancer le programme \n"); 
 + do{}while(1);} // Stop 
 + else { if(Trame_Recue.ident.extend.identificateur.ident==Ident_T_IRM1_Asservissement) 
 + // Si identificateur attendu 
 + {Etat_FC=~Trame_Recue.data[0]; // On récupére l'état des fin de course 
 + if(Etat_FC!=Etat_FC_Mem) // S'il y a eu une modification  
 + // de l'état des fin de courses -> on traite 
 + {Etat_FC_Mem=Etat_FC; // Mémorisation du nouvel état 
 +       // Traiter le changement de l'état 
 + if(I_Sens_Rotation) // Si on tourne en négatif 
 + {if(fcg) // Si on est arrivé en fin de course gauche 
 + {T_IM_Asservissement.data[2]=0; // On arrête la rotation négative 
 + T_IM_Asservissement.data[0]=0x26; // Adresse du registre PWM2DC 
 + Ecrire_Trame(T_IM_Asservissement); 
 + while(Lire_Trame(&Trame_Recue)==0){}; // On attend la réponse 
 + T_IM_Asservissement.data[2]=Module_Vitesse; // On commande la rotation positive 
 + T_IM_Asservissement.data[0]=0x25; // Adresse du registre PWM1DC 
 +   Ecrire_Trame(T_IM_Asservissement); 
 + while(Lire_Trame(&Trame_Recue)==0){}; // On attend la réponse 
 + I_Sens_Rotation=!I_Sens_Rotation; // On change d'état   
 + }} 
 + else {// On tourne en positif 
 + if(fcd) // Si on est arrivé en fin de course droit 
 + {T_IM_Asservissement.data[2]=0; // On arrête la rotation positive 
 + T_IM_Asservissement.data[0]=0x25; // Adresse du registre PWM1DC 
 + Ecrire_Trame(T_IM_Asservissement); 
 + while(Lire_Trame(&Trame_Recue)==0){}; // On attend la réponse 
 + T_IM_Asservissement.data[2]=Module_Vitesse; // On commande la rotation négative 
 + T_IM_Asservissement.data[0]=0x26; // Adresse du registre PWM2DC 
 +   Ecrire_Trame(T_IM_Asservissement); 
 + while(Lire_Trame(&Trame_Recue)==0){}; // On attend la réponse 
 + I_Sens_Rotation=!I_Sens_Rotation; // On change d'état   
 + }} 
 + }}} // Fin "Traiter changement d'état" 
 + } //  Fin boucle principale 
 +} // Fin fonction principale 
 +</code>
  
fr/examples/can/wiper/tp3.1267629121.txt.gz · Last modified: 2020/07/20 09:00 (external edit)
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0