Des résultats étranges en essayant de compter les impulsions

Je suis maintenant désespérément coincé et j'ai besoin d'aide s'il vous plaît.

Je suis en train de construire un observatoire automatisé et je suis maintenant sur le point d'essayer de contrôler la rotation du dôme de l'observatoire. Le dôme est tourné à l'aide d'un moteur pas à pas et cela fonctionne bien. La rotation du dôme fait également tourner un encodeur rotatif pour permettre une vérification indépendante du mouvement du dôme. Bien que RE soit de type en quadrature, je compte simplement les ticks RE des deux canaux car je n’ai pas besoin d’informations sur la direction.

Ce que j'essaie de faire maintenant, c'est de compter le nombre d'impulsions motrices qui se produisent entre chaque paire de tics RE. Le raisonnement derrière ceci est de surveiller les mouvements du dôme et de détecter les bourrages. Les impulsions réelles du moteur seront utilisées pour tous les calculs de position du dôme.

Pour ce faire, j'ai construit une petite carte avec un tampon inverseur ULN2803A et un Arduino Nano. Le Nano utilise les interruptions de changement de broche sur les broches D11 et D12 pour compter les tics RE. La broche D7 sera également utilisée pour surveiller un capteur de position d'origine, bien que cela n'existe pas à l'heure actuelle. Les impulsions du moteur sont envoyées via la mémoire tampon à la broche D2 (INT0) et la direction du moteur est envoyées via la mémoire tampon à la broche D3 (INT1). Les interruptions externes sont ensuite utilisées pour compter les impulsions du moteur, en montant ou en descendant, en fonction de la direction du moteur.

Pour information, une autre carte est présente, une carte USB Velleman VM110 USB, qui reçoit également les ticks de l'encodeur rotatif. Cela me donne un moyen simple de vérifier que ma carte donne la même réponse lorsque je fais tourner l’arbre du codeur rotatif à la main.

OK - désolé, ça fait si longtemps mais maintenant, ça devient bizarre.

Lorsque je tourne le RE sans le moteur en marche, tout va bien. Je reçois quatre fois plus d'impulsions que la carte Velleman lorsque j'utilise des changements de broche, ce qui déclenche chaque montée et chaque chute et non les cycles complets du codeur. Cela se produit comme prévu et tout est beau et stable.

MAIS - lorsque je démarre le moteur, je reçois des centaines de ticks RE chaque fois que je déplace l'encodeur et parfois, je reçois des ticks RE croissants sans même toucher le codeur.

La réponse évidente est une forme de RFI, mais j'ai tout fait pour l'éliminer. Le pilote du moteur et la carte Nano qui l’alimente sont installés dans un boîtier blindé, le câble qui le relie au moteur est un câble blindé et j’ai des selfs 10uH sur chacun des quatre câbles du moteur. Enfin, j’ai installé un filtre sur l’alimentation électrique entrante du boîtier du moteur afin de minimiser les RFI rediffusées sur les lignes électriques.

L'utilisation de la mémoire tampon ULN2803A était ma dernière tentative pour que cela fonctionne. Auparavant, les signaux passaient directement aux broches Nano. Avec le tampon, j'ai utilisé des tractions 20k du côté entrée et 10k des tractions en sortie. C’était une copie directe du circuit d’entrée de la carte Velleman qui, je le savais, fonctionnait sans problème.

J'ai examiné les impulsions du moteur au niveau de la broche d'entrée du Nano sur mon oscilloscope et ce sont de belles impulsions à la fois pointues et nettes d'une durée de 70 us et d'une fréquence de 497 Hz. Pas mal, car je règle le pouls à 500Hz avec la bibliothèque Accelstepper.

Je soupçonne maintenant qu'il s'agit d'un problème de logiciel. Cela ne me surprendrait pas car je suis très novice dans ce domaine. J'essaie juste d'apprendre suffisamment à chaque étape pour faire ce dont j'ai besoin. J'ai joint le code - il s'agit d'une version allégée sans beaucoup de choses I2C qui ne sont pas pertinentes pour mes problèmes.

