Vreemde resultaten proberen pulsen te tellen

Ik ben nu hopeloos vastgelopen en heb hulp nodig alstublieft.

Ik bouw een geautomatiseerde observatorium en nu in het stadium van het proberen om de koepelrotatie van het observatorium te controleren. De koepel wordt geroteerd met behulp van een stappenmotor en het werkt prima. De rotatie van de dome draait ook een roterende encoder om een ​​onafhankelijke verificatie uit te voeren dat de koepel beweegt. Hoewel de RE een kwadratuurtype is, tel ik eenvoudig RE-tikken van beide kanalen omdat ik geen richtingsinformatie nodig heb.

Wat ik nu probeer te doen, is om te tellen hoeveel motorstuurpulsen er zijn tussen elk paar RE-tikken. De redenering hierachter is om de bewegingen van de koepel te controleren en vastlopen te detecteren. De werkelijke motorimpulsen worden gebruikt voor alle koepelpositieberekeningen.

Om dit te bereiken, heb ik een klein bord met een ULN2803A inverterende buffer en een Arduino Nano gebouwd. De Nano maakt gebruik van Pin Change-interrupts op pinnen D11 en D12 om de RE-tikken te tellen. Pin D7 zal ook worden gebruikt om een ​​sensor voor de beginpositie te bewaken, hoewel deze momenteel niet bestaat. De motorimpulsen worden via de buffer verzonden naar Pin D2 (INT0) en de motorrichting wordt via de buffer verzonden naar pin D3 (INT1). De externe interrupts worden vervolgens gebruikt om de motorimpulsen te tellen, afhankelijk van de richting van de motor, omhoog of omlaag.

Voor informatie is er een ander bord aanwezig, een Velleman VM110 USB breakout-bord, dat ook de tikken van de roterende encoder ontvangt. Dit geeft me een eenvoudige manier om te controleren of mijn bord hetzelfde antwoord geeft als ik de roterende encoders met de hand draai.

OK - sorry dit is zo lang uit de lucht maar nu wordt het raar.

Als ik de RE draai zonder dat de motor loopt, is alles in orde. Ik krijg vier keer zoveel pulsen als het Velleman-bord, omdat ik Pin Changes gebruik, wat triggert bij elke stijging en daling, en niet bij volledige cycli van de encoder. Dit gebeurt zoals voorspeld en alles is leuk en stabiel.

MAAR - wanneer ik de motor start, krijg ik elke keer dat ik de encoder verplaats honderden RE-reeksen en soms krijg ik een stijgende RE-tik zonder de encoder helemaal te raken.

Het voor de hand liggende antwoord is een vorm van RFI, maar ik heb er alles aan gedaan om dit te voorkomen. De motorbesturing en het Nano-board dat deze voedt, bevinden zich in een afgeschermde behuizing, de kabel naar de motor is een afgeschermde kabel en ik heb 10uH-smoorspoelen op elk van de vier motordraden. Ten slotte heb ik een filter aangebracht op de binnenkomende stroomtoevoer naar de motorbox om elke RFI die door de hoogspanningsleidingen teruggaat te minimaliseren.

Het gebruik van de ULN2803A-buffer was mijn laatste poging om dit te laten werken. Eerder gingen de signalen direct naar de Nano-pinnen. Met de buffer heb ik 20k pull-ups gebruikt aan de invoerzijde en 10k pull-ups op de uitgangen. Dit was een directe kopie van het invoercircuit van de Velleman-kaart waarvan ik wist dat het zonder problemen werkte.

Ik heb gekeken naar de motorpulsen bij de ingangspen naar de Nano op mijn telescoop en het zijn mooie, scherpgerande pulsen met een duur van 70 us en een frequentie van 497 Hz. Niet slecht, want ik heb de hartfrequentie ingesteld met de Accelstepper-bibliotheek op 500Hz.

Ik vermoed nu dat dit een softwareprobleem is. Dit zou me niet verbazen, want ik ben erg nieuw voor dit alles, gewoon proberen genoeg te leren in elke fase om te doen wat ik nodig heb. Ik heb de code bijgevoegd - dit is een uitgeklede versie zonder veel I2C-dingen die niet relevant zijn voor mijn problemen.

Nogmaals, voor informatie. Ik heb geprobeerd de functie attachInterrupt() te gebruiken en de relevante registers rechtstreeks in te stellen - geen verschil!

Hoe dan ook, ik hoop echt dat iemand me kan helpen dit uit te zoeken.

