[Programmation] Utilisation de Macros avec paramètre
Répondre à la discussion
Affichage des résultats 1 à 18 sur 18

Utilisation de Macros avec paramètre



  1. #1
    scaypapa

    Question Utilisation de Macros avec paramètre


    ------

    Bonjour,

    Je cherche à contrôler la valeur de RBx en fonction d'un paramètre x et je ne vois pas comment m'y prendre...

    Contexte :
    Je cherche à réaliser une matrice de boutons de type triangulaire étendue comme décrite dans ce lien.

    Cela fonctionne à merveille : avec deux diodes par bouton, je peux bien appuyer sur n'importe quelle combinaison de boutons sans conflit et pour n entrées sur le Pic, je peux avoir n*(n-1) boutons. Pour mes essais, j'utilise 3 entrées et 6 boutons.
    Mon code est fonctionnel : il teste si un bouton est appuyé ou relâché, affecte la valeur 1 ou 0 au tableau de boutons Bouton[i], puis fait clignoter une LED pour indiquer quels boutons sont appuyés.

    Mon problème :
    Pour optimiser le code, dans l'idéal, j'aurai aimé avoir un paramètre #define nbBoutons 6 qui définit le nombre total de boutons, adaptant le programme en fonction.

    Je voudrais pour cela contrôler la valeur de RBx en fonction d'un paramètre x, sans passer par le test de RB0, RB1, RB2 dans le code. Je ne sais pas si je me fais bien comprendre...
    C'est pour cette raison que j'ai défini la constante = #define Col(x) PORTBbits.RB##x
    Malheureusement, dans le code, je peux sans problème contrôler la valeur de Col(1), Col(2), Col(3) etc... mais le code ne compile plus si j'y passe un paramètre :
    Code:
    j = (ligne-1)*2;
    for (i=1; i<=3; i++)
    {
         if (Col(i) == Bouton[j]) {  Bouton[j] = !Bouton[j]; }
         j++;
    }
    Comment pourrais-je faire pour tester le bon port RB en fonction d'un paramètre ?
    Je vous mets mon code complet pour le contexte, mais mon problème est bien d'envoyer un paramètre à mon "tableau de constantes" Col(x).

    Code:
    #include <xc.h>
    
    // CONFIG1H
    #pragma config FOSC = INTIO67   // Oscillator Selection bits (Internal Oscillator Block))
    
    // CONFIG2H
    #pragma config WDTEN = OFF      // Watchdog Timer Enable bits (Watch dog timer is always disabled. SWDTEN has no effect.)
    
    // CONFIG3H
    #pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<5:0> pins are configured as digital I/O on Reset)
    
    #define _XTAL_FREQ  1000000
    
    #define Debounce()  __delay_ms(5)
    
    #define Col(x)  PORTBbits.RB##x
    #define LED     LATDbits.LATD0
    
    char Bouton[] = {0,0,0,0,0,0};
    
    void ControleLine(unsigned char ligne)
    {
        
     /* Pour tester une ligne, on la pose en sortie, les autres étant remises en entrée puis on la place à 0
      * On testera ensuite chaque entrée une à une pour savoir si le bouton en question est appuyé ou non
      */
    
     /* Si le bouton est appuyé, avec le pull-up, on a la colonne correspondante à 0
      * On place la valeur de Bouton[i] à 1 s'il était à 0 (donc l'inverse)
      * Si on relâche le bouton, c'est que la colonne est repassée à 1 alors que le bouton était à 1 (égalité)
      */
    
        unsigned char i  = (ligne-1)*2;
        LATB = TRISB = 0xFF;
        
        switch (ligne)
        {
            case 1:
                // Active la ligne
                LATBbits.LATB1 = TRISBbits.TRISB1 = 0;
                // Contrôle
                if (Col(2) == Bouton[i] | Col(3) == Bouton[i+1])
                {
                    Debounce();
                    if (Col(2) == Bouton[i]) { Bouton[i] = !Bouton[i]; }
                    if (Col(3) == Bouton[i+1]) { Bouton[i+1] = !Bouton[i+1]; }
                }
                break;
            case 2:
                // Active la ligne
                LATBbits.LATB2 = TRISBbits.TRISB2 = 0;
                // Contrôle
                if (Col(1) == Bouton[i] | Col(3) == Bouton[i+1])
                {
                    Debounce();
                    if (Col(1) == Bouton[i]) { Bouton[i] = !Bouton[i]; }
                    if (Col(3) == Bouton[i+1]) { Bouton[i+1] = !Bouton[i+1]; }
                }
                break;
            case 3:
                // Active la ligne
                LATBbits.LATB3 = TRISBbits.TRISB3 = 0;
                // Contrôle
                if (Col(1) == Bouton[i] | Col(2) == Bouton[i+1])
                {
                    Debounce();
                    if (Col(1) == Bouton[i]) { Bouton[i] = !Bouton[i]; }
                    if (Col(2) == Bouton[i+1]) { Bouton[i+1] = !Bouton[i+1]; }
                }
                break;
        }
    }
    
    void main(void) {
        
        unsigned char i, j;
            
        TRISD = 0;
        LATD = 0;
    
        INTCON2bits.RBPU = 0;   // Pull-up on RB
        
        while(1) {
            
            /* Dans la pratique, on n'utilisera la valeur Bouton que pour enregistrer son dernier état 
             * Puis on transmettra immédiatement un signal sans utiliser d, actuellement les délais de clignotement ralentissent beaucoup le traitement.
             */
            
            for (i=1; i<=3; i++)
            {
                ControleLine(i);
            }
            
            for (i=0; i<6; i++)
            {
                if (Bouton[i] == 1) {
                    for (j=0; j<=i; j++)
                    {
                        LED = 1;
                        __delay_ms(50);
                        LED = 0;
                        __delay_ms(150);
                    }
                    __delay_ms(200);
                }
            }
        }
    }
    Merci pour votre aide...

    -----

  2. #2
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    Bonsoir,
    Col(x) est une macro, elle est traitée par le préprocesseur C, qui ne fait que de la manipualtion de texte.
    Col(1) va être remplacée avant compilation par PORTBbits.RB1, qui est un define qui pointe en dur vers un bit de registre.
    En mettant une variable, par exemple Col(v), ça va donner PORTBbits.RBv qui ne passe pas à la compilation.

    #define Col(x) ((PORTB>>x)&1)

  3. #3
    invitef86a6203

    Re : Utilisation de Macros avec paramètre

    ton col(x) est dans un define et il pointe un bit bien défini.

    Si tu veux pouvoir mettre le bit en paramètre, il faut
    1) lire le port en entier
    2) faire un mask qui correspond au bit choisi.

    le mask est la puissance de 2 du bit ciblé.
    soit en décimale
    1,2,4,8,16,32,64,128 pour les bits allant de 0 à 7

    Pour la transformation bit => mask on peut utiliser soit un tableau soit un prog pour décaler n fois le chiffre 1 vers la gauche
    mask = 1;
    for (i=0; i<x;i++) mask = mask << 1;

    puis on lit le bit avec un & logique (pas le && qui compare booléen) et on compare !=0
    la comparaison !=0 permet de definir comme vrai toute les valeurs du mask qui seront le résultat après "maskage".

    if ( (PORTB & mask) != 0) exécute la condition bouton vrai

  4. #4
    scaypapa

    Re : Utilisation de Macros avec paramètre

    Bonsoir et merci beaucoup pour vos réponses.

    @satinas : Il y a de ça en effet, mais le code que tu propose ne fonctionne pas non plus : Col(v) est rempacé par ((PORTB >> v) & 1) ce qui ne compile pas non plus.
    On est toujours face au même problème : on voudrait que v soit remplacé par sa valeur et non par son nom.

    En tout cas, le problème était bien là et en utilisant des masques (je découvre les opérations en bit par bit, c'est intéressant, mais ça demande du travail ) comme propose freepicbasic, j'ai réussi à trouver mon résultat.
    Mais je tiens à faire part d'un autre problème que j'ai rencontré à cause d'une erreur de compréhension de ma part.

    Pour moi, les #define permettaient de définir des constantes, mais je n'avais pas bien saisi ce qu'impliquait le fait que ce qui était défini était remplacé en dur dans le code.
    Comme je l'avais écrit, ce type de matrice permet avec n entrées d'utiliser n*(n-1) boutons.
    J'ai créé une constante à définir avant de programmer le PIC #define NbBoutons 6 et je calculais le nombre d'entrées nécessaires grâce à l'aide de Bastien31 avec
    #define NbEntrees (ceil (round(100 * ((1 + sqrt(4*NbBoutons + 1)) / 2)) / 100)) (ce qui renvoie le n dont je parlais ci-dessus).

    Je pensais bêtement qu'utiliser un #define plutôt qu'une variable permettrait d'avoir un code plus rapide puisque c'est une "constante".
    Mais les #define ne sont pas des constantes en tant que tel, ce sont des "remplacements dans le code".
    Du coup, mon nombre d'entrées était recalculé à chaque fois que j'y faisais référence dans le code et les temps de traitement se sont mis à EXPLOOOOSER !!
    En fait, il est préférable de déclarer une variable globale (en dehors du main) qui est calculée une seule fois à l'initialisation.

    Je laisse ici mon code, pour ceux qui en auraient l'usage, il gère les appuis et relâchements sur une matrice "triangulaire étendue polyphonique" comme définie dans le lien de mon premier message.
    En l'état, ça fonctionne jusqu'à 56 boutons (et donc 8 entrées) puisque je n'utilise que le port B. Pour aller plus loin, il faudrait utiliser un second port, lui adjoindre des pull-ups physiques (en tout cas pour mon Pic d'essais 18f46k22 qui ne propose des pull-ups que pour le port B) et modifier un peu le code pour prendre en compte cette modification.

    En tout cas merci beaucoup pour votre aide.

    Code:
    /*
     * Création d'une matrice de contacts 
     * On utilise n entrées pour n*(n-1) boutons suivant le schéma Triangulaire étendu et polyphonique de http://alpmn.byethost32.com/matrices.htm
     * Fonctionne jusqu'à 56 boutons après quoi il faudrait utiliser un port supplémentaires (plus de 8 entrées) et donc adjoindre au nouveau port des Pull-ups physiques
    
     * Amélioration possible :
     * On pourrait lancer un timer si plus aucune touche n'est enfoncée pour mettre le Pic en SLEEP() au bout d'un certain temps sans action
     * Il serait réveillé par une interruption sur un port Int avec la méthode décrite dans le lien ci-dessus.
     */
    
    #include <xc.h>
    #include <math.h>
    
    #pragma config FOSC = INTIO67   // Oscillator Selection bits (Internal Oscillator Block))
    #pragma config WDTEN = OFF      // Watchdog Timer Enable bits (Watch dog timer is always disabled. SWDTEN has no effect.)
    #pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<5:0> pins are configured as digital I/O on Reset)
    
    // ============== VALEURS DE CONFIGURATION ===================
    // Fréquence de l'oscillateur
    #define _XTAL_FREQ  1000000
    // Délai de debounce
    #define Debounce()  __delay_ms(5)
    // Entrées
    #define Entrees     PORTB
    #define Ligne        LATB = TRISB
    // Sortie
    #define LED         LATDbits.LATD0
    #define InitLED    TRISDbits.TRISD0
    // Nombre de boutons au total
    #define NbBoutons   6
    // =================== FIN DE LA CONFIG ======================
    
    char Bouton[NbBoutons] = {0};
    char NbEntrees;
    
    void ControleLine(unsigned char ligne)
    {
    /* Pour tester une ligne, on la pose en sortie, les autres étant remises en entrée puis on la place à 0
     * On testera ensuite chaque entrée une à une pour savoir si le bouton en question est appuyé ou non
     */
    
    	unsigned char i, j = 0, premierBouton  = ligne * (NbEntrees-1);
        unsigned char mask;
       
        // Active la ligne
        Ligne = ~(1 << ligne);
        
        // Contrôle
        for (i=0; i<NbEntrees; i++)
        {
            if (i != ligne) {
                /* Si le bouton est appuyé, avec le pull-up, on a la colonne correspondante à 0
                 * On crée donc un masque qui est l'inverse de ce que l'on devrait avoir si le bouton est appuyé et s'il y a un appui, on aura ((Entrees & mask) != 0) == 0
                 * Dans le cas contraire, on aura ((Entrees & mask) != 0) == 1
                 * Si le bouton était relâché (Bouton[x] = 0), et qu'on l'a appuyé, on aura donc égalité entre ((Entrees & mask) != 0) et Bouton[x]
                 * Idem pour un bouton qui était appuyé et que l'on a relâché.
                 */
                 mask = 1 << i;
                 if (((Entrees & mask) != 0) == Bouton[premierBouton+j]) {
                    Debounce();
                    if (((Entrees & mask) != 0) == Bouton[premierBouton+j]) {
                        Bouton[premierBouton+j] = !Bouton[premierBouton+j];
                    }
                }
                j++;
            }
        }
    }
    
    void main(void) {
        
        unsigned char i, j;
        // Nombres d'entrées dans le PIC
        /* Solution complète de x(x-1) = a : x = (1±sqrt(4a+1))/2
         * sqrt(4a+1) est toujours > 1, donc le - ne nous intéresse pas
         * ceil est la valeur entière supérieure ou égale la plus proche
         * On utilise round (100* x )/100 pour arrondir le résultat à 2 décimales. Sinon, le float étant limité en taille, on se retrouve parfois avec une virgule alors qu'on devrait avoir un nombre entier.
         */
        NbEntrees = ceil(round(100 * ((1 + sqrt(4*NbBoutons + 1)) / 2)) / 100);
        
        // Initialisation
        LED = InitLED = 0;
        INTCON2bits.RBPU = 0;   // Pull-up on RB
        
        while(1) {
                  
            for (i=0; i<NbEntrees; i++)
            {
                ControleLine(i);
            }
            
            // Transmission du Message
            /* Dans la pratique, on n'utilisera la valeur Bouton que pour enregistrer son dernier état 
             * Puis on traitera directement l'information.
             * Actuellement les délais de clignotement ralentissent beaucoup le traitement.
             */
            for (i=0; i<NbBoutons; i++)
            {
                if (Bouton[i] == 1) {
                    for (j=0; j<=i; j++)
                    {
                        LED = 1;
                        __delay_ms(20);
                        LED = 0;
                        __delay_ms(80);
                    }
                    __delay_ms(130);
                }
            }
        }
    }
    Dernière modification par Antoane ; 18/11/2016 à 08h03. Motif: màj du code, à la demande de l'auteur

  5. A voir en vidéo sur Futura
  6. #5
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    Bonjour,

    Code:
    #define Col(x) ((PORTB >> x) & 1)
    int y, v; 
    for (v=0; v<8; v++) {
      y = Col(v);
    }
    Ce code doit se compiler sans erreur, PORTB est une variable (readonly) qui contient l'état en entrée de toutes les broches du port B. On la décale par >> pour placer à droite le bit qui nous intéresse. et on isole ce bit avec le & logique.

  7. #6
    invitef86a6203

    Re : Utilisation de Macros avec paramètre

    #define Col(x) ((PORTB >> x) & 1)
    i
    Si x est entre 1 et 8 ! Le résultat est dans le Carry 'if (STATUS & CY) commande ...
    Si x est entre 0 et 7 ! Que fait le compilateur avec (PORTB >> 0) , pas sur qu'il génère un NOP ou rien, plutôt une erreur ...




    nt y, v;
    for (v=0; v<8; v++) {
    y = Col(v);
    }


    Quel intérêt d'avoir col(7) dans y à chaque fois?
    Car les autres valeurs sont écrasées puisque la boucle va jusqu’à (v<8) !

  8. #7
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    Si x est entre 1 et 8 ! Le résultat est dans le Carry 'if (STATUS & CY) commande ...#
    Le C il s'en fiche du carry, et on est censé passer 0 à 7 à la macro, ou alors j'ai rien compris.

    Si x est entre 0 et 7 ! Que fait le compilateur avec (PORTB >> 0) , pas sur qu'il génère un NOP ou rien, plutôt une erreur ...
    Il génère un shift 0 fois, c'est à dire qu'il laisse la copie de PORTB inchangée

    Car les autres valeurs sont écrasées puisque la boucle va jusqu’à (v<8) !
    C'était un exemple pour montrer que la macro se compile sans problème, pas pour polémiquer

    Tu pourrais dire aussi que int c'est pas très bien car le compilateur stocke les int sur 16 bits, alors que c'est un port 8 bits et un processeur 8 bits, t'as oublié
    En plus le décalage sur du signed, c'est pas super ...

  9. #8
    invitef86a6203

    Re : Utilisation de Macros avec paramètre

    La remarque sur les char signés est importante.
    Car le bit de poids fort risque de disparaitre.
    Le fait de passer par un int , résout le problème, mais lors du passage au PORTB un cast serait préférable pour éviter un Warning ou Error.


    Sur les pics midrange l'instruction de décalage concerne 1 bit seul , un décalage sur plusieurs bits sera répétitif, il y a des chances que le compilateur génère une boucle, mais bon... , il pourrait aussi générer des codes en rafales , ça va dépendre de l'écriture et du compilateur, aucune instruction ASM pour un shift 0. C'était juste une remarque pas une erreur.

  10. #9
    scaypapa

    Re : Utilisation de Macros avec paramètre

    Quand j'ai essayé, ça n'a pas fonctionné mais j'avais dû me planter ailleurs. Je retenterais, ce serait plus lisible.

    Savez-vous si une des 2 écritures demande moins de temps de traitement ?
    J'aurais tendance à dire que la macro est plus efficace, mais aucune certitude...

  11. #10
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    Bonjour,
    Elle est rapide à écrire, pratique, mais pour optimiser la vitesse, il faut passer par des masques.
    Comme l'a expliqué freepicbasic, ton processeur ne sait décaler qu'un seul bit à la fois, et il faut en tenir compte.
    Si ton algorithme permet de créer un masque que tu décales de 1 bit à chaque lecture de PORTB, cela soulage le processeur.
    Sur les processeurs qui savent shifter n fois en une seule instruction machine, elle est aussi efficace.

  12. #11
    scaypapa

    Re : Utilisation de Macros avec paramètre

    Coucou, je reviens vous embêter ici pour une question plus ou moins sur le même sujet.
    J'aimerais optimiser mon code pouvoir définir une seule fois mes ports d'entrée/sortie et gérer ensuite automatiquement l'ensemble des registres dont je vais avoir besoin.
    Un bout de code sera plus parlant que des discours :

    Voilà ce que je fais pour le moment :
    Code:
    // CONFIGURATION
    #define Out LATA0
    #define Out_TRIS TRISA0
    #define Out_ANS ANSA0
    // FIN CONFIG
    
    Out = Out_TRIS = Out_ANS = 0;
    Et je voudrais faire quelque chose comme ceci pour n'avoir qu'un A0 à renseigner :
    Code:
    // CONFIGURATION
    #define PortSortie A0
    // FIN CONFIG
    
    #define Out LAT##PortSortie
    #define Out_TRIS TRIS##PortSortie
    #define Out_ANS ANS##PortSortie
    
    Out = Out_TRIS = Out_ANS = 0;
    Comme précédemment, le code ne compile pas parce qu'au lieu de concaténer par exemple "TRIS" avec "A0", il concatène "TRIS" avec "PortSortie".
    J'ai essayé également
    Code:
    #define TRIS(x) TRIS##x
    
    //puis
    TRIS(Out) = 0;
    Mais sans résultat non plus...

  13. #12
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    LATA0 c'est quoi, c'est accepté par ton compilateur ?

  14. #13
    invitef86a6203

    Re : Utilisation de Macros avec paramètre

    Le TRIS n'est pas dans la même bank.
    Je me demande bien comment le compilateur pourrait comprendre ça.

  15. #14
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    J'arrive à faire ça sur C32, mais cela ne passe pas sur C18. Il faudrait voir sur xc8.
    C'est un problème pour le forum informatique.
    Code:
      #define PortOut              B
      #define PinOut               0
    
      #define LatPort(p)           RegPort(LAT,p)
      #define LatPin(p,i)          RegPin(LAT,p,i)
      #define RegPort(typereg,p)   typereg##p
      #define RegPin(typereg,p,i)  typereg##p##bits.typereg##p##i
    
      LatPort(PortOut) = 0;             // LATB = 0
      LatPin(PortOut,PinOut) = 0;       // LATBbits.LATB0 = 0

  16. #15
    scaypapa

    Re : Utilisation de Macros avec paramètre

    Cool ton code a l'air de compiler sur XC8. Merci à toi

    LATA0, TRISA0 compilent apparemment très bien sur XC8, c'est pour faire PORTA0 par exemple que je suis obligé de passer par PORTAbits.RA0

  17. #16
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    Si LATA0 passe tu dois pouvoir faire pareil en déclarant au départ
    #define PinOut A0

    RA0 passe pas ?

  18. #17
    scaypapa

    Re : Utilisation de Macros avec paramètre

    Non, rien à faire : RA0 et PORTA0 ne compilent pas alors que ça fonctionne pour beaucoup d'autres (TRISA0, LATA0 et ANSA0)...

    Encore une chose que je ne capte pas.
    Ce code compile parfaitement :
    Code:
    #define DataPin A0
    
    #define RegPort(typereg,p)  typereg##p
    #define Lat(p)              RegPort(LAT,p)
    
    #define Data        Lat(DataPin)
    J'ai voulu le raccourcir un tout petit peu comme ceci :
    Code:
    #define DataPin A0
    
    #define RegPort(typereg,p)  typereg##p
    
    #define Data        RegPort(LAT,DataPin)
    Ca ne compile plus (il écrit dans Data LATDataPin). Pourquoi est-ce que ce n'est pas la même chose ?
    Dernière modification par scaypapa ; 02/12/2016 à 01h16.

  19. #18
    invite830075ec

    Re : Utilisation de Macros avec paramètre

    Bonjour,
    ## ne fait que de la concaténation, il colle le texte sans l'analyser.
    En passant par une première macro Lat() DataPin est remplacé par A0 avant de l'envoyer à ##

Discussions similaires

  1. Système linéaire avec paramètre
    Par invitebce8a4b6 dans le forum Mathématiques du supérieur
    Réponses: 3
    Dernier message: 11/01/2016, 15h01
  2. aide, équation avec un paramètre b
    Par invite302ace44 dans le forum Mathématiques du collège et du lycée
    Réponses: 9
    Dernier message: 23/12/2013, 21h35
  3. équation avec paramètre
    Par invite97e7a7ed dans le forum Mathématiques du collège et du lycée
    Réponses: 1
    Dernier message: 19/09/2009, 20h27
  4. problème avec les macros sur flowcode
    Par invite6f3daadc dans le forum Électronique
    Réponses: 0
    Dernier message: 14/04/2009, 21h41
  5. limite galère avec paramètre
    Par invite0ae3ab06 dans le forum Mathématiques du supérieur
    Réponses: 1
    Dernier message: 14/10/2006, 21h26
Dans la rubrique Tech de Futura, découvrez nos comparatifs produits sur l'informatique et les technologies : imprimantes laser couleur, casques audio, chaises gamer...