Encore une fois, pour information. J'ai essayé d'utiliser la fonction attachInterrupt() et de définir directement les registres appropriés - pas de différence!

Quoi qu'il en soit, j'espère vraiment que quelqu'un pourra m'aider à résoudre ce problème.

Cordialement, Hugh

/*          
ABoard3  
ROTATION MONITORING AND POSITION
Version AB3_stripped
*****************************PURPOSE*****************************************
This sketch is used on an Arduino Nano to count the motor pulses from ABoard1
and the Rotary Encoder ticks. The motor pulse count between encoder ticks is
used to detect dome jamming.
****************************INPUTS*******************************************
PIN CHANGE INTERRUPTS
**********ROTARY ENCODER INPUT*********
The rotary encoder is a Bourns ENA1J-B28 L00064L 1139M MEX 64 cycle per turn
optical encoder. This is connected to ABoard3 Pins D11 and D12. These pins
connect to Channel A and Channel B respectively. Pin change interrupts are used
to receive the rotary encoder output.
(The output pulses from the rotary encoder is also sent to the Velleman board
for use by LesveDomeNet for finding dome position)
*********HOME POSITION INPUT*********
The home position sensor is an Omron EESX671 Optical Sensor.
The sensor output is connected to ABoard3 Pin D7.
Pin change interrupt PCINT21 records activation/deactivation of the sensor.
EXTERNAL INTERRUPTS
*********MOTOR PULSE INPUT***********
The pulses sent to the G251X stepper driver are also sent to Aboard3 Pin D2.
This pin is the External Interrupt pin, vector INT0
*********MOTOR DIRECTION INPUT********
Motor direction is input to ABoard3 from ABoard2. ABoard2 pin, pnVmInRotMotor
(AB2:A0{54}) is connected to Velleman pins DI4 and DO2 and also to AB3:D3.
AB3:D3 is an External Interrupt pin, vector INT1.
*/

#include                        
#include "streaming.h"                        
#include "I2C_AnythingHEG.h"   
#include                     

//CONSTANTS                       
//PIN ASSIGNMENTS For ABOARD3
const uint8_t pnMotorPulse = 2;      //Port PD2, INT0, ISR(INT0_vect){}
const uint8_t pnMotorDirection = 3;  //Port PD3, INT1, ISR(INT1_vect){}
const uint8_t pnDomeAtHome = 7;      //Port PD7, PCINT23,ISR(PCINT2_vect){}
const uint8_t pnREChanA = 11;        //Port PB3, PCINT3, ISR(PCINT0_vect){}
const uint8_t pnREChanB = 12;        //Port PB4, PCINT4, ISR(PCINT0_vect){}

//*****************EXPERIMENTAL STUFF FOR PULSE COUNTING*******************************                  
uint16_t volatile myTest = 0;
int32_t volatile ratioCount = 0L;
int32_t volatile totalCount = 0L;
int32_t volatile tickCount = 0L;
uint8_t volatile halftickCount = 0;
int32_t volatile addPulse = 0L; 

