Speeduino-Fork · Teensy 4.1 · GPL v3

Die
Software.

Die Hardware ist nur die halbe ECU. Hier zeige ich, was die Firmware zwischen zwei Zahnflanken wirklich rechnet — Drehzahl, Einspritzung, Zündung, Ladedruck. Zum Anfassen, mit echten Werten aus meinem Tune.

600 MHz
Cortex-M7
20 µs
Loop-Zeit
±0,05°
Zünd-Genauigkeit
TunerStudio mit dem Hayabusa-Tune: Kennfelder und Live-Daten TunerStudio · Live-Tuning
Der Weg eines Zündfunkens

Der Ablauf.

Von der Zahnflanke am Kurbelwellensensor bis zum Funken vergehen wenige Millisekunden — und dazwischen läuft diese Kette. Jeden Block antippen für Details.

alle 20 µs · Hauptloop RPM · Winkel MAP · TPS · CLT … VE % · Zündwinkel × Korrekturen µs & Grad Spule · Injektor 1 · Trigger-Decoder Interrupt pro Nocke 2 · Drehzahl & Winkel Festkomma · µs pro Grad 3 · Sensoren lesen gefiltert · kalibriert 4 · Kennfeld-Lookup 16×16 · bilinear interpoliert 5 · Korrekturen Sprit ×% · Zündung ±° 6 · Pulsweite & Winkel PW µs · Dwell · Zündwinkel 7 · Scheduler Winkel → Timer-Compare 8 · Endstufen MC33810 · 4× Zünd + 4× Inj

↑ Block antippen — hier erscheinen die Details.

Rechnung 01

Drehzahl & Kurbelwinkel.

Alles beginnt mit einer Zeitmessung: Der Decoder stoppt die Mikrosekunden zwischen den Flanken des Geberrads. Das Kurbelrad der Hayabusa trägt acht gleichmäßig verteilte Nocken — alle 45° eine Flanke, acht Messpunkte pro Umdrehung. Daraus leitet die Firmware ihre wichtigste Größe ab: Wie viele Mikrosekunden dauert ein Grad Kurbelwinkel?

RPM = 60.000.000 µs / TUmdrehung
µs pro Grad = TUmdrehung / 360

Bei 11.000/min bleiben pro Grad nur noch ~15 µs. Deshalb rechnet die Firmware hier nicht mit Gleitkomma, sondern mit Festkomma-Arithmetik — Winkel→Zeit als UQ24.8, Zeit→Winkel als UQ1.15. Das ist auf dem Cortex-M7 in wenigen Takten erledigt und driftet nicht.

Mehr Details
  • Code: crankMaths.cppangleToTimeMicroSecPerDegree() und timeToAngleDegPerMicroSec(), beide inline und LTO-optimiert.
  • Der Umrechnungsfaktor wird einmal pro Umdrehung neu gesetzt (setAngleConverterRevolutionTime()), nicht bei jedem Aufruf neu dividiert.
  • Sync: Weil alle acht Nocken gleich aussehen, liefert das Kurbelrad allein keine Referenz. Erst die einzelne Nocke an der Nockenwelle legt fest, welche Flanke welchem Kurbelwinkel entspricht — und welche der beiden Umdrehungen des Viertakts gerade läuft. In Speeduino ist das der Dual-Wheel-Decoder: gleichmäßiges Kurbelrad plus Nockenwellen-Sync.
  • Auflösung: 45° zwischen zwei Flanken sind grob. Zwischen den Nocken rechnet die Firmware linear aus der zuletzt gemessenen Umdrehungszeit hoch — bei stark wechselnder Drehzahl ist genau das die größte Fehlerquelle im Zündwinkel.
  • Verliert der Decoder den Sync, fällt die Firmware kontrolliert auf semi-sequentiellen Betrieb zurück, statt zu raten. Das aktuelle Tune fährt ohnehin schon so — Sequential kommt erst, wenn der Trigger-Winkel am Motor bestätigt ist.
  • Gemessene Genauigkeit auf dem Teensy 4.1: Trigger-Jitter < ±0,1°, Zündung ±0,05°.
1 Umdrehung · 8 Nocken 6.000 µs 1 Nocke · 45° 750 µs 1 Grad Kurbelwinkel 16,7 µs Animation 100× verlangsamt
Bei 10.000/min: ein Grad = 16,7 µs — die Loop läuft ~300× pro Umdrehung.
Rechnung 02

