Problem mit Interrupt - Arduino hängt sich auf?

Hallo zusammen,

mein Ardunio Uno soll einen Getränkeautomaten steuern.
Es existieren 6 Taster, 6 Magnetventile für die unterschiedlichen Getränke und ein Durchflussmesser.

Auf Knopfdruck soll der Arduino festgelegte Mengen in Millilitern durch die Magnetventile lassen.
Die Taster funktionieren, alle Magnetventile funktionieren auch problemlos.

Für die Steuerung zum Öffnen und Schließen habe ich mir eine Funktion geschrieben (open_valve, siehe Quelltext), der 100 ms lang den Durchflussmesser ausliest und es in eine Wassermenge in ml umrechnet.

Diese Wassermenge wird in einer while-Schleife aufaddiert und mit der Zielwassermenge verglichen.

Nun habe ich folgendes Problem:
Das Aufaddieren funktionert, die while-Schleife wird auch ordnungsgemäß verlassen.
Allerdings geht es danach nicht mehr weiter - der Arduino hängt sich irgendwie auf.
Wenn ich das sei() und das cli() weglasse (und eine andere Abbruchbedingung in der while-Schleife einfüge) funktioniert alles.

Könnt Ihr mir einen Tipp geben?
Mache ich etwas falsch mit der Verwendung von sei() und cli()?!

P.S.: Bei der Umrechnung der Impulse auf die Wassermenge stimmt auch noch irgendwas nicht, aber das ist hier nicht das Problem..

Es folgt der komplette Quelltext.

//#######################################################
//### Cocktails. Version 1. coded by Bjoern. ###
//#######################################################

//DECLARE global Variables
//----------------
volatile int NbTopsFan;            //measuring the rising edges                      
int hallsensor = 2;                //The pin location of the sensor

const int v1Pin = 8;              // valve 1
const int v2Pin = 9;              // valve 2
const int v3Pin = 10;              // valve 3
const int v4Pin = 11;              // valve 4
const int v5Pin = 12;              // valve 5
const int v6Pin = 13;              // valve 6

const int buttonPin1 = 0;          // push button 1
const int buttonPin2 = 3;          // push button 2
const int buttonPin3 = 4;          // push button 3
const int buttonPin4 = 5;          // push button 4
const int buttonPin5 = 6;          // push button 5
const int buttonPin6 = 7;          // push button 6



//FUNCTION rpm
//just measures the rising and falling edge of the hall effect sensors signal. 
void rpm ()
{ 
  NbTopsFan++;
} 

//FUNCTION setup
//method runs once, when the sketch starts
void setup()
{ 
  pinMode(hallsensor, INPUT);       //initializes digital pin from hallsensor as an input 
  Serial.begin(9600);               //This is the setup function where the serial port is initialised,
  attachInterrupt(0, rpm, RISING);  //and the interrupt is attached
  
  pinMode(v1Pin, OUTPUT);           // valve 1
  pinMode(v2Pin, OUTPUT);           // valve 2
  pinMode(v3Pin, OUTPUT);           // valve 3
  pinMode(v4Pin, OUTPUT);           // valve 4
  pinMode(v5Pin, OUTPUT);           // valve 5
  pinMode(v6Pin, OUTPUT);           // valve 6
  
  pinMode(buttonPin1, INPUT);       // push button 1
  digitalWrite(buttonPin1, HIGH);   // pull up for push button 1 
  pinMode(buttonPin2, INPUT);       // push button 2
  digitalWrite(buttonPin2, HIGH);   // pull up for push button 2
  pinMode(buttonPin3, INPUT);       // push button 3
  digitalWrite(buttonPin3, HIGH);   // pull up for push button 3
  pinMode(buttonPin4, INPUT);       // push button 4
  digitalWrite(buttonPin4, HIGH);   // pull up for push button 4
  pinMode(buttonPin5, INPUT);       // push button 5
  digitalWrite(buttonPin5, HIGH);   // pull up for push button 5
  pinMode(buttonPin6, INPUT);       // push button 6
  digitalWrite(buttonPin6, HIGH);   // pull up for push button 6
  
  delay(1000);
  //test_valves();
  
  Serial.print ("finished setup.");
} 