void setup() {
  //**********************************SERIAL FOR DEBUG ONLY************************
  Serial.begin(115200);
  //*************************INPUT PIN MODE SETUP**********************************
  //NOTE Set all UNUSED PCINT0:5 pins (D8, D9, D10, D11) to OUTPUT.
  //and set the value to LOW
  //The actual pins used to receive interrupts have external 10k pull-ups.
  //This is to reduce susceptibility to interference causing false triggering.
  pinMode(pnMotorPulse, INPUT); //D2
  pinMode(pnMotorDirection, INPUT); //D3
  pinMode(pnDomeAtHome, INPUT); //D7
  pinMode(pnREChanA, INPUT); //D11
  pinMode(pnREChanB, INPUT); //D12
  pinMode(4, OUTPUT); //D4
  digitalWrite(4, LOW);
  pinMode(8, OUTPUT); //D8
  digitalWrite(8, LOW);
  pinMode(9, OUTPUT); //D9
  digitalWrite(9, LOW);
  pinMode(10, OUTPUT); //D10
  digitalWrite(10, LOW);
  //******************SET UP AddPulse According to Motor Direction******************
  //This is needed to make sure the pulse counting starts in the correct direction
  //as the direction is only checked in the ISR after a change has occurred.
  //If Motor Direction is AntiClockwise, change to Subtract a pulse
  if( digitalRead(pnMotorDirection)) {
    addPulse = 1L;
  } else {
    addPulse = -1L;
  }
  //**************************SET UP PIN CHANGE INTERRUPTS**************************
  //Set the Pin Change Interrupt Masks
  //Clear all bits to start with
  PCMSK0 &= 0b00000000; //Clear all bits
  PCMSK1 &= 0b00000000; //Clear all bits
  PCMSK2 &= 0b00000000; //Clear all bits
  //Mask for PCINTs 0 through 7 is PCMSK0 (Rotary Encoder Pulses)
  //Need to allow interrupts on PCINT3 and PCINT4, so set bits 3 and 4
  //PCINT3 is Nano  pin D11 and PCINT4 is Nano pin D12
  //Use a compound bitwise OR to set the bits
  PCMSK0 |= 0b00011000; //Enable PCINT3 ONLY (bit 3)
  //Interrupt on pins 11 and 12, RE Ticks
  //Mask for PCINTs 16through 23 is PCMSK2 (Home Position)
  //Need to allow interrupts on PCINT23 so set bit 7
  PCMSK2 |= 0b10000000; //Interrupt on pin 7, Home Position Sensor activated
  //Now enable the interrupts (TURN THEM ON) by setting bits in PCICR
  //PCICR is Pin Change Interupt Control Register.Set bit 0 (PCIE0)  
  //to enable interrupts PCINT0:7 This catches PCINT3 and 4 - RE Ticks
  //Set bit 2 (PCIE2) to enable interrupts PCINT16:23. Catches PCINT21 - Home Position Sensor
  PCICR &= 0b00000000; //Clear PCICR register 
  PCICR |= 0b00000001; //Set bit 0 - Catches PCINT3 & PCINT4 - RE Ticks
  PCICR |= 0b00000100; //Set bit 2 - Catch PCINT21 - Home Position Sensor
  //**************************SET UP 'EXTERNAL INTERRUPTS'**************************
  //Interupts on External Interrupt Pins D2 (INT0) and D3 (INT1).
  //INT0 (Nano pin D2)occurs when a stepper motor driver pulse is received
  //INT1 (Nano pin D3)occurs when the ABoard2 pin, pnVmInRotMotor toggles 
  //indicating a motor direction change.
  //To use the 'External Interrupts' the following registers need to be set-up:         
  //EICRA External Interrupt Control Register A       
  //Need to set Interrupt Sense Control bits ISC11 .. ISC00 (bits 3:0 in EICRA)
  //ISC01     ISC00 (INT0)    Interrupt
  //ISC11     ISC01 (INT1)    Generated by
 // 0         0             Low level on Pin
 // 0         1             Any Logical Change
 // 1         0             Falling Edge
 // 1         1             Rising Edge
  //First clear all bits, then set as follows:  
  //For INT1 - Motor Direction - Generate on ANY LOGICAL CHANGE     
  //bit 3 ISC11 0     
  //bit 2 ISC10 1 This combination = Any logical change causes interrupt 
  //For INT0 - Stepper Motor Pulses  - Generate on RISING EDGE      
  //bit 1 ISC01 1     
  //bit 0 ISC00 1 This combination = Rising edge of pulse causes interrupt
  //NOTE: To provide some immunity to RFI, Aboard3:Pins 2 & 3 are pulled high
  //using 10k resistors. 
  //So, code is
  EICRA &= 0b00000000; //Clear EICRA register
  EICRA |= 0b00000111;//Set bits 0,1 and 2 in EICRA register
  //EIMSK External Interrupt Mask Register        
  //Need to set External Interrupt Request Enables INT1 & INT0  (bits 1:0)          
  //First clear both bits, then set as follows: 
  //bit 1  INT1  1 External interrupt pin (D3) enabled   
  //bit 0  INT0  1 External interrupt pin (D2) enabled   
  //So, code is
  EIMSK &= 0b00000000; //Clear EIMSK register
  EIMSK |= 0b00000011;//Set bits 0 and 1 in EIMSK register
  //NOTE: Setting EIMSK TURNS ON THE EXTERNAL INTERRUPTS
  //************VARIABLE INITIALISATION*********
  myCommand = 0;
  myTest = 0;
  tickCount = 0L;
  totalCount = 0L;
} //END of setup

