Jeg prøver at bruge portmanipulation i afbrydelser for at læse en roterende encoder. Jeg fandt kode til en Uno, så portopkaldet til ben 2 og 3 er forskellige. Jeg tror på Mega, at de er henholdsvis PORTH3 og PORTH4. Min kode fungerer ikke. Har jeg mine porte forkert? Jeg har prøvet at læse om dette, og jeg forstår ikke Bxxxxxxxx-delen fuldt ud. Jeg tror, det kan være, hvad der er forkert. Her er en del af min kode med en af afbrydelserne.
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 }
Kommentarer
- Har du nogensinde fået dette til at fungere? Jeg har også ønsket at bruge en Arduino Mega til at spore vinkelpositionen på en Omron Encoder med 3 afbrydelser. En af afbrydelserne, som han indekserer for at nulstille til 0 hver gang den fuldender en revolution. Jeg vil bruge den til at spore en roterende mast til at finde retning. Der er stykker kode, men intet er komplet. Er din komplet? [email protected]
Svar
Re “ben 2 og 3 er forskellige. Jeg tror på Mega, at de er henholdsvis PORTH3 og PORTH4”, det er rigtigt, at Arduino digitale pins 2 og 3 hører til forskellige porte på Uno vs Mega2560-kort.
Mega2560 “har seks INTx-ben, mod to sådanne på Uno”. På Mega er INT0 … INT3 PD0 … PD3, og INT4, INT5 er PE4, PE5. På Uno er INT0, INT1 PD2, PD3. Bemærk, i et svar på Kan eksterne afbrydelser ELLER “d sammen på” 328 (Uno)? Jeg viser et par rutiner, der viser passende masker til stifter på forskellige Arduinos. Se afsnittene “Brug af andre ben til PCIer” og “ISR-framework-genererende skitse”.
Her er et par problemer med koden vist i spørgsmålet:
-
aFlag
initialiseres til nul og er aldrig sat nul i den viste kode. Så den førsteif
betingelse iPinA()
er aldrig opfyldt. -
PinA()
er en afbrydningshåndterer, der er forbundet til afbrydelser ved hjælp af dineattachInterrupt()
-opkald. Da hardware deaktiverer afbrydelser, før den går ind i en afbrydelsesbehandler, er der ikke behov for afbrydelsesadministratoren for at deaktivere afbrydelser. Sletcli()
iPinA()
. Slet på samme mådesei()
i slutningen afPinA()
, fordi hardware gendanner afbrydelsesstatus, når en RETI-instruktion udføres. -
Arduino-softwaremiljøet definerer konstanter som
B00001100
med binære værdier, der matcher navnet. Det vil sige, i Arduino-softwaremiljøet har konstantB00001100
værdien0B00001100
. Efter min mening er den “en dum og unødvendig funktion. Jeg foreslår, at du i din kode bruger standard C-notation som0B00001100
, når du vil bruge binære konstante værdier. -
Jeg betragter skrivning af
encoderPos --
i stedet forencoderPos--
som en perversion og foreslår at slette det unødvendige mellemrum. -
Da du ikke har vist
PinB()
interrupt handler, ved jeg ikke, hvordan du skulle håndtereencoderPos++
sag. At dømme ud fra den viste kode bruger du en metode, der ikke håndterer afvisnings- og intermitterende fejl korrekt. Jeg foreslår at bruge pin-change interrupts og en state-machine metode, som illustreret i svar på:
• Læsning fra en KY-040 roterende encoder med Digispark
• Hvordan læses RPS til hurtig rotation med statusændringsmetode i Arduino?
• Læs RC-modtagerkanaler ved hjælp af Interrupt i stedet for PulseIn
• Kan eksterne afbrydelser ELLER “d” sammen på “328 (Uno)?
Svar
På mega læste du pinene med PINE.
Så din kode i PinA ville ændre sig til:
reading = PINE & 0b00110000; if(reading == 0b00110000 && aFlag) { encoderPos --; bFlag = 0; aFlag = 0; } else if (reading == 0b00010000) bFlag = 1;
I fandt en god diskussion, der talte om maskeringen, der kræves her: https://forum.arduino.cc/index.php?topic=561741.0