Estou tentando usar a manipulação de portas em interrupções para ler um codificador rotativo. Encontrei o código para um Uno, então a chamada de porta para os pinos 2 e 3 são diferentes. Acho que no Mega eles são PORTH3 e PORTH4 respectivamente. Meu código não está funcionando. Estou com minhas portas erradas? Eu tentei ler sobre isso e não estou entendendo totalmente a parte Bxxxxxxxx. Acho que pode ser isso que está incorreto. Aqui está parte do meu código com uma das interrupções.
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2 static int pinB = 3; // Our second hardware interrupt pin is digital pin 3 volatile byte aFlag = 0; // let us know when we"re expecting a rising edge on pinA to signal that the encoder has arrived at a detent volatile byte bFlag = 0; // let us know when we"re expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set) volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255 volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor) volatile byte reading = 0; void setup () { pinMode(pinA, INPUT); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases) pinMode(pinB, INPUT); attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, //looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below) attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, //looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below) } void PinA() { //CCW cli(); //stop interrupts happening before we read pin values reading = PORTH & 0xC; // read all eight pin values then strip away all but pinA and pinB"s values if (reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin"s rising edge encoderPos --; //decrement the encoder"s position count bFlag = 0; //reset flags for the next turn aFlag = 0; //reset flags for the next turn } else if (reading == B00000100) bFlag = 1; //signal that we"re expecting pinB to signal the transition to detent from free rotation sei(); //restart interrupts }
Comentários
- Você já conseguiu fazer isso funcionar? Eu também tenho desejado usar um Arduino Mega para rastrear a posição angular de um codificador Omron com 3 interrupções. Uma das interrupções para o índice é redefinir para 0 toda vez ele completa uma revolução. Eu quero usá-lo para rastrear um mastro giratório para encontrar a direção. Existem pedaços de código, mas nada completo. O seu está completo? [email protected]
Resposta
Sobre “os pinos 2 e 3 são diferentes. Acho que no Mega eles são PORTH3 e PORTH4 respectivamente”, é verdade que os pinos digitais 2 do Arduino e 3 pertencem a portas diferentes nas placas Uno vs Mega2560.
Os Mega2560 “s têm seis pinos INTx, contra dois desses nas placas Uno” s. No Mega, INT0 … INT3 são PD0 … PD3 e INT4, INT5 são PE4, PE5. No Uno, INT0, INT1 são PD2, PD3. Observe, em uma resposta em As interrupções externas podem ser OR “d juntas no” 328 (Uno)? Eu mostro algumas rotinas que exibirão máscaras apropriadas para pinos em diferentes Arduinos. Consulte as seções “Usando outros pinos para PCIs” e “Esboço de geração de estrutura ISR”.
Aqui estão alguns problemas com o código mostrado na pergunta:
-
aFlag
é inicializado para zero e nunca é definido como diferente de zero no código mostrado. Portanto, a primeiraif
condição emPinA()
nunca é atendida. -
PinA()
é um manipulador de interrupções, conectado a interrupções por suasattachInterrupt()
chamadas. Como o hardware desativa as interrupções antes de entrar em um manipulador de interrupções, não há necessidade do manipulador de interrupções desligar as interrupções. Ou seja, excluacli()
emPinA()
. Da mesma forma, excluasei()
no final dePinA()
porque o hardware restaura o status de interrupção quando uma instrução RETI é executada. -
O ambiente de software Arduino define constantes como
B00001100
com valores binários que correspondem ao nome. Ou seja, no ambiente de software Arduino, a constanteB00001100
tem o valor0B00001100
. Em minha opinião, é um recurso estúpido e desnecessário; sugiro que em seu código você use a notação C padrão como0B00001100
quando quiser usar valores constantes binários. -
Eu considero escrever
encoderPos --
em vez deencoderPos--
como uma perversão e sugiro excluir esse espaço desnecessário. -
Como você não mostrou o
PinB()
manipulador de interrupções, não sei como você lidaria com oencoderPos++
. A julgar pelo código mostrado, você está usando um método que não tratará corretamente os erros de salto e intermitentes. Sugiro o uso de interrupções de mudança de pino e um método de máquina de estado, conforme ilustrado nas respostas a:
• Leitura de um codificador rotativo KY-040 com Digispark
• Como ler RPS para rotação rápida com método de mudança de status no Arduino?
• Ler canais do receptor RC usando Interrupt em vez de PulseIn
• As interrupções externas podem ser OR “d juntas no” 328 (Uno)?
Resposta
No mega, você “leria os pinos com PINE.
Portanto, seu código no PinA mudaria para:
reading = PINE & 0b00110000; if(reading == 0b00110000 && aFlag) { encoderPos --; bFlag = 0; aFlag = 0; } else if (reading == 0b00010000) bFlag = 1;
I encontrei uma ótima discussão falando sobre o mascaramento necessário aqui: https://forum.arduino.cc/index.php?topic=561741.0