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.
TunerStudio · Live-Tuning
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.
↑ Block antippen — hier erscheinen die Details.
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?
µ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.cpp—angleToTimeMicroSecPerDegree()undtimeToAngleDegPerMicroSec(), 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°.
Die Einspritzformel.
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.
= 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 KettecomputeInitialPw → 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.
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.
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.
Kennlinien beispielhaft — die echten Kurven liegen im Tune.
Zündwinkel & Dwell.
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.
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()indwell.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°.
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.
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()inauxiliaries.cpp— Integer-PID (integerPID_ideal) direkt aufMAP → 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.
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
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.
Engine Protection
Öldruck, Kühlmitteltemperatur, Overboost: Verlässt ein Wert seinen Bereich, drosselt die Firmware über einen eigenen Schutz-Drehzahlbegrenzer, statt einfach weiterzufeuern.
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.
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.
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.
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.
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 ↔ Zeitfuel_calcs.cpp— Pulsweiten-Pipeline inkl. Staging & Duty-Limitcorrections.cpp— alle Sprit- und Zündkorrekturen, Lambda-PIDscheduler.cpp— Winkel → Hardware-Timer-Eventsauxiliaries.cpp— Boost-PID, Lüfter, Nitrous, Wasser-MethanolengineProtection.cpp— Limiter, Öldruck-/Overboost-Schutzacc_mc33810.cpp— SPI-Ansteuerung des Treiber-ICs der Rev-3.0-Hardware
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.