void loop() {
  //******************************COMMAND ACTIONS******************************
  if (myTest == 3) (
    //RE tick
    Serial << "Tick Count = " << tickCount << "  totalCount = " << totalCount << "\n";
    myTest = 0;
  }
}
//*************************FUNCTIONS/ISRs FROM HEREON*************************
ISR(INT0_vect) {
  //Triggered by stepper motor drive pulse from ABoard1
  totalCount = totalCount + addPulse;
}
ISR(INT1_vect) {
  //Triggered by a change in motor direction
  if(digitalRead(pnMotorDirection)) {
    addPulse = 1L;
  } else {
    addPulse = -1L;
  }
}
ISR(PCINT0_vect) {
  //Triggered by a ROTARY ENCODER TICK
  halftickCount++;
  if (halftickCount == 2) {
    //Make count same as Velleman
    tickCount++;
    halftickCount = 0;
    myTest = 3;
  }
}
ISR(PCINT2_vect) {
  //Triggered by activation of Home Position Sensor
  myTest = 4;
}
0
1) Vous avez une lunette, vous l’avez utilisée pour regarder les impulsions du moteur. Avez-vous regardé les impulsions du codeur également? A quoi est-ce qu'ils ressemblent? 2) Sur un codeur en quadrature, je m'attendrais à ce qu'au plus un canal soit «dynamique» à tout moment. Ensuite, si vous utilisez l'encodeur de la manière habituelle, vous devriez obtenir une séquence de +1, −1, +1, −1 ... en additionnant à zéro. 3) Le standard pour configurer les registres d’E/S de l’AVR est PCMSK0 = _BV (PCINT3) | _BV (PCINT4); , qui semble plus clair que PCMSK0 & = 0b00000000; PCMSK0 | = 0b00011000; , du moins à ceux qui sont familiarisés avec la programmation AVR.
ajouté l'auteur Sprogz, source
PCMSK0 & = 0b00000000;//Effacer tous les bits - PCMSK0 = 0; ne serait-il pas beaucoup plus clair? En attendant, je suis d'accord avec la réponse de Gerben. Essayez de "protéger" votre accès aux variables multi-octets définies par des interruptions. Voir Interruptions .
ajouté l'auteur Nick Gammon, source
Avez-vous une masse commune appropriée entre les sorties du codeur en quadrature et l’Arduino? Si tel est le cas, @EdgarBonet a raison. Les encodeurs en quadrature peuvent rebondir comme un fou, vous avez donc vraiment besoin de le lire correctement, en déterminant le sens du pas et le tout à partir de chaque lecture. De cette façon, si elle rebondit +1 puis -1, ou toute autre séquence, 100 fois de suite, vous finissez par savoir quel est le nouvel emplacement et vous l'avez donc également rejeté. Essayez également d’ajouter une limite de 2,2uF sur chaque sortie en quadrature, afin d’aider un peu plus au rebond matériel.
ajouté l'auteur meepsh, source
Bien lire le codeur est le logiciel anti-rebond dont vous avez vraiment besoin.
ajouté l'auteur meepsh, source
Votre encodeur rotatif est-il électrique ou optique? Je demande si je me demande si vous avez des problèmes avec le rebond de commutateur ou peut-être avec le récepteur optique qui s’allume et s’éteint à une limite. Si tel est le cas, un moyen de résoudre le problème consiste à décoder correctement les impulsions quad codées. Cet article de blog montre un moyen fiable de décoder des impulsions codées en quad: thewanderingengineer.com/2014/08/11/… Regads,
ajouté l'auteur CMaster, source
Une alternative au traitement anti-rebond dans le processeur consiste à utiliser une puce conçue à cet effet. En voici un qui pourrait convenir: elmelectronics.com/ic/elm402 Cordialement,
ajouté l'auteur CMaster, source

