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 :

Problème de vitesse de détection des impulsions d'un encodeur rotatif


Sujet :

Arduino

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut Problème de vitesse de détection des impulsions d'un encodeur rotatif
    Bonjour,

    Je suis débutant en arduino (mais pas en programmation). Je suis en train de tester un systeme qui me permettrait d'indiquer les coordonnées d'un astre visé avec un télescope. Dans ce but j'ai mis en place un Arduino Mega 2560 que j'avais sous la main et j'ai réussi à récupérer 2 encodeurs rotatif de la marque Sick qui ont 10000 pas par tour, soit un resolution d'environ 2 Arcminute par impulsion

    Voici les encodeurs: https://www.sick.com/fr/fr/codeurs/c...0000/p/p293589

    Le but est donc de me donner l'angle horizontale (Azimut) et l'angle vertical (Altitude). J'ai donc Ecrit un bout de code classique pour des encodeurs avec gestion des impulsions par interruption. Ça ne marche pas trop mal si je tourne très doucement mais dès que j'accélère, je perd vraiment beaucoup d'impulsion (jusqu'à 50% si je tourne très vite).

    Quelle est la cause de ce phénomène: Encodeurs trop précis et/ Arduino trop lent? ou un problème de code (que je joins ci dessous)? Y-a-t-il une solution pour régler ce soucis ou tout au moins le réduire de façon conséquente. Pour ceux qui connaissent, le télescope est un Dobson, donc tourner à la main.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    / CONSTANTES
    #define ENC_AZIM_A 2	                                      // Port signal A encodeur Azimut
    #define ENC_AZIM_B 22                                       // Port signal B encodeur Azimut
    #define NB_IMP_ENC 10000.0                                  // Nb impulsions par tour encodeur
    #define ENC_ALTI_A 3                                        // Port signal A encodeur Altitude
    #define ENC_ALTI_B 23                                       // Port signal B encodeur Altitudeaison
    #define SEC_PAR_TR 360.0*60*60                              // Nbre d'Arseconde par tour
    #define INC_SEC SEC_PAR_TR/NB_IMP_ENC                       // Angle en Arcseconde par pas de l'encodeur
     
    // VARIABLES GLOBALES
    volatile long ComptImpAzim;                          // Compteur d'impulsions en Azimut
    volatile long ComptImpAlti;                          // Compteur d'impulsions en Altitude
    unsigned long AncienMillis;                          // Pour tempo non bloquante
     
    // PROTO FONCTIONS
    char *arcsec2str( double arcsec);                    
    void Move_Azim();
    void Move_Alti();
     
     
    // Récupération des mouvements de l'azimut
    void Move_Azim()
    {
    	if (digitalRead(ENC_AZIM_B))
    		ComptImpAzim--;
        if (ComptImpAzim < 0) ComptImpAzim += NB_IMP_ENC;
    	else
    		ComptImpAzim++;
       if (ComptImpAzim >= NB_IMP_ENC) ComptImpAzim -= NB_IMP_ENC;
    }
     
    // Récupération des mouvements de l'altitude
    void Move_Alti()
    {
      if (digitalRead(ENC_ALTI_B))
        ComptImpAlti--;
        if (ComptImpAlti < 0) ComptImpAlti += NB_IMP_ENC;
      else
        ComptImpAlti++;
       if (ComptImpAlti >= NB_IMP_ENC) ComptImpAlti -= NB_IMP_ENC;
    }
     
    // Transforme l'angle en arcsec vers une chaine [+ddd° mm' ss.ss"]
    char *arcsec2str(double arsec )
    {
    	int degre;
    	int minute;
    	double seconde;
    	static char rep[20];
      char secch[5];
    	char minch[2];
     
    	degre = int(arsec/3600);
    	minute = int((arsec - (degre*3600)) /60);
    	seconde = (arsec - (degre*3600) - (minute*60));
     
      dtostrf(seconde, 5, 2, secch);
      dtostrf(minute, 2, 0, minch);
      // 
    	sprintf(rep,"%4d° %s' %s""",degre, minch, secch);
     
    	return rep;
    }
     
     
    void setup()
    {
    	// Pin A et B encodeur Az en entrée
    	pinMode(ENC_AZIM_A, INPUT);
    	pinMode(ENC_AZIM_B, INPUT);
     // Pin A et B encodeur AltC en entrée
      pinMode(ENC_ALTI_A, INPUT);
      pinMode(ENC_ALTI_B, INPUT);
    	// Pull up 
    	digitalWrite(ENC_AZIM_A, HIGH);
    	digitalWrite(ENC_AZIM_B, HIGH); 
    	digitalWrite(ENC_ALTI_A, HIGH);
      digitalWrite(ENC_ALTI_B, HIGH);
     
      // Init port série 0 pour moniteur
    	Serial.begin (9600);
     
      // Init à 0 des compteurs d'impulsions
      ComptImpAzim = 0;
      ComptImpAlti = 0;
     
      // Detection des mouvements d'encodeurs par interruption sur front descendant sur signal A
    	attachInterrupt(digitalPinToInterrupt(ENC_AZIM_A), Move_Azim, FALLING);
    	attachInterrupt(digitalPinToInterrupt(ENC_ALTI_A), Move_Alti, FALLING);
     
    }
     
     
     
    void loop()
    {
      float Ang_Azim;
      float Ang_Alti;
     
      AncienMillis = millis();
     
      // Affichage des angles toutes les 500ms sans blocage
      if ( (millis() - AncienMillis) > 500) {
        // Angle d'azimut
        Ang_Azim = ComptImpAzim*INC_SEC;
        Serial.print("Azim: ");
    	  Serial.print(arcsec2str(Ang_Azim));
     
        // Angle d'azimut
        Ang_Alti = ComptImpAlti*INC_SEC;
        Serial.print(" - Alt: ");
        Serial.print(arcsec2str(Ang_Alti));
     
        AncienMillis = millis();
      }
     
    /*
        * AUTRES TRAITEMENTS ET CALCUL FAIT ICI
        * Avec des tempo non bloquantes (millis())
        * 
        *
     */  
     
     
    }

  2. #2
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 010
    Points : 2 331
    Points
    2 331
    Par défaut
    Bonjour amathieu

    Peut être essayer avec la bibliothèque digitalWriteFast.h
    Le lecture/écriture des ports est ~10* plus rapide.

    digitalRead 1000 x
    3592 uSec.

    digitalReadFast 1000 x
    348 uSec.

    ou la bibliothèque Encoder qui est, elle, assez rapide et simplifie le code.

    Cordialement
    jpbbricole
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  3. #3
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Merci pour les conseils Jpp, je vais essayé cela dès que possible.

  4. #4
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 185
    Points : 11 551
    Points
    11 551
    Par défaut
    Salut,
    Quel est l'intérêt de lire l'état d'une broche dans une interruption ? C'est une perte de temps.

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Move_Azim()
    {
    	... // quand tu es ici, généralement il faut faire le moins de chose possible pour ne pas louper l'interruption suivante
    }

    Pourquoi relire l'état de la broche ?
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  5. #5
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 010
    Points : 2 331
    Points
    2 331
    Par défaut
    Bonjour Vincent
    Citation Envoyé par Vincent PETIT Voir le message
    Pourquoi relire l'état de la broche ?
    Pour connaître le sens de rotation?
    L'interrupt se fait sur la pin ENC_AZIM_A et dans l'interrupt il relis ENC_AZIM_B.
    Resp ENC_ALTI_A, ENC_ALTI_B

    Cordialement
    jpbbricole
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  6. #6
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 185
    Points : 11 551
    Points
    11 551
    Par défaut
    Citation Envoyé par jpbbricole Voir le message
    Pour connaître le sens de rotation?

    Bien vu

    La prochaine fois je prendrai plus de temps avant de répondre.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  7. #7
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Bonsoir,

    Suite à vos conseils, j'ai essayé la library encodeur que vous m'avez conseillé. Je ne recevais que des valeurs vraiment bizarre. Après plusieurs essais j'ai décidé de créer ma librairie (surtout à des fins didactitielles, vu ce qu'il y a déjà sur le marché). Je rappelle que j'ai des notions de programmations, mais pas de classes ni de C++.

    J'ai quelque chose de simple mais .... Ca ne marche pas. Le problème des interruptions dans des classes est (si j'ai bien compris tout ce que j'ai lu) STATIC mais l'interet est bien de différencier les 2 encodeurs. Donc d'y perd mon latin (et même mon C++) et je sollicites donc votre aide.


    Pour mon sketch, j'ai cela:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #include <simpleMinuteur.h>
    #include "DSC_Encoder.h"
     
    #define TMR_ENC 500		// Durée du Timer de lecture des positions
     
    // Déclaration des pins de l'arduino utilisées
    #define AZpinA 2		
    #define ALTpinA 3
    #define AZpinB 22
    #define ALTpinB 23
     
    // Création du Timer pour la lecture des encodeurs
    simpleMinuteur TMREncodeur(TMR_ENC); 
     
    // création des 2 encodeurs
    encodeur EncAzim(AZpinA, AZpinB);
    encodeur EncAlti(ALTpinA, ALTpinB);
     
    // Variables pour stocker les positions lues des encodeurs
    long PosAZ, PosALT;
     
    // Initialisations
    void setup()
    {
    	Serial.begin(115200);
     
    }
     
    // Boucle principales
    void loop(){
    	// Si Timee encodeur écoulé
    	if (TMREncodeur.estTermine()){
    		// Lectures des positions des 2 encodeurs
    		PosAZ = EncAzim.read();
    		PosALT = EncAlti.read();
    		// Affichage des positions sur moniteur série
    		Serial.print("Azimut: ");
    		Serial.print(PosAZ);
    		Serial.print(" - Altitude: ");
    		Serial.print(PosALT);
    		// Redémarre le timer encodeur
    		TMREncodeur.redemarrer();
    	}
     
    }

    ensuite mon entete (.h) donne ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #ifndef DSC_ENCODER_H
     
    #define DSC_ENCODER_H
    #include <arduino.h> 
     
     
    class encodeur 
    {
     
    	public :
     
    		// Constructeur
    		encodeur(int pin1, int pinB);  	// Création d'un encodeur sur les pin spécifiées
    		long read();					// Lecture de la postition d'un encodeur
    		void write(long p);				// Force la position d'un encodeur
    		void RAZ();						// Remise à 0 de la position de l'encodeur
     
     
     
     
        private:
     
    		int _pA, _pB;					// N° de pin en interne
        	volatile long position;			// Position courante de l'encodeur
        	static void FctInt();			// Fonction privée STATIC qui lance la procédure ISR
        	void encodeur_ISR();			// Traitement à faire lors d'une interruption (Incrémente Décrémente la position)
     
    };
     
     
    #endif

    et finalement le code proprement dit de la lib:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    #include <arduino.h>
    #include "DSC_encoder.h"
     
     
     
     
    encodeur::encodeur(int pinA, int pinB) {
     
    		_pA = pinA;														// Récupération des pin
    		_pB = pinB;
    		pinMode(_pA, INPUT);											// Pin A en entrée
    		digitalWrite(_pA, HIGH);										// Pull Up
    		pinMode(_pB, INPUT);											// Pin B en entrée 
    		digitalWrite(_pB, HIGH);										// Pull Up
    		position = 0;													// Initialisation de la position à 0
    		attachInterrupt( digitalPinToInterrupt(_pA), FctInt, CHANGE );  // Déclenche la fonction FctInt à changement de la pin A
    }
     
     
    // Renvoie la position de l'encodeur en nombre de pas
    long encodeur::read(){
    	long Valrtn;
    	noInterrupts();			// Bloque les interruptions
    	Valrtn = position;		// Recupère la position
    	interrupts();			// ReAutorise les interruptions
    	return Valrtn;			// Renvoie la valeur lue
    }
     
    // Force la position de l'encodeur en nombre de pas
    void encodeur::write(long p){
    	noInterrupts();			// Bloque les interruptions
    	position = p;			// Modifie la valeur de la position arduinovec la valeur passée en parametre
    	interrupts();			// ReAutorise les interruptions
    }
     
    // Remet la position de l'encodeur à 0 
    void encodeur::RAZ(){
    	write(0);				// Met à 0 la position courante
    }
     
     
    // Lancement de la fonction ISR sur l'interruption
    void FctInt(){
    	encodeur_ISR();	// Lance la fonction ISR 
    }
     
     
    // Traitement à reception de l'interruption
    void encodeur::encodeur_ISR(){
    	int etatB, etatA;
     
    	// Récupère les états des 2 pins
    	etatA = digitalRead(_pA);
    	etatB = digitalRead(_pB);
     
    	// Si les pins sont dans le même état
    	if (etatA == etatB )
    	{	
    		noInterrupts();		// Bloque les interruptions
    		position++;			// Incrémente la position
    		interrupts();		// re Autorise les interruptions
    	}
    	else // Si les états sont différents
    	{
    		noInterrupts();		// Bloque les interruptions
    		position--;			// Décrémente la position
    		interrupts();		// re Autorise les interruptions
    	}
     
     
     
     
    }
    J'ai bien noté, en parcourant certains articles, que la fonction d'interruption devait etre static . Mais l'interet étant bien évidemment pas que les 2 encodeurs soient complètement indépendant, j'ai créer comme conseillé une fonction static privée qui appelle la fonction public qui réalise le traitement à proprement parlé.
    Ceci dit le compilateur me renvoie ce message alors que la fonction est bien déclaré:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    [Build] F:/Users/Anthony/Documents/Arduino/DSCOO...
    [Step 1] Check Toolchain.
    [Step 2] Find all source files.
    [Step 3] Start building.
    [3.3%] Compiling DSCOO.ino.cpp...
    [6.7%] Compiling DSC_encoder.cpp...
    F:/Users/Anthony/Documents/Arduino/DSCOO/DSC_encoder.cpp: In function 'void FctInt()':
    F:/Users/Anthony/Documents/Arduino/DSCOO/DSC_encoder.cpp:44:15: error: 'encodeur_ISR' was not declared in this scope
      encodeur_ISR(); // Lance la fonction ISR 
                   ^
    [Build] Error occurred.

    Merci d'avance pour le temps que vous pourrez me consacrer.

  8. #8
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Bonjour

    Après avoir parcouru pas mal de sujets, j'ai modifié mon code en fonction de ce que j'ai pu apprendre. J'ai créé un tableau d'adresses d'encodeurs avec un nombre maxi d'encodeurs. A chaque construction j'incrémente l'indice de l'encodeur et stocke l'adresse de l'instance dans le tableau. Je lance dans la fonction fctInt qui est attaché à l'interruption, la fonction Encodeur_ISR de l'instance courante par Adresse_Instance->Encodeur_ISR().

    J'ai cependant encore une erreur de compil que je ne comprends pas. En effet, il m'indique que l'indice est non déclarée alors qu'il fait bien parti des propriété de la classe.
    J'ai du raté quelque chose, mais je ne vois franchement pas quoi.

    Voici mon code:

    l'entete .h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #ifndef DSC_ENCODER_H
     
    #define DSC_ENCODER_H
    #include <arduino.h> 
    #define MAX_ENCODEUR 4 // Nombre Maxi d'encodeur 
     
     
     
    class encodeur 
    {
     
     public :
     
     // Constructeur
     encodeur(int pin1, int pinB);   // Création d'un encodeur sur les pin spécifiées
     long read(); // Lecture de la postition d'un encodeur
     void write(long p); // Force la position d'un encodeur
     void RAZ(); // Remise à 0 de la position de l'encodeur
     
     
     
        private:
     
     int _pA, _pB; // N° de pin en interne
         volatile long position; // Position courante de l'encodeur
         static void FctInt(); // Fonction privée STATIC qui lance la procédure ISR
         void encodeur_ISR(); // Traitement à faire lors d'une interruption (Incrémente Décrémente la position)
         static int NbEnc; // Nombre d'encodeurs ( = Nbre d'instance de la classe)
       int IndEnc; // Indice de l'encodeur
     
    };
     
     
    #endif
    et le cpp de la classe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
     
    #include <arduino.h>
    #include "DSC_encoder.h"
     
     
    encodeur *AdrEnc[MAX_ENCODEUR]; // Tableau des adresses de chaque instance de classe des encodeurs
     
     
    encodeur::encodeur(int pinA, int pinB) {
     
     // La construction ne se fait que si le nombre max d'encodeurs n'est pas atteint
     if (NbEnc < MAX_ENCODEUR){
     IndEnc = NbEnc; // Indice de l'instance courante
     NbEnc++; // Nombre d'encodeurs
     AdrEnc[IndEnc] = this; // Adresse de l'instance courante
     _pA = pinA; // Récupération des pins
     _pB = pinB;
     pinMode(_pA, INPUT); // Pin A en entrée
     digitalWrite(_pA, HIGH); // Pull Up
     pinMode(_pB, INPUT); // Pin B en entrée 
     digitalWrite(_pB, HIGH); // Pull Up
     position = 0; // Initialisation de la position à 0
     attachInterrupt( digitalPinToInterrupt(_pA), FctInt, CHANGE );  // Déclenche la fonction FctInt à changement de la pin A
     }
    }
     
     
    // Renvoie la position de l'encodeur en nombre de pas
    long encodeur::read(){
     long Valrtn;
     noInterrupts(); // Bloque les interruptions
     Valrtn = position; // Recupère la position
     interrupts(); // ReAutorise les interruptions
     return Valrtn; // Renvoie la valeur lue
    }
     
    // Force la position de l'encodeur en nombre de pas
    void encodeur::write(long p){
     noInterrupts(); // Bloque les interruptions
     position = p; // Modifie la valeur de la position arduinovec la valeur passée en parametre
     interrupts(); // ReAutorise les interruptions
    }
     
    // Remet la position de l'encodeur à 0 
    void encodeur::RAZ(){
     write(0); // Met à 0 la position courante
    }
     
     
    // Lancement de la fonction ISR sur l'interruption
    void FctInt(){
     AdrEnc[IndEnc]->encodeur_ISR(); // Lance la fonction ISR  de l'instance courante
    }
     
     
    // Traitement à reception de l'interruption
    void encodeur::encodeur_ISR(){
     int etatB, etatA;
     
     // Récupère les états des 2 pins
     etatA = digitalRead(_pA);
     etatB = digitalRead(_pB);
     
     // Si les pins sont dans le même état
     if (etatA == etatB )
     { 
     noInterrupts(); // Bloque les interruptions
     position++; // Incrémente la position
     interrupts(); // re Autorise les interruptions
     }
     else // Si les états sont différents
     {
     noInterrupts(); // Bloque les interruptions
     position--; // Décrémente la position
     interrupts(); // re Autorise les interruptions
     }
     
     
     
     
    }

    résultat de la compil:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     
    [Build] F:/Users/Anthony/Documents/Arduino/DSCOO...
    [Step 1] Check Toolchain.
    [Step 2] Find all source files.
    [Step 3] Start building.
    [3.3%] Compiling DSCOO.ino.cpp...
    [6.7%] Compiling DSC_encoder.cpp...
    F:/Users/Anthony/Documents/Arduino/DSCOO/DSC_encoder.cpp: In function 'void FctInt()':
    F:/Users/Anthony/Documents/Arduino/DSCOO/DSC_encoder.cpp:51:9: error: 'IndEnc' was not declared in this scope
      AdrEnc[IndEnc]->encodeur_ISR(); // Lance la fonction ISR  de l'instance courante
             ^
    [Build] Error occurred.

  9. #9
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 185
    Points : 11 551
    Points
    11 551
    Par défaut
    Salut,
    C'est mon avis perso mais je trouve que le code s'est fortement complexifié maintenant qu'il est écrit en C++. Je vois aussi des choses qui vont te faire perdre du temps inutilement

    Dans un programme d'interruption et sur ton microcontrôleur, tu ne pourras pas être ré-interrompu, d'après la doc constructeur il n'a pas ce genre de fonctionnalité. Les noInterrupts(); sont inutiles et te font perdre du temps.
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     if (etatA == etatB )
     { 
     noInterrupts(); // Bloque les interruptions
     ...
     interrupts(); // re Autorise les interruptions
     }

    D'ailleurs, le problème est peut être à ce niveau (micro trop lent mais pour en être sur il faudrait un oscillo ou alors bidouiller quelque chose en logiciel.)

    Je te propose de redémarrer depuis le début et de valider certaine étape.
    1 - Simplifier le programme en le réduisant à l'essentiel
    2 - Essayer de déterminer la durée du programme d'interruption

    ps : le codeur incrémental semble actif (alimenté) il ne devrait pas être nécessaire d'activer les pullup des entrées du micro.

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    / CONSTANTES
    #define ENC_AZIM_A 2	                                      // Port signal A encodeur Azimut
    #define ENC_AZIM_B 22                                       // Port signal B encodeur Azimut
     
     
    // VARIABLES GLOBALES
    volatile long ComptImpAzim;                          // Compteur d'impulsions en Azimut
    volatile long ComptTourAzim;                          // Compteur tour en Azimut
    volatile long flag1;                          
     
    // PROTO FONCTIONS             
    void Move_Azim();
     
     
    // Récupération des mouvements de l'azimut
    void Move_Azim()
    {
        flag1 = 1; // voir dans le programme loop
     
    	if (digitalRead(ENC_AZIM_B)) 
    		ComptImpAzim--;  // je recule
    	else
    		ComptImpAzim++;  // j'avance
     
     
        if (ComptImpAzim < 10000) // j'ai fait un tour complet en reculant
        {
            ComptImpAzim = 0;
            ComptTourAzim--;
        }
     
        if(ComptImpAzim > 10000) // j'ai fait un tour complet en avançant
        {
            ComptImpAzim = 0;
            ComptTourAzim++;
        }
    }
     
    void setup()
    {
      // Pin A et B encodeur Az en entrée
    	pinMode(ENC_AZIM_A, INPUT);
    	pinMode(ENC_AZIM_B, INPUT);
      // Init port série 0 pour moniteur
    	Serial.begin (9600); 
      // Init
        ComptImpAzim = 0;
        ComptTourAzim = 0; 
        flag = 0;
      // Detection des mouvements d'encodeurs par interruption sur front descendant sur signal A
        attachInterrupt(digitalPinToInterrupt(ENC_AZIM_A), Move_Azim, FALLING);
    }
     
    void loop()
    {
        if(flag1 == 1) // histoire d'afficher que lorsque le codeur incrémental a changé
        {
            flag1 = 0;
            Serial.print(ComptTourAzim);
            Serial.print(",");
            Serial.print(ComptImpAzim);
            Serial.println(" ");
        }
    }

    L'idée est de savoir en combien de temps s'exécute la fonction d'interruption et cette durée sera la limite de vitesse de rotation du codeur. Au delà, le micro n'arrivera pas à chopper toutes les changements d'état du codeur.

    Lorsqu'on a un oscillo il est facile de faire ça :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void Move_Azim()
    {
       allumer_LED;
     
        flag1 = 1; // voir dans le programme loop
     
    	if (digitalRead(ENC_AZIM_B)) 
    	...
            ...
     
       etindre_LED;
    }

    Ensuite à l'oscillo on mesure la durée d'allumage de la LED ce qui donne la durée du programme d'interruption.

    Logiciellement parlant, je te laisse essayer mais peut être que ceci devrait fonctionner ?
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    volatile unsigned long time1; // variable globale a initialiser à 0 dans setup
    volatile unsigned long time2; // variable globale a initialiser à 0 dans setup
     
    ...
    ...
     
    void Move_Azim()
    {
       time1 = micros();
     
        flag1 = 1; // voir dans le programme loop
     
    	if (digitalRead(ENC_AZIM_B)) 
    	...
            ...
     
       time2 = micros();
    }
    ...
    ...
    ...
    void loop()
    {
        if(flag1 == 1) // histoire d'afficher que lorsque le codeur incrémental a changé
        {
            flag1 = 0;
            Serial.print(time2 - time1);
            ...
            ...
            ...
        }
    }

    Tout ceci est du pseudo code (je n'ai pas essayé, c'est juste pour expliquer)
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  10. #10
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Bonjour Vincent et merci de ton aide.

    J'ai supprimé le nointerrupts et interrupt dans la fonction ISR à proprement parlé car tu es raison,une interruption n'est pas elle même interruptible.
    J'ai fais des test dans la version sans classe pour estimer les temps d’exécution. Le traitement d'interruption (incrémentation + test pour rester dans la plage d'un tour) tourne à 20 microsecondes. Pour l'intervalle d'appel à cette fonction, en tournant très vite à la main je descend à une 30aine de microsecondes. Cette vitesse ne sera jamais atteint en fonctionnement réelle car elle correspond à plus d'un tour par seconde. Ce qui est environ 4 ou 5 fois plus rapide. En tournant à une vitesse proche de la réalité je suis à 100µs entre 2 appels. Je n'ai malheureusement pas d'oscilloscope pour faire des test plus précis.

  11. #11
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 010
    Points : 2 331
    Points
    2 331
    Par défaut
    Bonjour amathieu
    Citation Envoyé par amathieu Voir le message
    Je n'ai malheureusement pas d'oscilloscope pour faire des test plus précis.
    Pour travailler sur "nos" Arduino et pas chères:

    1 oscillo 1 trace 200 kHz ~20$
    1 analyseur logique 8 traces 24 mHz. ~8$

    Cordialement
    jpbbricole
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  12. #12
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Vu le prix, effectivement, c'est à envisager fortement.

    Pour en revenir à ma classe encodeur, étant donné que je tourne en rond depuis plusieurs jour, je crois que je vais revenir à la version classique sans classe. C'est dommage de devoir écrire le même code pour chaque encodeur mais je pense qu'il n'y a pas d'autre solution en l'état actuel des chose.

    J'ai bien entendu parlé de l'Arduino DUE qui intègre un "Decodeur d'encodeur" mais ce que j'ai vu à ce sujet est trop compliqué pour mon faible niveau de connaissance actuel. De plus le DUE est entièrement et uniquement en 3.3V. On peux effectivement mettre des diviseurs de tensions avec des résistances mais il va en falloir pas mal quand même. Et puis mon shield pour l'écran ne fonctionne qu'en 5V. Donc je penses que je vais rester avec le Mega et programmer sans classe (en tout cas la partie encodeur).

  13. #13
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 010
    Points : 2 331
    Points
    2 331
    Par défaut
    Citation Envoyé par amathieu Voir le message
    On peux effectivement mettre des diviseurs de tensions avec des résistances mais il va en falloir pas mal quand même.
    Non, il y a les level converter. C'est bidirectionnel

    Cordialement
    jpbbricole
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  14. #14
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 185
    Points : 11 551
    Points
    11 551
    Par défaut
    Salut,
    Le plus important c'est d'avancer pas à pas en éliminant le plus d'inconnue possible.

    Si le pseudo code fonctionne et que tu as réussi a mesurer la durée de l'interruption (un 30aine de us) maintenant il faut t'assurer de ce qui se passe si le second codeur incrémental déclenche une interruption pendant que tu te trouves dans le programme d'interruption du premier codeur incrémental. Peut être que tu t'es déjà assuré de ça ?

    Moi je testerai ceci (idem pseudo code, rien de testé) :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // CONSTANTES
    #define ENC_AZIM_A 2	                                      // Port signal A encodeur Azimut
    #define ENC_AZIM_B 22                                       // Port signal B encodeur Azimut
    #define ENC_ELEV_A 3                                        // Port signal A encodeur Altitude
    #define ENC_ELEV_B 23                                       // Port signal B encodeur Altitudeaison
     
    // VARIABLES GLOBALES
    volatile long flag1;                          
     
    // PROTO FONCTIONS             
    void Move_Azim();
    void Move_Elev();
     
     
    // Récupération des mouvements de l'azimut
    void Move_Azim()
    {
        delay(5000);  // attendre 5 secondes pour avoir le temps de tourner le codeur élevation durant ces 5 secondes 	
    }
     
    // Récupération des mouvements de l'azimut
    void Move_Elev()
    {
        flag1 = 1; // voir dans le programme loop
    }
     
    void setup()
    {
      // Pin A et B encodeur Az en entrée
    	pinMode(ENC_AZIM_A, INPUT);
    	pinMode(ENC_AZIM_B, INPUT);
    	pinMode(ENC_ELEV_A, INPUT);
    	pinMode(ENC_ELEV_B, INPUT);
      // Init port série 0 pour moniteur
    	Serial.begin (9600); 
      // Init 
        flag = 0;
      // Detection des mouvements d'encodeurs par interruption sur front descendant sur signal A
    	attachInterrupt(digitalPinToInterrupt(ENC_AZIM_A), Move_Azim, FALLING);
    	attachInterrupt(digitalPinToInterrupt(ENC_ALTI_A), Move_Elev, FALLING);
     
    }
     
    void loop()
    {
    // faire tourner le codeur incrémental Azimut (pour envoyer le programme dans Move_Azim
    // puis tourner le codeur incrémental Elevation dans les 5 secondes
    // Si le message ci dessus s'affiche, c'est que le ATMEGA est capable d'attraper toutes les impulsions même si les encodeurs tournent en même temps.
     
        if(flag1 == 1) 
        {
            Serial.println("L'élévation a été vu alors que j'étais entrain de poiroter dans la fonction d'interruption azimut");
        }
    }

    Si ça fonctionne, tu dois être en mesure d'attraper toutes les impulsions même si elles sont rapides (dans la mesure de l'acceptable bien sur) et même si tu tournes les codeurs en même temps.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  15. #15
    Membre actif Avatar de cedd70
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Mars 2012
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mars 2012
    Messages : 154
    Points : 263
    Points
    263
    Par défaut
    Bonjour,

    Sais-tu à quelle vitesse vont tes encodeurs ?
    Cela pourrait se calculer rapidement.

    J'avais le même problème sur le mega, la solution utilisée était un deviseur de clock, tu perds en précision mais au moins tu mesures.
    Sinon passe sur un plus gros proc genre STM

  16. #16
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Bonjour Ced.

    Je ne penses pas que la vitesse me pose de problème dans l'application que je suis en train d'étudier. Il s'agit d'équiper d'encodeur les 2 axes de rotation d'un télescope .
    Le télscope ressemble à ca:

    Nom : dob.jpg
