Wieso funktioniert interrupt bei mir nicht?

Hallo, ich möchte beim Wechsel an dem pin1, dass sich die Spannung die der DAC ausgibt ändert. Ich änderung simuliere ich zur Zeit noch in dem ich auf dem Steckbrett zwischen GND und 3 V umschalte. Das ist mein Code (ich verwende den Nano Every):

#include <Wire.h>
#include <MCP4726.h>
float vref = 4840;
MCP4726 dac;

volatile int x = 0;
int pin1 = A0;
int pin2 = A1;
int pin3 = A2;

void fall() {
  dac.setVoltage(x);
  x += 500;
  if (x >= 4096) {
    x = 0;
  }
}



void setup() {
  // For MCP4726 the address is 0x60 (A0 part address. Check MCP4726 part number suffix as address could be different)
  dac.begin(MCP4726_DEFAULT_ADDR);
  pinMode(12, OUTPUT),
  digitalWrite(12, HIGH);
  Serial.begin(9600);
  pinMode(pin1,INPUT);
  attachInterrupt(digitalPinToInterrupt(pin1), fall, CHANGE);
}

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Oh tut mir leid, sorry!

Wie kommst du auf die Idee, dass A0 einen Hardware Interrupt kennt?
Warum überhaupt Interrupts?

Übrigens:
In einer ISR sind Interrupts erstmal abgeschaltet.
Dabei braucht Wire Interrupts.
Die Folge: Kein I2C in ISRs

Wie kommst du auf die Idee, dass A0 einen Hardware Interrupt kennt?
Warum überhaupt Interrupts?

Ich dachte beim Every kann das jeder Pin...
Ich habe eine Schaltung, bei der Steuersignale sich ändern. je nach Signal soll ein anderer DAC-Wert geschrieben werden.

Die Folge: Kein I2C in ISRs

Kann ich das also überhaupt nicht umsetzten oder gibt es da einen Trick?

was ist das genaue Kriterium für " Wechsel am pin1" nur digitales 0/1 oder misst du Analog?

es geht nur um ein digtales 0/1. Später sollen so drei Steuersignale ausgewertet werden, wenn sich eines davon ändert und dann je nach Kombination ein bestimmter Spannungswert aus einem array ausgegeben werden

Beim Every sollten nach Doku alle digitalen GPIOs interruptfähig sein ...

Unabhängig davon wäre zu klären, ob ein Interrupt für den gewünschten Zweck notwendig und sinnvoll ist (es sei denn der Weg ist das Ziel :wink: ) ...

Wenn sonst nichts Wesentliches passiert, das dazu führen könnte, eine fallende Flanke zu "übersehen", kann man das Detektieren in der loop() umsetzen.

Die Umschaltung des dac läuft über I2C und das geht dann eh nur ausserhalb der ISR...

Wenn Du unbedingt eine ISR nutzen willst, wäre das Vorgehen in etwa so:

  • In setup() dac auf den Wert aktuelleSpannung setzen und zusätzlich als letzteSpannung speichern
  • In loop() prüfen, ob (aktuelleSpannung != letzteSpannung)
    • wenn ja: dac auf aktuelleSpannung setzen und letzteSpannung = aktuelleSpannung setzen
  • In der ISR ausschliesslich den Wert aktuelleSpannung anpassen

Beim Auslesen der Variablen aktuelleSpannung sicherstellen, dass kein Interrupt dazwischenfunkt.

Allerdings sollte das Ganze auch gut komplett in der loop() laufen ....

P.S.: Wenn die Variable in der ISR aktuelleSpannung heißt, sollte man in der loop() diesen Wert in eine anderslautende Variable "umkopieren"!

1 Like

Die Information war bisher geheim!
Warum auch immer .....

Gut!
Und wozu dann Interrupts?

So nicht!

Und ja, Tricks mag es geben....
Aber für eine solche triviale Geschichte in die "Dirty Hack Kiste" greifen? naja...

Vielleicht helfen dir ja Begriffe, wie "Event Queue" weiter

Die Intterrupts brauche ich, weil ich in der Main-Loop verschieden Spannungswerte einge und in arrays speichere. Währendessen ändern sich die drei Steuersignale ständig und schnell. Bei jedem ändern soll die Kombination der Signale aus HIGH und LOW ausgewertet werden und dann eine entsprechende Spannung am DAC ausgegeben werden

Wenn du meinst.......
Und da nicht von runter möchtest .....

Was verstehst Du unter schnell? Im millisekundenbereich? Kann der DAC über I2C überhaupt so schnell angesteuert werden? Dass Du ihn nicht in der Interruptroutine ansteuern kannst, wurde Dir ja schon gesagt. Do musst also nach der Erkennung der Änderung sowieso wieder in den loop().