Die Einspritzformel.

8,1 msPulsweite
40 %Duty Cycle
17,0 msPW-Limit (85 %)
0 %Duty-Limit 85 % ↑100 %
Arbeitszyklus (720°) Injektor offen: 8,1 ms von 20,0 ms Zyklus
Alles im Rahmen.

Die zentrale Formel der Einspritzung. Ausgangspunkt ist REQ_FUEL — die Einspritzzeit, die bei 100 % Zylinderfüllung genau das Stöchiometrie-Gemisch (14,7:1) ergibt. In meinem Tune: 8,3 ms pro Arbeitszyklus — gerechnet aus 1299 cm³, vier Zylindern und den Stock-Düsen mit 257 cm³/min. Das VE-Kennfeld sagt, wie voll der Zylinder wirklich ist, der MAP-Anteil skaliert mit dem Druck, und am Ende kommt die Injektor-Öffnungszeit dazu.

PW = REQ_FUEL · VE/100 · MAP/Baro · Korr/100 + töffn
   = 8.300 µs · VE · Druck · Korrekturen + 1.000 µs

Die harte Grenze ist der Duty Cycle: Der Injektor braucht Zeit zum Schließen. Die Firmware deckelt die Pulsweite auf 85 % des Arbeitszyklus — dreh den Regler hoch und schau, wann das Limit zuschlägt. Genau da entscheidet sich beim Turbo-Umbau die Injektorgröße: Mit den Stock-Düsen ist bei rund 150 PS Schluss. → Düsen im Tune

Mehr Details
  • Code: fuel_calcs.cpp — die Kette computeInitialPw → applyMapMode → applyAFRMultiplier → applyCorrections → includeOpenTime → includeAe, komplett in Ganzzahl-Arithmetik.
  • Der MAP/Baro-Faktor ist im aktuellen Tune abgeschaltet: Die Last kommt über die Drosselklappe, und ohne Lader bleibt der Saugrohrdruck unter dem Umgebungsdruck. Sobald der Turbo dran ist, muss er an — der Regler oben zeigt, was er dann rechnet.
  • Die Öffnungszeit (1,0 ms) wird batteriespannungs-korrigiert: Bei 6 V braucht der Injektor das 2,5-fache, ab 14 V den einfachen Wert (injOpen × batCorrection).
  • Einspritzung läuft derzeit semi-sequentiell, gepaart 1+3 und 2+4: zwei Einspritzungen pro Arbeitsspiel, dafür völlig unabhängig von der Nockenwellenphase. Die Firmware halbiert REQ_FUEL dafür selbst. Vollsequentiell wird erst umgestellt, wenn der Trigger-Winkel mit der Blitzpistole bestätigt ist.
  • PW-Limit im Code: dutyLim % × T_rev × 2 / nSquirts — die ×2, weil ein Viertakt-Zyklus zwei Umdrehungen dauert.
  • Beschleunigungsanreicherung (AE) reagiert auf die Änderungsrate der Drosselklappe (TPS-DOT) — das Pendant zur Beschleunigerpumpe beim Vergaser.
Rechnung 03

Die Korrekturen-Kette.

Das Kennfeld gilt für den warmen Motor am Meer. Alles andere regeln Korrekturen: Jede liefert einen Prozentwert, alle werden multipliziert und skalieren am Ende die Pulsweite. Kalter Motor? +30 %. Heiße Ansaugluft? Dünnere Luft, −5 %. E85 im Tank? Braucht ~30 % mehr Volumen.

Korr = WUE · ASE · AE · Lambda · IAT · Baro · Flex · … (Cap: 1500 %)

Bei der Zündung läuft dieselbe Idee als Addition: Der Basis-Zündwinkel aus dem Kennfeld wird Stufe für Stufe verschoben — Ethanol-Anteil früher, heiße Ansaugluft später, Klopfen sofort später. Fixe Winkel (Kurbeln, Grundeinstellung) überschreiben am Schluss alles.

