Resultados estranhos tentando contar pulsos

Estou agora desesperadamente preso e preciso de ajuda por favor.

Eu estou construindo um observatório automatizado e agora na fase de tentar controlar a rotação da cúpula do observatório. A cúpula é girada usando um motor de passo e funciona bem. A rotação da cúpula também transforma um codificador rotativo para fornecer uma verificação independente de que a cúpula está se movendo. Embora o RE seja um tipo de quadratura, estou simplesmente contando os ticks RE de ambos os canais, pois não preciso de informações de direção.

O que eu estou tentando fazer agora é contar quantos pulsos de direção motora ocorrem entre cada par de pulsos de EC. O raciocínio por trás disso é monitorar os movimentos da cúpula e detectar o congestionamento. Os pulsos reais do motor serão usados ​​para todos os cálculos de posição da cúpula.

Para conseguir isso, eu construí uma pequena placa com um buffer de inversão ULN2803A e um Arduino Nano. O Nano usa interrupções de troca de pinos nos pinos D11 e D12 para contar os pulsos de RE. O pino D7 também será usado para monitorar um sensor de posição inicial, embora isso não exista no momento. Os impulsos do motor são enviados através do buffer para o pino D2 (INT0) e a direção do motor é enviada através do buffer para o pino D3 (INT1). As interrupções externas são usadas para contar os impulsos do motor, para cima ou para baixo, dependendo da direção do motor.

Para informação, há outra placa presente, uma placa de breakout Velleman VM110 USB, que também recebe os ticks do codificador rotativo. Isso me dá uma maneira simples de verificar se a minha placa dá a mesma resposta quando eu girar o eixo do codificador rotativo com a mão.

OK - desculpe, isso é tão longo, mas agora fica estranho.

Quando eu giro o ER sem o motor ligado, tudo está bem. Eu recebo quatro vezes mais pulsos do que a prancha Velleman, já que estou usando o Pin Changes, ativando cada subida e descida, não em ciclos completos do encoder. Isso acontece como previsto e tudo é bom e estável.

MAS - quando eu começo o motor em funcionamento, eu recebo centenas de carrapatos RE toda vez que eu movo o codificador e às vezes eu recebo carrapatos RE crescentes sem tocar no codificador.

A resposta óbvia é alguma forma de RFI, mas fiz tudo o que podia para eliminar isso. O driver do motor e a placa Nano que o alimenta estão em um gabinete blindado, o cabo do motor é um cabo blindado e eu tenho 10uH bobinas em cada um dos quatro cabos do motor. Finalmente, adaptei um filtro à fonte de alimentação de entrada na caixa do motor para minimizar qualquer RFI que desce pelas linhas de energia.

O uso do buffer ULN2803A foi minha última tentativa de fazer isso funcionar. Anteriormente, os sinais iam direto para os pinos Nano. Com o buffer, usei pullups de 20k no lado da entrada e pullups de 10k nas saídas. Esta foi uma cópia direta do circuito de entrada da placa Velleman que eu sabia que funcionava sem problemas.

Eu olhei para os pulsos do motor no pino de entrada para o Nano no meu osciloscópio e eles são de boa aparência, pulsos afiados de duração 70 uS e uma frequência de 497 Hz. Nada mal, pois eu configurei a taxa de pulso usando a biblioteca Accelstepper para 500Hz.

Eu agora suspeito que isso seja um problema de software. Isso não me surpreenderia, já que sou muito novo em tudo isso, apenas tentando aprender o suficiente em cada estágio para fazer o que preciso. Anexei o código - esta é uma versão simplificada, sem muitas coisas I2C que não são relevantes para os meus problemas.

Mais uma vez, para informação. Eu tentei usar a função attachInterrupt() e definindo diretamente os registros relevantes - não houve diferença!

De qualquer forma, eu realmente espero que alguém possa me ajudar a resolver isso.

