Interrupts auf Pin 12 und 13 geben falsches Ergebnis

Hallo zusammen,

in meiner letzten Frage (Drucker Optical Encoder auslesen) habe ich versucht mittels Interrupts das Signal von einem Drucker Encoder auszulesen. Das funktioniert auf den Pins 8 und 9 auch wunderbar mit diesem Code und liefert realistische Ergebnisse:

#include <avr/interrupt.h> // AVR interrupt handling
#include <Arduino.h>       // Arduino core library
#include <stdint.h>        // Standard integer types
#include <stdbool.h>       // Standard boolean types

#define ENC_PIN_A 8
#define ENC_PIN_B 9

volatile uint8_t OldState = 0;           // Store the previous state of the encoder pins
volatile int32_t Position = 0;           // Store the current position value
volatile bool InvalidInc = false;        // Flag indicating an invalid increment
const int8_t IncArray[4][4] = {
  {0, -1, 1, 2},  // Lookup table for valid increments based on state transitions
  {1, 0, 2, -1},
  {-1, 2, 0, 1},
  {2, 1, -1, 0}
};

void setup() {
  Serial.begin(9600); // Initialize serial communication
  pinMode(ENC_PIN_A, INPUT);  // Configure digital pin 8 as input
  pinMode(ENC_PIN_B, INPUT);  // Configure digital pin 9 as input
  
  cli(); // Disable interrupts
  PCICR |= (1 << PCIE0); // Enable Pin Change Interrupt for PORT B (PCIE0)
  PCMSK0 |= (1 << PCINT0) | (1 << PCINT1); // Enable interrupt for PCINT0 (D8) and PCINT1 (D9)
  sei(); // Enable interrupts
}

void loop() {
  delay(100); // Delay for readability
  Serial.println(Position, DEC); // Print the current position in decimal
  
  if (InvalidInc) {
    Serial.println("LOST COUNT"); // Print a message indicating a lost count
    InvalidInc = false; // Reset the invalid increment flag
  }
}

ISR(PCINT0_vect) {
  uint8_t State = PINB & 0b00000011; // Read the state of the encoder pins
  int8_t Inc = IncArray[OldState][State]; // Get the increment value from the lookup table
  OldState = State; // Update the previous state
  Position += Inc; // Update the position value
  
  // Set the InvalidInc flag if the increment is 2
  InvalidInc |= (Inc == 2);
}

Nun möchte ich allerdings ein CNC Shield zur Ansteuerung eines Stepper Motors verwenden. Dadurch sind die Pins 8 und 9 aber nicht mehr frei. Aus einem Pinout des Shields (siehe: https://courses.ideate.cmu.edu/16-376/s2020/ref/text/hardware/cnc-shield.html#arduino-pin-assignments) habe ich gelesen, dass ich die Pins 12 und 13 nutzen könnte, da ich deren Shield Funktion nicht benötige. Also habe ich den Code folgendermaßen angepasst und vorerst ohne das Shield getestet:

#include <avr/interrupt.h> // AVR interrupt handling
#include <Arduino.h>       // Arduino core library
#include <stdint.h>        // Standard integer types
#include <stdbool.h>       // Standard boolean types

#define ENC_PIN_A 12
#define ENC_PIN_B 13

volatile uint8_t OldState = 0;           // Store the previous state of the encoder pins
volatile int32_t Position = 0;           // Store the current position value
volatile bool InvalidInc = false;        // Flag indicating an invalid increment
const int8_t IncArray[4][4] = {
  {0, -1, 1, 2},  // Lookup table for valid increments based on state transitions
  {1, 0, 2, -1},
  {-1, 2, 0, 1},
  {2, 1, -1, 0}
};

void setup() {
  Serial.begin(9600); // Initialize serial communication
  pinMode(ENC_PIN_A, INPUT);  // Configure digital pin 8 as input
  pinMode(ENC_PIN_B, INPUT);  // Configure digital pin 9 as input
  
  cli(); // Disable interrupts
  PCICR |= (1 << PCIE0); // Enable Pin Change Interrupt for PORT B (PCIE0)
  PCMSK0 |= (1 << PCINT4) | (1 << PCINT5); // Enable interrupt for PCINT4 (D12) and PCINT5 (D13)
  sei(); // Enable interrupts
}

void loop() {
  delay(100); // Delay for readability
  Serial.println(Position, DEC); // Print the current position in decimal
  
  if (InvalidInc) {
    Serial.println("LOST COUNT"); // Print a message indicating a lost count
    InvalidInc = false; // Reset the invalid increment flag
  }
}

ISR(PCINT0_vect) {
  uint8_t State = PINB & 0b00110000; // Read the state of the encoder pins
  int8_t Inc = IncArray[OldState][State]; // Get the increment value from the lookup table
  OldState = State; // Update the previous state
  Position += Inc; // Update the position value
  
  // Set the InvalidInc flag if the increment is 2
  InvalidInc |= (Inc == 2);
}

Jetzt lese ich allerdings Werte aus dem Encoder aus, die um ein Vielfaches höher sind, als noch mit Pin 8 und 9. Gleiches passiert übrigens auch mit anderen Pin Konfigurationen. Es kann doch nicht sein, dass nur Pin 8 und 9 für meinen Zweck gültig sind.

Ich hoffe, dass mir jemand weiterhelfen kann.

Viele Grüße
Nico

hast du irgendwo erwähnt welches Board du hast?

Sorry, habe ich vergessen. Ich habe ein Arduino Uno Rev3.

Update: Ich habe gerade einfach mal die Anzahl an Interrupts gezählt von Pin 8 und 9 vs. Pin 12 und 13 und die sind ungefähr gleich. Das Problem scheint damit in meiner Auswertung der Pins zu liegen. Also vermute ich, dass diese Zeile das Problem ist:

uint8_t State = PINB & 0b00110000; // Read the state of the encoder pins

Arduino PCINT (Pin Change Interrupts) fires each time a pin state is changed. Pins in the same port will fire the same interrupt signal if any of them has changed. Therefore, it’s our responsibility to save the pin states and compare them each time a PCINT interrupt is received to figure out which pin has changed.

das heißt man muss im Program nix ändern um von 8/9 auf 12/13 um zu steigen. und wenn an den anderen pins etwas angeschlossen ist werden die auch ISR auslösen. oder?

ISR(PCINT0_vect) {
  uint8_t State = (PINB & 0b00110000)>>4; // Read the state of the encoder pins
  if (OldState != State) {
    int8_t Inc = IncArray[OldState][State]; // Get the increment value from the lookup table
    OldState = State; // Update the previous state
    Position += Inc; // Update the position value

    // Set the InvalidInc flag if the increment is 2
    InvalidInc |= (Inc == 2);
  }
}

Danke für deine Hilfeversuche, aber anscheinend ist mir nicht mehr zu helfen :smiley:

Das Problem ist, dass ich hier uint8_t State = PINB & 0b00110000; zwar die richtigen Pins auslese, aber das Ganze trotzdem ein 8 Bit Integer ist. Dementsprechend muss ich das noch Shiften.

Hiermit bekomme ich keine zu großen Werte raus: uint8_t State = (PINB >> 4) & (0b00110000 >> 4);

Die Pins 12/13 werden auch für SPI benutzt, das könnte zu Konflikten führen.

Die möglichen PCINT kannst Du in PCMSKx nachschauen. Auf die berechnete Position sollte das keinen Einfluß haben, wenn gleichbleibende Zustände der Eingänge keine Zählung auslösen.

Hier liegt der Hund begraben! Der State muß durch Rechtsshift der eingelesenen Bits in den Bereich 0-3 gebracht werden, sonst wird Murks irgendwo aus dem Speicher für Inc gelesen.

ah, stimmt, du nutzst pins als Indexzahl, dann

uint8_t State = (PINB>>4) & 0b11;

im CNC wird SPI nicht genutzt

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.