Met vriendelijke groeten, 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) Je hebt een bereik, je hebt het gebruikt om naar de motorimpulsen te kijken. Heb je ook naar de pulsen van de encoder gekeken? Hoe zien ze eruit? 2) Op een quadrature-encoder zou ik verwachten dat er maar één kanaal op een bepaald moment "springerig" is. Als u de encoder vervolgens op de gebruikelijke manier gebruikt, moet u een reeks van +1, -1, +1, -1 ... optellen tot nul. 3) Het standaard idioom voor het instellen van de AVR I/O-registers is PCMSK0 = _BV (PCINT3) | _BV (PCINT4); , die er duidelijker uitziet dan PCMSK0 & = 0b00000000; PCMSK0 | = 0b00011000; , tenminste voor degenen die bekend zijn met AVR-programmering.
toegevoegd de auteur Sprogz, de bron
PCMSK0 & = 0b00000000;//Wis alle bits - zou PCMSK0 = 0; niet duidelijker zijn? Ondertussen ben ik het eens met het antwoord van Gerben. Probeer uw toegang tot variabelen van meerdere bytes die zijn ingesteld door onderbrekingen te 'bewaken'. Zie Onderbrekingen .
toegevoegd de auteur Nick Gammon, de bron
Heeft u een goede gedeelde basis tussen kwadratuur-encoderuitgangen en de Arduino? Als dat zo is, heeft @EdgarBonet gelijk. Quadratuur-encoders kunnen stuiteren als een gek, dus je moet echt de encoder goed lezen, de staprichting bepalen en alles van elke lezing. Op deze manier, als het +1 en vervolgens -1, of een andere reeks, 100 keer op rij bounced, weet je uiteindelijk wat de nieuwe locatie is, en daarmee heb je het ook ongedaan gemaakt. Probeer ook een uitgang op elke kwadratuuruitvoer toe te voegen, om een ​​klein beetje te helpen bij het debatteren van hardware.
toegevoegd de auteur meepsh, de bron
Juist het lezen van de encoder is de software die je echt nodig hebt.
toegevoegd de auteur meepsh, de bron
Is uw roterende encoder elektrisch of optisch? Ik vraag het, want ik vraag me af of je problemen hebt met het stuiteren van de schakelaar of mogelijk dat de optische ontvanger aan en uit gaat op een grens. Als dit het geval is, is een manier die kan helpen om het op te lossen, de quad-gecodeerde pulsen op de juiste manier te decoderen. Deze blogpost toont een betrouwbare manier om quad-gecodeerde pulsen te decoderen: thewanderingengineer.com/2014/08/11/… Regads,
toegevoegd de auteur CMaster, de bron
Een alternatief voor het debounce in de processor is om een ​​chip te gebruiken die speciaal voor dit doel is ontworpen. Hier is er een die mogelijk geschikt is: elmelectronics.com/ic/elm402 Regards,
toegevoegd de auteur CMaster, de bron

1 antwoord

Het is niet nodig om int32_t te gebruiken voor enkele van de variabelen. Het probleem met het gebruik van variabelen die meer dan 8 bits op een 8-bits processor zijn, is dat de processor 4 leeswaarden nodig heeft om de waarde te krijgen. In het midden van die leest kan een interrupt optreden, resulterend in een waarde die enkele bits van de oude waarde heeft, en enkele stukjes van de nieuwe.

Sommige variabelen hebben slechts 8 bits nodig.

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; 

Het volgende is om het lezen van de waarde atomisch te maken.

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. dit is niet noodzakelijk een antwoord, maar het paste niet in de opmerkingen.

1
toegevoegd
Er is hier geen echte stuit, maar waarschijnlijk niet zo duidelijk een overgang als je zou verwachten. Mijn gok is dat een schmitt-trigger alleen zal werken. Hoewel mijn antwoord je probleem niet oplost, duidt het nog steeds op een probleem dat je mogelijk met je code zou kunnen verantwoorden.
toegevoegd de auteur Al., de bron
Hallo, en bedankt Gerben en Nick. Ik heb de door jou aanbevolen wijzigingen gemaakt, maar ze leken de zaken eigenlijk alleen maar erger te maken, niet beter, omdat zelfs zonder de stappenmotor die ik kreeg meerdere triggering op elk van de RE-kanalen werd gebruikt.
toegevoegd de auteur SeaDrive, de bron
Oeps - wilde een nieuwe regel en drukte op return, maar die stuurde de opmerking voordat ik klaar was. Dus excuseer het gebrek aan opmaak vanaf nu. Ik ben nu tot de conclusie gekomen dat ik de Rotary Encoder moet negeren. Ik had ERVAN OVERTUIGD dat het, omdat het een optische encoder is, niet om redenering zou vragen, maar ik denk nu dat mijn aanname verkeerd was. Ik ga een kleine hardware-debouncer maken met RC-timing en een 74HC14 hex inverterende Schmitt trigger-IC. Hopelijk zal hat het probleem oplossen. Het goede nieuws is dat de werkelijke stappenmotorimpulsen zonder problemen worden geteld. Bedankt, Hugh.
toegevoegd de auteur SeaDrive, de bron
Hallo, ik heb geprobeerd mijn scope te gebruiken om naar de encoder-uitgangen te kijken, maar ik weet niet hoe ik deze goed moet gebruiken en kon geen zinvolle resultaten krijgen. Ik heb de debouncerprint gemaakt met een vertraging van ca 5 mS op elk encoderkanaal en uiteindelijk werkt alles zoals het hoort. Ik had dit probleem voor het eerst begin december, dus het heeft al die tijd geduurd om het opgelost te krijgen. Eigenlijk kocht ik mijn mogelijkheden om met dit specifieke probleem te helpen. Nogmaals bedankt aan iedereen die heeft geantwoord en becommentarieerd. Met vriendelijke groet, Hugh
toegevoegd de auteur SeaDrive, de bron
@HughGilhespie, als het probleem nu is opgelost, plaats dan een antwoord op je eigen vraag en markeer het als opgelost.
toegevoegd de auteur meepsh, de bron