Affichages : 1366
Taille : 6,1 Ko

    Pour te donner une idées le plateau tournant de la base fait environ 50cm et le tube mesure 120cm de longueur. L'ensemble est pivoté à la main (et en général pas comme un bourrin) sur sa base en azimut et le tube en hauteur sur l'axe marqué par la poignée noire sur le flanc.
    J'ai chronométré les mouvement en conditions normales je suis dans l'ordre d'un tour toute les 3 ou 4 secondes. Si je prends une marge de sécurité avec un facteur supérieur à 2 je ne monterai pas plus d' 1 tour/Sec soit 60 RPM.

    Il faut juste savoir que pour mes test, dans mon salon, je tourne les encodeurs à la main, et directement depuis leur axe de 6mm. Même si je vais lentement la vitesse est forcément plus grande qu'en condition réelle.

    Ceci dis mon problème s'est un peu transformer. En effet (je rappelle que c'est mon 1er projet et qu'il y a 1 semaine, je n'avais jamais vu un arduino), au début j'ai utiliser la librairie Encoder.h de "prjc". Les valeurs partait dans tous les sens. Ce week end, j'ai développé mes propres fonctions "classique", c'est à dire pas en POO avec les classes. J'ai réussi à avoir un fonctionnement correcte mais il faut écrire les fonction en double (1 pour chaque encodeur).

    Pour faire plus propre (et me former par la même occasion), j'ai donc voulu créer ma propre classe encodeur. Là c'est un autre problème qui est apparu: on ne peut pas distinguer la fonction ISR de chaque encodeur car celle ci devant être STATIC, ne peut accéder aux donnés de l'instance. J'ai donc renoncé à développer cette classe et je vais revenir à la programmation classique. Je me formerai à la POO sur une autre classe. Ceci dis j'ai déjà appris beaucoup en 1 semaine.

  17. #17
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 010
    Points : 2 331
    Points
    2 331
    Par défaut
    Bonjour amathieu

    Je suis assez étonné par les problèmes de vitesse que tu as avec ton codeur.
    Je me suis fait un générateur à quadrature pour faire des essais de vitesse avec un UNO comme générateur et un Nano comme récepteur, la bibliothèque Encoder.h exemple NoInterrupts.pde (avec interrupt) et j'arrive, sans erreurs, à 15kHz.

    N'aurais-tu pas des problèmes de câblage ou de conversion de signaux?

    Cordialement
    jpbbricole
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  18. #18
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Janvier 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Janvier 2014
    Messages : 8
    Points : 1
    Points
    1
    Par défaut
    Bonsoir JPP et merci à tous de l'attention que vous portez à mon problème et de l'aide que vous m'apportez.

    Mon problème de vitesse d'encodage est résolu. J'ai désinstallé l'IDE Arduino et je l'ai réinstallé avec le strict minimum des librairie dont j'avais besoin. J'ai refait un essai avec la librairie encoder de JPRC et ca fonctionne. J'ai même fait l'essai en connectant les 2 encodeurs sur 4 pins interruptibles. Ca va super vite et avec 40000 pas par tour, ce qui me fait une résolution de 30 Arcsecondes. Je perds un port série mais j'essaierai de m'en arranger. J'ai pour l'instant l'écran et le RTC qui tournent sur les pins séries. Il ne reste donc que le RX/TX0 que je garde pour le moniteur série. Si j'ai d'autres modules à rajouter je m'orienterai vers de l'I2C.

    Je pense qu'à force d'essayer pleins de librairies différentes, j'ai mis un tel bazar que l'IDE s'est perdu lui même.

    Je pense donc m'orienter vers cette librairie encoder qui d'après ce que j'ai pu lire reste la meilleure.

    Encore merci à tous.

Discussions similaires

  1. [Selenium] Problème détection des fenêtres
    Par hbennou dans le forum Tests et Performance
    Réponses: 1
    Dernier message: 02/12/2010, 15h53
  2. Problème métadonnée oracle (Détection des MAJ)
    Par Bouga74 dans le forum Développement de jobs
    Réponses: 6
    Dernier message: 22/04/2010, 10h08
  3. Réponses: 4
    Dernier message: 13/02/2008, 13h53
  4. [RegEx] problème de détection des accents
    Par benoitB dans le forum Langage
    Réponses: 1
    Dernier message: 02/01/2008, 00h43
  5. Réponses: 14
    Dernier message: 12/04/2007, 21h09

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