From DefCon Projects
Jump to: navigation, search
 
(132 intermediate revisions by the same user not shown)
Line 1: Line 1:
In diesen Artikel stelle ich vor, wie ein einfacher Laptimer realisiert werden kann.
In diesen Artikel stelle ich vor, wie ein einfacher Laptimer realisiert werden kann.
Ich habe ihn Primär eingesetzt um die Rundenzeit meines Karts zu erfassen. Für diesen
Ich habe ihn Primär dafür eingesetzt die Rundenzeit meines Rennkarts zu erfassen. Er kann aber auch individuell für diverse Einsatzzwecke adaptiert werden.
Einsatzbereich gibt es schon einige fertige Lösungen (MyChron, Alfano, etc..), diese sind aber recht teuer.  
Für den Einsatzbereich im Kart gibt es schon einige fertige Lösungen (MyChron, Alfano, etc..), diese sind aber recht teuer weswegen ich zu einem Selbstbau übergegangen bin. Außerdem ist bei einem Selbstbau der Lerneffekt um einiges größer.  
Außerdem ist bei einem Selbstbau der Lerneffekt um einiges größer.


== Hardware ==
== Hardware ==


Die Grundlage bildet der 8-Bit Mikrocontroller ATmega8 der Firma ATMEL. Dieser übernimmt alle Aufgaben die so anfallen. Das sind im großen und ganzen die Erfassung von:
Die Grundlage bildet der 8-Bit Mikrocontroller ATmega8 der Firma ATMEL der mit einer Taktfrequenz von 4 MHz betrieben wird. Dieser übernimmt alle Aufgaben der Steuerung und Berechnung. Das sind im großen und ganzen die Erfassung von:


* Drehzahl
* Drehzahl
Line 20: Line 19:
Zur Anzeige wird ein alphanummerisches 4x20 Zeilen LCD verwendet (HD44780 kompatibel). Die Stromversorgung wird noch über einen 9V-Block realisiert der über einen Schaltregler die 5V erzeugt. Im Schaltplan ist noch die Möglichkeit vorgesehen über eine Z-Diode die für den Mikroconroller notwendigen 5V zu erzeugen. Davon sollte aber in der Praxis abgesehen werden. In Zukunft gedenke ich den 9V Block durch einen alten Handy-Akku zu ersetzen.
Zur Anzeige wird ein alphanummerisches 4x20 Zeilen LCD verwendet (HD44780 kompatibel). Die Stromversorgung wird noch über einen 9V-Block realisiert der über einen Schaltregler die 5V erzeugt. Im Schaltplan ist noch die Möglichkeit vorgesehen über eine Z-Diode die für den Mikroconroller notwendigen 5V zu erzeugen. Davon sollte aber in der Praxis abgesehen werden. In Zukunft gedenke ich den 9V Block durch einen alten Handy-Akku zu ersetzen.
Mittels eines NPN-Transistors besteht die Möglichkeit die Hintergrundbeleuchtung des LCD's über den Mikrocontroller einzuschalten. Das sollte beim Batteriebetrieb aber nur für kurze Zeit, z.B. beim beenden einer Runde geschehen. Weiterhin wird über eine LED angezeigt ob eine neue Bestzeit gefahren wurde.
Mittels eines NPN-Transistors besteht die Möglichkeit die Hintergrundbeleuchtung des LCD's über den Mikrocontroller einzuschalten. Das sollte beim Batteriebetrieb aber nur für kurze Zeit, z.B. beim beenden einer Runde geschehen. Weiterhin wird über eine LED angezeigt ob eine neue Bestzeit gefahren wurde.
=== Eckdaten ===
Nachfolgend einige Eckdaten des Laptimers:
* Rundenzeitmessung von 15 sec. bis 12 min. Auflösung 10ms.
* Drehzahlmessung von 0 bis 240000 U/min. Auflösung 100 U/min.
* Geschwindigkeitsmessung von 0 bis 240 km/h. Auflösung 1 km/h.


=== Pinbelegung ===
=== Pinbelegung ===
Line 40: Line 48:
  14    PB0(ICP)        Rundenzeit (Reed-Kontakt)
  14    PB0(ICP)        Rundenzeit (Reed-Kontakt)
  15    PB1(OC1A)      Transistor(LCD_BACKGRND)
  15    PB1(OC1A)      Transistor(LCD_BACKGRND)
  16    PB2(SS)        -
  16    PB2(SS)        Taster "Hoch"
  17    PB3(MOSI)      -
  17    PB3(MOSI)      Taster "Runter"
  18    PB4(MISO)      -
  18    PB4(MISO)      Taster "OK/EXIT"
  19    PB5(SCK)        -
  19    PB5(SCK)        -
  20    AVCC            +5V
  20    AVCC            +5V
  21    AREF            RC-Network
  21    AREF            -
  22    AGND            GND
  22    AGND            GND
  23    PC0(ADC0)      LCD(DB7)
  23    PC0(ADC0)      LCD(DB7)
Line 53: Line 61:
  27    PC4(ADC4)      LCD(Enable)
  27    PC4(ADC4)      LCD(Enable)
  28    PC5(ADC5)      LCD(RS)
  28    PC5(ADC5)      LCD(RS)
