Jag försöker använda portmanipulation i avbrott för att läsa en roterande kodare. Jag hittade kod för en Uno, så portanropet för stift 2 och 3 är olika. Jag tror att på Mega är de PORTH3 respektive PORTH4. Min kod fungerar inte. Har jag mina portar fel? Jag har försökt läsa om detta och jag förstår inte helt Bxxxxxxxx-delen. Jag tror att det kan vara det som är felaktigt. Här är en del av min kod med en av avbrotten.
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
- Fick du någonsin det här? Jag har också velat använda en Arduino Mega för att spåra vinkelpositionen för en Omron Encoder med 3 avbrott. En av avbrotten för att han ska indexera till 0 varje gång det fullbordar en revolution. Jag vill använda den för att spåra en roterande mast för att hitta riktning. Det finns bitar av kod men ingenting komplett. Är din komplett? [email protected]
Svar
Re “stift 2 och 3 är olika. Jag tror att på Mega är de PORTH3 respektive PORTH4”, det stämmer att Arduino digitala stift 2 och 3 tillhör olika portar på Uno vs Mega2560-kort.
Mega2560 har sex INTx-stift, jämfört med två sådana på Uno. På Mega är INT0 … INT3 PD0 … PD3 och INT4, INT5 är PE4, PE5. På Uno är INT0, INT1 PD2, PD3. Observera, i ett svar på Kan externa avbrott vara ELLER ”d tillsammans på” 328 (Uno)? Jag visar ett par rutiner som visar lämpliga masker för stift på olika Arduinos. Se avsnitten ”Använda andra stift för PCI-datorer” och ”ISR-ramgenererande skiss”.
Här är några problem med koden som visas i frågan:
-
aFlag
initialiseras till noll och ställs aldrig noll i den visade koden. Så det förstaif
villkoret iPinA()
uppfylls aldrig. -
PinA()
är en avbrottshanterare, ansluten till avbrott med dinaattachInterrupt()
-samtal. Eftersom hårdvara inaktiverar avbrott innan den går in i en avbrottshanterare, finns det inget behov av avbrottshanteraren att stänga av avbrott. Ta bortcli()
iPinA()
. Ta också bortsei()
i slutet avPinA()
eftersom hårdvara återställer avbrottsstatus när en RETI-instruktion körs. -
Arduino-mjukvarumiljön definierar konstanter som
B00001100
med binära värden som matchar namnet. Det vill säga, i Arduino-mjukvarumiljön har konstantB00001100
värdet0B00001100
. Enligt min mening är den ”en dum och onödig funktion. Jag föreslår att du i din kod använder standard C-notering som0B00001100
när du vill använda binära konstantvärden. -
Jag ser att skriva
encoderPos --
istället förencoderPos--
som en perversion och föreslår att du tar bort det onödiga utrymmet. -
Eftersom du inte har visat
PinB()
avbrottshanteraren vet jag inte hur du skulle hanteraencoderPos++
fall. Att döma av koden som visas använder du en metod som inte hanterar studsningsfel och intermittenta fel korrekt. Jag föreslår att du använder avbrottsavbrott och en tillståndsmaskinsmetod, som illustreras i svar på:
• Läsning från en KY-040 roterande kodare med Digispark
• Hur läser man RPS för snabb rotation med statusändringsmetod i Arduino?
• Läs RC-mottagarkanaler med hjälp av Interrupt istället för PulseIn
• Kan externa avbrott ELLER ”d tillsammans” på ”328 (Uno)?
Svar
På mega läste du stiften med PINE.
Så din kod i PinA skulle ändras till:
reading = PINE & 0b00110000; if(reading == 0b00110000 && aFlag) { encoderPos --; bFlag = 0; aFlag = 0; } else if (reading == 0b00010000) bFlag = 1;
I hittade en bra diskussion som talade om maskeringen som krävs här: https://forum.arduino.cc/index.php?topic=561741.0