//FUNCTION open_valve
//valvePin      = ID of the valve to be opened
//liq_amount    = amount of liquid in ml
void open_valve(int valvePin, float liq_amount)
{
    float liq_count = 0;
    float liq_count_in_ml = 0;
    float liq_count_converted = liq_amount / 0.225;
    
    Serial.print ("open_valve called!\n");
    Serial.print ("opening valve no. ");
    Serial.print(valvePin);
    Serial.print (" for ");
    Serial.print(liq_amount);
    Serial.print (" ml..\n\n");

    
    digitalWrite(valvePin, HIGH);   //open the valve
    
    while (liq_count <= liq_count_converted)
    //while (liq_count <= 10)
    {
      NbTopsFan = 0;    //Set NbTops to 0 ready for calculations
      sei();            //Enables interrupts
      delay (100);      //Wait 100 miliseconds
      cli();            //Disable interrupts
      
        //Anzahl Pulse in 10 Millisekunden * 10 um auf 1 Sekunde zu kommen und durch 5. 
        //      Sensor = 5000 Impulse pro Liter, also 5 Impulse pro Mililiter.
        liq_count = liq_count + NbTopsFan;
        liq_count_in_ml = liq_count_in_ml + (liq_count/4.5);
       
        Serial.print (liq_count_in_ml);
        Serial.print (" ml - liq_count: ");
        Serial.print (liq_count);
        Serial.print (" liq_count_converted: ");
        Serial.print (liq_count_converted);
        Serial.print ("\n");
    } 
    
    digitalWrite(valvePin, LOW);   //close the valve

    delay(1000);   
    Serial.print ("\nvalve closed.\n");
}

void cocktail1()
{
    Serial.print ("push button 1 was pressed for 0.5 seconds!\n");
    Serial.print ("starting to mix cocktail number 1..\n");
    Serial.print ("-----------------------------------\n\n");
    open_valve(v1Pin, 30);     //open valve 1 for 30 ml
    open_valve(v2Pin, 30);    
    open_valve(v3Pin, 30);     
    open_valve(v4Pin, 30);     
    open_valve(v5Pin, 30);    
    open_valve(v6Pin, 30);     
    
}

void test_valves()
{
  Serial.println("testing the valves..\n");
  
  Serial.println("VALVE1..\n");
  delay(500);
  digitalWrite(v1Pin, HIGH);   //open the valve
  Serial.println("VALVE2..\n");
  delay(500);
  digitalWrite(v1Pin, HIGH);   //open the valve
  Serial.println("VALVE3..\n");
  delay(500);
  digitalWrite(v3Pin, HIGH);   //open the valve
  Serial.println("VALVE4..\n");
  delay(500);
  digitalWrite(v4Pin, HIGH);   //open the valve
  Serial.println("VALVE5..\n");
  delay(500);
  digitalWrite(v5Pin, HIGH);   //open the valve
  Serial.println("VALVE6..\n");
  delay(500);
  digitalWrite(v6Pin, HIGH);   //open the valve
  delay(1000);
  
  digitalWrite(v1Pin, LOW);   //close the valve
  digitalWrite(v2Pin, LOW);   //close the valve
  digitalWrite(v3Pin, LOW);   //close the valve
  digitalWrite(v4Pin, LOW);   //close the valve
  digitalWrite(v5Pin, LOW);   //close the valve
  digitalWrite(v6Pin, LOW);   //close the valve
}

//FUNCTION loop 
//method runs over and over again, as long as the arduino has power
void loop ()    
{
  int status_button1 = digitalRead(buttonPin1);   // read push button 1
  int status_button2 = digitalRead(buttonPin2);   // read push button 2
  int status_button3 = digitalRead(buttonPin3);   // read push button 3
  int status_button4 = digitalRead(buttonPin4);   // read push button 4
  int status_button5 = digitalRead(buttonPin5);   // read push button 5
  int status_button6 = digitalRead(buttonPin6);   // read push button 6
  
  int button_delay = 0;
  while (status_button1 == HIGH)
  {
    button_delay++;
    delay(100);
    Serial.println("holding button 1..\n");
    status_button1 = digitalRead(buttonPin1);
    if (button_delay >= 4)
    {
        cocktail1();
    }
  }
}

:wink:

Nachdem cocktail1 ausgeführt wurde, ist status_button1 immer noch HIGH und button_delay immer noch >= 4
Bist du sicher dass du mit diesem Code aus der while Schleife rauskommst Nachtrag:, ohne 2 Cocktails hintereinander zu mixen ?
...( Evtl. hab ich noch was übersehen )

