Associacion Robot ICAM Toulouse
  Dimanche 18 Mai 2025
RobotICAM
Coupe 2006
Notre Robot
Support Elec
Support Info
Support Meca

Tutorial: Programmation sur MPLAB C30

 
By Benjamin BAQUE

Télécharger

 

Le but de ce tutorial est d’apprendre à programmer les PIC30 de Microchip.
Nous allons voir pas à pas comment configurer les différents registres afin d’utiliser la COM série, le PWM, le CAN …
Tout d’abord, il faut déclaré les librairies à utiliser et configurer les registres d’initialisation du PIC.
Notez que la PLL est à 16. C'est-à-dire que la fréquence de l’horloge est multipliée par 16.

Exemple :

On à un quartz à 8MHZ. Il faut diviser par 4 pour avoir le nombre d’instruction par seconde. On obtient alors 2 MIPS (Million d’instruction par seconde). Enfin, comme la PLL est à 16, le PIC tourne alors à 32 MIPS.

Note : Pour l’utilisation des PWM, UART …, c’est la vitesse du quartz qu’il faut prendre en compte, sans la PLL.

  1.  
  2. //Déclaration du microcontroleur + des fichiers utilisé
  3. #include <p30f2010.h>
  4. #include <uart.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7.  
  8.  
  9. //Déclaration des différents registres
  10. _FOSC(CSW_FSCM_OFF & XT_PLL16)//XT with 16xPLL oscillator, Failsafe clock off
  11. _FWDT(WDT_OFF);                  //Turn off the Watch-Dog Timer.
  12. _FBORPOR(MCLR_EN & PWRT_OFF);    //Enable MCLR reset pin and turn off the
  13.                                  //power-up timers.
  14. _FGS(CODE_PROT_OFF);             //Disable Code Protection

 

Maintenant que cela est fait, on peut commencer le code. Comme pour le C classique, il faut crée une procédure principale « main », et d’autres procédures et fonctions.

Commençons tout d’abord, par la déclaration des variables globale du programme.

  1. //Déclaration des varaibles
  2. char buffer[16];        // Tableau de 16 éléments
  3. unsigned int temp0;     // Valeur de 0 à 32767
  4. unsigned char temp1;    // Valeur de 0 à 255
  5. signed char temp2;      // Valeur de -128 à +128
  6.  
  7. //Déclaration des constantes
  8. #define FCY       32000000  //Vitesse en MIPS
  9. #define BAUDRATE  19200     //Vitesse du com serie
  10.  
  11.  

 

Voici une procédure pour perdre du temps. La fonction Delay existe aussi dans C30. Il faut pour cela charger le fichier delay.h au début du programme.

  1. //----------------------------------------------------------------------------
  2. //      DELAY: Fonction pour perdre du temps
  3. //----------------------------------------------------------------------------
  4. void DelayCY(unsigned char j)
  5. {
  6.     while(j--) { }
  7. }
  8.  

 

Voici la procédure d’initialisation des ports du PIC. Le registre TRIS configure les ports en entrée ou en sortie. 0 = Output et 1 = Input.

Note: que le TRISC est un registre 16bits. (Voir datasheet)

Dans la deuxième partie, vous avez la configuration du convertisseur analogique numérique. Les pattes RA0 RA1 RA2 RA3 sont configurées en analogique. Les autres sont non configurés, et comme le port A et B partage les mêmes pattes, elles sont donc configurées en numérique, par TRISB.

Un grand nombre d’option sont disponible pour le CAN. Par exemple, il est ici configuré en 10bits. Mais il peut aussi fonctionner en 12 bits.

  1. //----------------------------------------------------------------------------
  2. // INITIALISATION DU PIC
  3. //----------------------------------------------------------------------------
  4. void inipic(void)
  5. {
  6.    OSCCON = 0b0011001101000000; //Config Oscillateur
  7.    TRISB = 0b11111111;          //Configuration du portB en INPUT
  8.    TRISC = 0b101111111111111;   //Configuration du port C en INPUT
  9.                                  //avec un output pour le port com
  10.    TRISD = 0;                    //met le portD et E en output
  11.    TRISE = 0;
  12.  
  13.    // Initialisation CAN
  14.    ADPCFG = 0xFFF0;  // Tout PORTB = Numérique; RA0 1 2 3 = Analogique
  15.    ADCON1 = 0x0000;  // SAMP bit = 0 Pour démarrer/Arreter la conversion
  16.  
  17.    ADCHS = 0x0000;  // Pour se connecter à la bonne entrée analogique (ici A0)
  18.    ADCSSL = 0;      // Skip ANx for input scan
  19.    ADCON3 = 0x0002; // Manual Sample, Tad = internal 2 Tcy: temps de messure
  20.    ADCON2 = 0;
  21.    ADCON1bits.ADON = 1; // turn ADC ON       
  22. } 

 

