IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Programmation de fonctions


Sujet :

Arduino

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    1 042
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 1 042
    Par défaut Programmation de fonctions
    Bonjour à tous,

    Désireux, de rendre plus pratique et présentable mon programme en cours , j'essaie de créer des onglets particuliers qui contiennent mes fonctions

    par exemple : un onglet "affichage.h" regroupant toutes mes fonctions utilisées dans mon programme etc ...
    j'ajoute que mes fonctions que je place dans le corps de mon programme ( après le LOOP ) se compilent sans erreur

    mais par contre plus rien ne fonctionne et je me heurte à de nombreuses erreurs de type "...was not declared in this scope"

    qu'elle en est la raison principale svp ?
    Images attachées Images attachées    

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    1 590
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 1 590
    Par défaut
    tes fichiers sont bien dans le même répertoire?
    ton fichier .h est bien enregistré? (bon ici, on oublie, ça s'enregistre automatiquement normalement)

  3. #3
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    1 042
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 1 042
    Par défaut
    Salut umfred

    Oui, les fichiers sont bien dans le même répertoire

    mais au-délà de çà, c'est surtout le principe que je souhaiterai comprendre
    la fonction lorsqu'elle est sous "LOOP" fonctionne normalement
    mais lorsque je la déplace avec "#include affiche.h" là çà ne marche plus (?)

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    1 590
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 1 590
    Par défaut
    Je ne saurais dire, je ne rencontre pas le soucis en copiant ton code.
    Quelle version d'arduino IDE ? (j'ai testé sous la v2.3.6)

  5. #5
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    1 042
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 1 042
    Par défaut
    même version Arduino


    En fait il ne s'agit que d'un exemple d'illustration , mon code est un peu plus volumineux mais je ne savais pas comment le mettre à disposition
    autrement que sous la forme d'un fichier zip
    puisqu'il comporte des sous-programmes .h

    En fait ma question serait plutôt orientée pour savoir comment utiliser efficacement les fichiers .h et .cpp

  6. #6
    Responsable Arduino et Systèmes Embarqués


    Avatar de f-leb
    Homme Profil pro
    Enseignant
    Inscrit en
    Janvier 2009
    Messages
    13 268
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 13 268
    Billets dans le blog
    48
    Par défaut
    Bonjour,

    Ce serait plutôt comme ci-dessous.

    sketch_oct15a.ino
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include "affichage.h"
     
    void setup() {
      // put your setup code here, to run once:
      ma_fonction(10, 10);
    }
     
    void loop() {
      // put your main code here, to run repeatedly:
    }

    affichage.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef AFFICHAGE_H
    #define AFFICHAGE_H
     
    void ma_fonction(int x, int y); // prototype de la fonction
     
    #endif
    affichage.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void ma_fonction(int x, int y) {
      // code de la fonction ici
    }

  7. #7
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 924
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 924
    Par défaut
    mais sommes-nous obligés de répéter 2 fois la même variable
    une fois en déclaration puis en définition
    En C++, il est essentiel de bien distinguer la déclaration, qui informe le compilateur de l’existence d’une variable (en précisant son nom et son type) ou d’une fonction (en indiquant son nom, le type de ses paramètres et le type de retour), de la définition, qui correspond à l’allocation effective de la mémoire pour cette variable ou à la fourniture du corps de la fonction.

    Dans l’IDE Arduino, lorsqu’on crée plusieurs onglets avec l'extension .ino, l’IDE les fusionne automatiquement en un seul gros fichier .ino avant la compilation puis il parcourt ce fichier et génère automatiquement en début de code toutes les déclarations de fonctions nécessaires, en indiquant leur nom, leurs paramètres et leur type de retour.

    => Cela permet au compilateur de connaître toutes les fonctions, même si elles sont définies plus bas dans le code, et explique pourquoi on peut appeler une fonction avant sa définition sans problème. Grâce à ce mécanisme, il n’est souvent pas nécessaire d’utiliser des fichiers `.h`, `extern` ou d’autres séparations classiques.

    En revanche, dans le cadre d'une compilation séparée standard avec des fichiers `.h` et `.cpp, chaque fichier est compilé indépendamment.

    Il faut se souvenir que le préprocesseur, lorsqu’il rencontre un `#include`, ne fait rien d’autre que copier textuellement le contenu du fichier `.h` à l’endroit où le `#include` apparaît. Cela signifie que tout ce qui se trouve dans le `.h` — qu’il s’agisse de déclarations ou de définitions — est littéralement inséré dans chaque fichier qui inclut ce `.h`.

    Si ce fichier `.h` contient des définitions, c’est-à-dire des allocations mémoire pour des variables ou le corps complet de fonctions, et qu’il est inclus plusieurs fois dans un même projet ou dans plusieurs fichiers `.cpp`, le compilateur va générer plusieurs définitions identiques pour chaque fichier compilé.

    Puis, lors de l’édition de lien (qui consiste à assembler tous les fichiers compilés en un exécutable unique), le linker va se retrouver avec ces multiples définitions et produira une erreur, car il ne peut pas gérer plusieurs allocations pour la même variable ou plusieurs corps identiques pour la même fonction.

    Pour éviter ce problème, la règle d'or consiste à mettre uniquement les déclarations dans le fichier `.h`. Ces déclarations indiquent simplement au compilateur que telle fonction ou telle variable existe, avec son type et son nom, mais sans réserver de mémoire ni fournir le corps de la fonction et que le linker les retrouvera plus tard.

    Les définitions, qui correspondent à l’allocation réelle de la mémoire ou à la mise en place du code de la fonction, sont placées dans un fichier `.cpp` qui sera compilé une seule fois. De cette façon, on peut inclure le même fichier `.h` dans tous les fichiers nécessaires — qu’il s’agisse d’autres `.cpp` ou même d’un `.ino` dans l’IDE Arduino — et le compilateur saura de quoi il s’agit grâce aux déclarations, tout en évitant de créer plusieurs allocations mémoire pour la même entité.

    Donc pour votre question, OUI, on réptète deux fois, mais une fois c'est la DECLARATION et l'autre fois c'est la DEFINITION.

    pour une variable, quand on ne veut pas allouer la mémoire (ie on fait la déclaration) on utilise extern
    pour une fonction, quand on ne veut pas allouer la mémoire (sans le code donc) pas besoin de extern, on met juste le prototype de la fonction suivi d'un ; au lieu du corps de la fonction.





    C'est plus clair?

  8. #8
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    1 042
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 1 042
    Par défaut
    Merci encore à tous les deux pour votre patience....

    Pour savoir si c'est clair je propose un exemple concret

    Dans mon fichier .ino , j'ai ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    GxEPD2_BW<GxEPD2_213_BN, GxEPD2_213_BN::HEIGHT> display(GxEPD2_213_BN(/*CS=5*/ 5, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); 
    U8G2_FOR_ADAFRUIT_GFX u8g2Fonts; 
    --/--
    #include "affichage.h"
    dans mon fichier affichage.h , j'ai ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    #ifndef AFFICHAGE_H
    #define AFFICHAGE_H
     
    void DisplayAstronomySection(int x, int y);
     
    #endif // AFFICHAGE_H
    et enfin dans mon fichier affichage.cpp j'ai la fonction suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    #include "affichage.h"
    #include <Arduino.h>
     
    //#########################################################################################
    void DisplayAstronomySection(int x, int y) {   <======
      display.drawRect(x, y+85 , 240, 25, GxEPD_BLACK);
      u8g2Fonts.setFont(u8g2_font_helvB08_tf);
      drawString(x+5,y+95, affichalarme, LEFT);
      display.display(false); // Full screen update mode
    }
    si je veux compiler par ex
    display.drawRect(x, y+85 , 240, 25, GxEPD_BLACK);


    je dois donc "découper" la dite fonction dans affichage.h en :

    void display(bool partial_update_mode = false) ;
    void drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
    uint16_t color);
    etc ...

    c'est çà ?

  9. #9
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 924
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 924
    Par défaut
    Non ce serait dommage d'avoir à reprendre chaque fonction de la bibliothèque d'affichage comme drawRect() et d'en faire une fonction dans votre .cpp




    la question à vous poser est plutôt de savoir quelle est la logique des différents fichiers pour savoir quoi déclarer et où

    Dans votre exemple, vous voulez avoir des onglets qui gèrent les affichages (affichage.h et affichage.cpp) donc il me semblerait logique de mettre la définition de display et de u8g2Fonts dans affichage.cpp et vous conservez toutes le fonctions liées à l'affichage et qui utilisent display dans affichage.cpp. Les autres modules (si la logique est respectée) n'utilisant que les fonctions n'ont pas à connaître l'existence de ces variables. il faudra sans doute avoir dans affichage.cpp une fonction begin() qui se chargera de l'initialisation de l'écran et que vous appelez depuis le setup().

    C'est pour moi ce qui serait cohérent.




    Sinon si vous voulez rester avec votre structure actuelle, il faut que affichage.cpp connaisse l'existence de display et de u8g2Fonts.
    Il faudra donc rajouter dans affichage.h leur déclaration pour que affichage.cpp puisse l'utiliser. On mettrait donc

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #ifndef AFFICHAGE_H
    #define AFFICHAGE_H
     
    extern GxEPD2_BW<GxEPD2_213_BN, GxEPD2_213_BN::HEIGHT> display;
    extern U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
     
    void DisplayAstronomySection(int x, int y);
     
    #endif // AFFICHAGE_H
    et le linker retrouvera ces 2 variables qui sont définies dans le .ino au moment de l'édition de lien.



    Sinon il est d'usage de mettre tous les include au début du fichier, donc pas après la déclaration de la variable
    ==>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // les includes
    #include "affichage.h"
     
    // les variables globales 
    GxEPD2_BW<GxEPD2_213_BN, GxEPD2_213_BN::HEIGHT> display(GxEPD2_213_BN(/*CS=5*/ 5, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); 
    U8G2_FOR_ADAFRUIT_GFX u8g2Fonts; 
     
    ...

Discussions similaires

  1. Mettre a jour un programme en fonction de l'utilisateur
    Par morgan47 dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 02/05/2007, 10h33
  2. Réponses: 1
    Dernier message: 28/03/2007, 15h33
  3. Réponses: 10
    Dernier message: 11/01/2007, 21h45
  4. Programmer une fonction joindre_fichier
    Par leCcsympas dans le forum Réseau
    Réponses: 5
    Dernier message: 03/12/2006, 19h51
  5. Réponses: 2
    Dernier message: 31/05/2005, 09h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo