Stören I2C-Bus-Befehle hardware-interrupts

Ich habe einen Rotary-Encoder mit 100 schritten pro Umdrehung auf die Pins 28 / 29 eines ESP32 gelegt und gleichzeitig mit 2 I2C-Bus-Expandern experimentiert.
Ein Expander liest Taster ein, der andere steuert LEDs.
Dabei ist mit aufgefallen, dass der Interrupt nur noch 10% bis 15% der Encoder-Impulse erkennt, wenn Schreib- und Lesebefehle in der Loop in schneller Folge an die I2C-Expander gesendet werden.
Der Rotary-Encoder wird absolut zuverlässig ausgelesen, wenn die I2C-Expander nicht mehr angesprochen werden.
Das Verhalten überrascht mich, da ich dachte, dass HW-Interrupts in jedem Falle Vorrang haben.
Was kann man machen, das HW-Interrupts nicht mehr durch I2C-Schreib- oder Lesebefehle gestört werden?

Für den Anfang würde es helfen, wenn Du hier Deinen ketch in Code Tags postest, damit wir sehen können, was Du machst.


// LCD-Ausgabe
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x3F for a 16 chars and 2 line display

#include <PCF8574_library.h>
#include <PCF8574.h>

// Set i2c HEX address für die beiden I2C-Expander
PCF8574 Expander_1(0x21);
PCF8574 Expander_2(0x25);

#define CLK_PIN 25  // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin
#define DT_PIN 26   // ESP32 pin GPIO26 connected to the rotary encoder's DT pin

volatile int counter;

float set_value;
float refresh_time;
char Text_Buffer[10];

bool Switch_State;
bool ready_to_toggle;
int Value;

void IRAM_ATTR ISR_encoder() {
  if (digitalRead(DT_PIN) == HIGH) {
    counter--;
  } else {
    counter++;
  }
}

void setup() {
  lcd.init();
  lcd.clear();         
  lcd.backlight(); 

  Expander_1.pinMode(P0, INPUT_PULLUP);
  Expander_2.pinMode(P0, OUTPUT);

  Expander_1.begin();
  Expander_2.begin();

  // configure encoder pins as inputs
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  
  // call ISR_encoder() when CLK pin changes from LOW to HIGH
  attachInterrupt(CLK_PIN, ISR_encoder, RISING );
  refresh_time = millis();
  set_value = 0;
  
  lcd.setCursor(0,0); lcd.print("set_value");

  Switch_State = false;
  ready_to_toggle = true;
}

void loop() 
{  
  Value = Expander_1.digitalRead(P0);  // Sobald diese Zeile aktiviert ist, werden nur noch ca. 70% der Encoder-Pulse ausgelesen

  if (Switch_State == LOW)
  {
    set_value = set_value + counter*0.01;
    counter = 0;
  }
  else
  {
    set_value = set_value + counter;
    counter = 0;
  }
     
  

  if ((millis() - refresh_time) > 200)
  {
    dtostrf(set_value,10,2,Text_Buffer);
    lcd.setCursor(0,1); lcd.print(Text_Buffer);
    refresh_time = millis();

    if ((Value == 0) && (ready_to_toggle == true)) {Switch_State = !Switch_State; ready_to_toggle = false;}
    if (Value == 1) {ready_to_toggle = true;}

  }

}

Danke für den Hinweis; ist mein erster Versuch mit dem Forum. Bin gespannt!

Wen ich mich nicht irre I²C nutzt selber Interrupts, da kann schon zu Kollision kommen.
Wurde auf SPI umsteigen ist viel Schneller als i²C, wenn der Encoder Probleme macht.

Ok SPI nutzt auch Interrupt nur bei 80MHz sollte das nicht so gravierend sein, als Display ein SPI Display, LCD1602 habe noch nie als SPI gesehen, wenn jedoch Pins reichen den als 4Bit ansteuern, ohne der I²C Platine
MCP23S17 kann auch SPI .

Wieso alle 200 mSec. ? Ich würde nur eine Ausgabe geben wenn sich was an deinem Wert geändert hat.
Ebenso würde ich nicht den Expander bei jeden Loop abfragen.
Ich bevorzuge den /INT Pin des PCF8574 auf ein Pin zu legen. diesen frage ich dann einfach ab. Hat dann der PCF den INT auf LOW frage ich den Baustein ab. So erhalte ich dann eine Minimalbelastung der Prozessorzeit.

1 Like

Mit MCP23S08:
https://werner.rothschopf.net/202009_arduino_liquid_crystal_spi_en.htm

Ok wieder was neues.