Das hat mit interrupts nichts zu tun.
Brauchst du überhaupt einen Durchflussmesser ? Zeitmessung ist einfacher...

Edit: na gut , nach dem 2. cocktail ist wohl Schluss, wenn man den Knopf während der Herstellung des ersten endlich loslässt.

burnz3000:

const int buttonPin1 = 0;          // push button 1

...
Serial.begin(9600);               //This is the setup function where the serial port is initialised,




Könnt Ihr mir einen Tipp geben?

Es ist überhaupt keine gute Idee, dem RX-Pin (Pin-0!) der seriellen Schnittstelle als Doppelfunktion noch die Aufgabe eines Button-Pins zu geben.

Pin-0 kannst Du notfalls als Button-Pin verwenden, wenn die serielle Schnittstelle im Programm nicht verwendet wird.

Aber wenn Du die serielle Schnittstelle verwendest, dann sollte der RX-Pin auch nur als RX-Pin zum Empfang serieller Zeichen verwendet werden.

@michael_x:
Ja, im Moment werden 2 Cocktails hintereinander gemixt - das ist aber nicht das Problem :wink:
Der ein oder andere Fehler ist noch drin, ich weiss :slight_smile:
Durchflussmesser nutze ich, weil die Flüssigkeiten nur mit dem hydrostatischen Druck durch die Leitungen gehen. Je nach dem wie die Füllhöhe ist fließen die Flüssigkeiten mit unterschiedlichen Geschwindigkeiten.
Rein über die Zeit zu gehen wäre mir zu ungenau :slight_smile:

@jurs:
Das mit dem Pin 0 werde ich mal ändern, gut zu wissen.

Mein Problem ist ja, dass ich per serielle Schnittstelle den Arduino die ganze Zeit auslese.
Und nach dem ersten Aufruf von der open_valve - Funktion macht der Arduino nichts mehr, und gibt auch nichts mehr über die serielle Schnittstelle aus.
Am Ende der Funktion steht ja Serial.print ("\nvalve closed.\n");. Das "valve closed" bekomme ich gar nicht mehr..

Sonst noch wer ne Idee? :expressionless:

Wieso machst du da sei() und dann cli()? Die Interrupts sind standardmäßig schon aktiviert. Normalerweise benutzt man diese Funktionen anders herum, um zu verhindern dass Code unterbrochen wird.

EDIT:
Ich glaube du willst da zählen wie viele Impulse in einer Zeit ankommen. Dafür gibt es extern getaktete Timer/Counter.

Schau mal ins Datenblatt auf Seite 137:

Dafür muss man den 16-bit Timer1 verwenden und die Taktquelle an T1 anschließen (Pin 5 beim UNO). Man aktiviert dann den Timer für 100ms (am Ende der Zeit einfach die ClockSelect Bits auf 000 setzen) und liest danach den Inhalt des Counter-Registers TCNT1 aus.

Die Bits in den Registern zu setzen geht einfach so:
TCCR1B = (1<<CS12) | (1<<CS11) | (1<<CS10);

Dann wird der Timer mit der steigenden Flanke von T1 getriggert (fallend geht mit CS12 und CS11 auf 1).

Um ihn zu Deaktivieren das machen:
TCCR1B = 0;

Und das Ergebnis kann man so auslesen:
unsigned int result = TCNT1;

Danach muss man das Register auch wieder auf 0 setzen.

Siehe auch hier:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Zähler_des_AVR
(wobei da dieser Modus nicht explizit erklärt ist, aber man sieht die Syntax für das setzen der Bits)

Kann sein, dass ich was übersehen haben, aber prinzipiell sollte das so gehen. Ist z.B. eine einfache Möglichkeit Frequenzen zu messen.

Vielen Dank für die Tipps!

Nun habe ich sei() und cli() verstanden.
Ich habs nun weggelassen, nun funktioniert alles wie gewünscht.

Die Timer-Lösung habe ich mir auch angeschaut. Allerdings bin ich mit der Interrupt-Lösung sehr zufrieden, ich lasse es nun so.

Danke!!

Der Arduino hat sich sicherlich aufgehängt weil du mit cli() die Interrupts für die meiste Zeit des Programms abgeschaltet hast. Da läuft z.B. im Hintergrund ein Timer, der in in einem Interrupt hochgezählt wird und für millis() und delay() gebraucht wird.