Timer Interrupt

Ich möchte ZWEI Timer, den ich Starte. dann läuft eine Zeit. Nach Ablauf der Zeit soll etwas ausgeführt werden. Es soll aber möglich sein, den Timer neuzustarten bevor er abgelaufen ist, sodass die Zeit wieder von neu läuft ohne das sonst etwas ausgeführt wird.
Und das ganz per Interrupt.

Hier habe ich mich schon mal versucht:

#include "TimerOne.h"
int alle_x_sekunden = 3;
int i = 0;
void setup()   {
  Serial.begin(9600);
  delay(2000);
  Serial.println("Starte...");
  
  int alle_x_sekunden = 5;
  Timer1.initialize(alle_x_sekunden * 1000000);
  Timer1.attachInterrupt(blinken);
  //Timer1.setPeriod(3000000);//3 sek
  //Timer1.stop();

}


void blinken() {
  Serial.println("Ausgeführt");
  if (i >= 1) { //Nur wenn Interrupt das 2te mal aufgerufen wurde, weil er beim Start direkt einmal schon durchfläuft

    //Mache etwas
    Serial.println("Stop");
    Timer1.stop();
    //Timer1.detachInterrupt();
  }
  i++;
}


void loop()    {
  Timer1.restart();
  Timer1.start();
  Serial.println("starte");
  delay(800);
}

Das Problem ist, das bei jedem Start der Interrupt schon 1x durch läuft.

Und wie ich noch einen zweiten Timer, der davon unabhänig ist mache weiß ich gar nicht.

Über welche Zeiten sprechen wir denn hier....
Doch im Sekundenbereich,...

Muss es denn unbedingt per Interrupt sein?
Und: Warum, wenn ja?

1 sekunde - 100ms das weiß ich noch nicht ganz genau.

Ich habe in der loop() ein Can Bus Shield was von meinem Auto ne menge Daten bekommt und das möchte ich so wenig wie möglich verlangsamen.

Hallo,
Nach einer ordentliche Dokumentation über Interrupts suche ich noch, will mich bald damit auseinandersetzen.
Ich kann dir nur diese 3 teilige Doku (über Multitasking) von Adafruit anbieten. (Teil 2 = Interrupt)

lg dony

aggro600:
Ich habe in der loop() ein Can Bus Shield was von meinem Auto ne menge Daten bekommt und das möchte ich so wenig wie möglich verlangsamen.

Dann sollte Dein erster Weg der Verzicht auf delay sein.

Gruß Tommy

Der delay ist ja nur um zu testen ob der interrupt klappt.

Ja, gut...
Aber das ist doch kein Grund für Interrupts...
(glaube ich)

Ich könnte dir einen Timer zur Verfügung stellen, welcher keinen zusätzlichen Interrupt nutzt.
Aber wovon man mehrere Instanzen gleichzeitig einsetzen kann und welcher Restart fähig ist.

BlinkWithSimpleTimer:

// Wieder eine Variation des BlinkWithoutDelay

#include "SimpleTimer.h"

SimpleTimer timer; // timer Instanz anlegen

void setup()
{
  pinMode(LED_BUILTIN,OUTPUT);
}

void loop()
{
  if(timer(1000)) // wenn abgelaufen
  { 
    digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
    timer.start();
  }
}

Die Klasse(SimpleTimer.h):

   class SimpleTimer
    {
      private:
      unsigned long timeStamp;      // Zeitmerker
      bool abgelaufen;              // default Status: timer abgelaufen
    
      public:
      SimpleTimer():timeStamp(0),abgelaufen(true){}
      
      void start()
      {
        timeStamp   = millis();
        abgelaufen  = false;
      }
    
      bool operator()(const unsigned long ablaufZeit) 
      {
        if(!abgelaufen) abgelaufen = millis() - timeStamp >= ablaufZeit;
        return abgelaufen;
      }
    };

dony:
Nach einer ordentliche Dokumentation über Interrupts suche ich noch,

Das Datenblatt deines Prozessors.

Hallo,

wenn es unbedingt mit Hardware-Timer sein soll, dann vielleicht so. Man schreibt sich die benötigten Funktionen und ruft sie an geeigneter Stelle auf. Wenn die "Zeit gestoppt" werden soll nachdem die Zeitspanne um ist, dann füge ohne Atomic {} die disable Comp Zeile in die zugehörige ISR ein. Ansonsten kannst du jederzeit die nächste Zeitspanne mit der enable Comp Funktion setzen. Wenn das ISR stoppen und neue Zeitspanne setzen getrennt erfolgen soll, dann zerteile die letzten beiden Funktionen. Aktuell werde beide seriellen Ausgaben jeweils für sich betrachtet immer im gleichen zeitlichen Abstand ausgegeben.