Mehr Details — die echten Ketten aus dem Code
  • Sprit (correctionsFuel()): Warmup (WUE) → Nachstart (ASE) → Kurbeln → Beschleunigung (AE) → Flood Clear → Lambda-Regelung (PID) → IAT-Dichte → Baro → Flex Fuel → Sprittemperatur → Launch → Schubabschaltung (DFCO). Produkt gedeckelt bei 1500 %.
  • Zündung (correctionsIgn()): Flex → Wasser-Methanol → IAT-Retard → CLT-Advance → Leerlauf → Soft-Limiter → Nitrous → Soft-Launch → Flat-Shift → Klopf-Retard → DFCO → fixe Winkel zuletzt.
  • Aktiv sind heute nur Warmlauf, Nachstart, Anlassen, Ansaugluftdichte und Beschleunigung. Lambda-Regelung, Flex-Fuel, Klopf-Retard und Schubabschaltung sind im Tune abgeschaltet — die Regler oben zeigen, was die Kette rechnet, sobald sie an sind. Eine Regelung, die auf ein noch grobes VE-Kennfeld draufregelt, verdeckt genau die Fehler, die man finden will. → Korrekturen im Tune
  • Die Lambda-Regelung ist ein eigener PID-Regler, der die Breitband-Sonde gegen den AFR-Zielwert aus dem Kennfeld regelt — aber nur im eingeschwungenen Betrieb, nicht beim Beschleunigen.
  • Klopf-Retard (in Arbeit, zusammen mit der TPIC8101-Hardware): Klopfen erkannt → Zündung sofort ein paar Grad später, danach langsame Erholung in 0,5°-Schritten.
  • Batteriespannung korrigiert nicht die Pulsweite, sondern gezielt die Injektor-Öffnungszeit und den Dwell — dafür gibt es getrennte Spannungs-Kennlinien.
WUE 100 % × IAT 100 % × Flex 100 % × λ 100 % = 100 %
70 %100 %200 %
Betriebswarm, Normalluft — Kennfeld pur.

Kennlinien beispielhaft — die echten Kurven liegen im Tune.

Rechnung 04

Zündwinkel & Dwell.

OT Spule lädt (Dwell) Zündfunke Dwell in Grad 96° Ladebeginn 114° v. OT
1,6 ms Dwell bei 10.000/min = 96° Kurbelwinkel nur fürs Laden.

Die Zündspule braucht eine feste Ladezeit (Dwell) — in meinem Tune 1,6 ms im Betrieb und 2,5 ms beim Anlassen, weil die Bordspannung dann einbricht. Das Problem: Die Firmware plant in Millisekunden, die Kurbelwelle dreht aber in Grad. Also wird der Dwell mit dem µs-pro-Grad-Faktor aus Rechnung 01 in einen Winkel umgerechnet und rückwärts vom Zündzeitpunkt geplant.

Dwell° = Dwell µs · Grad/µs
Ladebeginn = Zündwinkel + Dwell°  (vor OT)

Dreh die Drehzahl hoch: Aus denselben 1,6 ms werden immer mehr Grad — bei 10.000/min lädt die Spule gut ein Viertel der Umdrehung. Genau deshalb hat die Busa vier Einzelspulen mit je einem eigenen IGBT. Im aktuellen Tune läuft die Zündung als Wasted COP: Jede Spule feuert zweimal pro Arbeitsspiel, also einmal pro Umdrehung — auch in den Ausstoßtakt hinein. Das ist phasenunabhängig und deshalb sicher für die Inbetriebnahme, halbiert aber die Pause zwischen zwei Ladevorgängen. → Dwell im Detail

Mehr Details
  • Code: computeDwell() in dwell.cpp — beim Kurbeln gilt ein eigener (viel längerer) Dwell, im Betrieb wahlweise Festwert oder 3D-Dwell-Kennfeld.
  • Dwell wird spannungskorrigiert: bei 6 V das 2,4-fache, bei 10 V das 1,5-fache, ab 14 V der einfache Wert (correctionsDwell()).
  • Sicherheitsnetz im Tune: Dwell-Limit 4 ms — eine dauerbestromte Spule brennt sonst ab. Zusätzlich sitzt in der Rev-3.0-Hardware der Watchdog-Not-Aus dahinter.
  • Zündmodus heute: Wasted COP, phasenunabhängig. Mit bestätigtem Trigger-Winkel wird auf sequentiell umgestellt — dann feuert jede Spule nur noch auf ihren eigenen Verdichtungstakt und die thermische Belastung halbiert sich.
  • Der Scheduler übersetzt „Zündung in 23°“ per angleToTimeMicroSecPerDegree() in einen Timer-Compare-Wert — der Funke kommt aus dem Hardware-Timer, nicht aus der Loop. Deshalb ±0,05°.