Erzähle doch mal genauer, was das Ganze werden soll und wie die Randbedingungen sind.

Gruß Tommy

Das mit den Interrupts klingt meist einfacher, als es sich im wahren Leben darstellt.

Wenn die Änderungen schneller kommen, als die loop() sie abarbeiten kann, muss der Code das behandeln können. Und sehr häufige Interrupts beeinflussen die Abläufe auf dem Controller natürlich zusätzlich.

Insofern wäre es hilfreich für eine Beurteilung der Auslegung, wenn Du das Timing der erwarteten Änderungen darstellen könntest.

Ca. alle 80 ms

Ich poste hier einfach mal meinn kompletten Code:

#include <Wire.h>
#include <MCP4726.h>

MCP4726 dac;

// name pins
const byte ADMUX0 = A0;
const byte ADMUX1 = A1;
const byte ADMUX2 = A2;

int SW = 9;               // Switch for VDD
int LED = 10;             // LED       
int MUXmain = 12;         // Multiplexer on mainboard 
int vdd_pin = A7;

volatile int index;  // variable for binary 

float vref;


const byte NUM_CHANNELS = 8; //Number of Channels
volatile float voltages[NUM_CHANNELS];  //array for set voltages

byte calculateIndex(byte pin0, byte pin1, byte pin2) {
  return (digitalRead(pin0) ? 1 : 0) * 1 + (digitalRead(pin1) ? 1 : 0) * 2 + (digitalRead(pin2) ? 1 : 0) * 4;
}

void printncompare() {
  const char* channelNames[] = {"TC", "O2", "CO", "PRESS", "COLD", "TEMP_O2", "TEMP_CO", "TEMP_PRESS"};
  Serial.println("Current set voltages of all channels:");
  
  for (int i = 0; i < NUM_CHANNELS; i++) {
    Serial.print(channelNames[i]);
    Serial.print(": ");
    Serial.print(voltages[i], 4);
    Serial.println(" V");
  } 
  Serial.println();
}

int current_chnnel;

void chng_mux() {
  index = calculateIndex(ADMUX0, ADMUX1, ADMUX2);
  dac.setVoltage(voltages[index]);
  current_channel = index;
}

void set_val_zero() {
  volatile float voltages[NUM_CHANNELS] = {1270, 2274, 1225, 1225, 987.7, 1225, 1225, 1225};
}

void prepareSim() {
  Serial.println("Device connected");
  digitalWrite(LED, HIGH);
  digitalWrite(MUXmain, HIGH);
}

void setup() {
  pinMode(ADMUX0, INPUT);
  pinMode(ADMUX1, INPUT);
  pinMode(ADMUX2, INPUT);
  pinMode(vdd_pin, INPUT);
  Serial.begin(9600);
  attachInterrupt(ADMUX0, chng_mux, CHANGE);
  attachInterrupt(ADMUX1, chng_mux, CHANGE);
  attachInterrupt(ADMUX2, chng_mux, CHANGE);
  dac.begin(MCP4726_DEFAULT_ADDR);
}

