Hallo zusammen,
bin gerade dabei wie schon in diesem Artikel beschrieben,
Beitrag "Carrera Digital 132 Zeitmessung / Rundenzähler"
eine Erfassung für Carrera digital1xx Autos zu realisieren.
Die Autos senden über eine IR LED 4000000/1024*256*n an einen
Phototransistor. Soweit alles klar ich bekomme ein schönes
rechtecksignal mit schönen Flanken.
Mein bisheriger Ansatz mit INT0 und Timer0 liefert jedoch nicht das
gewünschte resultat.
1
#define F_CPU 16000000L
2
3
volatileintcountSensorA=0;
4
volatilecharBuffer[20];
5
volatileintlast=0;
6
volatileintcount=0;
7
8
9
SIGNAL(INT0_vect)
10
{
11
// steigende flanke kommt
12
countSensorA++;
13
// Timer0 in betrieb setzen
14
if(countSensorA==1)
15
{
16
TIMSK0|=(1<<TOIE0);
17
}
18
}
19
20
21
SIGNAL(TIMER0_OVF_vect)
22
{
23
// Timer overflow
24
cli();
25
26
// Timer ausschalten
27
TIMSK0&=~(1<<TOIE0);
28
if(countSensorA>5)
29
{
30
// pulse anzeigen
31
itoa(countSensorA,(char*)Buffer,10);
32
Oputs((char*)Buffer);
33
Oputs(" Pulse gezählt\r\n");
34
35
}
36
// zähler auf 0
37
countSensorA=0;
38
39
// bischen warten bis Auto weg ist
40
_delay_ms(500);
41
count=0;
42
sei();
43
}
44
45
46
voidinitIo()
47
{
48
EICRA|=(1<<ISC01)|(1<<ISC00);// Steigende Flanke von INT0 als auslöser
49
EIMSK|=(1<<INT0);// interruptmaske
50
EIFR|=(1<<INTF0);
51
52
// Timer 0 mit Prescaler 1024
53
TCCR0B|=(1<<CS02)|(1<<CS00);
54
}
Ist meine Ansatz der falsche oder hab ich was übersehen?
Gruß Harry
Hallo Harry,
dein Ansatz ist unglücklich. so viel Programm packt man nie in einen
Timerinterrupt und ein delay schon gar gar gar nicht.
Lass mit dem Timerinterrupt eine Variable meinetwegen in 0,1sec takt
hochzählen und im Eingangsinterupt wird die Variable abgefragt und
wieder auf Null gesetzt.
Axel
Harry schrieb:> Die Autos senden über eine IR LED 4000000/1024*256*n an einen> Phototransistor.
Eier, Kartoffeln? Liter?
Besitzt der MEGA324P eine Input-Capture-Unit?
Dann solltest du diese benutzen.
Harry schrieb:> SIGNAL (TIMER0_OVF_vect)>> {>> // Timer overflow>> cli();>>>> // Timer ausschalten>> TIMSK0 &= ~(1<<TOIE0);>> if(countSensorA > 5)>> {>> // pulse anzeigen>> itoa( countSensorA, ( char* ) Buffer, 10 );>> Oputs(( char* ) Buffer);>> Oputs(" Pulse gezählt\r\n");>>>> }>> // zähler auf 0>> countSensorA = 0;>>>> // bischen warten bis Auto weg ist>> _delay_ms(500);>> count = 0;>> sei();>> }
"cli" und vor allem "sei" haben in einer ISR nichts zu suchen!
Deine Messmethode muss man nicht verstehen, oder?
Kannst du die mal in Worten beschreiben?
Vielleicht auch noch ein paar Angaben zum Takt?
Während das Programm in der ISR ist läuft der Zähler schon für den
nächsten Timer weiter, d.h. du darfst in der ISR nur so viel Code
ausführen wie zeitlich zwischen zwei Timer-Interrupts passt.
Die IR LED der Autos senden permanent mit 4MHz/(256*CARNUMBER)
IR LED Auto_1 sendet 15625 Hz
.
.
.
IR LED Auto_6 sendet 2604 Hz
IR LED Auto_7 sendet 2232 Hz
Mein Ansatz war Auto fährt über den Sensor INT0 wird ausgelöst, TIMER0
startet, INT0 zählt bis TIMER0 Overflow erreicht und das soll dann das
Messergebnis sein.
Gibt es AVR's mit 4 ICP Modulen?
Überarbeite gerade die ISR
Gruß Harry
Harry schrieb:> TIMER0 startet,
Der startet aber nicht dadurch, dass man den Overflowinterrupt erlaubt,
sondern der läuft dauernd bei Deinem Code.
Was passiert eigentlich, wenn mehrere Fahrzeuge gleichzeitig den Sensor
durchfahren ?
> Gibt es AVR's mit 4 ICP Modulen?
XMega.
Aus dem momentanen Code zu urteilen wird das aber noch ein weiter Weg,
zudem die XMega's um Einiges komplexer sind.
Was willst du eigentlich messen?
Dein Programm, bzw. die Programmidee ist ziemlich wirr. Für mich ist da
nicht wirklich erkennbar, was eigentlich gemessen werden soll.
ICP ist gut, aber ob du die Rundenzeit eines Autos auf +- 1
Zehntausendstel Sekunden genau feststellst oder +- deren 2, spielt dann
nicht wirklich die grosse Rolle. Das kriegt man in dem Fall sicherlich
auch ohne ICP hin. Dafür allerdings auf 5 Kanälen, solange nur sicher
gestellt ist, dass die Durchfahrten der Autos auseinandergehalten werden
können.
Karl Heinz Buchegger schrieb:> Für mich ist da> nicht wirklich erkennbar, was eigentlich gemessen werden soll.
Also der prinzipielle Wille ist aus dem Code schon erkennbar :D
MWS schrieb:> Was passiert eigentlich, wenn mehrere Fahrzeuge gleichzeitig den Sensor> durchfahren ?
Die Autos überfahren den Sensor. D.h. pro Spur nur 1 Auto am Sensor.
Aber 4 Spuren.
Das ganze ist keine neue Erfindung von mir ich möchte es nur
nachstellen.
Die Harware steht schon. Es wurde damals auch nicht mit ICP gemacht,
sondern mit mega88 über PD2(INT0), PD3(INT1), PD7(AINT1) und PD4(T0).
Leider hat der Herr (www.slotbaer.de) keine Zeit mehr.
@Karl Heinz Buchegger
> Was willst du eigentlich messen.
Eine Anzahl von Pulsen(15,5 kHz - 2,2kHz) in einer bestimmten
Zeit(5-10ms)
Gruß Harry
Harry schrieb:> @Karl Heinz Buchegger>> Was willst du eigentlich messen.> Eine Anzahl von Pulsen(15,5 kHz - 2,2kHz) in einer bestimmten> Zeit(5-10ms)
Ah!
Du willst erst mal nur feststellen, welches Auto da über den Sensor
gerauscht ist?
OK.
MWS hats ja schon gesagt.
Ein Timer wird nicht dadurch gestartet oder gestoppt, in dem du seinen
Overflow Interrupt freigibst.
Bei dir läuft der Timer ständig, d.h. es ist bei dir mehr oder weniger
zufällig, wieviele Pulse in der Zeit vom ersten Puls bis zum Overflow
eintreffen.
Wenn der erste Puls kommt, musst du den Timer definiert bei 0 wegzählen
lassen! Erst dann hast du deine definierte Zeit bis zum Overflow.
Oder du lässt deinen Timer überhaupt einfach durchlaufen und vergleichst
die Zählerstände bei 2 aufeinanderfolgenden Interrupts.
Da du ja die Frequenzen kennst, musst du nicht aufs Hz genau messen. Ein
kleiner Fehler spielt zur Fahrzeugerkennung keine große Rolle.
Karl Heinz Buchegger schrieb:> Oder du lässt deinen Timer überhaupt einfach durchlaufen und vergleichst> die Zählerstände bei 2 aufeinanderfolgenden Interrupts.
manuelle ICP.. ;)
Karl Heinz Buchegger schrieb:> Da du ja die Frequenzen kennst, musst du nicht aufs Hz genau messen. Ein> kleiner Fehler spielt zur Fahrzeugerkennung keine große Rolle.
Es interessier ja auch nur die Periodendauer...
Wie schnell mag so ein Auto über den Fotosensor fahren?
2-3 Perioden?
Harry schrieb:> Eine Anzahl von Pulsen(15,5 kHz - 2,2kHz) in einer bestimmten> Zeit(5-10ms)
Auch eine Möglichkeit, aber auch das macht der Code oben sicher nicht.
Harry schrieb:
> Eine Anzahl von Pulsen(15,5 kHz - 2,2kHz) in einer bestimmten> Zeit(5-10ms)
5ms sollten auf jeden Fall reichen. Das auto 7 hat mit seiner Frequenz
eine Periodendauer von 0,5ms. Das wären für schon 10 Perioden.
Bei Auto 1 ist die Periodendauer nur noch 64µs lang - das wären dann
entsprechend mehr Perioden/Impuls pro 5ms.
Harry schrieb:> Aber 4 Spuren.
Basierend auf Deinem momentanen Konzept (Messung über Torzeit)
bräuchtest Du 4 unabhängige voneinander zu startende Timer, in HW hast
Du die nicht, könnte man in SW bauen.
Oder in der ext. Int den Zähler sampeln und nach soundsoviel Differenz
eine Messung als beendet betrachten, hat aber auch Nachteile.
Oder Periodenzeitmessung, wobei im worst-case bei 4 konkurrierenden
Signalen, also 4 Autos gleichzeitig, eine ausreichend genaue Messung der
Periodendauer stattfinden muss.
Zeit also sich über's später verwendbare Konzept Gedanken zu machen und
dann den Code daraufhin ausgerichtet zu beginnen.
Harry schrieb:> PD2(INT0), PD3(INT1), PD7(AINT1) und PD4(T0).
Unwahrscheinlich. Sinnvoll war Int1 & 2, als auch 2 der PCInts, für den
M324 dann Int0-2 & ein PCInt, oder PCINT0-3.
MWS schrieb:> Oder Periodenzeitmessung, wobei im worst-case bei 4 konkurrierenden>> Signalen, also 4 Autos gleichzeitig, eine ausreichend genaue Messung der>> Periodendauer stattfinden muss.
Das arbeite ich gerade aus. Dauert aber länger muss mir das erst mit DB
erabeiten ;-)
Später werd ich das über die PCINT lösen.
mfg
Harry
Harry schrieb:> MWS schrieb:>> Oder Periodenzeitmessung, wobei im worst-case bei 4 konkurrierenden>>>> Signalen, also 4 Autos gleichzeitig, eine ausreichend genaue Messung der>>>> Periodendauer stattfinden muss.>> Das arbeite ich gerade aus. Dauert aber länger muss mir das erst mit DB> erabeiten ;-)
Aus dem Bauch heraus würde ich mal folgende Fragen angehen:
Bei den gegebenen Auto-Frequenzen und den möglichen Vorteilern, wie weit
kann ein Zähler in der Zeit zwischen 2 Pulsen zählen. Und zwar bei allen
auftretenden Frequenzen.
Ziel ist es, den Vorteiler zu finden, so dass der Timer in der Zeit
zwischen 2 Pulsen nicht "überläuft" UND die festgestellten
Timer-differenz-werten sich bei den jeweiligen Autofrequenzen stark
genug unterscheiden, so dass sie eindeutig und sicher
auseinandergehalten werden können.
Ich denke weiters, dass du mit dem 8-Bit Timer nicht weit kommen wirst.
Ein Zählumfang von 0 bis 255 ist nun mal nicht besonders gross, wenn man
~15khz von ~2khz nur anhand der Periodendauer auseinanderhalten soll UND
in die Zählerstände nach Möglichkeit die Overflows nicht eingerechnet
werden sollen (vereinfacht das Handling insgesamt).
@Karl Heinz Buchegger
danke für den Hinweis hab mal etwas gerechnet:
FCPU=16Mhz, prescaler=0, 16MHz/256 = 62,5kHz 1/62,5kHz = 16us
[CAR1] kürzeste Periode 1/15,62kHz = 64us /16us = 4
[CAR7] längste Periode 1/2,232kHz = 448us/16us = 28
[CAR6] 384us/16us = 24
Sollte ich alles richtig verstanden haben würde das so funktionieren
oder?
mfg
Harry
Harry schrieb:> @Karl Heinz Buchegger> danke für den Hinweis hab mal etwas gerechnet:>> FCPU=16Mhz, prescaler=0, 16MHz/256
welches ist der nächst kleinere Prescaler? Hast du zb 64 zur Verfügung?
> [CAR1] kürzeste Periode 1/15,62kHz = 64us /16us = 4> [CAR7] längste Periode 1/2,232kHz = 448us/16us = 28> [CAR6] 384us/16us = 24> Sollte ich alles richtig verstanden haben würde das so funktionieren> oder?
Könnte hinkommen. Da dein Timer durchläuft, sollte man auch noch einen
Zählfehler von +-1 mit einkalkulieren. du hast dann anstellt von 28 und
24, die Werte 27 und 25. Sollte noch unterscheidbar sein, aber dann darf
nicht mehr viel passieren.
Wenn du zb einen Prescaler von 64 hast, dann hast du 4-fache Werte.
112 zu 96
da jetzt noch einen Zählfehler von +-1 eingerechnet, sind wir bei 111 zu
97 und da gibt es dann schon einen viel schöneren Grenzwert in der
Mitte, zu dem die erwarteten Ergebnisse einen ordentlichen Abstand
haben.
112 ist immer noch nicht Ende der Fahnenstange. Bei einem Vorteiler von
32 wäre der Wert 224. Hast du 32 zur Verfügung?
Karl Heinz Buchegger schrieb:> welches ist der nächst kleinere Prescaler? Hast du zb 64 zur Verfügung?
Ich verwende doch schon den kleinsten prescaler = 0
Du meinst ich soll den 16bit Timer nehmen und mich da mit den prescaler
an meinen Bereich nähern?
Ich hab 0, 8, 64, 256, 1024 als Prescaler.
Harry schrieb:> Karl Heinz Buchegger schrieb:>> welches ist der nächst kleinere Prescaler? Hast du zb 64 zur Verfügung?>> Ich verwende doch schon den kleinsten prescaler = 0
1. Bei einem Prescaler von 0 steht der Timer.
Trotzdem.
Mein Fehler.
>1. Bei einem Prescaler von 0 steht der Timer.
Ja klar mein Fehler div/0 und so ;-)
Hab alles mal etwas umgeschrieben aber jetzt kommt nur noch unsinn:
1
//#define F_CPU 16000000
2
#define LOOP 1
3
4
#include<stdio.h>
5
#include<stdlib.h>
6
#include<avr/io.h>
7
#include<avr/interrupt.h>
8
#include"usart/usart.h"
9
10
volatileunsignedlongintstartTime=0;
11
volatileunsignedlongintstopTime=0;
12
volatileunsignedlongintcount=0;
13
unsignedlongintnewWert=0;
14
15
voiddisplay();
16
voidinitIo();
17
18
SIGNAL(INT0_vect)
19
{
20
startTime=stopTime;
21
stopTime=TCNT0;
22
count++;
23
}
24
25
//SIGNAL (TIMER0_OVF_vect)
26
//{
27
// timerOVF++;
28
//}
29
30
intmain()
31
{
32
unsignedlongintlastWert=0;
33
34
initIo();
35
sei();
36
37
while(LOOP)
38
{
39
if(count==2)
40
cli();
41
42
lastWert=newWert;
43
44
if(stopTime<startTime)
45
newWert=256+stopTime-startTime;
46
else
47
newWert=stopTime-startTime;
48
49
stopTime=0;
50
startTime=0;
51
52
if(lastWert!=newWert)
53
display();
54
55
sei();
56
}
57
return0;
58
}
59
60
voiddisplay()
61
{
62
charBuffer[20];
63
itoa(newWert,(char*)Buffer,10);
64
Oputs((char*)Buffer);
65
Oputs(" \r\n");
66
}
67
68
voidinitIo()
69
{
70
InitUart();
71
Oputs("UART Init 9600 Baud\r\n");
72
EICRA|=(1<<ISC01)|(1<<ISC00);// Steigende Flanke von INT0 als auslöser
73
EIMSK|=(1<<INT0);// Interruptmaske
74
EIFR|=(1<<INTF0);
75
TCCR0B|=(1<<CS00);// Timer 0 mit Prescaler 0 (16000000/0)/256 Hz = 62,5kHz = 1/62,5kHz s = 16us2604
Zumindest 2 Dinge fallen mir auf:
- nach dem "if(count == 2)" fehlen die Klammern für einen Block
- count wird nicht zurückgesetzt.
Und ein paar Kleinigkeiten:
- stopTime und startTime braucht nicht auf 0 gesetzt werden, da die
beiden Variablen eh bei jedem Interrupt gesetzt werden.
- Im Interrupt würde ich die Start und Stop-Zeiten nur dann setzen, wenn
es auch nötig ist.
- Nicht auf Gleichheit (count == 2) prüfen, besser auf größer gleich
prüfen, vermeidet bei Problemfälle
Damit ergeben sich folgende Änderungen:
Morgen,
Volkmar Dierkes schrieb:> Zumindest 2 Dinge fallen mir auf:>> - nach dem "if(count == 2)" fehlen die Klammern für einen Block>> - count wird nicht zurückgesetzt.
Danke für deine hilfe habe ich selbst schon bemerkt und den rest hab ich
angepasst. Leider ohne sichtlichem Erfolg:
DEBUG AUSGABE:
96 result, 2 count, 0 startTime, 96 stopTime
250 result, 2 count, 96 startTime, 90 stopTime
Das ist die Ausgabe der 15,6kHz Quelle! (result sollte eigentlich 4
sein. 4*16 = 64us)
Jetzt stellt sich mir die frage, ob der uC überhaupt mitkommt. In dem
Projekt vom slotbaeren wurden nämlich 20MHz Takt benutzt.
Macht es sinn den 16bit timer zu nehmen um mehr "Timer counts" zu
bekommen?
Ich glaub nicht darann, dass das nur mit ICP zu lösen ist!
mfg
Harry
> volatile unsigned long int startTime = 0;
runter mit den Datentypen.
uint8_t reicht völlig. Du hast es nur mit 8 Bit Zahlen zu tun!
Und wenn du das hast, braucht es auch die Unterscheidung hier nicht
> Das ist die Ausgabe der 15,6kHz Quelle! (result sollte eigentlich 4> sein. 4*16 = 64us)
Jetzt muss ich mir doch deine Berechnungen dazu mal genauer ansehen.
Rein gefühlsmässig kann das nicht stimmen.
....
Ich weiss schon, wo der Fehler ist.
Du hast hier gerechnet:
> FCPU=16Mhz, prescaler=0, 16MHz/256 = 62,5kHz 1/62,5kHz = 16us>> [CAR1] kürzeste Periode 1/15,62kHz = 64us /16us = 4
Dein Timer produziert OVERFLOWS mit einer Frequenz von 62.5kHz! Die
Division durch 256 (die ich ursprünglich für einen Vorteiler gehalten
hatte) ist der Zählumfang des Timers.
D.h. von einem Overflow zum nächsten vergehen 16us.
d.h. (in der weiteren Rechnung): der Timer zählt nicht bis 4, sondern
bei dieser Eingangsfrequenz produziert der Timer 4 Overflows!
Ist mir doch gleich so spanisch vorgekommen, dass ein Zähler bei einem
Vorteiler von 1 und lächerlichen EIngangsfrequenzen grade mal bis 4
zählen soll. Ich hätt doch auf mein Gefühl vertrauen sollen.
Also: entweder das Zählsystem auf die Zählung von Overflows umstellen
oder dem Timer einen Vorteiler verpassen (idealerweise 256, wenn du hast
- dann ändert sich an den Zahlen nichts).
Karl Heinz Buchegger schrieb:> Also: entweder das Zählsystem auf die Zählung von Overflows umstellen> oder dem Timer einen Vorteiler verpassen (idealerweise 256, wenn du> hast).
Hab grad hochgescrollt. Du sagtest, es gäbe unter anderem 256 und 64.
64 wäre besser. Die Zahlen werden schöner.
Du hast es irgendwie mit den unsigned long. Das ist scheinbar dein
Lieblingstyp :-)
> volatile unsigned long int count = 0;
volatile uint8_t count = 0;
Bei dieser Variablen ist es ganz besonders wichtig, dass du sie atomar
lesen und schreiben kannst! einen unsigned long kannst du nicht atomar
schreiben/lesen. einen uint8_t schon.
Generell: Du willst immer den kleinsten Datentyp benutzen, mit dem sich
alles ausgeht. Wenn irgendwie möglich einen uint8_t. Das ist sozusagen
des AVR Leib und Liebingsdatentyp. Damit kann er wunderbar umgehen.
Alles andere ist für ihn mächtig Aufwand. Und: man kann sich damit auch
mächtig Probleme einhandeln, die nicht so offensichtlich sind wie
Overflows in Berechnungen.
(*) atomar: in einem Rutsch. Die Operation ist unteilbar, nicht
unterbrechbar.
> Jetzt stellt sich mir die frage, ob der uC überhaupt mitkommt.
Logisch nachdenken und mal überschlagsmässig abschätzen:
Der µC läuft mit 16Mhz. Deine Eingangsfrequenz ist ca. 16kHz. Das ist
ein Faktor von ungefähr 1000.
Ein Faktor von 1000 kann man veranschaulichen:
Du zählst vor dich hin: 1, 2, 3, 4, 5 ....
wobei du ca. jede Sekunde immer um 1 weiterzählt.
Und alle Viertelstunde rufe ich: Halt, wie weit bist du gekommen.
Du hast also den 'Stress' mit jeder Sekunde und ich hab genügend Zeit,
dass ich mir in der Zwischenzeit die Quantentheorie reinziehe. Deinen
Zählerstand zwischendurch wieder mal abfragen und mal kurz umrechnen
mach ich mit Links nebenher. Sozusagen in der Werbepause :-)
Hallo,
ich sehe noch Folgendes. In der ISR solltest Du den Befehl "sensor0++;"
noch in das if mit reinziehen. Sonst könnte es bei extremer Auslastung
des Prozessors passieren das die Variable sensor0 überläuft und wieder
bei 0 beginnt.
1
if(sensor0<2)
2
{
3
sensor0++;
4
startTime=stopTime;
5
stopTime=TCNT0;
6
}
Desweiteren sollte das Zurücksetzen der Variablen "sensor0 = 0;" erst
nach der Übernahme der Werte startTime und stopTime erfolgen:
1
if(sensor0>=2)
2
{
3
lastResult=result;
4
result=stopTime-startTime;
5
6
sensor0=0;
Sonst kann es passieren, daß die ISR schon wieder zuschlägt und die
Werte verändert bevor sie ausgelesen wurden.
Und noch ein 2.tes Problem
Du musst unter allen Umständen verhindern, dass dir hier in dieser
Sequenz
1
if(sensor0>=2)
2
{
3
sensor0=0;
4
5
lastResult=result;
6
result=stopTime-startTime;
Der Interrupt zuschlägt und dir 'unter dem Hintern' die Werte verändert.
Das darf auf keinen Fall passieren. Auch dann nicht, wenn gerade die
Berechnung von
stopTime - startTime
im Gange ist. Es muss sichergestellt sein, dass beide Werte aus
derselben Messung stammen.
Dazu gibt es 3 Möglichkeiten
* entweder du kapselst das alles in eine cli/sei INterruptsperre
* oder du nutzt dein Wissen aus, dass dir die ISR die Werte nicht
verändern wird, solange sensor0 größer/gleich 2 ist.
Hier wäre es daher wichtig, dass das 0-Setzen von sensor0 als letztes
erfolgt, nachdem sicher gestellt ist, dass du die stopTime bzw
startTime nicht mehr brauchst.
* oder du verlagerst die Differenzberechnung überhaupt in die ISR.
Das bischen Zeitreserve hast du dort allemal.
Das alles wird sich zum jetzigen Zeitpunkt noch nicht bemerkbar machen.
Aber wenn dein Programm dann mal ein anderes Timingverhalten hat und die
Zeiten enger werden, dann kann das zu einem Problem werden.
Edit: Hat sich mit Volkmars Analyse überschnitten.
Volkmar Dierkes schrieb:> ich sehe noch Folgendes. In der ISR solltest Du den Befehl "sensor0++;"> noch in das if mit reinziehen. Sonst könnte es bei extremer Auslastung> des Prozessors passieren das die Variable sensor0 überläuft und wieder> bei 0 beginnt.>> if(sensor0<2)> {> sensor0++;> startTime = stopTime;> stopTime = TCNT0;> }
ok neues Rätsel wenn ich das so mache klapt das micht mehr.
Aber warum?
--> OK Kein Rätsel es hat vorher nur andere Messwerte geliefert!!