Forum: Compiler & IDEs Timer Interruptproblem


von Bernhard W. (dieseldunst)


Lesenswert?

Hallo Leute,

mein Problem betrifft die Timer Interrupt Funktion meines 
ArduinoMega2560 (ATmega2560).

Mein Schrittmotorprogramm berechnet die entsprechende Schrittweite vor 
jedem Schritt neu. Bei einem Schritt erfolgt das Ausgabesignal dann 
durch einen kurzen Puls (~20us). Anschließend wird überprüft wieviele 
Mikrosekunden vergangen sind ,sodass der nächste Schritt ausgeführt 
wird.

Ich möchte aber nun ein Rechtecksignal für den Schrittmotortreiber 
erzeugen, das genau die Hälfte des Schrittes ein und die andere 
ausgeschalten ist. Durchführen wollte ich es, in dem ich bei Beginn des 
Schrittes die aktuelle Systemzeit des Mikrocontrollers einlese und die 
Hälfte der Schrittdauer dazu addiere. Anschließend läuft ein 
Timerinterrupt (alle 20us) und vergleicht die aktuelle Systemzeit mit 
der "Halbzeit" und schaltet nach der "Halbzeit" das Signal aus, sodass 
ein 50/50 Rechtecksignal entsteht.

Leider funktioniert das bis jetzt überhaupt nicht so, wie ich es mir 
gedacht habe. In einem kleinen Programm habe ich einmal versucht meinen 
Fehler zu finden. Da ich mit keiner delay Funktion (bei 
Interruptverwendung) eine Frequenz für die SChrittweite vorgeben kann 
mache ich es im Beispiel durch eine Vielzahl an Rechnungen. Der Code 
generiert ein Rechtecksignal mit 0,5Hz. Das funktioniert aber nur wenn 
ich eine Funktion wie delayMicroseconds(1) in der Whileschleife 
mitaufrufe.

Sieht hier jemand den Fehler, wieso die delayFunktion unbedingt in die 
While-Schleife gehört? Mache ich einen Grundsätzlichen Fehler bei der 
Verwendung des Interrupts?
1
unsigned long     zeit=0;
2
float             rechenwert;
3
unsigned long     messzeit=0;
4
unsigned long     endzeit=0;
5
6
void setup()
7
{   
8
  Serial.begin(9600);      // open the serial port at 9600 bps:    
9
  pinMode(3, OUTPUT);    
10
  digitalWrite(3, LOW);    
11
  pinMode(2, OUTPUT);    
12
  digitalWrite(2, LOW);   
13
}
14
  
15
16
void loop()
17
{
18
  Inter1();  
19
  while(zeit==0)
20
  {
21
      
22
      endzeit=3000000; ////neuer Wert mit einer Sekunde Wartezeit
23
     
24
      delayMicroseconds(1); 
25
     
26
      for(long i=0;i<1223258;i++)
27
      {
28
      rechenwert=sqrt(987651);
29
      }   
30
      
31
      endzeit=0;  /////neuer Wert mit einer Sekunde Wartezeit
32
      for(long i=0;i<1223258;i++)
33
      {
34
      rechenwert=sqrt(987651);
35
      }   
36
        
37
      //endzeit=micros()+halbeschrittweite;
38
           
39
  }  
40
}
41
42
43
44
void Inter1() //Interrupt Funktion Einschalten
45
{  
46
  
47
 // initialize timer1 
48
  noInterrupts();           // disable all interrupts
49
  TCCR4A = 0;
50
  TCCR4B = 0;
51
  TCNT4  = 0;
52
53
  OCR4A = 1600;            // compare match register 16MHz/256/2Hz    160
54
  TCCR4B |= (1 << WGM42);   // CTC mode  
55
  TCCR4B |= (0<<CS42)|(1<<CS41)|(0<<CS40);    // 256 prescaler       8
56
  TIMSK4 |= (1 << OCIE4A);  // enable timer compare interrupt        -----> 10 us
57
  interrupts();             // enable all interrupts                  
58
 
59
  }
60
61
62
void noInter1() //Interrupt Funktion Ausschalten
63
{
64
  TCCR4B |= (0<<CS42)|(0<<CS41)|(0<<CS40);    // 256 prescaler 
65
}
66
67
68
ISR(TIMER4_COMPA_vect)          // timer compare interrupt service routine
69
{
70
  // if (micros()<endzeit)
71
 if (2000000<endzeit)
72
    {
73
    digitalWrite(3, HIGH);        
74
    }
75
 else
76
   {
77
    digitalWrite(3, LOW);
78
    }
79
    
80
}

von Karl H. (kbuchegg)


Lesenswert?

> Mache ich einen Grundsätzlichen Fehler bei der Verwendung des Interrupts?

Ich hätte gesagt: ja.

Das würde ich komplett anders machen.
Der Timer wird auf Compare Match gesetzt (kein CTC!). Vorteiler so 
gewählt, dass sich da vernünftige Zeiten ergeben.

Im Compare Match Interrupt wird der Pin umgeschaltet und einfach eine 
Differenz zum OCR Register dazugezählt. D.h. mit dieser Differenz 
steuerst du, wann der Compare Match, und damit das Pin umschalten das 
nächste mal passiert.

Und das ganze Gefrickel mit Millisekunden fällt raus.

1
  TCCR4B |= (0<<CS42)|(1<<CS41)|(0<<CS40);    // 256 prescaler       8
2
  TIMSK4 |= (1 << OCIE4A);  // enable timer compare interrupt        -----> 10 us
3
  interrupts();             // enable all interrupts                  
4
5
6
.....
7
8
ISR(TIMER4_COMPA_vect)          // timer compare interrupt service routine
9
{
10
  digitalWrite(3, !digitalRead( 3 ) );        
11
12
  OCR4A += Differenz;
13
}

durch variieren der Werte in "Differenz" steuerst du die Frequenz mit 
der der Pin toggelt.

von Bernhard W. (dieseldunst)


Lesenswert?

Danke für den Hinweis! Hat mir sehr geholfen. Musste mein Verständnis 
für die Timer Interrupts erweitern ^^ Auf die Idee wäre ich zudem nicht 
so schnell gekommen. Grüße

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.