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
volatileuint16_tx=0;
8
9
voidsetup()
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
voidloop(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
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.
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.
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
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
volatileuint16_tx=0,y=0;
15
16
voidsetup()
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
voidloop(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
elseif(x==2)
83
{
84
y=2;
85
}
86
elseif(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
"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.
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