Forum: Mikrocontroller und Digitale Elektronik Timer in Interrupt Service Routine starten und stoppen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Paul O. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ich habe mir einen Münzprüfer zugelegt und möchte diesen mit dem 
Microcontroller (ATmega 2560) ansteuern. Der Münzprüfer sendet für jede 
eingestellte Münze (in meinem Fall 50c, 1€ und 2€) eine unterschiedliche 
Pulsanzahl mit einer Pausenzeit von 100ms pro Puls aus. Dieses Pulse 
zähle ich per externen Interrupt. Um jetzt die eingeworfene Münze 
erkennen zu können, fasse ich die Pulse in "Blöcke" zusammen, damit ich 
erkennen kann, dass z.B zuerst eine 1€ Münze mit 10 Pulsen und dann eine 
2€ Münze mit 20 Pulsen eingeworfen wurde. Dazu starte ich nach jeden 
Interrupt einen 16-bit Timer in der ISR des externen Interrupts mit 
einem Prescaler von 1024 (in diesem Fall testweise) um auf eine Laufzeit 
von 4.19s zu erhalten (Quarztakt vom µC sind 16MHz). Wenn ein externen 
Interrupt auftritt, bevor der Timer Overflow Interrupt ausgelöst wird, 
soll der Timer neugestartet werden. Wenn dann der letzte Puls 
aufgetreten ist, wird der Timer bis zum Overflow nicht mehr neugestartet 
und dann in der Timer Overflow ISR "beendet". Dabei wird auch ausgegeben 
welche Münze eingeworfen würde und der Zählstand zurückgesetzt.

Ich habe den Code geschrieben und stehe vor dem Problem, dass der Timer 
direkt nach dem setzen der Register auslöst wird und in die Timer 
Overflow ISR geht.
Der externe Interrupt und die Übertragung zum PC funktioniert 
einwandfrei.
Benutzt habe ich AVR Studio 7.0 und programmiert habe ich in c.

Ist dieses Vorgehen so wie ich es vorhabe machbar?
1
#include <Arduino.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include "USART.h"
5
#include "Wait.h"
6
7
volatile uint16_t x=0;
8
9
void setup()
10
{
11
  DDRE=0x00;
12
  PORTE=0xFF;
13
  DDRA=0xFF;
14
  PORTA=0x00;
15
16
  USARTInit(0,9600,0,0,1,0);
17
  
18
  //PIN2 INT4
19
  EICRB|=(1<<ISC41)|(1<<ISC40);     //externer Interrupt
20
  EIMSK|=(1<<INT4);
21
22
  sei();
23
}
24
25
void loop(void)
26
{
27
  PORTA=0x00;
28
  WaitMs( 500);
29
  PORTA=0xFF;
30
  WaitMs(500);
31
}
32
33
ISR(INT4_vect)
34
{
35
  TCCR5B&=~((1<<CS52)|(1<<CS50));      //Timer löschen
36
  
37
  //Timer 5 1024 Prescaler
38
  TCCR5B|=(1<<CS52)|(0<<CS51)|(1<<CS50); //Timer starten
39
  TIMSK5|=(1<<TOIE5);
40
  
41
  x++;
42
}
43
44
ISR(TIMER5_OVF_vect)
45
{
46
  if (x==1)
47
  {
48
    printf("50 Cent \n");
49
  }
50
  else if (x==10)
51
  {
52
    printf("1 Euro \n");
53
  }
54
  else if (x==20)
55
  {
56
    printf("2 Euro \n");
57
  }
58
  else
59
  {
60
    printf("Fehler \n");
61
  }
62
  x=0;
63
  TCCR5B&=~((1<<CS52)|(1<<CS50));
64
}