Ein anderes Bsp. ist hier: Arduin Timer0/Timer1 - #5 by Doc_Arduino - Deutsch - Arduino Forum

#include <util/atomic.h>

volatile bool flag_compA = false;
volatile bool flag_compB = false;

unsigned int count_CompA = 40000; // Zeitspanne
unsigned int count_CompB = 50000;

void setup() {
  Serial.begin(500000);
  preSet_Timer1();
  run_Timer1();
}

void loop() {

  if (flag_compA == true) {
    /*
      ...
      ... mach was sinnvolles
      ...
    */
    Serial.print('A'); Serial.print('\t'); Serial.println( millis() );
    flag_compA = false;
  }

  if (flag_compB == true) {
    /*
      ...
      ... mach was sinnvolles
      ...
    */
    Serial.print('B'); Serial.print('\t'); Serial.println( millis() );
    flag_compB = false;
  }

}

ISR(TIMER1_COMPA_vect)
{
  flag_compA = true;
}


ISR(TIMER1_COMPB_vect)
{
  flag_compB = true;
}


void preSet_Timer1 ()    // Normal Mode
{
  cli();        // Interrupts ausschalten
  TCCR1A = 0;   // Reset TCCR1A Register
  TCCR1B = 0;   // Reset TCCR1B Register
  TIMSK1 = 0;   // Reset TIMSK1 Register
  TCNT1 =  0;   // Start 0
  OCR1A = count_CompA;   // Compare A
  OCR1B = count_CompB;   // Compare B
  TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B); // enable Compare Match A&B ISR
  sei();        // Interrupts einschalten
}


void run_Timer1 ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TCCR1B |= (1 << CS12) | (1 << CS10); // Prescaler 1024
  }
}


void stop_Timer1 ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TCCR1B &= ~( (1 << CS12) | (1 << CS11) | (1 << CS10) );
  }
}


void disable_CompA ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TIMSK1 &= ~( (1 << OCIE1A) ); // disable Compare Match A Interrupt
  }
}


void disable_CompB ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TIMSK1 &= ~( (1 << OCIE1B) ); // disable Compare Match B Interrupt
  }
}


void enable_CompA ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    OCR1A = OCR1A + count_CompA;  // nächsten Compare Match setzen
    TIMSK1 |= (1 << OCIE1A);      // enable Compare Match A Interrupt
  }
}


void enable_CompB ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    OCR1B = OCR1B + count_CompB;  // nächsten Compare Match setzen
    TIMSK1 |= (1 << OCIE1B);      // enable Compare Match B Interrupt
  }
}

Hi

Wenn kein Interrupt benötigt wird, könntest Du per millis() dutzende Timer nutzen - zwar Alle dann nur in ms Auflösung und Alle mit gleichem Takt, aber so zu starten/pausieren/stoppen/rücksetzen wie Du Das willst/programmierst.

Meine CAN-Module haben einen INT-Pin, womit Diese dem Arduino signalisieren, daß 'was da ist'.

Was passiert bei Dir bzw. was für Daten bekommst Du?
(meine Module sind 8MHz, im Auto werden wohl 16MHz benötigt ... gefährliches Halbwissen :wink: )

MfG

postmaster-ino:
Hi

Wenn kein Interrupt benötigt wird, könntest Du per millis() dutzende Timer nutzen - zwar Alle dann nur in ms Auflösung und Alle mit gleichem Takt, aber so zu starten/pausieren/stoppen/rücksetzen wie Du Das willst/programmierst.

Meine CAN-Module haben einen INT-Pin, womit Diese dem Arduino signalisieren, daß 'was da ist'.

Was passiert bei Dir bzw. was für Daten bekommst Du?
(meine Module sind 8MHz, im Auto werden wohl 16MHz benötigt ... gefährliches Halbwissen :wink: )

MfG

Ich habe auch nur ein Modul mit 8 Mhz. Ich lese nur Tasten zB. Lenkradtasten und Zündung bzw den Schlüssel Status.

Mein Problem ist, das ich einen Button habe und nur ein Signal bekomme, wenn er gedrückt wurde.

Jetzt möchte ich unterscheiden zwischen einem normalen, langen und doppelten Druck.