1 Réponses

Il n'est pas nécessaire d'utiliser int32_t pour certaines variables. Le problème lié à l'utilisation de variables de plus de 8 bits sur un processeur 8 bits est que le processeur a besoin de 4 lectures pour obtenir la valeur. Au milieu de ces lectures, une interruption peut survenir, donnant une valeur qui contient certains bits de l'ancienne valeur et d'autres du nouveau.

Certaines variables n'ont besoin que de 8 bits.

uint8_t volatile myTest = 0;
int32_t volatile totalCount = 0L;
int32_t volatile tickCount = 0L;
uint8_t volatile halftickCount = 0;
int8_t volatile addPulse = 0L; 

La prochaine chose à faire est de faire la lecture de la valeur atomique.

void loop()                                             
{                                               
//******************************COMMAND ACTIONS******************************  

if (myTest == 3)   //RE tick
{
    noInterrupts();
    int32_t tickCountCopy = tickCount;
    int32_t totalCountCopy = totalCount;
    interrupts();
    Serial << "Tick Count = " << tickCountCopy << "  totalCount = " << totalCountCopy << "\n"; 
    myTest = 0; 
}
}   

PS ce n'est pas nécessairement une réponse, mais cela ne rentre pas dans les commentaires.

1
ajouté
Il n'y a pas de réel rebond ici, mais probablement pas une transition aussi claire que ce à quoi vous vous attendiez. À mon avis, un déclencheur de Schmitt fonctionnera seul. Même si ma réponse ne résout pas votre problème, cela indique tout de même un problème que vous pourriez expliquer avec votre code.
ajouté l'auteur Al., source
Bonjour et merci Gerben et Nick. J'ai apporté les modifications que vous avez recommandées, mais elles semblaient en réalité aggraver les choses, pas mieux: même sans le moteur pas à pas en marche, les déclenchements multiples se produisaient sur chacun des canaux RE.
ajouté l'auteur SeaDrive, source
Oops - voulait une nouvelle ligne et avait appuyé sur la touche Entrée, mais avait envoyé le commentaire avant que je ne termine. Donc, excusez le manque de formatage à partir de maintenant. Je suis maintenant arrivé à la conclusion que je dois faire échec au Rotary Encoder. J'avais supposé que comme il s'agissait d'un encodeur optique, il n'exigerait pas de rebond mais je pense maintenant que mon hypothèse était fausse. Je vais faire un petit debouncer matériel en utilisant la synchronisation RC et un IC 74HC14 inversant le trigger de Schmitt. Espérons que cela résoudra le problème. La bonne nouvelle est que les impulsions du moteur pas à pas sont comptées sans problème. Merci, Hugh
ajouté l'auteur SeaDrive, source
Bonjour, j'ai essayé d'utiliser mon oscilloscope pour examiner les sorties du codeur, mais je ne savais pas comment l'utiliser correctement et je ne pouvais pas obtenir de résultats significatifs. J'ai créé la carte debouncer, en utilisant un délai d'environ 5 ms sur chaque canal de l'encodeur et, finalement, tout fonctionne comme prévu. J'ai eu ce problème pour la première fois début décembre, il a donc fallu tout ce temps pour le résoudre. En fait acheté ma portée pour aider avec ce problème spécifique. Merci encore à tous ceux qui ont répondu et commenté. Cordialement Hugh
ajouté l'auteur SeaDrive, source
@HughGilhespie, si le problème est résolu maintenant, postez s'il vous plaît une réponse à votre propre question et marquez-la comme résolue.
ajouté l'auteur meepsh, source