Hallo,
ich arbeite gerade mit einem Windsensor und muss die Zeit messen die für
eine Umdrehung gebraucht wird. Dafür läuft der Timer0 und liefert alle
1ms einen interrupt. In der ISR zähle ich eine Varible hoch.
Der windsensor löst bei jeder Umdrehung einen Interrupt aus. In dieser
ISR möchte ich dann die verstrichene Zeit in eine andere Varible retten
und die zählvariable auf null setzen. Das funktioniert auch alles und
die Variablen haben auch in den ISRs ihre werte. Aber sobald ich im Main
die Dauer auswerten möchte ist sie dort immer 0. Trotz volatile bei der
Variable ;-)
1
/************************************************
2
3
Wetterstation
4
ATmega8 XTAKL 4Mhz
5
22.08.2009
6
7
************************************************/
8
9
10
volatileunsignedintzeit=0;
11
volatileunsignedintdauer=0;
12
13
14
#define F_CPU 4000000UL
15
16
#include<avr/io.h>
17
#include<avr/interrupt.h>
18
#include<stdlib.h>
19
#include<util/delay.h>
20
21
#include"uartsubs.h"
22
23
24
25
intmain(void)
26
{
27
charbuffer2[16];
28
29
uartinit(9600);
30
31
// Port Init
32
PORTD|=(1<<PD2);// Pullup für Windsensor
33
34
35
// Timer init
36
TCCR0|=(1<<CS01)|(1<<CS00);
37
TIMSK|=(1<<TOIE0);
38
39
// Interrupt
40
GICR|=(1<<INT0);// INT0 frei
41
MCUCR|=(1<<ISC01);// Fallende Flanke
42
sei();
43
44
45
46
for(;;)
47
{
48
49
putbyte('#');
50
51
itoa(dauer,buffer2,10);
52
uart_puts(buffer2);
53
54
putbyte('\r');
55
putbyte('\n');
56
57
for(unsignedchari=0;i<100;i++)
58
_delay_ms(10);
59
}
60
}
61
62
ISR(INT0_vect)
63
{
64
cli();
65
dauer=zeit;// Zeit speichern
66
zeit=0;
67
68
for(unsignedchari=0;i<15;i++)// Prellen abwarten, dauert 150 us Maximal
69
_delay_us(10);
70
71
sei();
72
}
73
74
75
ISR(TIMER0_OVF_vect)// alle 1,008ms
76
{
77
zeit++;
78
TCNT0=192;
79
}
Ich wäre sehr dankbar wenn mir jemand sagen/ helfen könnte warum die
variable "dauer" im main immer 0 ist.
Danke
Nicht direkt zu deinem Problem, aber die beiden Befehle "cli ();" und
"sei ();" in der Interruptroutine sind meines Wissens nach überflüssig.
Denn alle weiteren Interrupts werden während der Abarbeitung eines
Interrupts automatisch gesperrt.
> Ich wäre sehr dankbar wenn mir jemand sagen/ helfen könnte warum die> variable "dauer" im main immer 0 ist.
Weil durch das Prellen die INT0-ISR zwei mal direkt hintereinander
ausgeführt wird. Und wegen dem sei am Ende wird vom Main-Code nicht mal
der eine obligatorische Befehle dazwischen ausgeführt, so dass das
"immer" sogar tatsächlich zu 100% stimmt. Und nein, die Schleife in der
ISR verhindert das nicht.
Wenn ich mich nicht irre, dann habe ich einen weiteren Fehler entdeckt
der allerdings nicht direkt mit deinem Problem zusammenhängt.
Wenn ich mich nicht verrechnet habe, so wird deine ISR (TIMER0_OVF_vect)
nicht jede Millisek. ausgeführt, sondern nur alle 4,1 Millisek.
Stimmt, das cli() und sei() in der ISR INT0 ist sinnlos weil int0 sowiso
höchste priorität hat.
warum soll die Schleife da nicht helfen?
Beim schalten habe ich eine perfekte fallende Flanke, dann 10us pause
und dann für etwa 110us Prellen. Und in diesen 10us macht mein
controller immerhin 40 zyklen. da sollte der Interrupt bereits gesperrt
sein bevor das Prellen eigentlich beginnt.
Timer overflow stimmt schon mit 1ms
4Mhz -> 250ns
250ns*64(vorteiler) -> 16us
16us * (256-192) -> 1,024ms
Aber bleiben wir bei der Frage warum die variable "dauer" im main immer
0 ist. In der isr ist sie es nämlich nicht!
alias_lars schrieb:
> warum soll die Schleife da nicht helfen?> Beim schalten habe ich eine perfekte fallende Flanke, dann 10us pause> und dann für etwa 110us Prellen. Und in diesen 10us macht mein> controller immerhin 40 zyklen. da sollte der Interrupt bereits gesperrt> sein bevor das Prellen eigentlich beginnt.
Weil in der Zeit, in der du wartest, das Interrupt-Flag durch das
Prellen schon längst wieder gesetzt ist. Also feuert der Interrupt
sofort ein weiteres mal.
> Aber bleiben wir bei der Frage warum die variable "dauer" im main immer> 0 ist. In der isr ist sie es nämlich nicht!
Weil eben die ISR zweimal hintereinander ausgeführt wird, und damit
ergibt sich quasi dieser Code:
Wunderbar so hats geklappt. Einfach das INT0 Flag im Register GIFR
zurücksetzten.
Achtung: dazu muss eine 1 an die betreffende Stelle geschrieben werden,
keine 0! Und GIFR ist nicht bitadressierbar. Also zuerst GIFR in eine
Variable kopieren. So umschreiben wie man es haben will und dann wieder
auf GIFR ablegen.
Ich hoffe dieser Beitrag kann anderen auch helfen.
Bei dir ist aber auch noch etwas Nachhilfe in Sachen Interrupt nötig.
;-)
Denn das hier ist Quatsch:
> Stimmt, das cli() und sei() in der ISR INT0 ist sinnlos weil int0 sowiso> höchste priorität hat.
Beim AVR können sich Interrupts grundsätzlich nicht gegenseitig
unterbrechen, es sei denn man erlaubt es explizit mit einem sei() in der
ISR. Deshalb ist dein cli/sei nicht nur überflüssig, sondern sogar
kontraproduktiv. Mit Prioritäten hat das beim AVR nichts zu tun. Die
Priorität entscheidet nur, welcher Interrupt zuerst kommt, wenn mehrere
Flags gesetzt sind.
> Und GIFR ist nicht bitadressierbar. Also zuerst GIFR in eine> Variable kopieren. So umschreiben wie man es haben will und dann wieder> auf GIFR ablegen.
Nein. Damit löschst Du alle Interrupt-Flags, die evtl. gerade gesetzt
sind. Das mach vielleicht im Moment nix, kann aber später mal peinlich
werden, wenn das Programm größer wird.
Einfach eine 1 in das gewünschte Bit reinschreiben. Nichts auslesen,
die andern Bits null lassen. Denn alle Bits, die mit 0 beschrieben
werden, bleiben unverändert (es wird KEINE Null eingeschrieben).