Voici la configuration du PWM. Ce code va faire générer au pic, un signal créneau, a rapport cyclique variable.

Pour le faire varier, il faut simplement modifier les registres ci-dessous. Notez que le PWM3 est configuré et que c’est la patte H (high) qui va émettre le signal. Il suffit de mettre des 1 sur les registres se terminant par 3L. Le signal PWM sera identique. Mais il est possible de configurer ces registre pour que les signaux 3L et 3H soit complémentaire l’un de l’autre.

Le PWM fonctionne ici en Freemode. C'est-à-dire qu’il ne consomme pas de temps processeur pour fonctionner.

  1. //----------------------------------------------------------------------------
  2. // INIT PWM: PWM EN FREEMODE
  3. //----------------------------------------------------------------------------
  4.  
  5. void init_pwm(void)
  6. {
  7.    //Config PWM
  8.    PTCONbits.PTEN =1;
  9.    PTCONbits.PTSIDL =0;
  10.    
  11.    PTCONbits.PTOPS =0b1111; //Postcaler 1:16 (sert a rien en freemode)
  12.    PTCONbits.PTCKPS =0b00;  //Prescaler 1:1
  13.    PTCONbits.PTMOD =0b00;   //Free mode runing
  14.    
  15.    PTMRbits.PTDIR = 0;     //Counting up
  16.    PTMRbits.PTMR = 5000;   //Time base Count Value
  17.    PTPER = 300;            //Time base Periode value
  18.    PDC3 = 350;             //Duty Cycle Rapport cyclique sur PWM3
  19.  
  20.    //Config PWM3 sur Port E5 (PWM3L)
  21.    PWMCON1bits.PEN3L =0 ;
  22.    PWMCON1bits.PEN3H =1 ;
  23.    PWMCON1bits.PMOD3 =1 ;
  24.  
  25.    OVDCONbits.POVD3H =1;
  26.    OVDCONbits.POVD3L =0;
  27. }

 

Pour la configuration du port série, il n’y a rien à comprendre. La seule chose importante, c’est de ne pas se tromprer lorsque l’on indique la vitesse de transfert. La valeur de baudvalue est calculée automatiquement en fonction des constantes définies en début de programme.

Note: Fcy est divisé par 16 car nous avons une PLL à 16. Ce qui importe pour UART, c’est la vitesse du quartz et non celle du PIC.

  1. //----------------------------------------------------------------------------
  2. // INIT PORT SERIE
  3. //----------------------------------------------------------------------------
  4. void init_serie(void)
  5. {
  6.    unsigned int baudvalue;
  7.    unsigned int U1MODEvalue;
  8.    unsigned int U1STAvalue;
  9.  
  10.    ConfigIntUART1(UART_RX_INT_DIS & UART_TX_INT_DIS );
  11.    U1MODEvalue = UART_EN & UART_IDLE_CON & UART_ALTRX_ALTTX &
  12.               UART_DIS_WAKE & UART_DIS_LOOPBACK &
  13.               UART_DIS_ABAUD & UART_NO_PAR_8BIT &
  14.               UART_1STOPBIT;
  15.    U1STAvalue =  UART_INT_TX_BUF_EMPTY &
  16.               UART_TX_PIN_NORMAL &
  17.               UART_TX_ENABLE & UART_INT_RX_CHAR &
  18.               UART_ADR_DETECT_EN &
  19.               UART_RX_OVERRUN_CLEAR;
  20.    baudvalue = ((FCY/16)/BAUDRATE) - 1;
  21.    OpenUART1(U1MODEvalue, U1STAvalue, baudvalue);
  22. }
  23.  

 

Maintenant que le port série est configuré, il ne reste plus qu’à recevoir des données et à envoyer la réponse appropriée.

Le code ici est très simple. On regarde si l’on a un « b » dans le buffer de réception et si tel est le cas, on envoie le contenu du tableau «*data ». Nous verrons dans la procédure main, comment remplir correctement ce tableau.

  1. //----------------------------------------------------------------------------
  2. // COM SERIE
  3. //----------------------------------------------------------------------------
  4. void USART_com(char *data) // Envoi d'une chaine de caractere sur serie
  5. {
  6. char test;
  7.  
  8. //Receptione
  9.    test = 0;
  10.    IFS0bits.U1RXIF = 0;
  11.    while( DataRdyUART1())
  12.    {
  13.       test = ReadUART1(); //copie le buffer dans test
  14.    }
  15.  
  16. //Envoie les données
  17.    if (test == 98) //si on apuis sur la touche "b"
  18.    {
  19.       // Change la couleur de la diode
  20.       if (PORTDbits.RD1 == 0)   {PORTDbits.RD1 = 1;}
  21.       else                              {PORTDbits.RD1 = 0;}
  22.  
  23.       //Envoie
  24.       do
  25.       {
  26.          while (BusyUART1());
  27.          putcUART1(*data);
  28.       }
  29.       while (*data++);
  30.    }
  31. }

 