Nun bekomme ich wenn es ein normaler ist ja nur 1 mal das Signal und weiß nicht ob noch ein Doppelter kommt und muss darauf 110 ms warten.

Hier erstmal als Version mit Button damit ich nicht immer ins Auto rennen muss

const int buttonPin = 8;     // the number of the pushbutton pin
const int ledPin =  10;      // the number of the LED pin


int buttonState = 0;       

unsigned long DifferenzStempMillis = 0;
unsigned long PreviousStempMillis = 0;
unsigned long CurrentStempMillis = 0;
//unsigned long CurrentStemp2Millis = 0;
//unsigned long CurrentStemp3Millis = 0;

unsigned long anCurrentMillis = 0;
unsigned long anPreviousMillis = 0;
unsigned long anZeitDifferenz = 0;

unsigned int EinfacherDruck = 0;
unsigned int DoppelterDruck = 0;
unsigned int LangerDruck = 0;

unsigned int ZeitNeuerDruck = 300; // Ab hier beginnt ein ganz neuer Druck
//unsigned int ZeitDelayHalten = 20; // Zeit die beim Halten zwischen jedem Ausführen gewartet werden soll.
//unsigned int ZeitEinfacherDruck = 500;
unsigned int ZeitLangerDruck = 110; //Bis hier ist ein Druck lang je kleiner desto eher ist er lang


unsigned int ErsterDruck = 0;

void DauerAuswerten(int Typ) //NUR BEI BUTTON HIGH
{
  //Akutelle Zeit  von diesem Druck
  anCurrentMillis = millis();
  anZeitDifferenz = anCurrentMillis - anPreviousMillis;
  anPreviousMillis = anCurrentMillis;
  //Serial.print("Druck Differenz: ");
  //Serial.println(anZeitDifferenz);
  
  ErsterDruck = 0;
  
/////////NEUER DRUCK///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
  if (
    anZeitDifferenz >= ZeitNeuerDruck //Ist es einer neuer Druck? //ZeitNeuerDruck=500 wenn letzter druck 300ms her dann setzte alles zurück und beginne mit Einfacher druck
    || DoppelterDruck == 1 && anZeitDifferenz >= 120 //Wenn DoppelterDruck erkannt wurde soll es schnell gehen bis man wieder neu drücken kann
    || LangerDruck == 1 && anZeitDifferenz >= 120 //Wenn Langer erkannt wurde soll es schnell gehen bis man wieder neu drücken kann
  )
  {
    Serial.println("NEUER DRUCK");
    ErsterDruck = 1;
    LangerDruck = 0;
    EinfacherDruck = 1;
    DoppelterDruck = 0;
    //Zeit vom Letzen Druck
    CurrentStempMillis = millis();
    DifferenzStempMillis = CurrentStempMillis - PreviousStempMillis;
    PreviousStempMillis = CurrentStempMillis;
  }
///////////NICHT SCHALTEN///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  else if (
    anCurrentMillis - CurrentStempMillis >= ZeitLangerDruck //(ZeitNeuerDruck - 200) // Wenn bis hier nicht losgelassen wurde, dann gilt es nicht mehr als EinfacherDruck
    && 
    EinfacherDruck == 1
  )// && anZeitDifferenz <= 110)//Ist es ein langer Druck? //erst 350ms jetzt 400 // Verhindert das bei einem langen druck geschaltet wird
  {
    EinfacherDruck = 0;
    Serial.println("NICHT SCHALTEN");
  }
/////////DOPPELT/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
  if (                                                         //Ist es ein Doppelter Druck?
    //anCurrentMillis - CurrentStempMillis >= 80     //100 Vllt Sinnlos?
    //Fällt weg mit ErsterDruck && anCurrentMillis - CurrentStempMillis <= ZeitNeuerDruck   //500 Obere Grenze sonst würe DoppelterDruck zu lange möglich
    //&&
    anZeitDifferenz >= ZeitLangerDruck                          //200 unterscheidet zwischen langem und doppelten drücken
    //&& DoppelterDruck == 0
    && ErsterDruck == 0
    && LangerDruck == 0
    || DoppelterDruck == 1
  )
  {
    Serial.println("DoppelterDruck Erkannt...");
    //CurrentStemp2Millis = millis();
    DoppelterDruck = 1;
    EinfacherDruck = 0;

  }