Außerdem gibt es noch eine vierpolige Stiftleiste. Diese dient zum Flashen der Firmware auf den
Mikrocontroller, zum Debuggen oder zum Senden von Messwerten die dann bequem Protokolliert werden können.
Die Schnittstelle hat die folgende Belegung (5V Pegel, nur über Pegelwandler an serielle Schnittstelle anschließen):
Pin    Connected with
-----------------------------------------
1      +5V
2      GND
3      RXD
4      TXD
=== Stromverbrauch ===
Aktuell betreibe ich den Laptimer mit einem Wald-und-Wiesen 9V-Block, der Kapazität von ungefähr
600 mAh haben müsste. Dabei ergeben sich für verschiedene Zustände in denen sich der Laptimer befindet
auch verschiedene Laufzeiten die ich nachfolgend in einer Tabelle aufgeführt habe (für einen 9V-Block a 600mAh):
{| class="wikitable" style="float:left; margin-right:1em"
! Zustand || Stromaufnahme (mA) || Laufzeit (std)
|-
| Standardanzeige (*) + LCD-Hintergrundbeleuchtung an || 44 mA  || 13 std (**)
|-
| Standardanzeige (*) + LCD-Hintergrundbeleuchtung aus || 16 mA  || 37 std (**)
|-
| Rundenzeitmessung läuft || 44 mA  || 13 std (**)
|-
| Standby || 16 mA  || 37 std (**)
|-
|}
<div style="clear:both"></div>
(*) Anzeige der Messwerte nach dem Einschalten des Laptimers (keine Messung läuft)
(**) Die Berechnung gehen von einer vollständigen Entladung aus. Mitunter schafft es der Schaltregler aber
nicht mehr die 5V-Versorgungsspannung aufrecht zu halten. Die Zeiten sind als Best-Case anzunehmen.
Den größten Anteil am Stromverbrauch hat sicherlich die LCD-Hintergrundbeleuchtung<ref>[http://www.abacom-online.de/div/LCD20x4.pdf Datenblatt LCD 20x4 (PCM2004A)]</ref> mit aktuelle 80 mA. Dieser kann natürlich
noch über den Vorwiderstand R17 gesenkt werden.
<math>I_f = \frac{U_{VCC} - U_f}{R_{17}} = \frac{5~V - 4,2~V}{10~\Omega} = 80~mA</math>
Bedenken sollte man allerdings das dann mitunter die Erkennbarkeit des Displays, bei starker Sonneneinstrahlung, darunter leidet.


== Software ==
== Software ==
Line 62: Line 114:


<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
N_STROKE_ENGINE 2 // 2-Takt oder 4-Takt Motor? Zwei oder Vier eintragen!
SEC_LOCK_ICP 1 // Zeit die der ICP nach auslösen gesperrt bleibt, in Sekunden.
SEC_LOCK_ICP 1 // Zeit die der ICP nach auslösen gesperrt bleibt, in Sekunden.
STANDBY_AFTER_MIN 6 // Zurücksetzen der Rundenzeitmessung und Standby (Runde nach x Minuten noch nicht beendet)
STANDBY_AFTER_MIN 6 // Zurücksetzen der Rundenzeitmessung und Standby (Runde nach x Minuten noch nicht beendet)
MAX_INTERIM_TICKS 12000 // Schwelle für Differenz zu Best-Zwischenzeiten (ganze Minuten!!)
MAX_INTERIM_TICKS 12000 // Schwelle für Differenz zu Best-Zwischenzeiten (ganze Minuten!!)
AVERAGE_VALUE 4 // Zeitkonstante für die Mittelwertberechnung
AVG_N_ORDER 8 // Ordnung der gleitenden Mittelwertberechnung
AMOUNT_MAGNETIC_LOOPS 3 // Anzahl der Magnetschleifen auf der Rennstrecke
AMOUNT_MAGNETIC_LOOPS 3 // Anzahl der Magnetschleifen auf der Rennstrecke
KPH_TO_MPS 3.6 // Umrechnung km/h <-> m/s
KPH_TO_MPS 3.6 // Umrechnung km/h <-> m/s
Line 76: Line 129:
=== Zeiten ===
=== Zeiten ===


Die Berechnung der Zeiten geschaltet sich im ganzen Programm nach dem selben Schema. Ausgehend von den "ticks" der über den
Der Atmega8 besitzt drei Timer die alle genutzt werden. Nachfolgend ist aufgelistet welchen Zweck die der jeweilige Timer genutzt wird und
welches Zeitnormal er erzeugt.
 
{| class="wikitable" style="float:left; margin-right:1em"
! TimerX || Wird benutzt für || Zeitnormal
|-
| Timer0 || Geschwindigkeits- und Drehzahlmessung  || 100µs
|-
| Timer1 ||  Rundenzeit || 10ms
|-
| Timer2 || PWM für LCD Hintergrundbeleuchtung  || -
|-
|}
<div style="clear:both"></div>
 
 
Die Berechnung der Zeiten gestaltet sich im ganzen Programm nach dem selben Schema. Ausgehend von den "overflow_ticks_10ms" die über den
internen Timer des Atmegas alle 10ms erhöht wird, werden die daraus folgenden Zeiten abgeleitet.
internen Timer des Atmegas alle 10ms erhöht wird, werden die daraus folgenden Zeiten abgeleitet.


Line 109: Line 178:
</syntaxhighlight>
</syntaxhighlight>


Diese funktioniert im groben. Allerdings gibt es noch einige Funktionen die ich
=== Interrupt Service Routinen ===
umsetzen würde bevor ich die version 1.0 veröffentliche.
 
==== Timer0 ====
 
<syntaxhighlight lang="c">
/******** ISR des Overflow von Timer0 ********/
ISR(TIMER0_OVF_vect)
{
    TCNT0 = -50; // Timer Register wird mit einem Wert vorbelegt, 2^8 - 206 = 50 als Rest
// somit ist mit jedem Aufruf der ISR 0.1ms vergangen
 
    overflow_ticks_100us++; // Mit jedem Aufruf der ISR sind 100µs vergangen
}
</syntaxhighlight>
 
==== Timer1 ====
 
<syntaxhighlight lang="c">
/******** ISR des Input Capture (Rundenzeit)(Timer1 / 16Bit) ********/
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an ICP
{
    TIMSK &= ~(1 << TICIE1); // Input Capture Interrupt deaktivieren
    TIFR &= (1 << ICF1); // Zur Sicherheit das IC-Flag löschen, durch schreiben einer eins
 
    if(initial_start == FALSE) // Kein Erststart wenn = 0
    {
        loop_crossed = TRUE; // Flag signalisiert das soeben eine Magnetschleife überfahren wurde
        act.interim[run_over_magnetic_loops] = overflow_ticks_10ms; // Zwischenzeit speichern
 
        run_over_magnetic_loops++; // Anzahl der überfahrenen Magnetschleifen hochzählen
    }
 
    else // Wenn Magnetschleife das erste mal überfahren wird = 1
    {
        ICR1 = TCNT1 - ICR1; // Wert für Input Capture Register berechnen, die Takte die zwischen aufruf der ISR und dem setzen von ICR1 vergangen sind
        TCNT1 = -5000 + (ICR1 + 6); // Takte die zwischen vorherigen Befehl ablaufen werden dazuaddiert
 
        initial_start = FALSE;
    }
 
    // Wenn alle Magnetschleifen auf der Rennstrecke überfahren wurden
    // d.h. eine Runde abgeschlossen wurde
    if(run_over_magnetic_loops == AMOUNT_MAGNETIC_LOOPS)
    {
        run_over_magnetic_loops = 0; // Überfahrene Magnetschleifen zurücksetzen
        ICR1 = TCNT1 - ICR1; // Wert für Input Capture Register berechnen, die Takte die zwischen aufruf der ISR und dem setzen von ICR1 vergangen sind
        TCNT1 = -5000 + (ICR1 + 2); // Takte die zwischen vorherigen Befehl ablaufen werden dazuaddiert
        lap_complete = TRUE; // Flag setzen wenn Runde beendet
 
        lap = act; // Werte übertragen
        overflow_ticks_10ms = 0; // Overflow Zähler auf Null setzen
    }
 
    // Sorgt dafür das Pegeländerungen welche durch Prellen des Reed-Kontaktes
    // hervorgerufen werden nicht an den ICP1 Pin gelangen können
    temp = act.sec;
}
 
/******** ISR des Overflow von Timer1 ********/
ISR(TIMER1_OVF_vect)
{
    TCNT1 = -5000; // Timer Register wird mit einem Wert vorbelegt, 2^16 - 60536 = 5000 als Rest
  // somit ist mit jedem Aufruf der ISR 10^-2 Sekunden (0.01) vergangen
 
    if(initial_start == FALSE) // Erstüberfahrt einer Magnetschleife
    {
        // d.h jedes mal wenn overflow_ticks_10ms um eins inkrementiert wird,
        // sind 10ms vergangen
        overflow_ticks_10ms++; // max. 65536 da 16Bit Variable
    }
 
    // Timeout Zähler der Drehzahl und Geschwindigkeit
    if(timeout_ticks_rpm < (TIMEOUT_RPM / 100))
    {
        timeout_ticks_rpm++;
        timeout_rpm = FALSE;
    }
 
    else timeout_rpm = TRUE;
 
    if(timeout_ticks_kph < (TIMEOUT_KPH / 100))
    {
        timeout_ticks_kph++;
        timeout_kph = FALSE;
    }
 
    else timeout_kph = TRUE;
}
</syntaxhighlight>
 
==== Timer2 ====


TODO:
<syntaxhighlight lang="c">
* Zwischenzeiten unterstützen
/******** ISR des Compare Match von Timer2 ********/
* Bestzeit im EEPROM sichern
ISR(TIMER2_COMP_vect)
* Nach gewisser Zeit Rundenzeitmessung beenden (Kontakt länger nicht belegt)   
{
* Taster unterstützung (Reset aktuelle Runde, Bestzeit löschen)
    PORTB &= ~(1 << PB1); // LCD-Hintergrundbeleuchtung (PWM)
* LCD Hintergrundbeleuchtung dimmen
}
* LCD Menüsteuerung (Anzahl Magnetschleifen, Geschwindigkeit / Drehzahl erfaasen?, ...)
 
/******** ISR des Overflow von Timer2 ********/
ISR(TIMER2_OVF_vect)
{
    PORTB |= (1 << PB1); // LCD-Hintergrundbeleuchtung (PWM)
}
</syntaxhighlight>
 
=== Fuses ===
 
[[File:Laptimer_Fuses.jpg|right|thumb|400px|Fuses Einstellungen des Atmega8]]
 
Die Fuses sind dazu da dem Mikrocontroller bestimmte Einstellungen mitzugeben. Das sind dinge wie "Welche Takquelle wird benutzt?".
Diese Informationen benötigt der Mikrocontroller um richtig zu laufen. Nähere Informationen dazu hier<ref>[http://www.mikrocontroller.net/articles/AVR_Fuses  AVR Fuses einstellen]</ref>.
 
Ich habe meinen Atmega8 mit folgenden Fuses programmiert:
 
* High Fuses: 0xD4
* Low Fuses: 0x1C
 
Kurz zusammengefasst:
 
* BOOTRST und BOOTSZ0 müssen gesetzt sein wenn der Bootloader benutzt wird
* BODLEVEL und BODEN aktivieren den Brownout<ref>[http://www.mikrocontroller.net/articles/Brownout Brownout]</ref> und setzen die Grenzspannung auf 4V
* EESAVE sorgt dafür das die Einstellungen im EEPROM auch nach dem übertragen einer neuen Firmware erhalten bleiben
 
=== Flashen ===
 
[[File:Laptimer_Bootloader_Flash_FW.jpg|right|thumb|400px|Flashen mittels FastBoot Bootloader]]
 
Um das Programm auf den Mikrocontroller zu bekommen gibt es mehrere möglichkeiten, aber für alle wird zunächst
einmal ein ISP-Programmer benötigt. Da es diese recht günstig (~10€) in der Bucht gibt gehe ich im weiteren nicht mehr darauf ein.
 
Zum Flashen muss der entsprechende ISP-Programmer mit den Pins Reset, MISO, MOSI sowie SCK verbunden werden. Diese
Möglichkeit habe ich in meinem Layout allerdings nicht vorgesehen, deswegen bietet es sich an den Mikrocontroller
extern zu Programmieren. Im Layout gibt es die Möglichkeit den Mikrocontroller über einen Bootloader zu Flashen (nur Flash kein EEPROM) allerdings muss dafür auch zunächst der Bootloader über einen ISP-Programmer in den Chip geschrieben werden.
 
Im Grunde kann dafür jeder Bootloader benutzt werden, ich benutze zurzeit den Bootloader FastBoot von Peter Dannegger.<ref>[http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger AVR Bootloader FastBoot von Peter Dannegger]</ref>
 
Das Flashen geschieht mittels eines Bootloader über die serielle Schnittstelle (UART) des Atmega8 (Pins PD0 und PD1). Nutzt man diese Möglichkeit sollte man darauf achten, dass der Mikrocontroller auch keinen Fall direkt mit der seriellen Schnittstelle des PCs verbunden werden darf. Beachtet man das nicht zerschießt man sich die entsprechenden Eingangspins des Atmega.
Es muss also zwingend eine Pegelwandlung (± 12V PC <> 0-5V µC) z.B. mit einem MAX232 (es gibt in der Bucht auch schon fertige Schaltungen direkt mit USB/Seriell-Wandler für ~2€) durchgeführt werden. Die Belegung der Stiftleiste kann unter [[Laptimer#Pinbelegung|Pinbelegung]] nachgelesen werden.
 
=== Protokollierung ===
 
Über die serielle Schnittstelle erfolgt nicht nur das Flashen des Mikrocontrollers. Die Schnittstelle kann unter anderem auch zum Debuggen bei der Fehlersuche
genutzt werden. Ein weiteres nettes Feature ist das Protokollieren von Messwerten wie z.B. Drehzahl, Geschwindigkeit, Rundenzeit um nur einige Beispiele zu nennen.
Zum Protokollieren benutze ich zur Zeit den Datenlogger "Openlog" von Sparkfun<ref>[https://www.sparkfun.com/products/9530 Sparkfun - Openlog]</ref>. Der Datenlogger protokolliert die Messwerte auf eine microSD-Karte und kommt erstaunlicherweise direkt mit den 5V-Pegel der seriellen Schnittstelle zurecht (Datennlogger wird intern mit 3,3V betrieben). Um den Datenlogger nutzen zu können muss man lediglich die CONFIG.TXT auf die richtige Baudrate einstellen (Ich benutze per Default 19200 baud/sek).
 
Hier noch ein Beispiel wie die Ausgabe über die serielle Schnittstelle mit dem Mikrocontroller durchgeführt werden kann. Das Senden erfolgt zurzeit in der main-Routine
mit jedem Schleifen durchlauf ist also in keinem Fall zeit-deterministisch. Möchte man die Ausgabe immer in gleich Zeitabständen durchführen muss das mit Hilfe eines Timers erfolgen.
 
<syntaxhighlight lang="c">
USART_send(utoa(pre_rpm, buffer, 10)); // Drehzahl ausgeben
USART_send(",");
USART_send(utoa(kph, buffer, 10)); // Geschwindigkeit ausgeben
USART_send("\n");
</syntaxhighlight>
 
Die Ausgabe über die serielle Schnittstelle erfolgt in diesem Beispiel als CSV (comma seperated value) mit anschließenden Newline.
 
=== EEPROM ===
 
Das EEPROM dient dazu diverse Werte im Mikrocontroller zu speichern die auch nach einem Spannungsverlust erhalten bleiben sollen.
Aktuell werden die folgenden Daten im EEPROM des Mikrocontrollers gespeichert:
 
* Einstellungen (Anzahl der Magnetschleifen, Reifenumfang, Ritzel- und Kettenradzahl)
* Bestzeit + dazugehörige Zwischenzeiten
 
Möchte man die Daten die im EEPROM gespeichert sind zurücksetzen (um z.B. den Laptimer auf einer anderen Rennstrecke
einzusetzen) muss man den Ok/Exit-Taster bei einschalten des Laptimers gedrückt halten. Die nun erfolgte
Inititialisierung wird durch den Text "Init. EEPROM!" auf dem Display und dem Blinken des Bestzeit-LED signalisiert.
 
Nachfolgend sind die Standardwerte angegeben die nach der erfolgten Initialiserungen für die Einstellungen im EEPROM stehen:
 
* Magnetschleifen = 3
* Reifenumfang = 0.86
* Ritzel = 10
* Kattenrad = 86
 
 
=== Bugs ===
 
In der Version 1.0:
 
* Funktion "EEPROM_readBesttime()" ließt falsche ticks ein.
 
<syntaxhighlight lang="c">
// EEPROM leer --> Defaultwert übernehmen
if(temp_word == EEPROM_WORD_DEF) besttime->ticks = EEPROM_WORD_DEF;
else besttime->ticks = temp_byte;
</syntaxhighlight>
 
ersetzen durch
 
<syntaxhighlight lang="c">
// EEPROM leer --> Defaultwert übernehmen
if(temp_word == EEPROM_WORD_DEF) besttime->ticks = EEPROM_WORD_DEF;
else besttime->ticks = temp_word;
</syntaxhighlight>
 
== Genauigkeit ==
 
Da alle alle Zeiten aus dem externen Quarz abgeleitet werden, hängt die Genauigkeit (Rundenzeiten, ...) maßgeblich von der Genauigkeit des verwendeten Quarz ab. Dieser ist natürlichen Schwankungen aufgrund von Temperatureinflüssen und Alterung unterlegen. Der von mir eingesetzte 4 MHz Quarz im HC-49/US Gehäuse weist einen Temperaturkoeffizient von ± 30 ppm und eine Frequenztoleranz von ebendfalls ± 30 ppm auf. Bei der Verschaltung des Quarz ist außerdem auf die richtige Lastkapazität zu achten entspricht diese nicht genau der vom Hersteller angebenen Kapazität kommt eine weitere Fehlerquelle hinzu. Dabei gilt: Bei kleinere Last schwingt der Quarz schneller und bei größerer Last langsamer.
Ausgehend von
 
<math>F_r=\left(\frac{f_{ist}}{f_{soll}} - 1 \right) \cdot 10^6</math>
 
umgestellt nach
 
<math>f_{ist}=\left(\frac{F_r}{10^6} + 1\right)\cdot f_{soll} = \left(\frac{30~ppm}{10^6} + 1\right)\cdot 4~MHz = 4~MHz \pm 120~Hz</math>
 
ergibt sich für einen 4 Mhz Quarz eine Gangabweichung von 120 Hz bei einer Frequenztoleranz von ± 30 ppm.
 
{| class="wikitable" style="float:left; margin-right:1em"
! Quarz-Typ || Frequenzfehler (25°C) || Abweichung (min/jahr) || Abweichung (s/tag) || Abweichung (s/h) || Abweichung (ms/min) || Frequenabw. bei 4 MHz
|-
| Standardquarz (Grundtton) || 30 ppm  || 16 min/jahr || 2,6 s/tag || 108 ms/h || 1,8 ms/s || 120 Hz
|-
|}
<div style="clear:both"></div>
 
Das bedeutet konkret das man bei einer Rundenzeit von ca. 10 min mit einen Fehler von 18 ms rechnen muss. Lässt man auch noch Temperatureinflüsse mit in die
Rechnung einfließen kann sich das ganze im Worst-Case noch verdoppeln. Alterungseffekte wurden dabei außer acht gelassen.
 
=== Abstimmen des Quarz ===
 
Möchte man diese einflüsse so gut es geht kompensieren bietet es sich an zunächst einmal zu schauen mit welcher Frequenz der Quarz den
tatsächlich schwingt. Das gestaltet sich allerdings nicht so einfach wie es auf den ersten Blick aussieht. Da der Quarz schon durch geringe Kapazitäten von einigen pF messbar verstimmt wird, würde ein direktes Messen den Quarz messbar verstimmen. Um das Problem zu umgehen kann man den Takt über den Vorteiler eines Timers auf einen
IO-Pin ausgeben und dann dort messen.<ref>[http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC#Echtzeituhr_mit_Uhrenquarz AVR - Die genaue Sekunde – Echtzeituhr mit Uhrenquarz]</ref>
 
Zum Abstimmen erstzt man einen Lastkondensator durch einen Trimmkondensator (0-47pF). Dieser wird dann so lange verdreht bis die gewünschte Frequenz auf dem Display des Frequenzzählers angezeigt wird.<ref>[http://sprut.de/electronic/mess/frequenz.htm Das Quarzproblem]</ref>


== Funktionsweise ==
== Funktionsweise ==
Line 125: Line 414:


=== Magnetstreifen ===
=== Magnetstreifen ===
In die Kartbahn sind in der Regel an bestimmten stellen Magnetstreifen eingelassen. Dazu werden einfache Permanent-Magneten genutzt (siehe Quelle Patenschrift). Ein Magnetstreifen sollte sich auf jedenfall auf der Start-/Ziellinie befinden. Je nach Kartbahn gibt es aber auch mehrere Magnetstreifen auf der Strecke die es ermöglichen Zwischenzeiten zu erfassen. Die Magnetstreifen sind i.d.R. ca. 1,8cm breit und umfassen die ganze Länge der Kartbahn. Ein Reed-Kontakt schaltet in ca. 2cm abstand, wobei dieser Wert stark von der Empfindlichkeit des Reed-Kontaktes abhängt.
In die Kartbahn sind in der Regel an bestimmten stellen Magnetstreifen eingelassen. Dazu werden einfache Permanent-Magneten genutzt (siehe Quelle Patenschrift). Ein Magnetstreifen sollte sich auf jedenfall auf der Start-/Ziellinie befinden. Je nach Kartbahn gibt es aber auch mehrere Magnetstreifen auf der Strecke die es ermöglichen Zwischenzeiten zu erfassen. Die Magnetstreifen sind i.d.R. ca. 1,8cm breit und umfassen die ganze Länge der Kartbahn. Ein Reed-Kontakt schaltet in ca. 2cm Abstand, von den Permanent-Magneten wobei dieser Wert stark von der Empfindlichkeit des Reed-Kontaktes abhängt.


=== Rundenzeit erfassen ===
=== Rundenzeit erfassen ===


Um die Rundenzeit zu erfassen, muss zunächst einmal das Magnetfeld des Magnetstreifens beim überfahren erfasst werden. Dazu kann man einen Hallsensor oder Hallschalter einsetzen. Es sollte aber auch eine Reed-Kontakt reichen. Beim überfahren des Magnetstreifens wird der Reed-Kontakt für kurze Zeit geschlossen. Der dadurch entstehende Pegelwechsel kann über den Mikrocontroller einfach über den ICP-Pin (Input Capture) erfasst werden.
Um die Rundenzeit zu erfassen, muss zunächst einmal das Magnetfeld des Magnetstreifens beim überfahren erfasst werden. Dazu kann man einen Hallsensor oder Hallschalter einsetzen. Es sollte aber auch eine Reed-Kontakt reichen. Beim überfahren des Magnetstreifens wird der Reed-Kontakt für kurze Zeit geschlossen. Der dadurch entstehende Pegelwechsel wird über den Mikrocontroller über den ICP-Pin (Input Capture) erfasst.


=== Drehzahl erfassen ===
=== Drehzahl erfassen ===
Line 144: Line 433:
=== Rundenzeit ===
=== Rundenzeit ===
Zunächst berechne ich wielange der Reed-Kontakt Zeit hat um im "Worst-Case" zu schließen. Dazu nehme ich eine Geschwindigkeit von
Zunächst berechne ich wielange der Reed-Kontakt Zeit hat um im "Worst-Case" zu schließen. Dazu nehme ich eine Geschwindigkeit von
<math> 100~km/h \rightarrow 27.78~m/s </math>
<math>v = 100~km/h = 27,78~m/s </math>
an beim überfahren der Magnetschleife.  
beim überfahren der Magnetschleife, an.  


<math> s = v \cdot t \rightarrow t = \left( \frac{s}{v} \right) = \left( \frac{1.8 \cdot 10^-2~m}{27.78~m/s} \right) = 0.65~ms </math>
<math> s = v \cdot t \rightarrow t = \left( \frac{s}{v} \right) = \left( \frac{1,8 \cdot 10^-2~m}{27,78~m/s} \right) = 0,65~ms </math>


Der Reed-Kontakt hat also ca. 0.65 ms Zeit um zu schließen. Bei 50 km/h dementsprechend doppelt so viel (1,3 ms).
Der Reed-Kontakt hat also ca. 0,65 ms Zeit um zu schließen. Bei 50 km/h dementsprechend doppelt so viel (1,3 ms).


=== Drehzahl / Geschwindigkeit ===
=== Drehzahl / Geschwindigkeit ===
Line 156: Line 445:
Impulsen gemessen und daraus dann die entsprechende Geschwindkeit bzw. Drehzahl berechnet. Da sich Drehzahl und Geschwindigkeit grob nur in dem Übersetzungsverhältnis
Impulsen gemessen und daraus dann die entsprechende Geschwindkeit bzw. Drehzahl berechnet. Da sich Drehzahl und Geschwindigkeit grob nur in dem Übersetzungsverhältnis
zwischen Ritzel und Kettenrad unterscheiden, kann mitunter von einer expliziten zusätzlichen Erfassung der Geschwindigkeit oder Drehzahl abgesehen werden.
zwischen Ritzel und Kettenrad unterscheiden, kann mitunter von einer expliziten zusätzlichen Erfassung der Geschwindigkeit oder Drehzahl abgesehen werden.
Eine Umrechnung der Geschwindigkeit aus der Drehzahl erfolgt nach der folgenden Gleichung:
<math>v = n \cdot \frac{x_{Ritzel}}{x_{Kettenrad}} \cdot U \cdot 0,06 \frac{km}{min}</math><br><br>
<math>v~\textrm{in}~\frac{km}{h}</math><br>
<math>n~\textrm{in}~min^{-1}</math>


Berechnungen die ich dazu angestellt habe befinden sich im nachfolgenden Bild oder in dem dazugehörigen PDF im Anhang.
Berechnungen die ich dazu angestellt habe befinden sich im nachfolgenden Bild oder in dem dazugehörigen PDF im Anhang.
Line 161: Line 456:
[[File:Berechnungen_Drehzahl_Geschwindigkeit.gif]]
[[File:Berechnungen_Drehzahl_Geschwindigkeit.gif]]


Anmerkung: Nach jedem "Tick" sind 100µs vergangen.
'''Anmerkung: Nach jedem "Tick" sind 100µs vergangen.'''
 
== Steuerung ==
 
Die (Menü-)Steuerung des Laptimers geschieht über drei auf der Vorderseite angebrachte Taster (Ok/Exit, Hoch, Runter).
Die Taster dienen zum einem dazu das Navigieren durch das Menü zu ermöglichen zum anderen aber auch zum zurücksetzen von
gespeicherten Rundenzeiten oder zum zurücksetzen des Laptimers.
 
=== Einstellungen ===
 
Durch drücken des Ok/Exit-Tasters kommt man ins Einstellungen-Menü. In dem Menü können die folgenden
Einstellungen vorgenommen werden:
 
* Anzahl der Magnetschleifen (1...6)
* Reifenumfang in Meter (0.01...2.50)
* Anzahl der Zähne des Ritzels (0...25)
* Anzahl der Zähne des Kettenrades (0...100)
 
Durch drücken des Hoch- oder Runter-Tasters kann der jeweilige Menüpunkt ausgewählt werden, was durch ein
Symbol (>) vor dem Menüpunkteintrag kenntlich gemacht wird. Durch drücken der Ok/Exit-Taste kann der Wert ausgewählt
und über den Hoch- oder Runter-Taster editiert werden. Durch drücken der Ok/Exit-Taste wird der Wert übernommen.
 
Bei der Ermittlung der Geschwindigkeit gibt es die Möglichkeit diese aus der Drehzahl des Motors zu berechnen oder
diese über den Sensor an der Hinterachse zu bestimmen. Durch die Wahl der Ritzel-/Kettenradzahl kann man aus einen
der beiden Optionen wählen.
 
Sind für Ritzel und Kettenrad Werte ungleich Null eingestellt, wird die Geschwindigkeit aus der Drehzahl des Motors
berechnet (Belastet den Mikrocontroller mit zusätzlichen Berechnungen). Wurde für die Ritzel- und Kettenradzahl hingegen
Null eingestellt wird die Geschwindigkeit mittels des Geschwindigkeitssensors direkt an der Hinterachse ermittelt.
Die zweite Option ist deutlich weniger Rechenintensiv und sollte deswegen wenn möglich bevorzugt werden.
 
Durch längeres Drücken (>3sek) des Ok/Exit-Tasters werden die geänderten Werte ins EEPROM des Mikrocontrollers geschrieben.
Das schreiben wird durch eine Nachricht "Save to EEPROM" auf dem Display und durch Blinken der Bestzeit-LED signalisiert.
Die Änderungen sind dann dauerhaft in dem Mikrocontroller gespeichert.


== Bilder ==
== Bilder ==
Line 168: Line 496:
File:Laptimer_Rev_E_1.jpg
File:Laptimer_Rev_E_1.jpg
File:Laptimer_Rev_E_2.jpg
File:Laptimer_Rev_E_2.jpg
File:Laptimer Laptime Reed Switch 1.jpg
File:Laptimer_RevE_1_New.jpg
File:Laptimer Laptime Reed Switch 2.jpg
File:Laptimer_RevE_2_New.jpg
File:Laptimer Laptime Reed Switch 3.jpg
File:Laptimer_RevE_3_New.jpg
File:Laptimer Laptime Reed Switch 4.jpg
File:Laptimer_Laptime_Reed_Switch_1.jpg
File:Laptimer_Laptime_Reed_Switch_2.jpg
File:Laptimer_Laptime_Reed_Switch_3.jpg
File:Laptimer_Laptime_Reed_Switch_4.jpg
File:Laptimer_Velocity_Sensor.jpg
File:Laptimer_Laptime_Sensor.jpg
File:Laptimer_RevF_1.jpg
File:Laptimer_RevF_OpenLog.jpg
</gallery>
</gallery>


== Schaltplan ==
== Schaltplan ==


[http://defcon-cc.dyndns.org/files/Laptimer_Rev_E.pdf Laptimer_Rev_E.pdf]
[[File:Laptimer_RevE_sch.pdf]]


== Quelltext ==
== Quelltext ==
Aktuelle Version: 1.1
Quelltext ver1.1: [[File:Laptimer Code_ver1.1.pdf]] <br>
Laptimer Project Files ver1.1 (AVR-Studio 4): [[File:Laptimer_ver1.1.zip]]


Kommt sobald ich den Code noch ein wenig schön gemacht habe.
Quelltext ver1.0: [[File:Laptimer Code_ver1.0.pdf]] <br>
Laptimer Project Files ver1.0 (AVR-Studio 4): [[File:Laptimer_ver1.0.zip]]
 
== TODO ==
 
* THT-Layout verbessern (kleiner, integrierte Spannungsversorgung)
* SMD-Layout erstellen
* Menü-Auswahl Drehzahl 2-Takt / 4-Takt
* Kommunikation über RS232 mit anderer Peripherie (GPS, Accelerometer, Lenkwinkel, ...)
* Praktischer Test: Drehzahl schwankt sehr stark? (Einstreuungen)
* Praktischer Test: Geschwindigkeit erreicht irgendwann einen Endwert.
* Kontrast des Display schlecht bei Sonneneinstrahlung
* Ablesen durch Vibrationen teilweise schwierig
* Grafik-Display Unterstützung


== Quellen ==  
== Quellen ==  
Line 187: Line 539:
[http://www.google.com/patents/EP0632350B1?cl=de Patentschrift Alfano E.P.0632350]<br>
[http://www.google.com/patents/EP0632350B1?cl=de Patentschrift Alfano E.P.0632350]<br>
[[File:Laptimer_Drehzahl_Geschwindigkeit.pdf]]
[[File:Laptimer_Drehzahl_Geschwindigkeit.pdf]]
== Einzelnachweise ==
<references />

Latest revision as of 05:39, 30 April 2019

In diesen Artikel stelle ich vor, wie ein einfacher Laptimer realisiert werden kann. Ich habe ihn Primär dafür eingesetzt die Rundenzeit meines Rennkarts zu erfassen. Er kann aber auch individuell für diverse Einsatzzwecke adaptiert werden. Für den Einsatzbereich im Kart gibt es schon einige fertige Lösungen (MyChron, Alfano, etc..), diese sind aber recht teuer weswegen ich zu einem Selbstbau übergegangen bin. Außerdem ist bei einem Selbstbau der Lerneffekt um einiges größer.

Hardware

Die Grundlage bildet der 8-Bit Mikrocontroller ATmega8 der Firma ATMEL der mit einer Taktfrequenz von 4 MHz betrieben wird. Dieser übernimmt alle Aufgaben der Steuerung und Berechnung. Das sind im großen und ganzen die Erfassung von:

  • Drehzahl
  • Geschwindigkeit
  • Rundenzeit

sowie die Steuerung der

  • LCD-Anzeige
  • Hintergrundbeleuchtung
  • Bestzeit-LED

Zur Anzeige wird ein alphanummerisches 4x20 Zeilen LCD verwendet (HD44780 kompatibel). Die Stromversorgung wird noch über einen 9V-Block realisiert der über einen Schaltregler die 5V erzeugt. Im Schaltplan ist noch die Möglichkeit vorgesehen über eine Z-Diode die für den Mikroconroller notwendigen 5V zu erzeugen. Davon sollte aber in der Praxis abgesehen werden. In Zukunft gedenke ich den 9V Block durch einen alten Handy-Akku zu ersetzen. Mittels eines NPN-Transistors besteht die Möglichkeit die Hintergrundbeleuchtung des LCD's über den Mikrocontroller einzuschalten. Das sollte beim Batteriebetrieb aber nur für kurze Zeit, z.B. beim beenden einer Runde geschehen. Weiterhin wird über eine LED angezeigt ob eine neue Bestzeit gefahren wurde.

Eckdaten

Nachfolgend einige Eckdaten des Laptimers:

  • Rundenzeitmessung von 15 sec. bis 12 min. Auflösung 10ms.
  • Drehzahlmessung von 0 bis 240000 U/min. Auflösung 100 U/min.
  • Geschwindigkeitsmessung von 0 bis 240 km/h. Auflösung 1 km/h.


Pinbelegung

Pin    Description     Connected with
-----------------------------------------
1      PC6(RESET)      RC-Network
2      PD0(RXD)        Connector (Bootloader)
3      PD1(TXD)        Connector (Bootloader)
4      PD2(INT0)       Drehzahl
5      PD3(INT1)       Geschwindigkeit
6      PD4(XCK/T0)     -
7      VCC             +5V
8      GND             GND
9      PB6(XTAL1)      4Mhz Quarz
10     PB7(XTAL2)      4Mhz Quarz
11     PD5(T1)         -
12     PD6(AIN0)       -
13     PD7(AIN1)       Bestzeit-LED
14     PB0(ICP)        Rundenzeit (Reed-Kontakt)
15     PB1(OC1A)       Transistor(LCD_BACKGRND)				
16     PB2(SS)         Taster "Hoch"
17     PB3(MOSI)       Taster "Runter"
18     PB4(MISO)       Taster "OK/EXIT"
19     PB5(SCK)        -
20     AVCC            +5V
21     AREF            -
22     AGND            GND	
23     PC0(ADC0)       LCD(DB7)
24     PC1(ADC1)       LCD(DB6)
25     PC2(ADC2)       LCD(DB5)
26     PC3(ADC3)       LCD(DB4)
27     PC4(ADC4)       LCD(Enable)
28     PC5(ADC5)       LCD(RS)


Außerdem gibt es noch eine vierpolige Stiftleiste. Diese dient zum Flashen der Firmware auf den Mikrocontroller, zum Debuggen oder zum Senden von Messwerten die dann bequem Protokolliert werden können. Die Schnittstelle hat die folgende Belegung (5V Pegel, nur über Pegelwandler an serielle Schnittstelle anschließen):

Pin    Connected with
-----------------------------------------
1      +5V
2      GND
3      RXD
4      TXD

Stromverbrauch

Aktuell betreibe ich den Laptimer mit einem Wald-und-Wiesen 9V-Block, der Kapazität von ungefähr 600 mAh haben müsste. Dabei ergeben sich für verschiedene Zustände in denen sich der Laptimer befindet auch verschiedene Laufzeiten die ich nachfolgend in einer Tabelle aufgeführt habe (für einen 9V-Block a 600mAh):

Zustand Stromaufnahme (mA) Laufzeit (std)
Standardanzeige (*) + LCD-Hintergrundbeleuchtung an 44 mA 13 std (**)
Standardanzeige (*) + LCD-Hintergrundbeleuchtung aus 16 mA 37 std (**)
Rundenzeitmessung läuft 44 mA 13 std (**)
Standby 16 mA 37 std (**)

(*) Anzeige der Messwerte nach dem Einschalten des Laptimers (keine Messung läuft)

(**) Die Berechnung gehen von einer vollständigen Entladung aus. Mitunter schafft es der Schaltregler aber nicht mehr die 5V-Versorgungsspannung aufrecht zu halten. Die Zeiten sind als Best-Case anzunehmen.

Den größten Anteil am Stromverbrauch hat sicherlich die LCD-Hintergrundbeleuchtung[1] mit aktuelle 80 mA. Dieser kann natürlich noch über den Vorwiderstand R17 gesenkt werden.

Bedenken sollte man allerdings das dann mitunter die Erkennbarkeit des Displays, bei starker Sonneneinstrahlung, darunter leidet.

Software

Parameter

Über Software defines können eine fülle von Einstellungen an dem Laptimer vorgenommen werden ohne Teile im Code ändern zu müssen. Zurzeit habe ich die folgenden Parameter implementiert

N_STROKE_ENGINE 2 // 2-Takt oder 4-Takt Motor? Zwei oder Vier eintragen!
SEC_LOCK_ICP 1 // Zeit die der ICP nach auslösen gesperrt bleibt, in Sekunden.
STANDBY_AFTER_MIN 6 // Zurücksetzen der Rundenzeitmessung und Standby (Runde nach x Minuten noch nicht beendet)
MAX_INTERIM_TICKS 12000 // Schwelle für Differenz zu Best-Zwischenzeiten (ganze Minuten!!)
AVG_N_ORDER 8 // Ordnung der gleitenden Mittelwertberechnung
AMOUNT_MAGNETIC_LOOPS 3 // Anzahl der Magnetschleifen auf der Rennstrecke
KPH_TO_MPS 3.6 // Umrechnung km/h <-> m/s
PERIMETER_TYRE 0.86 // Reifenumfang in Meter
FRONT_SPROCKET 11 // Zähnezahl Ritzel
REAR_SPROCKET 86 // Zähnezahl Kettenrad
SEC_SHOW_LAPTIME 5 // Zeit in Sekunden, bei der die alte Rundenzeit angezeigt wird

Zeiten

Der Atmega8 besitzt drei Timer die alle genutzt werden. Nachfolgend ist aufgelistet welchen Zweck die der jeweilige Timer genutzt wird und welches Zeitnormal er erzeugt.

TimerX Wird benutzt für Zeitnormal
Timer0 Geschwindigkeits- und Drehzahlmessung 100µs
Timer1 Rundenzeit 10ms
Timer2 PWM für LCD Hintergrundbeleuchtung -


Die Berechnung der Zeiten gestaltet sich im ganzen Programm nach dem selben Schema. Ausgehend von den "overflow_ticks_10ms" die über den internen Timer des Atmegas alle 10ms erhöht wird, werden die daraus folgenden Zeiten abgeleitet.

void splitTicks(uint16_t ticks, volatile struct myTime* result)
{
    uint16_t centiSec = 0;
    uint16_t seconds = 0;

    centiSec = ticks; // Zenti-Sekunden (10ms)
    seconds = centiSec / 100; // Sekunden berechnen

    result->ticks = ticks;
    result->csec = centiSec  % 100; // Rest bei der Berechnung der Sekunden --> Zenti-Sekunden
    result->min = seconds  / 60; // Minuten berechnen
    result->sec = seconds  % 60; // Rest bei der Berechnung der Minuten -- > Sekunden
}

Dabei wird eine eigens angelegte Struktur "myTime" genutzt, die wie folgt definiert ist:

// Struktur für die diversen Zeiten
struct myTime 
{
    uint8_t min;
    uint8_t sec;
    uint8_t csec;
    uint16_t ticks;
    uint16_t interim[AMOUNT_MAGNETIC_LOOPS];
};

Interrupt Service Routinen

Timer0

/******** ISR des Overflow von Timer0 ********/
ISR(TIMER0_OVF_vect)
{
    TCNT0 = -50; // Timer Register wird mit einem Wert vorbelegt, 2^8 - 206 = 50 als Rest
		 // somit ist mit jedem Aufruf der ISR 0.1ms vergangen

    overflow_ticks_100us++; // Mit jedem Aufruf der ISR sind 100µs vergangen
}

Timer1

/******** ISR des Input Capture (Rundenzeit)(Timer1 / 16Bit) ********/
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an ICP
{
    TIMSK &= ~(1 << TICIE1); // Input Capture Interrupt deaktivieren
    TIFR &= (1 << ICF1); // Zur Sicherheit das IC-Flag löschen, durch schreiben einer eins

    if(initial_start == FALSE) // Kein Erststart wenn = 0
    {
        loop_crossed = TRUE; // Flag signalisiert das soeben eine Magnetschleife überfahren wurde	
        act.interim[run_over_magnetic_loops] = overflow_ticks_10ms; // Zwischenzeit speichern

        run_over_magnetic_loops++; // Anzahl der überfahrenen Magnetschleifen hochzählen
    }

    else // Wenn Magnetschleife das erste mal überfahren wird = 1
    {
        ICR1 = TCNT1 - ICR1; // Wert für Input Capture Register berechnen, die Takte die zwischen aufruf der ISR und dem setzen von ICR1 vergangen sind
        TCNT1 = -5000 + (ICR1 + 6); // Takte die zwischen vorherigen Befehl ablaufen werden dazuaddiert 	

        initial_start = FALSE;
    }

    // Wenn alle Magnetschleifen auf der Rennstrecke überfahren wurden 
    // d.h. eine Runde abgeschlossen wurde
    if(run_over_magnetic_loops == AMOUNT_MAGNETIC_LOOPS)
    {
        run_over_magnetic_loops = 0; // Überfahrene Magnetschleifen zurücksetzen
		
        ICR1 = TCNT1 - ICR1; // Wert für Input Capture Register berechnen, die Takte die zwischen aufruf der ISR und dem setzen von ICR1 vergangen sind
        TCNT1 = -5000 + (ICR1 + 2); // Takte die zwischen vorherigen Befehl ablaufen werden dazuaddiert 	
        lap_complete = TRUE; // Flag setzen wenn Runde beendet

        lap = act; // Werte übertragen
        overflow_ticks_10ms = 0; // Overflow Zähler auf Null setzen
    }

    // Sorgt dafür das Pegeländerungen welche durch Prellen des Reed-Kontaktes 
    // hervorgerufen werden nicht an den ICP1 Pin gelangen können
    temp = act.sec;
}

/******** ISR des Overflow von Timer1 ********/
ISR(TIMER1_OVF_vect)
{
    TCNT1 = -5000; // Timer Register wird mit einem Wert vorbelegt, 2^16 - 60536 = 5000 als Rest
		   // somit ist mit jedem Aufruf der ISR 10^-2 Sekunden (0.01) vergangen

    if(initial_start == FALSE) // Erstüberfahrt einer Magnetschleife
    {
        // d.h jedes mal wenn overflow_ticks_10ms um eins inkrementiert wird,
        // sind 10ms vergangen
        overflow_ticks_10ms++; // max. 65536 da 16Bit Variable
    }

    // Timeout Zähler der Drehzahl und Geschwindigkeit
    if(timeout_ticks_rpm < (TIMEOUT_RPM / 100))
    {
        timeout_ticks_rpm++;
        timeout_rpm = FALSE;
    }

    else timeout_rpm = TRUE;

    if(timeout_ticks_kph < (TIMEOUT_KPH / 100))
    {
        timeout_ticks_kph++;
        timeout_kph = FALSE;
    }

    else timeout_kph = TRUE;
}

Timer2

/******** ISR des Compare Match von Timer2 ********/
ISR(TIMER2_COMP_vect)
{
    PORTB &= ~(1 << PB1); // LCD-Hintergrundbeleuchtung (PWM) 
}

/******** ISR des Overflow von Timer2 ********/
ISR(TIMER2_OVF_vect)
{
    PORTB |= (1 << PB1); // LCD-Hintergrundbeleuchtung (PWM)
}

Fuses

Fuses Einstellungen des Atmega8

Die Fuses sind dazu da dem Mikrocontroller bestimmte Einstellungen mitzugeben. Das sind dinge wie "Welche Takquelle wird benutzt?". Diese Informationen benötigt der Mikrocontroller um richtig zu laufen. Nähere Informationen dazu hier[2].

Ich habe meinen Atmega8 mit folgenden Fuses programmiert:

  • High Fuses: 0xD4
  • Low Fuses: 0x1C

Kurz zusammengefasst:

  • BOOTRST und BOOTSZ0 müssen gesetzt sein wenn der Bootloader benutzt wird
  • BODLEVEL und BODEN aktivieren den Brownout[3] und setzen die Grenzspannung auf 4V
  • EESAVE sorgt dafür das die Einstellungen im EEPROM auch nach dem übertragen einer neuen Firmware erhalten bleiben

Flashen

Flashen mittels FastBoot Bootloader

Um das Programm auf den Mikrocontroller zu bekommen gibt es mehrere möglichkeiten, aber für alle wird zunächst einmal ein ISP-Programmer benötigt. Da es diese recht günstig (~10€) in der Bucht gibt gehe ich im weiteren nicht mehr darauf ein.

Zum Flashen muss der entsprechende ISP-Programmer mit den Pins Reset, MISO, MOSI sowie SCK verbunden werden. Diese Möglichkeit habe ich in meinem Layout allerdings nicht vorgesehen, deswegen bietet es sich an den Mikrocontroller extern zu Programmieren. Im Layout gibt es die Möglichkeit den Mikrocontroller über einen Bootloader zu Flashen (nur Flash kein EEPROM) allerdings muss dafür auch zunächst der Bootloader über einen ISP-Programmer in den Chip geschrieben werden.

Im Grunde kann dafür jeder Bootloader benutzt werden, ich benutze zurzeit den Bootloader FastBoot von Peter Dannegger.[4]

Das Flashen geschieht mittels eines Bootloader über die serielle Schnittstelle (UART) des Atmega8 (Pins PD0 und PD1). Nutzt man diese Möglichkeit sollte man darauf achten, dass der Mikrocontroller auch keinen Fall direkt mit der seriellen Schnittstelle des PCs verbunden werden darf. Beachtet man das nicht zerschießt man sich die entsprechenden Eingangspins des Atmega. Es muss also zwingend eine Pegelwandlung (± 12V PC <> 0-5V µC) z.B. mit einem MAX232 (es gibt in der Bucht auch schon fertige Schaltungen direkt mit USB/Seriell-Wandler für ~2€) durchgeführt werden. Die Belegung der Stiftleiste kann unter Pinbelegung nachgelesen werden.

Protokollierung

Über die serielle Schnittstelle erfolgt nicht nur das Flashen des Mikrocontrollers. Die Schnittstelle kann unter anderem auch zum Debuggen bei der Fehlersuche genutzt werden. Ein weiteres nettes Feature ist das Protokollieren von Messwerten wie z.B. Drehzahl, Geschwindigkeit, Rundenzeit um nur einige Beispiele zu nennen. Zum Protokollieren benutze ich zur Zeit den Datenlogger "Openlog" von Sparkfun[5]. Der Datenlogger protokolliert die Messwerte auf eine microSD-Karte und kommt erstaunlicherweise direkt mit den 5V-Pegel der seriellen Schnittstelle zurecht (Datennlogger wird intern mit 3,3V betrieben). Um den Datenlogger nutzen zu können muss man lediglich die CONFIG.TXT auf die richtige Baudrate einstellen (Ich benutze per Default 19200 baud/sek).

Hier noch ein Beispiel wie die Ausgabe über die serielle Schnittstelle mit dem Mikrocontroller durchgeführt werden kann. Das Senden erfolgt zurzeit in der main-Routine mit jedem Schleifen durchlauf ist also in keinem Fall zeit-deterministisch. Möchte man die Ausgabe immer in gleich Zeitabständen durchführen muss das mit Hilfe eines Timers erfolgen.

USART_send(utoa(pre_rpm, buffer, 10)); // Drehzahl ausgeben
USART_send(",");
USART_send(utoa(kph, buffer, 10)); // Geschwindigkeit ausgeben
USART_send("\n");

Die Ausgabe über die serielle Schnittstelle erfolgt in diesem Beispiel als CSV (comma seperated value) mit anschließenden Newline.

EEPROM

Das EEPROM dient dazu diverse Werte im Mikrocontroller zu speichern die auch nach einem Spannungsverlust erhalten bleiben sollen. Aktuell werden die folgenden Daten im EEPROM des Mikrocontrollers gespeichert:

  • Einstellungen (Anzahl der Magnetschleifen, Reifenumfang, Ritzel- und Kettenradzahl)
  • Bestzeit + dazugehörige Zwischenzeiten

Möchte man die Daten die im EEPROM gespeichert sind zurücksetzen (um z.B. den Laptimer auf einer anderen Rennstrecke einzusetzen) muss man den Ok/Exit-Taster bei einschalten des Laptimers gedrückt halten. Die nun erfolgte Inititialisierung wird durch den Text "Init. EEPROM!" auf dem Display und dem Blinken des Bestzeit-LED signalisiert.

Nachfolgend sind die Standardwerte angegeben die nach der erfolgten Initialiserungen für die Einstellungen im EEPROM stehen:

  • Magnetschleifen = 3
  • Reifenumfang = 0.86
  • Ritzel = 10
  • Kattenrad = 86


Bugs

In der Version 1.0:

  • Funktion "EEPROM_readBesttime()" ließt falsche ticks ein.
// EEPROM leer --> Defaultwert übernehmen
if(temp_word == EEPROM_WORD_DEF) besttime->ticks = EEPROM_WORD_DEF;
else besttime->ticks = temp_byte;

ersetzen durch

// EEPROM leer --> Defaultwert übernehmen
if(temp_word == EEPROM_WORD_DEF) besttime->ticks = EEPROM_WORD_DEF;
else besttime->ticks = temp_word;

Genauigkeit

Da alle alle Zeiten aus dem externen Quarz abgeleitet werden, hängt die Genauigkeit (Rundenzeiten, ...) maßgeblich von der Genauigkeit des verwendeten Quarz ab. Dieser ist natürlichen Schwankungen aufgrund von Temperatureinflüssen und Alterung unterlegen. Der von mir eingesetzte 4 MHz Quarz im HC-49/US Gehäuse weist einen Temperaturkoeffizient von ± 30 ppm und eine Frequenztoleranz von ebendfalls ± 30 ppm auf. Bei der Verschaltung des Quarz ist außerdem auf die richtige Lastkapazität zu achten entspricht diese nicht genau der vom Hersteller angebenen Kapazität kommt eine weitere Fehlerquelle hinzu. Dabei gilt: Bei kleinere Last schwingt der Quarz schneller und bei größerer Last langsamer. Ausgehend von

umgestellt nach

ergibt sich für einen 4 Mhz Quarz eine Gangabweichung von 120 Hz bei einer Frequenztoleranz von ± 30 ppm.

Quarz-Typ Frequenzfehler (25°C) Abweichung (min/jahr) Abweichung (s/tag) Abweichung (s/h) Abweichung (ms/min) Frequenabw. bei 4 MHz
Standardquarz (Grundtton) 30 ppm 16 min/jahr 2,6 s/tag 108 ms/h 1,8 ms/s 120 Hz

Das bedeutet konkret das man bei einer Rundenzeit von ca. 10 min mit einen Fehler von 18 ms rechnen muss. Lässt man auch noch Temperatureinflüsse mit in die Rechnung einfließen kann sich das ganze im Worst-Case noch verdoppeln. Alterungseffekte wurden dabei außer acht gelassen.

Abstimmen des Quarz

Möchte man diese einflüsse so gut es geht kompensieren bietet es sich an zunächst einmal zu schauen mit welcher Frequenz der Quarz den tatsächlich schwingt. Das gestaltet sich allerdings nicht so einfach wie es auf den ersten Blick aussieht. Da der Quarz schon durch geringe Kapazitäten von einigen pF messbar verstimmt wird, würde ein direktes Messen den Quarz messbar verstimmen. Um das Problem zu umgehen kann man den Takt über den Vorteiler eines Timers auf einen IO-Pin ausgeben und dann dort messen.[6]

Zum Abstimmen erstzt man einen Lastkondensator durch einen Trimmkondensator (0-47pF). Dieser wird dann so lange verdreht bis die gewünschte Frequenz auf dem Display des Frequenzzählers angezeigt wird.[7]

Funktionsweise

In den nachfolgenden Kapiteln wird ausführlich auf einzelnen Komponenten des Laptimers eingegangen.

Magnetstreifen

In die Kartbahn sind in der Regel an bestimmten stellen Magnetstreifen eingelassen. Dazu werden einfache Permanent-Magneten genutzt (siehe Quelle Patenschrift). Ein Magnetstreifen sollte sich auf jedenfall auf der Start-/Ziellinie befinden. Je nach Kartbahn gibt es aber auch mehrere Magnetstreifen auf der Strecke die es ermöglichen Zwischenzeiten zu erfassen. Die Magnetstreifen sind i.d.R. ca. 1,8cm breit und umfassen die ganze Länge der Kartbahn. Ein Reed-Kontakt schaltet in ca. 2cm Abstand, von den Permanent-Magneten wobei dieser Wert stark von der Empfindlichkeit des Reed-Kontaktes abhängt.

Rundenzeit erfassen

Um die Rundenzeit zu erfassen, muss zunächst einmal das Magnetfeld des Magnetstreifens beim überfahren erfasst werden. Dazu kann man einen Hallsensor oder Hallschalter einsetzen. Es sollte aber auch eine Reed-Kontakt reichen. Beim überfahren des Magnetstreifens wird der Reed-Kontakt für kurze Zeit geschlossen. Der dadurch entstehende Pegelwechsel wird über den Mikrocontroller über den ICP-Pin (Input Capture) erfasst.

Drehzahl erfassen

Die Drehzahl wird Potentialfrei gemessen was kapazitiv über einen Draht geschieht der um das Zündkabel (Verbindung Zündkerze <> Zündspule) gewickelt wird.

Geschwindigkeit erfassen

Bei der Erfassung der Geschwindigkeit gibt es zwei möglichkeiten. Die Drehzahl der hinteren Achse kann direkt erfasst werden, was z.B. ebendfalls mittels eines Reed-Kontaktes und Magnet (wie beim Fahrrad) geschehen kann. Es besteht aber auch die Option die Geschwindigkeit aus der Drehzahl und dem Übersetzungsverhältnis (Ritzel <> Kettenrad) zu berechnen.

Berechnungen

Rundenzeit

Zunächst berechne ich wielange der Reed-Kontakt Zeit hat um im "Worst-Case" zu schließen. Dazu nehme ich eine Geschwindigkeit von beim überfahren der Magnetschleife, an.

Der Reed-Kontakt hat also ca. 0,65 ms Zeit um zu schließen. Bei 50 km/h dementsprechend doppelt so viel (1,3 ms).

Drehzahl / Geschwindigkeit

Das ermitteln der Drehzahl bzw. der Geschwindigkeit gestaltet sich ähnlich. Es wird bei beiden zu messenden Größen die Zeitdifferenz die zwischen zwei Impulsen gemessen und daraus dann die entsprechende Geschwindkeit bzw. Drehzahl berechnet. Da sich Drehzahl und Geschwindigkeit grob nur in dem Übersetzungsverhältnis zwischen Ritzel und Kettenrad unterscheiden, kann mitunter von einer expliziten zusätzlichen Erfassung der Geschwindigkeit oder Drehzahl abgesehen werden.

Eine Umrechnung der Geschwindigkeit aus der Drehzahl erfolgt nach der folgenden Gleichung:




Berechnungen die ich dazu angestellt habe befinden sich im nachfolgenden Bild oder in dem dazugehörigen PDF im Anhang.

Anmerkung: Nach jedem "Tick" sind 100µs vergangen.

Steuerung

Die (Menü-)Steuerung des Laptimers geschieht über drei auf der Vorderseite angebrachte Taster (Ok/Exit, Hoch, Runter). Die Taster dienen zum einem dazu das Navigieren durch das Menü zu ermöglichen zum anderen aber auch zum zurücksetzen von gespeicherten Rundenzeiten oder zum zurücksetzen des Laptimers.

Einstellungen

Durch drücken des Ok/Exit-Tasters kommt man ins Einstellungen-Menü. In dem Menü können die folgenden Einstellungen vorgenommen werden:

  • Anzahl der Magnetschleifen (1...6)
  • Reifenumfang in Meter (0.01...2.50)
  • Anzahl der Zähne des Ritzels (0...25)
  • Anzahl der Zähne des Kettenrades (0...100)

Durch drücken des Hoch- oder Runter-Tasters kann der jeweilige Menüpunkt ausgewählt werden, was durch ein Symbol (>) vor dem Menüpunkteintrag kenntlich gemacht wird. Durch drücken der Ok/Exit-Taste kann der Wert ausgewählt und über den Hoch- oder Runter-Taster editiert werden. Durch drücken der Ok/Exit-Taste wird der Wert übernommen.

Bei der Ermittlung der Geschwindigkeit gibt es die Möglichkeit diese aus der Drehzahl des Motors zu berechnen oder diese über den Sensor an der Hinterachse zu bestimmen. Durch die Wahl der Ritzel-/Kettenradzahl kann man aus einen der beiden Optionen wählen.

Sind für Ritzel und Kettenrad Werte ungleich Null eingestellt, wird die Geschwindigkeit aus der Drehzahl des Motors berechnet (Belastet den Mikrocontroller mit zusätzlichen Berechnungen). Wurde für die Ritzel- und Kettenradzahl hingegen Null eingestellt wird die Geschwindigkeit mittels des Geschwindigkeitssensors direkt an der Hinterachse ermittelt. Die zweite Option ist deutlich weniger Rechenintensiv und sollte deswegen wenn möglich bevorzugt werden.

Durch längeres Drücken (>3sek) des Ok/Exit-Tasters werden die geänderten Werte ins EEPROM des Mikrocontrollers geschrieben. Das schreiben wird durch eine Nachricht "Save to EEPROM" auf dem Display und durch Blinken der Bestzeit-LED signalisiert. Die Änderungen sind dann dauerhaft in dem Mikrocontroller gespeichert.

Bilder

Schaltplan

File:Laptimer RevE sch.pdf

Quelltext

Aktuelle Version: 1.1

Quelltext ver1.1: File:Laptimer Code ver1.1.pdf
Laptimer Project Files ver1.1 (AVR-Studio 4): File:Laptimer ver1.1.zip

Quelltext ver1.0: File:Laptimer Code ver1.0.pdf
Laptimer Project Files ver1.0 (AVR-Studio 4): File:Laptimer ver1.0.zip

TODO

  • THT-Layout verbessern (kleiner, integrierte Spannungsversorgung)
  • SMD-Layout erstellen
  • Menü-Auswahl Drehzahl 2-Takt / 4-Takt
  • Kommunikation über RS232 mit anderer Peripherie (GPS, Accelerometer, Lenkwinkel, ...)
  • Praktischer Test: Drehzahl schwankt sehr stark? (Einstreuungen)
  • Praktischer Test: Geschwindigkeit erreicht irgendwann einen Endwert.
  • Kontrast des Display schlecht bei Sonneneinstrahlung
  • Ablesen durch Vibrationen teilweise schwierig
  • Grafik-Display Unterstützung

Quellen

Schaltplan Drehzahlteil
Patentschrift Alfano E.P.0632350
File:Laptimer Drehzahl Geschwindigkeit.pdf

Einzelnachweise

Cookies help us deliver our services. By using our services, you agree to our use of cookies.