Voici la fonction d’acquisition de valeur analogique. Le portA mesure la tension sur la patte indiqué, puis renvoie une valeur correspondante. Si il renvoi 0, alors la tension est nulle et si il renvoie 1024, alors la tension est de 5V.

Note: Il est possible de fournir au pic des tensions de références autres que 0 et 5 afin de faire une conversion plus précise. Par exemple, cela peut être utilisé si la valeur maximale renvoyée par un capteur, ne peut excéder 3.3V.

La variable Chx contient le numéro de la patte analogique à mesurer. Exemple : si Chx = 2 alors on mesure sur la patte AN2.

Grâce au registre ADCBUF, il est possible de lire les 16 dernières valeurs acquises. C’est en quelque sorte une mémoire tampon. Mais surtout, il est possible de lancer un multiple Sampling. C'est-à-dire, le PIC va prendre par exemple 16 mesures de suite, faire la moyenne, et renvoyer une seule valeur.

  1. //----------------------------------------------------------------------------
  2. //      FONCTIONS D ACQUISITION: Renvoie une valeur sur 10bits
  3. //----------------------------------------------------------------------------
  4. unsigned int LectureCH (unsigned int Chx)
  5. {
  6.    ADCHS = Chx;               // Selectione la bonne entrée
  7.    ADCON1bits.SAMP = 1;       // start sampling ...
  8.    DelayCY(20);               // Attend un peu
  9.    ADCON1bits.SAMP = 0;       // start Converting
  10.    while (!ADCON1bits.DONE)// conversion done?
  11.       {  }
  12.    return ADCBUF0;            //on revoie la VALEUR
  13. } 

 

Maintenant que nous avons déclaré quelques procédures, nous allons les utiliser.

Dans le code suivant, nous allons tout d’abord initialiser le PIC, puis nous exécuterons en boucle le même code.

Ce code va tout d’abord, faire 4 CAN sur les pattes RA0-3, puis faire la somme. Si cette valeur est inférieure à 512, alors il va faire clignoter la diode de la patte RD0.

Ensuite, nous allons utiliser la procédure de communication par port série.

Dans le tableau de caractère ASCII « buffer » nous allons copier le chiffre 56 (En ASCII) puis nous allons indiquer le caractère terminal « \0 ». Une fois que buffer est rempli, on lance la procédure de com.

Elle va vérifier si le caractère « b » est présent dans le buffer d’arriver, et si tel est le cas, il enverra en réponse le chiffre 56. Il est possible de tester cela grâce à hyper terminal. Notez que la vitesse est ici de 19600. Il est possible de monter jusqu'à 1Mbps, en utilisant un module USB mais surtout en vérifiant dans la datasheet que la vitesse du quartz est compatible avec la vitesse de communication que l’on souhaite.

  1. //----------------------------------------------------------------------------
  2. // MAIN
  3. //----------------------------------------------------------------------------
  4. int main(void)
  5. {
  6.    //Declaration des variables locales
  7.    char capteur[5];
  8.    unsigned int i;
  9.  
  10.    //Initialisation
  11.    inipic();
  12.    init_serie();
  13.    init_pwm();
  14.    
  15.  
  16.    //Code principal
  17.    while (1)
  18.    {
  19.       //Lecture des 4 pattes analogiques et passage de 10bits à 8bits
  20.       capteur[1] = LectureCH(0x0000)/4;   //AN0
  21.       capteur[2] = LectureCH(0x0001)/4;   //AN1
  22.       capteur[3] = LectureCH(0x0002)/4;   //AN2
  23.       capteur[4] = LectureCH(0x0003)/4;   //AN3
  24.  
  25.       temp0 = capteur[1] + capteur[2] + capteur[3] + capteur[4];
  26.       if (temp0 < 512)
  27.       {
  28.          // Change la couleur de la diode
  29.          if (PORTDbits.RD1 == 0)    {PORTDbits.RD0 = 1;}
  30.          else                       {PORTDbits.RD0 = 0;}
  31.       }
  32.  
  33.       //Comunication serie
  34.       buffer[0] = 5+0x30;    //Rempli le buffer avec 56
  35.       buffer[1] = 6+0x30;
  36.       buffer[2] = '\0';
  37.  
  38.       USART_com(buffer);
  39.  
  40.       //Attend un peu
  41.       for (i=0;i< 20000 ;i++)
  42.       {
  43.          DelayCY(255);
  44.       }
  45.    }
  46. }

Pour toutes questions: benjamin.baque AT polymtl.ca

Copyright © 2006 RobotIcamToulouse