Rechnung 05

Boost-Regelung — PID.

Fürs Turbo-Kapitel: Das Wastegate-Ventil wird per PWM angesteuert, und ein PID-Regler vergleicht den gemessenen Ladedruck (MAP) mit dem Ziel aus dem Boost-Kennfeld (Drosselklappe × Drehzahl). Zu wenig Druck → mehr Duty, Wastegate bleibt zu. Zu viel → Duty runter, Abgas am Turbo vorbei.

e = Ziel − MAP
Duty = Kp·e + Ki·∫e dt + Kd·de/dt

Spiel mit den Reglern: Viel Kp reagiert schnell, überschwingt aber — beim Ladedruck heißt Überschwingen Overboost. Ki bügelt den Restfehler weg, macht es aber träge-schwingend, wenn er zu groß wird.

Mehr Details
  • Code: boostControl() in auxiliaries.cpp — Integer-PID (integerPID_ideal) direkt auf MAP → boostDuty.
  • Die Regelung aktiviert sich erst oberhalb des Umgebungsdrucks — darunter regelt nur die Wastegate-Feder, der Regler sammelt keinen sinnlosen Integral-Fehler.
  • Flex-Boost: Mit steigendem Ethanol-Anteil darf das Ziel automatisch höher (E85 kühlt und klopft später) — eigene Korrekturtabelle auf dem Boost-Ziel.
  • Ehrlicher Stand: Im aktuellen Tune ist Boost Control aus; als letzte Sicherung liegt ein Ladedruck-Cut bei 200 kPa hinterlegt — der Motor läuft erst noch ohne Turbo ein. Die Regelstrecke hier ist eine vereinfachte Simulation.
  • Übers Boost-Kennfeld ist auch Boost by Gear möglich — weniger Ladedruck in den unteren Gängen, damit das Hinterrad Bodenkontakt behält.
250 175 100 MAP in kPa · Ziel gestrichelt
100 kPaMAP
200 kPaZiel (1,0 bar)
0 %Wastegate-Duty
Ziel springt alle paar Sekunden — beobachte das Einschwingen.
Wenn's ernst wird

Schutz & Grenzen.

Die Firmware kann all das hier. Wie viel davon im aktuellen Tune wirklich scharf ist, steht in jeder Karte dabei — bei einer Erstinbetriebnahme sind die Grenzen absichtlich eng und die Komfortfunktionen aus. → Schutz im Tune

01 / Drehzahl

Zweistufiger Limiter

Ab 5.500/min stellt der Soft-Limiter die Zündung auf feste 10° und macht den Motor spürbar stumpf, bei 6.000/min schneidet der Hard-Cut Sprit und Zündung. Zwei Stufen, damit der Übergang kein Schlag ins Getriebe ist. Bewusst weit unter dem Serien-Begrenzer von 10.900 — er wird erst schrittweise angehoben, jede Stufe mit Datenlog.

02 / Motor

Engine Protection

Öldruck, Kühlmitteltemperatur, Overboost: Verlässt ein Wert seinen Bereich, drosselt die Firmware über einen eigenen Schutz-Drehzahlbegrenzer, statt einfach weiterzufeuern.

03 / Sensorik

Fail-Safe-Werte

Liefert ein Sensor Unsinn (MAP unplausibel, CLT abgesteckt), rechnet die Firmware mit sicheren Ersatzwerten weiter und meldet den Fehler — liegen bleiben ist keine Option.

04 / Spulen

Dwell-Limit

Maximal 4 ms Bestromung pro Ladevorgang — egal, was Kennfeld oder Spannungskorrektur sagen. Dahinter sitzt zusätzlich der Hardware-Watchdog der Rev-3.0-Platine.

05 / Schub

DFCO

Schubabschaltung: Gas zu, Drehzahl hoch → Einspritzung komplett aus, mit sanftem Taper beim Wiedereinsetzen. Spart Sprit und hält den Auspuff kühl — außer man will Anti-Lag. Im aktuellen Tune aus, weil der Übergang ein sauber abgestimmtes Kennfeld voraussetzt.