von Erich (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Du solltest zuerst aufzeichnen (auf Papier) welche Szenarien auftreten 
können. Denn wahrscheinlich wird man auch mehrere Münzen nacheinander 
einwerfen dürfen. Es braucht irgendwie einen "Timeout", einen maximale 
Zeit in welcher noch weitere Münzen zum aktuellen Vorgang addiert 
werden.
Weiter ist die Auswertung und v.a. Ausgabe im Programm NIEMALS in einer 
INT-Routine. INT-Routinen erfassen nur die Ereignisse und setzen Flags 
oder Zählwerte. Der Rest muss in main() laufen.

von xXx (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Paul O. schrieb:
> Ich habe den Code geschrieben und stehe vor dem Problem, dass der Timer
> direkt nach dem setzen der Register auslöst wird und in die Timer
> Overflow ISR geht.

Wundert dich, dass wirklich?  Der µC ist schnell, denk daran.

ISR(TIMER5_OVF_vect) halte ich für überflüssig. Auswertung eher in Main.

von Patrick J. (ho-bit-hun-ter)


Bewertung
0 lesenswert
nicht lesenswert
Hi

Du kannst, bevor Du die Interrupts zulässt, das entsprechende 
Interrupt-Flag löschen.
Z.B. PCINT gehören da auch zu, dort setze ich meine Pins auf die 
Start-Werte, stelle den Interrupt ein, lösche das Flag (teilweise durch 
Einschreiben einer 1 !!) und gebe Interrupts frei.
Durch das nicht mehr gesetzte Flag wird dieser Interrupt auch erst 
ausgelöst, wenn 'ein Echter' eintrifft.

Bei mir allerdings klassischer AVR-Assembler

ODER

Du setzt eine Variabel 'Start=0' und im Interrupt prüfst Du Diese und 
machst bei Start==0 NIX - auch fertig, wenn Du nicht an die Flags ran 
kommst.

Dann im Programm aber Start ungleich Null setzen :)

MfG

: Bearbeitet durch User
von Paul O. (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die schnellen Antworten!

Ich habe den Code verändert ,die Anzahl der Pulse verringert und die 
Wartezeiten und die printfs und so rausgenommen und bin auf einen Fehler 
gestoßen, der verhindert hat, dass der Timer richtig starten konnte. 
Nämlich TIMSK5|=(1<<TOIE5); war das Problem. Wenn man den | nun weglässt 
funktioniert das Programm bis auf eine Kleinigkeit einwandfrei:

Leider wird nur bei der ersten Münze nach dem Programmstart immer sofort 
die Overflow Interrupt Routine ausgelöst, also nicht gewartet bis der 
Timer den Overflow erreicht. Danach arbeitet das Programm wie es soll.
Ich habe ein Bild mit der Ausgabe auf dem Computer beigefügt. Dabei 
wurde das Programm 2 mal neu gestartet. Die ersten 2 Zeilen waren eine 
1€(2 Pulse) Münze und die zweiten 2 Zeilen waren eine 2 € Münze(3 
Pulse).
1
/*
2
 * Coinacceptor.cpp
3
 *
4
 * Created: 31.01.2017 20:32:18
5
 *  Author: PO
6
 */ 
7
8
#include <Arduino.h>
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include "USART.h"
12
#include "Wait.h"
13
14
volatile uint16_t x=0,y=0;
15
16
void setup()
17
{
18
  DDRE=0x00;
19
  PORTE=0xFF;
20
  DDRA=0xFF;
21
  PORTA=0x00;
22
23
  USARTInit(0,9600,0,0,1,0);
24
  
25
  cli();
26
  TCCR5A=0;
27
  TCCR5B=0;
28
  
29
  //PIN2 INT4
30
  EICRB|=(1<<ISC41)|(1<<ISC40);
31
  EIMSK|=(1<<INT4);
32
33
  sei();
34
}
35
36
void loop(void)
37
{
38
39
  if (y==1)
40
  {
41
    printf("50 Cent \n");
42
    y=0;
43
  }
44
  if (y==2)
45
  {
46
    printf("1 Euro \n");
47
    y=0;
48
  }
49
  if (y==3)
50
  {
51
    printf("2 Euro \n");
52
    y=0;
53
  }
54
  if (y==4)
55
  {
56
    printf("Fehler \n");
57
    y=0;
58
  }
59
}
60
61
ISR(INT4_vect)
62
{
63
  TCNT5=40000;
64
  TCCR5B&=~((1<<CS52)|(1<<CS50));
65
  TIMSK5&=~(1<<TOIE5);
66
  TIFR5&=~(1<<TOV5);        //Falls Interrupt wird Flag rückgesetzt
67
  
68
  //Timer 5 1024 Prescaler
69
  TCCR5B|=(1<<CS52)|(0<<CS51)|(1<<CS50);
70
  TIMSK5=(1<<TOIE5);
71
72
  x++;
73
  TIFR5&=~(1<<TOV5);      //Falls Interrupt wird Flag rückgesetzt
74
}
75
76
ISR(TIMER5_OVF_vect)
77
{
78
  if (x==1)
79
  {
80
    y=1;
81
  }
82
  else if (x==2)
83
  {
84
    y=2;
85
  }
86
  else if (x==3)
87
  {
88
    y=3;
89
  }
90
  else
91
  {
92
    y=4;
93
  }
94
  x=0;
95
  
96
  
97
  TCNT5=40000;
98
  TCCR5B&=~((1<<CS52)|(1<<CS50));
99
  TIMSK5&=~(1<<TOIE5);
100
  TIFR5&=~(1<<TOV5);        //Falls Interrupt wird Flag rückgesetzt
101
}

von Högel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
"TOVn is automatically cleared when the Timer/Countern Overflow 
Interrupt Vector is executed. Alternatively, TOVn
can be cleared by writing a logic one to its bit location."

Außerdem löschen TOV auch noch vor SEI.

von Paul O. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank!!! Das war das Problem ;D Jetzt funktioniert alles!

von Sascha W. (sascha-w)


Bewertung
0 lesenswert
nicht lesenswert
Paul O. schrieb:
> Vielen Dank!!! Das war das Problem ;D Jetzt funktioniert alles!

trotzdem noch ein Hinweis, es reicht wenn du in der ISR4 den Zählwert 
des Timers lädst und den Timer mit schreiben der CS-Bits startest. Es 
ist nicht erforderlich den Timer erst anzuhalten, ebenso reicht es 1x am 
Programmanfang den Timerinterrupt freizugeben. Der kann dann anbleiben - 
wenn der Timer gestoppt ist wird auch kein INT ausgelöst.

Sascha

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.