Hallo,
ich will mir einen eigenen Laptimer für mein Kart bauen, durch auf der
Kartbahn im Boden eingelassene Induktionsschleifen. Bei dem überfahren
der Induktionsschleife soll bekommt der entsprechende Sensor einen
Impuls (steigende Flanke), die er dann an den ICP (PB0) Pin weitergibt,
dieser soll bei steigender Flanke am Port-Pin einen Interrupt auslösen
(Input Capture).
Jetzt soll angefangen werden die Zeit hochzuzählen, mit einer
genauigkeit von 10ms was ich über den Timer1 (16Bit) mit einer
Quarzfrequenz von 4Mhz mit einem Compare Match mache, der auf einen
Vergleichswert von OCR1A = 25536 ohne Vorteiler eingestellt ist. Jetzt
wird die Zeit solange hochgezählt bist erneut eine Steigende Flanke an
PB0 anliegt und den Interrupt auslöst.
Zu anzeige der Rundenzeit nutze ich ein 2x16 Zeichen LCD Display, das
mir durch die Lange durchlaufszeit beim Anzeigen der Werte aber einige
Probleme bereitet, wie kann ich also Timer und Anzeige voneinander
unabhängig gestaltet, sodass wenn z.B. gerade der Compare Match erreicht
ist er nicht noch in der LCD Routine herumschwirrt?
Ein weiteres Problem ist, das ich die Zeit nicht genau hinkriege.
Ich starte die Zeit durch einen Taster den ich am ICP Pin angeschlossen
haben und vergleiche die hochlaufenden Sekunden mit der Windows Uhr ;-),
hierbei habe ich aber Differenzen von ca. 5sek bei einem
betrachtungszeitraum von 15sek. Eine Änderung des Vergleichswertes OCR1A
hat scheinbar keinen Einfluss, was mich wundert.
Anbei der Code
Deine Frage ist ein Bisschen quer formuliert!
Eins kann ich aber rauslesen was man nicht macht:
>z.B. gerade der Compare Match erreicht>ist er nicht noch in der LCD Routine herumschwirrt?
Die Anzeigeroutine gehört in Main-Loop, damit sie sich durch den
Compare Match Interrupt unterbrechen lässt.
Patrick Langosch schrieb:
> Zu anzeige der Rundenzeit nutze ich ein 2x16 Zeichen LCD Display, das> mir durch die Lange durchlaufszeit beim Anzeigen der Werte aber einige> Probleme bereitet, wie kann ich also Timer und Anzeige voneinander> unabhängig gestaltet, sodass wenn z.B. gerade der Compare Match erreicht> ist er nicht noch in der LCD Routine herumschwirrt?
Hast du doch schon.
Die Interrupts laufen auch dann, wenn dein Hauptprogramm mit
aktualisieren der Anzeige beschäftigt ist.
Das einzige was du sicherstellen solltest, ist hier
1
...
2
cli();
3
LCD_struct.LAP_act_min=(uint8_t)act_min;
4
LCD_struct.LAP_act_sec=(uint8_t)act_sec;
5
LCD_struct.LAP_act_msec=(uint8_t)act_msec;
6
sei();
7
...
damit dir nicht ein Interrupt in die Quere kommt, wenn du dir momentanen
Werte holst.
> Ein weiteres Problem ist, das ich die Zeit nicht genau hinkriege.> Ich starte die Zeit durch einen Taster den ich am ICP Pin angeschlossen> haben und vergleiche die hochlaufenden Sekunden mit der Windows Uhr ;-),> hierbei habe ich aber Differenzen von ca. 5sek bei einem> betrachtungszeitraum von 15sek. Eine Änderung des Vergleichswertes OCR1A> hat scheinbar keinen Einfluss, was mich wundert.
Ich finde den ganzen Aufbau etwas besch...eiden.
Das würde ich so nicht machen.
Schmeiss den ganzen Compare Match Kram raus. Den braucht kein Mensch.
Zähl lieber mit, wieviele Overflow der Timer von einem Input Capture zum
nächsten macht, damit dir der Timer nicht 5 mal von 0 bis 65535 zählt
bis der nächste Puls kommt und du das nicht mitkriegst.
Pfeif fürs erste auf die Umrechnung in Zeit. Sieh einfach nur zu, dass
du die Anzahl der Timerticks von einem Capture Event zum nächsten
richtig bekommst. Und dann kommt der große Trick. Du weißt ja, wie
schnell der Timer tickt. Wenn der Timer daher bis zb 200 zählen konnte,
lässt sich das ganz einfach in eine Zeit umrechnen. Du brauchst dazu
keinen Compare Match oder sonstiges.
Danke kbuchegg für die ausführliche Antwort, habe meinen Code jetzt so
geändert wie du es beschrieben hast.
Meine variable overflow_ticks zählt jetzt die Overflows zwischen den
Input Capture, was mache ich alerdings wenn diese Variable überläuft
(>65536)?
Habe auch noch ein verständnisproblem in Bezug auf den Timer und wie ich
die Umrechnung in Millisekunden, Sekunden und Minuten bewerkstellige.
Ich schreibe hier mal auf wie ich denke das es geht.
Also:
CPU_Takt: 4Mhz
Vorteiler: 1
Timer: 16Bit (65536)
Die CPU arbeitet mit einem Takt von 4Mhz das entspricht 4000000 Takte
pro Sekunde, der Timer1 erhöht das TCNT1 Register um 1 alle 65536 Takte,
da 16Bit Timer. D.h. bei einem Vorteiler von eins, 4000000 / 65536 = ~61
Overflows die Sekunde? Was mache ich mit dem Rest? Wie komme ich auf die
10ms genauigkeit?
Wo sollte ich den Umrechnung in die Zeit am besten machen, schon im
Interrupt oder in der main?
mfg p.langosch
Patrick Langosch schrieb:
> Meine variable overflow_ticks zählt jetzt die Overflows zwischen den> Input Capture, was mache ich alerdings wenn diese Variable überläuft> (>65536)?
Warum nicht einfach so
1
ISR(TIMER1_CAPT_vect)// Aufruf bei steigender Flanke an PinB0
2
{
3
ICR1=0;//
4
TCNT1=0;
5
6
if(lap_status==0)
7
{
8
start_lap=ICR1;
9
overflow_ticks=0;
10
lap_status=1;
11
}
12
13
else
14
{
15
end_lap=ICR1;
16
lap_status=0;
17
}
18
}
19
20
/******** ISR des Overflow von Timer1 ********/
21
ISR(TIMER1_OVF_vect)
22
{
23
overflow_ticks++;// max. 65536 da 16Bit Variable
24
}
>> Habe auch noch ein verständnisproblem in Bezug auf den Timer und wie ich> die Umrechnung in Millisekunden, Sekunden und Minuten bewerkstellige.> Ich schreibe hier mal auf wie ich denke das es geht.>> Also:> CPU_Takt: 4Mhz> Vorteiler: 1> Timer: 16Bit (65536)>> Die CPU arbeitet mit einem Takt von 4Mhz das entspricht 4000000 Takte> pro Sekunde, der Timer1 erhöht das TCNT1 Register um 1 alle 65536 Takte
Nein.
Wenn du einen Vorteiler von 1 hast, dann erhöht der Timer das TCNT1
Register mit jedem Takt. Wäre der Vorteiler 64, dann würde das TCNT1
Register alle 64 Takte erhöht werden etc.
> da 16Bit Timer. D.h. bei einem Vorteiler von eins, 4000000 / 65536 = ~61> Overflows die Sekunde?
Das wiederrum stimmt.
Der Timer muss ja bis 65535 zählen und danach kommt der Overflow und der
Timer fängt wieder bei 0 im TCNT1 Register an.
Bei einem Vorteiler von 64, zählt der Timer daher das TCNT1 Register mit
jedem 64-ten Takt 1 weiter. Hat das TCNT1 Register den Wert 65535
erreicht erfolgt der Overflow Interrupt, TCNT1 wird wieder auf 0 gesetzt
und die Zählerei mit jedem 64-ten Takt geht weiter.
> Was mache ich mit dem Rest? Wie komme ich auf die> 10ms genauigkeit?
Du hast ja immer noch den Wert im TCNT1 Register.
Beides zusammen sagt dir, wieviel Zeit vergangen ist.
Wenn du so willst, dann ist das TCNT1 Register dein 'Sekundenzeiger' und
die Anzahl der Overflow dein 'Minutenzeiger'. Nur dass in deinem Timer
eine 'Minute' 65536 Sekunden hat. (Die Analogie Sekunden-Minuten nicht
wörtlich nehmen. Der Mechanismus ist der gleiche, aber die Zeiträume
sind anders)
> Wo sollte ich den Umrechnung in die Zeit am besten machen, schon im> Interrupt oder in der main?
main.
Du willst den Interrupt so schnell wie möglich verlassen.
Hallo,
ich hab mal eine Uhr gebastelt, hat auch gut funktioniert. Für eine hohe
Genauigkeit würde ich dir aber vorschlagen einen externen Quarz zu
benutzen. Bei meinem Projekt hab ich einen 14,7456MHz-Quarz benutzt. Das
hat den Vorteil, dass man auf ganzzahlige Teile von 1 Sek kommt,
außerdem ist der interne Quarz sehr ungenau und temperaturabhängig. Hier
meine Interruptroutine und das main-Programm:
volatile unsigned char time_flag = 0;
volatile unsigned char _100tel_sek = 0;
volatile unsigned char sek = 0;
volatile unsigned char min = 0;
ISR(TIMER1_COMPA_vect)
{
_100tel_sek++;
if(_100tel_sek>=100)
{
_100tel_sek=0;
sek++;
if(sek>=60)
{
sek=0;
min++;
//usw.
}
}
}
int main (void)
{
//Timer 1:
TCCR1A = 0b00000001;
TCCR1B = 0b00010101; //1024 Vorteiler
OCR1A = 72; //Interrupt alle 10ms
TIMSK = (1<<OCIE1A); //Timer Interruptfreigabe
sei(); //Interrupts freigeben
while (1)
{
if (time_flag)
{
time_flag = 0;
//zähler erhöhen oder sonst irgendwas machen
}
}
}
Das ist nur ein Minimalbeispiel, die nicht relavanten Teile von mir hab
ich weggelassen.
Wenn du den 4 Mhz-Quarz weiterhin verwenden willst, dann musst du das
OCR1A und TCCR1B(Vorteiler) Register enstprechend anpassen. Allerdings
ist die genauigkeit dahin, weil es keinen Wert gibt, der genau 10ms
ergibt. Du kannst z.B. den Wert auch auf 720 ändern, wenn dir eine
genauigkeit von 100ms ausreicht (Interruptroutine dann entsprechend
anpassen)
Ich hoffe, es ist verständlich und hilft dir weiter
Karl heinz Buchegger schrieb:
> Warum nicht einfach so
Dann würde er bei mir die ganze Zeit die overflow_ticks hochzählen, ich
will aber das er erst anfängt hochzuzählen, wenn er einen ersten impuls
am ICP Pin bekommt.
Patrick Langosch schrieb:
> Karl heinz Buchegger schrieb:>> Warum nicht einfach so> Dann würde er bei mir die ganze Zeit die overflow_ticks hochzählen, ich> will aber das er erst anfängt hochzuzählen, wenn er einen ersten impuls> am ICP Pin bekommt.
Macht doch nichts.
Wenn der Startpuls kommt, wird der Overflow Zähler auf 0 zurückgesetzt.
Der Overflow Zähler kann doch in der Zwischenzeit machen was er will,
interessiert doch keinen.
Oh. Seh gerade, dann sollte man den Overflow Zähler allerdings auch beim
Eintreten des Ende Pulses wegsichern ...
1
uint32_ttotalTicks;
2
3
ISR(TIMER1_CAPT_vect)// Aufruf bei steigender Flanke an PinB0
4
{
5
ICR1=0;//
6
TCNT1=0;
7
8
if(lap_status==0)
9
{
10
start_lap=ICR1;
11
overflow_ticks=0;
12
lap_status=1;
13
}
14
15
else
16
{
17
end_lap=ICR1;
18
19
totalTicks=(end_lap-start_lap);
20
if(overflow_ticks>1)
21
totalTicks+=((uint32_t)(overflow_ticks-1))<<16;
22
23
lap_status=0;
24
}
25
}
... damit die Zählerwerte und die Anzahl der Overflows zusammenstimmen.
totalTicks als 32 Bit Zahl enthält jetzt die komplette Anzahl an
Zeitinkrementen (was auch immer bei dir ein Zeitinkrement abhängig von
der Timereinstellung und der Taktfrequenz ist).
Du hast einen Takt von 4Mhz und einen Vorteiler von 1. 1 Timertick ist
daher 0.00000025 Sekunden lang. Ein 32 Bit Wert von 8762345 entspricht
daher dann 2.1905.. Sekunden. Gemessen mit 0.00000025 Sekunden
Auflösung.
Hmm, ne irgendwie verstehe ich das immer noch nicht.
Verstehe nicht warum wenn ich mit Compare Match arbeite auf eine
genauigkeit von 10ms komme, ohne rest und wenn ich es jetzt die
ticks/overflows umrechnen will immer ein rest bleibt.
Habe mit der Umrechnung in eine menschenlesbare Form sehr große
Verständnis Probleme.
Hier strotzdem nochmal meine ISR's, umrechnung kriege ich aber immer
noch nicht auf die Kette, bin ich zu blöd für.
1
/******** ISR des Input Capture (Timer1 / 16Bit) ********/
2
ISR(TIMER1_CAPT_vect)// Aufruf bei steigender Flanke an PB0 (ICP)
3
{
4
if(lap_status==0)// Wenn Induktionsschleife das erste mal überfahren wird
5
{
6
ICR1=0;// Input Capture Register auf "Null" setzen
7
TCNT1=0;// Timer1 Register auf "Null" setzen
8
start_lap=ICR1;
9
lap_status=1;// Induktionsschleife wurde das erste mal überfahren
10
}
11
12
else// Wenn Induktionsschleife das zweite mal überfahren wurde
13
{
14
end_lap=ICR1;
15
remaining_ticks=(end_lap-start_lap);// Rest ticks aus dem Register laden und in remaining_ticks verstauen
16
lap_status=0;// Auf null setzen, das ganze geht wieder von vorne los.
17
}
18
}
19
20
/******** ISR des Overflow von Timer1 ********/
21
ISR(TIMER1_OVF_vect)
22
{
23
if(lap_status==1)// Wenn Induktionsschleife das erste mal überfahren wurde
Kann man den überhaupt eine genauigkeit von 10ms bzw. 1ms erreichen mit
meinen oben genannten Eckdaten?
Vllt. hat jemand eine Formel oder einen Ansatz wie ich durch meine
overflow_ticks und remaining_ticks die zeit in min. sek und msec
umrechne?
Patrick Langosch schrieb:
> Kann man den überhaupt eine genauigkeit von 10ms bzw. 1ms erreichen mit> meinen oben genannten Eckdaten?>> Vllt. hat jemand eine Formel oder einen Ansatz wie ich durch meine> overflow_ticks und remaining_ticks die zeit in min. sek und msec> umrechne?
Ach komm.
Ich hab dir doch jetzt schon sogar die Formel angegeben, wie du die
Anzahl der Overflows und die Ticks im Capture Interrupt miteinander
verrechnen musst.
Das ergibt eine Gesamtzahl an Ticks und 4000000 (in Worten: 4 Millionen
Ticks) sind 1 Sekunde. Dies deshalb weil du einen 4Mhz Quarz hast und
mit Vorteiler 1 den Timer bedienst.
Also: Anzahl Sekunden gesamt = totalTicks / 4000000
Minuten = Anzahl Sekunden gesamt / 60
Sekunden = Anzahl Sekunden gesamt % 60
Patrick Langosch schrieb:
> genauigkeit von 10ms komme, ohne rest und wenn ich es jetzt die> ticks/overflows umrechnen will immer ein rest bleibt.
Weil du jetzt noch viel genauer misst.
> Habe mit der Umrechnung in eine menschenlesbare Form sehr große> Verständnis Probleme.
Wo liegt das Problem.
Du kennst Division (/) ?
Du kennst 'Rest bei einer Division' (%) ?
Der Rest ist trivial.
Wenn du 478 Äpfel hast und in eine Kiste passen jeweils 60 Äpfel, dann
brauchst du
478 / 60 = 7 Kisten
und 478 % 60 = 58 Äpfel bleiben dir übrig.
Dies deshalb, weil 7 * 60 + 58 wieder die 478 ergibt.
Jetzt hast du keine Äpfel und Kisten sondern 4-millionstel Sekunden und
willst du in Sekunden verpacken.
Georg schrieb:
> Hallo,> ein ähnliches Projekt würde ich auch gerne umsetzen. Kannst Du erklären,> wie Du die Schleife im Boden erkennst?>> Grüße, Georg
Ich denke da gibt es mehrere möglichkeiten der umsetzung, zum einen ein
Hallsensor der würde das magnetische Feld erkennen, jetzt müsste man nur
schauen wo der Peak liegt, und den gegebenfalls mitm Op ein wenig
verstärken.
Eine weitere möglichkeit ist eine Reedsensor wie die von Meder
(http://www.meder.com/rechteck-sensoren.html?&tx_jppageteaser_pi1[backId]=44)
Ist halt alles eine Frage, wie stark das Magnetfeld ist, das durch die
Induktionsspule erzeugt wird, wobei ich hier von der annahme ausgegangen
bin das es sich um Induktionsspulen handelt.
Da ich den Laptimer auf Kartrennstrecken einsetzen will, die kaufbaren
aber leider zu teuer sind.
Leider finde ich nur sehr wenig Informationen über die auf Kartbahnen
gängige Technik, bin um jeden Tip dankbar.
Karl heinz Buchegger schrieb:
> Also: Anzahl Sekunden gesamt = totalTicks / 4000000> Minuten = Anzahl Sekunden gesamt / 60> Sekunden = Anzahl Sekunden gesamt % 60
Hmm, nach dieser lösung müsste ich float/double variablen benutzen,
zumindest funktioniert das bei mir nicht.
Ich weiß nicht mehr weiter, werde da mal eine Nacht drüber
schlafen......
>Hmm, nach dieser lösung müsste ich float/double variablen benutzen,>zumindest funktioniert das bei mir nicht.
Nö. Rechnen in der Grundschule mit Rest.
Die Anzahl der Sekunden durch 60 geteilt ergibt die Minuten mit einem
Sekundenrest.
Diese beiden Zahlen bekommt man durch die ganzzahlige Division "/" und
die Modulo-Operation "%". (Modulo ist der Rest, also die
Nachkomma-Stellen eines Ganzahl-Quotienten.)
Patrick Langosch schrieb:
> Karl heinz Buchegger schrieb:>> Also: Anzahl Sekunden gesamt = totalTicks / 4000000>> Minuten = Anzahl Sekunden gesamt / 60>> Sekunden = Anzahl Sekunden gesamt % 60>> Hmm, nach dieser lösung müsste ich float/double variablen benutzen,> zumindest funktioniert das bei mir nicht.
[Zitat Program]
act_msec = total_ticks / 40000;
act_min = total_ticks / 60;
act_sec = total_ticks % 60
[/Zitat Program]
Du musst noch viel genauer werden.
Und vor allen Dingen mitdenken.
Wenn total_ticks nach 1 Sekunde Realzeit einen Wert von um die 4
Millionen aufweist, dann wird wohl total_ticks / 60 nicht die Minuten
ergeben.
Das was du hier rechnen musst, können 9-jährige in der Grundschule.
Wenn du das nicht raffst, dann rechne doch einfach mal in die andere
Richtung. Wieviele 1/4 millionstel Sekunden sind denn 3 Minuten und 15
Sekunden und 22 Hunderstel?
3 Minuten sind 3 * 60 = 180 Sekunden; sind 180 * 4000000 = 720000000
15 Sekunden sind 15 * 4000000 = 60000000
0.22 Sekunden sind 0.22*4000000; sind 22 * 40000 = 880000
---------
780880000
3 Minuten, 15 Sekunden und 22 Hunderstel sind also 780 Millionen 880
Tausend 1/4 millkionstel Sekunden.
Und jetzt zurückgerechnet.
Wieviele Millisekunden sind den 780 Millionen ... 1/4 millionstel
Sekunden?
780880000 / 4000 = 195220 Millisekunden
Wieviele Sekunden sind das?
195220 / 1000 = 195 ganze Sekunden und
195220 % 1000 = 220 Millisekunden bleiben neben den 195 Sekunden übrig
Diese 195 Sekunden sind
195 / 60 = 3 ganze Minuten und
195 % 60 = 15 Sekunden bleiben neben den 3 Minuten noch übrig
780880000 1/4 Millisekunden entsprechen also
3 Minuten, 15 Sekunden und 220 Millisekunden
Ich rate dir dringend, ein paar derartige Rechnungen auf dem Papier zu
machen. Ansonsten wird es schwierig, Fehler in deiner Berechnung zu
suchen. Auch ist es eine gute Idee, dir die total_ticks auszugeben und
auf dem Papier zu kontrollieren, ob deine angezeigten Ergebnisse richtig
sind.
Hi
Warum nimmst du nicht einfach ein paar Variablen, die im Interrupt alle
10 ms hochgezählt werden. (erste 0 bis 99 ) Danach eine jeweils für die
Sekunden (0-59), Minuten ( 0 - 59) und Stunden (0-255). In der ISR holst
du dir den Wert ms, addiert eins drauf und vergleicht auf Überlauf, wenn
ja, Wert zu 0 und die nächste Stufe erhöhen. In der Programmschleife
fragst du dann die Variablen ab. Nun zu den Start- und Stopp- Signalen.
Sinn macht es, in der Timer ISR ein Steuerbyte (Bit) abzufragen, ob die
Zählung überhaupt stattfinden soll. In der Ereignis ISR wird der
aktuelle Zähler in einen Zwischenspeicher kopiert, die Zeiterfasung zu 0
gesetzt, das Freigabebit beeinflußt. In der Programmschleife kann nun
der aktuelle Zeitwert sowie der gespeicherte Zeitwert zur Anzeige
geschickt werden. Über Reset wird Zeitwert, Speicher und Freigabebit
gesetzt. Wenn du Angst hast, dein Controlleer schaff's nicht, in der
Interrupt-Routine einen Zeitzähler zu füllen, also ich hab damit keine
Probleme, selbst bei 8 Zeiterfassungen nicht, die dann am PC
visualisiert werden. Ach ja, ich benutze Assembler. Daher keine
Mathematik, die zeitaufwendige Routinen benutzt. Dadurch hab ich mir
auch angewöhnt in kleinen Blöcken (Unterprogrammen) zu denken......
Gruß oldmax
So hab da jetzt eine Nacht drüber geschlafen und hab das Programm
schoneinmal so weit das wenn der Impuls am ICP Pin kommt die Zeit
hochläuft, deswegen habe ich jetzt die berechnung in der main gemacht,
damit man die Zeit schön hochlaufen sieht.
1
intmain()
2
{
3
DDRB=0x02;
4
PORTD=0x02;
5
6
cli();// Globale Interrupts ausschalten
7
8
LCD_init();// LCD Anzeige initialisieren
9
LCD_clear();// LCD Anzeige komplett leeren
10
11
TCCR1B=(1<<ICNC1)|(1<<ICES1)|(1<<CS10);// Noise Canceler an, Steigende Flanke, Kein Vorteiler
LCD_showLaptime(&LCD_struct);// Rundenzeiten per LCD anzeigen
49
}
50
51
return0;// Wird niemals erreicht
52
}
Der Sekundenzähler läuft jetzt schoneinmal einigermaßen genau, bei
vergleich mit der windows-uhr, was mir noch ein wenig sorgen macht ist
das remaining_ticks, erst beim zweiten impuls dazugerechnet wird, womit
ich aber leben kann.
Im auskommentierten Bereich sieht man schon meine noch nicht
funktionierende erweiterung, damit wenn z.B. der act_sec zähler die 60
Sekunden erreicht hat, wieder von vorne an beginnt zu zählen, im mom
zählt er halt weiter 61, 62, .....
Die ISR's sehen jetzt folgendermaßen aus:
1
/******** ISR des Input Capture (Timer1 / 16Bit) ********/
2
ISR(TIMER1_CAPT_vect)// Aufruf bei steigender Flanke an PB0 (ICP)
3
{
4
if(lap_status==0)// Wenn Induktionsschleife das erste mal überfahren wird
5
{
6
ICR1=0;// Input Capture Register auf "Null" setzen
7
TCNT1=0;// Timer1 Register auf "Null" setzen
8
lap_status=1;// Induktionsschleife wurde das erste mal überfahren
9
}
10
11
else// Wenn Induktionsschleife das zweite mal überfahren wurde
12
{
13
remaining_ticks=ICR1;// Rest ticks aus dem Register laden und in remaining_ticks verstauen
14
lap_status=0;// Auf null setzen, das ganze geht wieder von vorne los.
15
}
16
}
17
18
/******** ISR des Overflow von Timer1 ********/
19
ISR(TIMER1_OVF_vect)
20
{
21
if(lap_status==1)// Wenn Induktionsschleife das erste mal überfahren wurde
22
{
23
overflow_ticks++;// max. 65536 da 16Bit Variable
24
}
25
}
Danke erstmal für die Geduld die ihr mit mir hattet.
> Im auskommentierten Bereich sieht man schon meine noch nicht> funktionierende erweiterung, damit wenn z.B. der act_sec zähler die 60> Sekunden erreicht hat, wieder von vorne an beginnt zu zählen, im mom> zählt er halt weiter 61, 62, .....>> total_ticks = (overflow_ticks * 65536) + remaining_ticks;>> act_csec = total_ticks / 40000;> act_sec = total_ticks / 4000000;> act_min = total_ticks / 240000000;
Liest du eigentlich was ich so schreibe, oder betrachtest du das als
Fingerübungen meinerseits?
Karl heinz Buchegger schrieb:
> Liest du eigentlich was ich so schreibe, oder betrachtest du das als> Fingerübungen meinerseits?
Ja lese ich allerdings fluppt das bei mir nicht bzw. kann ich es nicht
ordentlich programmtechnisch umsetzen.
So sieht das aus, er zählt bis 2sek und dann fängt er wieder von vorne
an.
Patrick Langosch schrieb:
> Ja lese ich allerdings fluppt das bei mir nicht bzw. kann ich es nicht> ordentlich programmtechnisch umsetzen.
Dann sollten wir daran arbeiten.
> act_csec = total_ticks / 40000;> act_sec = act_csec / 100;> act_csec = act_csec % 100;> act_min = act_sec / 60;> act_sec = act_sec % 60;
Und wie sind die Datentypen?
Wenn ich das jetzt einfach mal mit dem Beispiel von oben durchrechne
act_csec = 780880000 / 40000 = 19522
act_sec = 19522 / 100 = 195
act_csec = 19522 % 100 = 22
act_min = 195 / 60 = 3
act_sec = 195 % 60 = 15
act_csec ist nach der ersten Division noch heftig gross. Da ist
sicherlich noch ein uint32_t angebracht, der dieses Zwischenergebnis
aufnehmen muss. Ein uint16_t wird das Ergebnis bei etwas längeren
Zeiträumen nicht abbilden können.
act_sec hat zwischendurch einen Wert von 195 für 3 Minuten. Wenn der ein
uint8_t ist, dann ist da nicht mehr viel Spielraum nach oben (255) bis
der überläuft. D.h. an dieser Stelle sollte man auch eine uint16_t
Variable nehmen. Zumindest um das Zwischenergebnis abzuspeichern.
Ab dort ist dann alles unkritisch, die Ergebnisse und Zwischenergebnisse
passen alle in uint8_t.
Also ergibt sich:
1
uint32_tcsec_total=total_ticks/40000;
2
uint16_tsec_total=csec_total/100;
3
act_csec=csec_total%100;
4
act_min=sec_total/60;
5
act_sec=sec_total%60;
Formeln zu haben ist eine Sache. Aber man muss auch untersuchen, ob die
Berechnungen von den Bitzahlen her funktionieren oder ob es irgendwo
Überläufe gibt. Programmieren ist mehr als einfach nur ein paar magische
Wörter hinschreiben.
Karl heinz Buchegger schrieb:
> Formeln zu haben ist eine Sache. Aber man muss auch untersuchen, ob die> Berechnungen von den Bitzahlen her funktionieren oder ob es irgendwo> Überläufe gibt. Programmieren ist mehr als einfach nur ein paar magische> Wörter hinschreiben.
Ja, da hatte ich nicht mehr dran gedacht, als ich mit dem Projekt
angefangen hatte bin ich nicht davon ausgegangen, das ich mit den act_
Variablen irgendwelche Berechnungen ausführen werde, deshalb 8-Bit groß,
so eine Rundenzeit kommt ja im regelfall nicht über 256min ;-)
Hatte ich auch selber drauf kommen können, dann ist die variable nach
2sek übergelaufen...
Ja, dann läuft die Zeitmessung soweit, dank der Unterstützung von
kbuchegg, jetzt kommt das nächste Problem, ich will das wenn der zeite
Impuls am ICP Pin kommt, die Anzeige für ca. 5sek die Rundenzeit
anzeigt, im Hindergrund aber weiterzählt, nach 5sek. dann auf die
aktuelle Rundenzeit umspringt.
Patrick Langosch schrieb:
> Ja, dann läuft die Zeitmessung soweit, dank der Unterstützung von> kbuchegg, jetzt kommt das nächste Problem, ich will das wenn der zeite> Impuls am ICP Pin kommt, die Anzeige für ca. 5sek die Rundenzeit> anzeigt, im Hindergrund aber weiterzählt, nach 5sek. dann auf die> aktuelle Rundenzeit umspringt.
Falscher Ansatz.
Du willst:
2 Zeiten im Programm haben
1) die zuletzt gemessene Rundenzeit für eine komplette Runde
2) die aktuelle Rundenzeit, die ständig mitläuft
Wenn der 2-te ICP Puls kommt (oder einer danach) dann wird die aktuelle
Rundenzeit zur zuletzt gemessenen Rundenzeit und die aktuelle Rundenzeit
beginnt wieder bei 0.
Gleichzeitig benachritigt die ISP die Haupschleife über eine andere
globale Variable, dass eine neue Rundenzeit vorliegt (da du mit einem
LCD arbeitest: könnte man nicht beide gleichzeitig anzeigen?) damit die
Hauptschleife den Update machen kann.
Karl heinz Buchegger schrieb:
>> Falscher Ansatz.>> Du willst:>> 2 Zeiten im Programm haben> 1) die zuletzt gemessene Rundenzeit für eine komplette Runde> 2) die aktuelle Rundenzeit, die ständig mitläuft>> Wenn der 2-te ICP Puls kommt (oder einer danach) dann wird die aktuelle> Rundenzeit zur zuletzt gemessenen Rundenzeit und die aktuelle Rundenzeit> beginnt wieder bei 0.> Gleichzeitig benachritigt die ISP die Haupschleife über eine andere> globale Variable, dass eine neue Rundenzeit vorliegt (da du mit einem> LCD arbeitest: könnte man nicht beide gleichzeitig anzeigen?) damit die> Hauptschleife den Update machen kann.
Ja, so in der art habe ich mir das auch gedacht habe jetzt noch einen
Satz Variablen hinzugefügt, die act_min, act_sec und act_csec Variablen
sind ja bekannt und für die laufende Ausgabe auf dem LCD verantwortlich,
wenn jetzt der 2-te ICP Impuls kommt werden die Werte act_* Variablen in
die neuen lap_* (lap_min, lap_sec, lap_csec) geschrieben.
Jetzt muss ich noch bewerkstelligen das direkt komplett auf null gesetzt
wird und erneut hochgezählt wird, ist ein wenig tricky, denke ich.
Beide gleichzeitig wäre auch eine Idee, ich will aber den Bestehenden
LCD "Freiraum" für die Bestzeit nutzen, deswegen fällt das leider flach.
Hab mal den aktuellen Stand drangehängt..
Hallo,
da bin ich wieder habe den Code soweit fertig.
Stelle hier der Code online falls es noch Leute gibt die ihn als
Grundsubstanz für ein Projekt nutzen wollen.
Folgende Eigenschaften beeinhaltet der Code
- Einstellbar ab wieviel steigenden Flanken am ICP1 Pin das als volle
Runde (lap_complete = 1) angesehen wird, ist interessant für
Rennstrecken mit mehreren Magnetschleifen.
- Anzeigen der Bestzeit, und vergleichen der Rundenzeit nach ablauf
einer Runde mit der gespeicherten Bestzeit ist diese schneller neue
Rundenzeit als Bestzeit ansehen.
-Zeitmessung sollte einigermaßen genau laufen, werde sicherlich noch
einige Verbesserungen finden. ;-)
Habe den Quelltext noch einmal ein wenig entrümpelt, nicht sinnvolle
sache wie das struct der LCD anzeige habe ich komplett rausgeworfen.
Patrick Langosch schrieb:
> Habe den Quelltext noch einmal ein wenig entrümpelt, nicht sinnvolle> sache wie das struct der LCD anzeige habe ich komplett rausgeworfen.
Darf ich was anregen.
In deinem Programm wird viel mit Zeiten hantiert.
Sinnvoll wäre es, dafür eine eigene Struktur und ein paar
inline-Funktionen bzw. Makros zu definieren um die Lesbarkeit und
Verständlichkeit des Codes zu erhöhen
1
structmyTime{
2
uint8_tmin;
3
uint8_tsec;
4
uint8_tcsec;
5
};
6
7
volatilestructmyTimeact;// aktuell mitlaufende Zeit
8
structmyTimeshow;// die angezeigte Zeit
9
structmyTimelap;// die letzte Rundenzeit
10
structmyTimebest;// Bestzeit
Deine Funktionen vereinfachen sich dann etwas, weil man in C Strukturen
als ganzes zuweisen kann und du des öfteren ganze Zeiten in einem Rutsch
an andere Zeiten zuweist.
1
ISR(TIMER1_CAPT_vect)// Aufruf bei steigender Flanke an PB0 (ICP)
2
{
3
...
4
lap_complete=1;// Flag setzen wenn Runde beendet
5
6
lap=act;// Aktuelle Zeit beim �berfahren der Induktionsschleife als Rundenzeit merken
7
overflow_ticks=0;// Overflow Z�hler auf Null setzen
8
}
9
}
1
voidLCD_showTime(structMyTime*time)
2
{
3
charbuffer[4]="";// Puffer f�r LCD Anzeige
4
5
LCD_string(itoa(time->min,buffer,10));
6
7
LCD_string(":");
8
9
if(time->sec<10)
10
LCD_string("0");
11
LCD_string(itoa(time->sec,buffer,10));
12
13
LCD_string(".");
14
15
if(show_csec<10)
16
LCD_string("0");
17
LCD_string(itoa(time->csec,buffer,10));
18
}
19
20
voidLCD_showLaptime(/*show_data*/void)
21
{
22
LCD_home();// Setze die Cursor Position an den Anfang
23
24
LCD_string("AKT: ");// "ACT:" auf dem Display anzeigen
25
LCD_showTime(&show);
26
27
LCD_set_cursor(0,2);// Cursor auf Position setzen
28
LCD_string("BEST: ");// "BEST:" auf dem Display anzeigen
29
LCD_showTime(&best);
30
}
1
voidsplitTicks(uint32_tticks,structmyTime*result)
2
{
3
uint32_tcentiSec;
4
uint32_tseconds;
5
6
centiSec=ticks/40000;// Zenti-Sekunden berechnen
7
seconds=centiSec/100;// Sekunden berechnen
8
9
result->csec=centiSec%100;// Rest bei der Berechnung der Sekunden --> Zenti-Sekunden
10
result->min=seconds/60;// Minuten berechnen
11
result->sec=seconds%60;// Rest bei der Berechnung der Minuten -- > Sekunden
12
}
13
14
intmain()
15
{
16
....
17
18
19
if(overdrive_magnetic_loops!=0)// Wenn Rundenzeit-Messung am laufen
20
{
21
22
remaining_ticks=TCNT1;// Aktuelle Werte aus dem Timer Register beziehen
23
total_ticks=(overflow_ticks*65536)+remaining_ticks;// Gesamte ticks ausrechnen
24
25
splitTicks(total_ticks,&act);
26
27
if(lap_complete!=1)
28
show=act;
29
}
30
31
if(lap_complete!=0)// Wenn Runde beendet d.h. alle Magnetschleifen �berfahren
32
{
33
if(act.sec<5)// Entspricht 3sek., Solange zeigt das LCD die alte Rundenzeit an.
34
{
35
show=lap;// Rundenzeit anzeigen, aktuelle Rundenzeit sollte dabei weiter laufen
36
}
37
38
else
39
{
40
show=act;// Aktuelle Rundenzeit anzeigen
41
lap_complete=0;
42
}
43
44
if(best.min==0&&best.sec==0&&best.csec==0)// Wenn noch keine Bestzeit eingetragen ist erste Rundenzeit zur Bestzeit machen
45
{
46
best=lap;// Rundenzeit in Bestzeit �bertragen
47
}
48
49
elseif(best.min>=lap.min&&best.sec>=lap.sec&&best.csec>lap.csec)// Wenn Rundenzeit schneller als Bestzeit, neue Rundenzeit zur Bestzeit
50
{
51
best=lap;// Rundenzeit in Bestzeit �bertragen
52
}
53
}
54
55
LCD_showLaptime();// Rundenzeiten per LCD anzeigen
56
}
57
....
Das hier
1
if(best.min>=lap.min&&best.sec>=lap.sec&&best.csec>lap.csec)// Wenn Rundenzeit schneller als Bestzeit, neue Rundenzeit zur Bestzeit
solltest du noch einmal überdenken.
So einfach ist das nicht mit dem Vergleich :-)
Nimm einfach mal an, deine bisherige Bestzeit wäre 1:02.0 gewesen und
die neue Zeit sei 0:58.0
best.min ist zwar größer als lap.min, aber best.sec ist nicht größer ls
lap.sec.
Dein Vergleich wird fehl schlagen.
Noch was:
Kommentiere nicht das, was sowieso schon im Code steht. Das bringt
nichts
1
LCD_string("AKT: ");// "ACT:" auf dem Display anzeigen
Was sagt mir dieser Kommentar, was ich nicht sowieso auchim Code lesen
kann?
Oder hier
1
centiSec=ticks/40000;// Zenti-Sekunden berechnen
2
seconds=centiSec/100;// Sekunden berechnen
3
4
result->csec=centiSec%100;// Rest bei der Berechnung der Sekunden --> Zenti-Sekunden
5
result->min=seconds/60;// Minuten berechnen
6
result->sec=seconds%60;// Rest bei der Berechnung der Minuten -- > Sekunden
Schön. Am Anfang werden die Centisekunden berechnet. Das steht im Code
und auch im Kommentar. Der Kommentar erzählt mir nichts, was ich nicht
auch im Code lesen würde. Aber was mich interessieren würde: Warum wird
da durch 40000 dividiert. Das steht aber leider nicht im Kommentar.
Das bei % ein Rest berechnet wird, weiß jeder C-Programmierer. Aber
warum wird er berechnet? Wieder: Im Kommentar steht nur das, was ich
auch im Code sehen kann. Damit ist der Code im besten Fall einfach nur
unnütz, im schlechtesten Fall (wenn der Code wegen eines Bug Fixes
geändert wird) ist er hingegen oft falsch (weil nicht angepasst worden).
Wie zb hier
1
LCD_string("AKT: ");// "ACT:" auf dem Display anzeigen
Im Kommentar wird versprochen, dass ACT (mit C geschrieben) ausgegeben
wird, tatsächlich wird aber AKT (mit K geschrieben) ausgegeben. Das ist
hier natürlich keine große Sache, aber es zeigt sehr schön den möglichen
Fehlerfall. Tatsächlich braucht allerdings kein Mensch diesen Kommentar.
Wäre er nicht da, wäre es kein Verlust zum Verständnis dessen, was hier
passiert.
Hier dasselbe noch einmal
1
if(act.sec<5)// Entspricht 3sek.,
Huch. Warum entspricht es 3 Sekunden, wenn act.sec kleiner als 5 ist?
Das kann irgendwie nicht stimmen.
Beschreibe in einem Kommentar nicht das 'Wie', das steht im Code.
Beschreibe das 'Warum'!
Werde deine Anregungen beherzigen und habe deshalb wie oben beschrieben
die Zeit nach deinem Code als struct umzusetzen versucht. Problem das
bei den folgenden zuweisungen immer der Fehler "error: dereferencing
pointer to incomplete type" kommt.
Patrick Langosch schrieb:
> Werde deine Anregungen beherzigen und habe deshalb wie oben beschrieben> die Zeit nach deinem Code als struct umzusetzen versucht. Problem das> bei den folgenden zuweisungen immer der Fehler "error: dereferencing> pointer to incomplete type" kommt.>>
1
>LCD_string(itoa(time->min,buffer,10));
2
>if(time->sec<10)
3
>LCD_string(itoa(time->sec,buffer,10));
4
>.......
5
>
Weil ich einen Tippfehler gemacht habe
1
voidLCD_showTime(structMyTime*time)
2
{
die Struktur schreibt sich myTime, mit kleinem M
1
voidLCD_showTime(structmyTime*time)
2
{
Da hätte aber eigentlich der Compiler auch meckern müssen.
Ja da hatte der Compiler auch gemeckert mir ist der Tippfehler aber
leider aufgefallen.
Habe jetzt "alle" Kritikpunkte hoffentlich beseitigt, Code nochmal
anbei.
Eine Warnung bekomme ich noch vom Compiler:
warning: passing argument 2 of 'splitTicks' discards qualifiers from
pointer target type
an gewarnter Stelle wird dann die Funktion aufgerufen:
1
splitTicks(total_ticks,&act);
Der Compiler hat wohl ein Problem mit dem volatile, Lösung wüsste ich
jetzt aber auch nicht, vllt. hast du noch eine Idee.
volatile struct myTime act; // aktuell mitlaufende Zeit
Jetzt noch eine Frage zu dem struct, den Vorteil darin das man alles in
einem Rutsch übertragen kann ich nachvollziehen, was gibt es noch für
nachteile? , was ich speziell als Problem bei meinem Program sehe das
wenn gerade z.B. einer 16-Bit Variable ein Wert zugewiesen wird und in
diesem Moment einem Interrupt kommt, kann das ja zu Problemen führen,
ist diese Fehlerquelle durch das struct micht mehr gegeben.
Hab mich noch nicht so mit struct's auseinandergesetzt, da ich in der
C-Materie nicht so drin stecke...
mfg P.Langosch
Patrick Langosch schrieb:
> Der Compiler hat wohl ein Problem mit dem volatile, Lösung wüsste ich> jetzt aber auch nicht, vllt. hast du noch eine Idee.
Ja.
Der Compiler bemängelt, dass total_ticks eine volatile Variable ist,
dass splitTicks aber keine volatile Variable annimmt.
Die Lösung ist wie so oft:
Du beschwichtigst den Compiler mit einem Cast:
"Lieber Compiler. Ich weiß schon was ich tue. Und ich möchte diese
Variable an die Funktion übergeben, auch wenn dadurch das volatile
wegfällt. .... Also halt gefälligst die Fresse!"
1
splitTicks((uint32_t)total_ticks,&act);
> einem Rutsch übertragen kann ich nachvollziehen, was gibt es noch für> nachteile?
keine.
Ausser du bezeichnest Ordnung im Programm als Nachteil.
> , was ich speziell als Problem bei meinem Program sehe das> wenn gerade z.B. einer 16-Bit Variable ein Wert zugewiesen wird und in> diesem Moment einem Interrupt kommt, kann das ja zu Problemen führen,> ist diese Fehlerquelle durch das struct micht mehr gegeben.
Das hat damit nichts zu tun.
Ein struct ist dein Werkzeug um Daten die logisch zusammengehören auch
im Programm als zusammengehörend zu betrachten. Du erzeugst dir einen
eigenen Datentyp, bei dem die logische Gruppierung zum Ausdruck kommt.
In einem Programm besteht ein Name immer aus einem Vornamen und einem
Nachnamen:
1
structname
2
{
3
charVorname[20];
4
charNachname[40];
5
};
In einem anderen Programm, werden Büroangestellte immer dadurch
gekennzeichnet, dass sie einen Namen haben (der wie wir inzwischen
wissen immer aus Vorname und Nachname besteht), eine Personalnummer
haben und ein Geburtsdatum haben. Ein Datum wiederrum besteht immer aus
Monat, Tag Jahr. Ausserdem wohnt der auch irgendwo
1
structdate
2
{
3
uint8_tTag;
4
uint8_tMonat;
5
uint8_tJahr;
6
};
7
8
structadresse
9
{
10
charOrt[40];
11
charPLZ[6];
12
charStrasse[40];
13
};
14
15
structangestellter
16
{
17
structnameName;// er hat einen Namen ( = Vorname, Nachname)
18
uint16_tPersonalNr;// er hat eine Personalnummer
19
structdateGeburtstag;// und er hat irgendwann Geburtstag ( = Tag, Monat, Jahr
und was immer ich mit dem x mache, zb es an Funktionen zu übergeben,
wird mit dem kompletten Daten des Angestellten x gemacht. Also mit
seinem Namen (der wie wir wissen aus ...), PersonalNr und Geburtsdatum.
1
voidkuendigen(structangestellterperson)
2
{
3
...
4
}
Auf der anderen Seite wird es wohl eine Firma geben, die einen Namen
hat, irgendwo residiert und (aus welchen Gründen auch immer) maximal 100
Angestellte. Wobei jeder Angestellte einen Namen hat .... (eh scho
wissen)
1
structfirma
2
{
3
charName[40];
4
structadresseFirmensitz;
5
structangestellterBelegschaft[100];
6
};
> Hab mich noch nicht so mit struct's auseinandergesetzt, da ich in der> C-Materie nicht so drin stecke...
Dann wirds Zeit
Karl heinz Buchegger schrieb:
> Ja.> Der Compiler bemängelt, dass total_ticks eine volatile Variable ist,> dass splitTicks aber keine volatile Variable annimmt.>> Die Lösung ist wie so oft:> Du beschwichtigst den Compiler mit einem Cast:> "Lieber Compiler. Ich weiß schon was ich tue. Und ich möchte diese> Variable an die Funktion übergeben, auch wenn dadurch das volatile> wegfällt. .... Also halt gefälligst die Fresse!">
Ne, der Compiler meint das zweite Argument das ich der Funktion
splitTicks übergebe ist ja der Zeiger auf das struct myTime *result,
jetzt gefällt dem Compiler das "volatile struct myTime act;" nicht, nur
warum?