/////////LANG/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
  else if (
    anCurrentMillis - CurrentStempMillis >= (ZeitNeuerDruck - 100) 
    && anZeitDifferenz <= ZeitLangerDruck //200 unterscheidet zwischen langem und doppelten drücken
    //&& LangerDruck == 0 
    && DoppelterDruck == 0
  ) // 400
  {
    LangerDruck = 1;
      Serial.println("LangerDruck Erkannt...");
   

    //CurrentStemp3Millis = millis();
  } 
  
  else if (EinfacherDruck == 1)
  {
    Serial.println("Einfacher Druck");
  }
  
    
//Serial.println("-----------");
  delay(100);
}


void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop() {

  buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH) {
    //Serial.println("gedrückt");
    digitalWrite(ledPin, HIGH);
    DauerAuswerten(1);
  } else {
    //erial.println("nicht gedrückt");
    digitalWrite(ledPin, LOW);
  }
}

Ist das normal, dass wenn ich den Interrupt Starte, er direkt einmal sein dazugehörige Funktion ausführt?

#include <util/atomic.h>

volatile bool flag_compA = false;
volatile bool flag_compB = false;

unsigned int count_CompA = 40000; // Zeitspanne
unsigned int count_CompB = 50000;

void setup() {
  Serial.begin(500000);
  delay(2000);
  Serial.println("Starte...");
  preSet_Timer1();
  run_Timer1();
  Serial.println("FERTIG!");
}

void loop() {

  if (flag_compA == true) {
    /*
      ...
      ... mach was sinnvolles
      ...
    */
    Serial.print('A'); Serial.print('\t'); Serial.println( millis() );
    flag_compA = false;
  }

  if (flag_compB == true) {
    /*
      ...
      ... mach was sinnvolles
      ...
    */
    Serial.print('B'); Serial.print('\t'); Serial.println( millis() );
    flag_compB = false;
  }

}

ISR(TIMER1_COMPA_vect)
{
  flag_compA = true;
  Serial.println("INTERRUPT 1");
}


ISR(TIMER1_COMPB_vect)
{
  flag_compB = true;
  Serial.println("INTERRUPT 2");
}


void preSet_Timer1 ()    // Normal Mode
{
  cli();        // Interrupts ausschalten
  TCCR1A = 0;   // Reset TCCR1A Register
  TCCR1B = 0;   // Reset TCCR1B Register
  TIMSK1 = 0;   // Reset TIMSK1 Register
  TCNT1 =  0;   // Start 0
  OCR1A = count_CompA;   // Compare A
  OCR1B = count_CompB;   // Compare B
  TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B); // enable Compare Match A&B ISR
  sei();        // Interrupts einschalten
}


void run_Timer1 ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TCCR1B |= (1 << CS12) | (1 << CS10); // Prescaler 1024
  }
}


void stop_Timer1 ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TCCR1B &= ~( (1 << CS12) | (1 << CS11) | (1 << CS10) );
  }
}


void disable_CompA ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TIMSK1 &= ~( (1 << OCIE1A) ); // disable Compare Match A Interrupt
  }
}


void disable_CompB ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TIMSK1 &= ~( (1 << OCIE1B) ); // disable Compare Match B Interrupt
  }
}


void enable_CompA ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    OCR1A = OCR1A + count_CompA;  // nächsten Compare Match setzen
    TIMSK1 |= (1 << OCIE1A);      // enable Compare Match A Interrupt
  }
}


void enable_CompB ()
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    OCR1B = OCR1B + count_CompB;  // nächsten Compare Match setzen
    TIMSK1 |= (1 << OCIE1B);      // enable Compare Match B Interrupt
  }
}

Raus kommt:

Starte...
INTERRUPT 1
INTERRUPT 2
FERTIG!
A	2000
B	2000
INTERRUPT 1
A	4560
INTERRUPT 2
B	5200
INTERRUPT 1
A	8755
INTERRUPT 2
B	9395
INTERRUPT 1
A	12949
INTERRUPT 2
B	13589

Zwischen Starte und Fertig soll nichts ausgeführt werden!

Dein Problem betrifft eigentlich alle Interruptquellen, nicht nur diesen Timer.
Lösche die betreffenden Interruptflags, bevor du sei(); machst.

Tipp: TIFR1

Übrigens:
Serial.print() in einer ISR ist eine ganz dumme Idee!

Hallo aggro,

was denkst du warum die serielle Ausgabe in der loop mit Flag Auswertung stattfindet?
Zudem, welche funktionale Änderung hast du wenn die serielle in der ISR steht?
Kannste zwar nicht wissen, okay, sollte aber angesprochen werden.
Ist nicht böse gemeint.