Atenciosamente, 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) Você tem um escopo, você usou para olhar os pulsos do motor. Você também observou os pulsos do encoder? Como eles se parecem? 2) Em um codificador de quadratura, eu esperaria que no máximo um canal fosse “saltitante” a qualquer momento. Então, se você usar o codificador da maneira usual, você deve obter uma seqüência de +1, -1, +1, -1 ... somando até zero. 3) O idioma padrão para configurar os registros de E/S do AVR é ​​ PCMSK0 = _BV (PCINT3) | _BV (PCINT4); , que parece mais claro que PCMSK0 & = 0b00000000; PCMSK0 | = 0b00011000; , pelo menos para aqueles que estão familiarizados com a programação do AVR.
adicionado o autor Sprogz, fonte
PCMSK0 & = 0b00000000;//Limpar todos os bits - não seria PCMSK0 = 0; seria muito mais claro? Enquanto isso, concordo com a resposta de Gerben. Tente "guardar" o seu acesso a variáveis ​​multi-byte definidas por interrupções. Veja Interrompe .
adicionado o autor Nick Gammon, fonte
Você tem um terreno compartilhado adequado entre as saídas do codificador de quadratura e o Arduino? Se assim for, então @EdgarBonet está certo. Os codificadores de quadratura podem saltar como loucos, então você realmente precisa ler o codificador corretamente, determinando a direção da etapa e todos de cada leitura. Dessa forma, se ele salta +1 e, em seguida, -1, ou qualquer outra sequência, 100 vezes seguidas, você acaba sabendo qual é o novo local e, portanto, também o debilitou. Tente adicionar um limite de 2,2uF em cada saída em quadratura também, para ajudar um pouco com a depuração de hardware.
adicionado o autor meepsh, fonte
Ler corretamente o codificador é o software que você realmente precisa.
adicionado o autor meepsh, fonte
O seu codificador rotativo é elétrico ou óptico? Eu pergunto como eu me pergunto se você tem problemas com salto de switch ou possivelmente o receptor óptico ligando e desligando em um limite. Se este for o caso, uma maneira que pode ajudar a resolvê-lo é decodificar corretamente os pulsos codificados em quad. Esta postagem de blog mostra uma maneira confiável de decodificar pulsos codificados em quad: thewanderingengineer.com/2014/08/11/… Regads,
adicionado o autor CMaster, fonte
Uma alternativa para fazer o debounce no processador é usar um chip projetado para o propósito. Aqui está uma que pode ser adequada: elmelectronics.com/ic/elm402 Atenciosamente,
adicionado o autor CMaster, fonte

1 Respostas

Não há necessidade de usar int32_t para algumas das variáveis. O problema com o uso de variáveis ​​que são mais de 8 bits em um processador de 8 bits é que o processador precisa de 4 leituras para obter o valor. No meio dessas leituras, pode ocorrer uma interrupção, resultando em um valor que possui alguns bits do valor antigo e alguns bits do novo.

Algumas variáveis ​​precisam apenas 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; 

O próximo passo é fazer a leitura do valor atômico.

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. isso não é necessariamente uma resposta, mas não se encaixou nos comentários.

1
adicionado
Não há salto real aqui, mas provavelmente não é uma transição tão clara quanto você esperaria. Meu palpite é que um gatilho só vai funcionar. Mesmo que a minha resposta não resolva o seu problema, ainda aponta para um problema que você pode ter em conta com o seu código.
adicionado o autor Al., fonte
Olá e obrigado Gerben e Nick. Eu fiz as mudanças que você recomendou, mas elas na verdade pareciam piorar as coisas, não melhor, pois mesmo sem o motor de passo rodando eu estava recebendo vários acionamentos em cada um dos canais RE.
adicionado o autor SeaDrive, fonte
Opa - queria uma nova linha e pressionei o botão de retorno, mas enviei o comentário antes de terminar. Então, desculpe a falta de formatação a partir de agora. Cheguei agora à conclusão de que preciso debitar o Codificador Rotativo. Eu tinha ASSUMIDO que, como é um codificador óptico, não precisaria ser debitado, mas agora acho que minha suposição estava errada. Eu vou fazer um pequeno debouncer de hardware usando temporização RC e um inversor Schmitt IC 74HC14 hex. Espero que o chapéu resolva o problema. A boa notícia é que os pulsos reais do motor de passo são contados sem problemas. Obrigado, Hugo.
adicionado o autor SeaDrive, fonte
Oi, tentei usar meu escopo para ver as saídas do codificador, mas não sei como usá-lo corretamente e não consegui resultados significativos. Eu fiz o debouncer board, usando ca 5 mS de atraso em cada canal do encoder e, finalmente, tudo funciona como deveria. Primeiro tive esse problema no início de dezembro, então demorou todo esse tempo para resolvê-lo. Na verdade comprei meu escopo para ajudar com esse problema específico. Obrigado novamente a todos que responderam e comentaram. Atenciosamente Hugh
adicionado o autor SeaDrive, fonte
@HughGilhespie, se o problema for resolvido agora, por favor poste uma resposta para sua própria pergunta e marque-a como resolvida.
adicionado o autor meepsh, fonte