06 / Start

Launch & Flat-Shift

Launch Control hält die Drehzahl am Start per Zünd-Cut, der Quickshifter (über das Sensor-Modul) schaltet unter Volllast ohne Kupplung — beides über dieselbe Zündwinkel-Maschinerie. Beides im aktuellen Tune aus — das kommt, wenn der Motor läuft.

Unter der Haube

Der Stack.

Die Firmware ist mein Fork des Open-Source-Projekts Speeduino — angepasst auf das Dropbear-Board-Konzept mit Teensy 4.1 und die Busa-Hardware. Kein Grund, ein Engine-Management von null zu schreiben: Speeduino ist seit Jahren in tausenden Motoren unterwegs, und jede Zeile ist nachlesbar.

Getunt wird über TunerStudio — Kennfelder live am laufenden Motor, dazu Datenlogging auf SD-Karte für die Analyse danach. Gebaut wird mit PlatformIO, ein Befehl vom Quellcode zum Flash: pio run -e teensy41 -t upload.

Code-Struktur
  • decoders.cpp — Trigger-Decoder (Interrupt-Ebene, jede Flanke zählt)
  • crankMaths.cpp — Festkomma-Umrechnung Winkel ↔ Zeit
  • fuel_calcs.cpp — Pulsweiten-Pipeline inkl. Staging & Duty-Limit
  • corrections.cpp — alle Sprit- und Zündkorrekturen, Lambda-PID
  • scheduler.cpp — Winkel → Hardware-Timer-Events
  • auxiliaries.cpp — Boost-PID, Lüfter, Nitrous, Wasser-Methanol
  • engineProtection.cpp — Limiter, Öldruck-/Overboost-Schutz
  • acc_mc33810.cpp — SPI-Ansteuerung des Treiber-ICs der Rev-3.0-Hardware
Basis
Speeduino-Fork
Lizenz
GPL v3
Build
PlatformIO
Tuning
TunerStudio
Flash-Nutzung
1,2 / 8 MB
RAM-Nutzung
256 KB / 1 MB
Einspritz-Timing
±10 µs
Status
Base-Tune in Arbeit
Häufige Fragen

FAQ.

Warum Speeduino und nicht selbst geschrieben?

Ein Engine-Management steckt voller Spezialfälle — Sync-Verlust, Sensor-Ausfälle, Limiter-Übergänge. Speeduino hat die alle schon durchlebt und ist offen (GPL v3): Ich kann jede Berechnung nachlesen und für die Busa anpassen, statt jahrelang eigene Bugs zu jagen. Meine Anpassungen leben im eigenen Fork auf GitHub.

Was ist ein VE-Kennfeld?

Eine 16×16-Tabelle über Drehzahl und Last, die sagt, wie voll sich der Zylinder wirklich füllt (Volumetric Efficiency in %). Zwischen den Stützstellen wird bilinear interpoliert. Zusammen mit REQ_FUEL ergibt das direkt die Einspritzzeit — siehe Rechnung 02.

Woher weiß die ECU, welcher Zylinder dran ist?

Das Kurbelrad liefert den Winkel innerhalb einer Umdrehung — aber ein Viertakt-Zyklus dauert zwei. Erst das Nockenwellensignal löst die Mehrdeutigkeit auf. Mit diesem Sync können Einspritzung und Zündung vollsequentiell laufen: jeder Zylinder exakt auf seinen eigenen Takt. Im Erststart-Tune ist das noch nicht eingeschaltet — solange der Trigger-Winkel nicht mit der Blitzpistole bestätigt ist, läuft alles bewusst phasenunabhängig.

Läuft der Motor damit schon?

Noch nicht. Firmware und ein vollständiges Erststart-Tune stehen — alle Zahlen auf dieser Seite kommen daraus —, aber der Motor ist damit noch nie gelaufen. Als Nächstes: Sensoren kalibrieren, Trigger-Winkel mit der Blitzpistole bestätigen, Saugbetrieb einfahren. Erst danach kommt der Turbo und mit ihm Boost-Regelung und Klopfschutz. Die Hardware dafür ist fertig — beide Platinen sind durchgeroutet, Details auf der ECU-Seite.

Code lesen?

Der komplette Fork ist offen — oder schau dir an, auf welcher Hardware das alles läuft.