Wenn es jedoch nur um banale Dinge wie Taster etc. und solchen Kleinkram geht, dann sind die Hardwaretimer eh überflüssig. Wäre gut gewesen wenn das vorher bekannt gewesen wäre. Denn der Aufwand lohnt nicht. Man kann sich zwar damit auch befassen, aber nicht dafür.

Vielleicht rückt combie seine Lib raus oder verlinkt sie, da greife ich jetzt nicht vor. Da sind viele Funktionen zusammengefasst enthalten. Ich selbst hätte nur eine eigene Funktion um einen kurzen von einem langen Tastendruck zu unterscheiden. Mit Doppelklick habe ich noch nichts probiert. Läuft alles mit der Nutzung von millis() hinaus. Egal was du machst oder nutzt.

Hallo,

wegen den Interrupt. Du müsstest stop_Timer1 ganz am Anfang von setup schreiben, danach alles andere, dann sollte das klappen Scheinbar läuft der Timer1 im Hintergrund schon los obwohl er nicht genutzt wird. Das Versteckspiel des IDE Komfort's.

Vielleicht rückt combie seine Lib raus oder verlinkt sie, da greife ich jetzt nicht vor. Da sind viele Funktionen zusammengefasst enthalten.

Im Grunde gerne!

Es kann Tastendrücke entprellen und zählen.
Aber spezialisiert auf lange/kurze oder Doppelklick ist es noch nicht.

Basieren tut das auf dem Timer, den ich hier eben schon gepostet habe.

Die Lib findet sich hier im Anhang.
Ist immer noch nicht fertig, wird sich also noch ändern.

CombieLib.zip (31.3 KB)

Hallo,

wegen kurzer und langer Tastendruckerkennung zeige ich mal meinen Code dazu. Vielleicht kann man den vereinfachen vielleicht auch nicht. Wichtig ist das er erstmal funktioniert. :wink: Vielleicht kann es jemand gebrauchen, keine Ahnung. Ich habe damit den einen Taster vom Alpsencoder zu "zweien" gemacht für eine klitze kleine Menüsteuerung.

Beim langen Tastendruck wird auch gewartet bis man wieder loslässt. Eigentlich könnte reagiert werden wenn die max. Wartezeit erreicht ist, habe dazu aber noch keinen wirklichen Ansatz gefunden ohne die komplette Funktion umzubauen. Erweiterbar wäre es noch auf 3 Wartezeiten, wobei das dann vom Benutzer eine gewisse Übung verlangen würde. Vielleicht nicht so sinnvoll.

/*
  Doc_Arduino - german Arduino Forum
  IDE v1.8.5
  08.12.2017
  Arduino Mega2560
  kurzen und langen Tastendruck erkennen
*/


const byte _Taster = 2;
const byte _LED1 = 30;
const byte _LED2 = 31;


void setup() {
  
  Serial.begin(500000);
  pinMode(_Taster, INPUT_PULLUP);     // mit INPUT_PULLUP invertierte Logik
  pinMode(_LED1, OUTPUT);               
  pinMode(_LED2, OUTPUT);    
}

void loop() {

  update_Taster();
}


void Taster ()
{
  static bool state_Taster = false;
  static unsigned long last_push = 0;
  
  unsigned long diff = 0;
  static bool toggle_long = false;
  static bool toggle_short = false;  
  unsigned long ms = millis();
  
  bool read_Taster = digitalRead(_Taster);      // Tasterstatus einlesen
  
  if (read_Taster != state_Taster) {            // wenn Tasterstatus verschieden zu vorher
    state_Taster = read_Taster;
    if (read_Taster == LOW) {
      last_push = ms;                           // letztes Kontakt prellen merken
    }
    else {
      diff = ms - last_push;                    // wenn Taster losgelassen Differenz berechnen
    }
  }
  
  if (diff > 450) {
    toggle_long = !toggle_long;
    digitalWrite(_LED1, toggle_long);
    Serial.println(diff);
  }
  
  if (diff > 50 && diff < 250) {
    toggle_short = !toggle_short;
    digitalWrite(_LED2, toggle_short);
    Serial.println(diff);
  }
}


void update_Taster ()
{
  static unsigned long last_millis = 0;
  const byte Entprellzeit = 20;           // Taster Entprellzeit [ms]
  
  if (millis()-last_millis > Entprellzeit) {
    last_millis = millis();
    Taster();
  }
  
}