void loop() {
  float vdd_value = analogRead(vdd_pin);
  vref = vdd_value * (5.0 / 1024.0); //Caution: The value '5.0' must be adjusted if the operating voltage is not equal to 5.0 V
    if (vdd_value > 3.0) {
      prepareSim();
      Serial.println("Which signal should be changed?");
      
      while (Serial.available() == 0); // Überprüfung, ob Daten über die serielle Schnittstelle verfügbar sind
      char input[20];  // Array zur Speicherung der Eingabe
      Serial.readStringUntil('\n').toCharArray(input, sizeof(input));  // Lesen der seriellen Daten in den char-Array
      while (Serial.available() > 0) Serial.read();  // das leert den Buffer
      
      const char* channelNames[] = {"TC", "O2", "CO", "PRESS", "COLD", "TEMP_O2", "TEMP_CO", "TEMP_PRESS"};
      int channelIndex = -1;
      
      for (int i = 0; i < sizeof(channelNames) / sizeof(channelNames[0]); i++) {
        if (strcmp(input, channelNames[i]) == 0) {
          channelIndex = i;
          break;
      }
    }
    
    if (channelIndex != -1) {
      Serial.println("Which voltage value should be set?");  // Prompt User for input
      while (Serial.available() == 0);
      float voltage = Serial.parseFloat();
      while (Serial.available() > 0) Serial.read();  // das leert den Buffer
      
      if (voltage <= vref) {
        voltages[channelIndex] = voltage;
      } else {
        Serial.println("Error. Voltage value exceeds Vref.");
      }
    } else {
      Serial.println("Error. Unknown signal");
    }
    printncompare();
    }
    else {
      digitalWrite(LED, LOW);
      digitalWrite(SW, LOW);
      Serial.println("Measuring device is not connected!");
      Serial.println(vdd_value);
      delay(1500);
    }
     
}```

Die Fragen zu beantworten hast Du nicht nötig?

Gruß Tommy

Das sind Welten für einen Microprozessor. Da hat er sich die Däumchen schon wundgedreht ( wenn er nichts anders zu tun hätte :innocent: ). Ein ordentlich programmierter Loopdurchlauf ist wesentlich schneller.
Etwas mehr Info zu dem ganzen Projekt wäre schon hilfreich.

Oha, wenn ich mich täusche, liegt da ja noch einiges im Argen ... :wink:

Mal losgelöst von der Interrupt-Frage, hier das, was mir so aufgefallen ist:

#include <Wire.h>
#include <MCP4726.h>

MCP4726 dac;

// name pins
const byte ADMUX0 = A0;
const byte ADMUX1 = A1;
const byte ADMUX2 = A2;

/************************************************************
Die folgende Deklarationen werden während des Programmlaufs
nicht verändert. Sie könnten wie die Pins oben als
const byte bzw. constexpr byte festgelegt werden.

Die Pins SW, LED und MUXMain werden als OUTPUT genutzt,
jedoch im setup() nicht per pinMode() als OUTPUT
konfiguriert. 
************************************************************/

int SW = 9;               // Switch for VDD
int LED = 10;             // LED       
int MUXmain = 12;         // Multiplexer on mainboard 
int vdd_pin = A7;

/************************************************************
  volatile erzwingt das regelmässige komplette Einlesen eines Wertes
  aus der zugeordneten Adresse und ist z.B. sinnvoll für Portzugriffe,
  deren Wert sich "von außen" ändern kann. Es verhindert aber nicht(!), 
  dass ein Interrupt dazwischenfunkt. Dafür braucht es das ATOMIC_BLOCK Macro
  siehe z.B. hier https://www.mikrocontroller.net/topic/170451
 ************************************************************/
volatile int index;  // variable for binary 

float vref;


const byte NUM_CHANNELS = 8; //Number of Channels
/************************************************************
Zu volatile siehe oben
************************************************************/
volatile float voltages[NUM_CHANNELS];  //array for set voltages

byte calculateIndex(byte pin0, byte pin1, byte pin2) {
  return (digitalRead(pin0) ? 1 : 0) * 1 + (digitalRead(pin1) ? 1 : 0) * 2 + (digitalRead(pin2) ? 1 : 0) * 4;
}

void printncompare() {
/************************************************************
Die channelNames[] werden weiter unten erneut verwendet. Wäre es 
ggf. sinnvoll, dieses Array global zu definieren,
damit man z.B.  Änderungen nur einer Stelle vornehmen muss?
************************************************************/
  const char* channelNames[] = {"TC", "O2", "CO", "PRESS", "COLD", "TEMP_O2", "TEMP_CO", "TEMP_PRESS"};
  Serial.println("Current set voltages of all channels:");
  
  for (int i = 0; i < NUM_CHANNELS; i++) {
    Serial.print(channelNames[i]);
    Serial.print(": ");
    Serial.print(voltages[i], 4);
    Serial.println(" V");
  } 
  Serial.println();
}

/************************************************************
current_channel oder current_chnnel
************************************************************/
int current_chnnel;

void chng_mux() {
  index = calculateIndex(ADMUX0, ADMUX1, ADMUX2);
  dac.setVoltage(voltages[index]);
/************************************************************
current_channel oder current_chnnel? So compiliert es nicht ...
************************************************************/
  current_channel = index;
}

/************************************************************
  volatile float voltages[NUM_CHANNELS] = {1270, 2274, 1225, 1225, 987.7, 1225, 1225, 1225};
  a) Erzeugt ein temporäres Array, das nach dem Aufruf von set_val_zero() wieder
     in Vergessenheit gerät
  b) Die Werte scheinen direkte Analogwerte zu sein und keine Spannungen in Volt,
     obwohl ein gleichnamiges Array an anderer Stelle offensichtlich für das Speichern 
     von z.B. 3.3 oder 5.0  dienen soll
  c) Alles nicht  so schlimm, da die Funktion eh nirgends aufgerufen wird.

  Es lässt aber Zweifel aufkommen, was denn nun tatsächlich in einem
  voltages[] verwendet werden soll.
************************************************************/
void set_val_zero() {
  volatile float voltages[NUM_CHANNELS] = {1270, 2274, 1225, 1225, 987.7, 1225, 1225, 1225};
}

void prepareSim() {
  Serial.println("Device connected");
  digitalWrite(LED, HIGH);
  digitalWrite(MUXmain, HIGH);
}

void setup() {
  pinMode(ADMUX0, INPUT);
  pinMode(ADMUX1, INPUT);
  pinMode(ADMUX2, INPUT);
  pinMode(vdd_pin, INPUT);
  Serial.begin(9600);
  attachInterrupt(ADMUX0, chng_mux, CHANGE);
  attachInterrupt(ADMUX1, chng_mux, CHANGE);
  attachInterrupt(ADMUX2, chng_mux, CHANGE);
  dac.begin(MCP4726_DEFAULT_ADDR);
}

void loop() {
  float vdd_value = analogRead(vdd_pin);
/************************************************************
   vdd_value ist offensichtlich ein Wert zwischen 0 und 1023
   wie auch die Ableitung von vref verdeutlicht.
************************************************************/
  vref = vdd_value * (5.0 / 1024.0); //Caution: The value '5.0' must be adjusted if the operating voltage is not equal to 5.0 V
/************************************************************
   Hier wird vdd_value nun jedoch gegen 3.0 verglichen?
   Sollte das hier möglicherweise vref heißen???
 ************************************************************/
    if (vdd_value > 3.0) {
      prepareSim();
      Serial.println("Which signal should be changed?");
      
      while (Serial.available() == 0); // Überprüfung, ob Daten über die serielle Schnittstelle verfügbar sind
      char input[20];  // Array zur Speicherung der Eingabe
      Serial.readStringUntil('\n').toCharArray(input, sizeof(input));  // Lesen der seriellen Daten in den char-Array
      while (Serial.available() > 0) Serial.read();  // das leert den Buffer
/************************************************************
Zu channelNames[] siehe oben
************************************************************/
      
      const char* channelNames[] = {"TC", "O2", "CO", "PRESS", "COLD", "TEMP_O2", "TEMP_CO", "TEMP_PRESS"};
      int channelIndex = -1;
      
      for (int i = 0; i < sizeof(channelNames) / sizeof(channelNames[0]); i++) {
        if (strcmp(input, channelNames[i]) == 0) {
          channelIndex = i;
          break;
      }
    }
    
    if (channelIndex != -1) {
/************************************************************
Hier wäre noch eine Zeile wie
     Serial.print(channelNames[channelIndex]);Serial.print("\t");
hilfreich, um zu sehen, welcher Kanal akzeptiert wurde
************************************************************/      
      Serial.println("Which voltage value should be set?");  // Prompt User for input

/*********************************************************
Das folgende Konstrukt geht jedenfalls in der Wokwi-Simulation
regelmäßig schief.

So funktioniert es dort

      float voltage = 0;
      while (voltage == 0){
        voltage = Serial.parseFloat();
      }
Allerdings sollte/muss man auf blockierende Funktionen
verzichten, da sich die Interuptroutine nicht dazu eignet,
serielle oder I2C-Schnittstellen zu verwenden, wie es
hier benötigt wird. 	  	  	  
************************************************************/ 
      while (Serial.available() == 0);
      float voltage = Serial.parseFloat();
      while (Serial.available() > 0) Serial.read();  // das leert den Buffer
      
      if (voltage <= vref) {
        voltages[channelIndex] = voltage;
      } else {
        Serial.println("Error. Voltage value exceeds Vref.");
      }
    } else {
      Serial.println("Error. Unknown signal");
    }
    printncompare();
    }
    else {
      digitalWrite(LED, LOW);
      digitalWrite(SW, LOW);
      Serial.println("Measuring device is not connected!");
/************************************************************
   Hier wird vdd_value ausgegeben. 
   Sollte das hier möglicherweise auch vref heißen???
 ************************************************************/
      Serial.println(vdd_value);
      delay(1500);
    }
     
}


Kann mich natürlich an der einen oder anderen Stelle täuschen, aber vielleicht hilft's ja weiter.

Da sehr viele blockierende Funktionen (delay(1500), while (Serial.available() == 0); usw.) im Einsatz sind, muss das ganze Konzept noch mal überdacht werden. Man kann seriellen Input auch nicht-blockierend programmieren. Das ist auf jeden Fall zu empfehlen!

Funktionen wie Serial.parseFloat(); sind dabei hinderlich. Besser in der loop alle Zeichen sammeln, bis ein Abschluss erkannt wird und dann den so erhaltenen String auswerten.

Da steckt jedenfalls noch Arbeit drin :wink:

Muß ich Dir widersprechen, War nur gut versteckt am Ende der Beschreibung.

Was bedeutet schnell?
unter µS ?

Grüße Uwe

Nagut!
Da habe ich bestimmt 3 mal nach gesucht!
Und wohl genau so oft übersehen....

Also:
Ab jetzt behaupte ich das Gegenteil!
Aber nur in dem Punkt, der Rest bleibt gültig.

Hat er in #14 auch schon beantwortet: ca. 80ms. Nichts, weshalb man einen Interrupt bräuchte.