Impulsdauer / Impulszähler / Frequenzanzeige - Umsetzungsmöglichkeit fehlt.

Hallo Freunde,

ich bin gerade dabei eine Multifunktionsanzeige fürs Auto herzustellen...

Sie folgende Funktionen erfüllen:

  • Öltemperatur anzeigen
  • Öldruck anzeigen
  • Ladelufttemperatur anzeigen
  • Ladedruck anzeigen
  • Lambdawert anzeigen
  • Abgastemperatur anzeigen
  • Wassertemperatur anzeigen
  • Innenraumtemperatur anzeigen
  • Boardspannung anzeigen.

Außerdem (hier liegt der Knackpunkt) möchte ich:

Geschwindigkeit anzeigen UND Verbrauch errechnen. ----- LIKE MPGUINO

Alle anderen Funktionen sind Betriebsbereit, nur hier klemmts.

Mit Geschwindigkeit habe ich weniger das Problem. Hierfür sehe ich mehrere Möglichkeiten - Bloß die Umsetzung klappt nicht so richtig.
Als Signalquelle habe ich das VSS Signal, welches ein Rechtecksignal liefert - aus der Frequenz kann man die Geschwindigkeit ausrechnen bzw. aus Anzahl der Impulse die Entfernung.

So entsprechen bei meinem Fahrzeug 5083 Impulse ~ 1Km

was wiederrum bedeutet dass bei einer Geschwindigkeit von 250Km/h das VSS Signal eine Frequenz von 352,99Hz hat.
bei 200km/h - 282,39Hz
bei 150km/h - 211,79Hz
bei 100km/h - 141,94Hz
usw.

(werde die These mitm Oszi prüfen.)

daraus ergibt sich

f * 0,70824 = V

oder

1/T * 0,70824 = V

Die Geschwindigkeitsanzeige lässt sich somit irgendwie mit pulseIn() und millis() realisieren. Wie genau ist mir noch unklar.

Das größere Problem sehe ich in der Messung der Einspritzzeiten.

Um den Verbrauch möglichst genau zu Berechnen muss die Dauer des Einspritzzyklen aufgezeichnet werden.
(Werde später die Einspritzzeiten einer Tankfüllung messen und kann somit den Kraftstoffverbrauch berechen -> Zeit * x = Liter )

  • Theoretisch mit pulseIn() möglich, nur wenn die Funktion in void loop integriert ist kann man nicht gewährleisten dass genau zu der Einspritzzeit das Program an der Stelle ausgeführt wird. - oder habe ich einen Denkfehler?

  • Theoretisch möglich wäre auch die benutzung von attachInterrupt() - nur gibts hier auch ein Problem. Bei 8000U/min werden ca. 70 Einspritzzyklen/Sec durchgeführt. Der Dutycycle beträgt hierbei bis zu 85%.
    Reicht die verbleibende Zeit um das restliche Programm auszuführen?

Mein anderes Problem ist, wenn ich das Arduino ausschalte möchte ich dass die Daten gespeichert bleiben(Zumindest die Einspritzzeiten) Wie und wohin speichere ich sie? externes i2c eeprom?

Ich erwäge momentan ob ich für die Aufzeichnung der Einspritzzeiten nicht von einem separaten Arduino ausführen lasse, der außerdem nichts anderes macht und sie dann über i2c an das "Hauptarduino" übertrage.

Vieles ist mir gerade beim schreiben und Biertrinken klar geworden :slight_smile: ganz schlüssig bzgl. der Ausführung bin ich dennoch nicht.

Was haltet ihr davon? Habt ihr Vorschläge oder Anregungen?

Beste Grüße und vielen Dank im Vorraus.

pulseIn geht nicht, weil das hängt bis die erwartete Flanke gekommen ist.

Ich würde Interrupts nehmen. 70 Pulse / sec ist zwar kein Problem, aber mit einer Interrupt-Routine geht am sichersten nichts verloren.
Den anderen Interrupt kannst du für die Geschwindigkeit nehmen.

Die Interrupt-Routinen sammeln nur und sind so sehr schnell.
Die loop() ist dadurch sehr unkritisch, kann lange brauchen wie sie will, und verarbeitet die Pulse seit dem letzten Mal.

Zum Speichern der aufsummierten Zähler (Strecke / Einspritzdauer) würde ich z.B. den RAM einer RTC nehmen. Für die nächste Erweiterung ( Logging ) brauchst du die Uhr sowieso :wink:

@Orestos Du schreibst nicht, ob dein Fahrzeug ein Diesel oder ein Benziner ist.
Ist es nicht einfacher diese Daten vom CAN-Bus zu holen?

Grüße Uwe

es ist ein Benziner, kein Motorcanbus.
Man könnte die Daten aber aus OBD holen.

Das VSS-Signal und Injektorsignal habe ich aber bereits am Radiostecker. Muss sie nur abgreifen.

ist ein möglich in einem Interrupt mit millis() zu arbeiten? Meinte irgendwo zu lesen dass man es nicht machen sollte.

Wie könnte ich die Zeit eines Interrupts sonst "stoppen"?

nehmen wir an ich belege 2 Interrupt Pins mit dem selben signal (hab eh ein Mega) und mach folgendes:

void setup() {
volantine unsigned long startP=0;
volantine unsigned long endP=0;
volantine unsigned long lasttime=0;
volantine unsigned long time=0;

double liter;

attachInterrupt(0, startPulse, RISING);
attachInterrupt(1, endPulse, FALLING);
}

void loop () {

liter=time/1000/60*.44; //millis -> min * 0.44 da die Einspritzdüsen 440cc/min liefern.

}

void startPulse () {

startP = millis();

}

void endPulse (){

endP = millis();
lasttime = endP - startP;
time= time + lasttime;
}

würde es funktionieren?

für Geschwindigkeit/Entfernung das selbe Prinzip? Wobei da 1 interrupt Pin reichen würde. Muss nur die Zeit von Flanke bis Flanke wissen.

ist ein möglich in einem Interrupt mit millis() zu arbeiten? Meinte irgendwo zu lesen dass man es nicht machen sollte.

Geht.
Du kannst nur nicht erwarten, dass millis() sich jemals ändert, solange du in der ISR bist.

Nicht so:

void MyIRHandler() {
   unsigned long now = millis();
   while ( millis() == now ) {} // wait for the next millisecond
   // kommt hier nicht hin und loop() steht auch
}

ich belege 2 Interrupt Pins mit dem selben signal

Nicht nötig. attachInterrupt(0, MyHandler, CHANGE); ist genau dafür gemacht.
Innerhalb MyHandler kannst du dann mit if / else beide Fälle abhandeln.

In loop solltest du allerdings während des Zugriffs auf time die Interrups zumachen, da sich sonst während des Bearbeitens einzelne bytes ändern können und loop() dann weder mit dem alten, noch dem neuen Wert arbeitet.
Du hast dann die Wahl, unter geschlossenem Interrupt entweder deine float-Berechnung zu machen oder erstmal nur in eine andere lokale unsigned long Variable umkopieren.

Nicht nötig. attachInterrupt(0, MyHandler, CHANGE); ist genau dafür gemacht.
Innerhalb MyHandler kannst du dann mit if / else beide Fälle abhandeln.

quasi so?

void MyHandler(){

if(pinInjektor==HIGH){
    startP = millis();
}

else {
   endP = millis();
   lasttime = endP - startP;
   time= time + lasttime;
}

In loop solltest du allerdings während des Zugriffs auf time die Interrups zumachen, da sich sonst während des Bearbeitens einzelne bytes ändern können und loop() dann weder mit dem alten, noch dem neuen Wert arbeitet.
Du hast dann die Wahl, unter geschlossenem Interrupt entweder deine float-Berechnung zu machen oder erstmal nur in eine andere lokale unsigned long Variable umkopieren.

Den zweiten Satz verstehe ich nicht ganz.

Für meinen Boostcontroller waren diese ganzen Sachen zu "Zeitraubend" ich habe die Auswertung der Drehzahl mit LM2907 gemacht ist im Insgesamten einfacher. un die Einspritzeiten habe ich mit einem LM358 gemacht und dann eine "analog Spannung" 0-5V (0-100%) ontime gemacht.
Gruß
Der Dani

volvodani:
Für meinen Boostcontroller waren diese ganzen Sachen zu "Zeitraubend" ich habe die Auswertung der Drehzahl mit LM2907 gemacht ist im Insgesamten einfacher. un die Einspritzeiten habe ich mit einem LM358 gemacht und dann eine "analog Spannung" 0-5V (0-100%) ontime gemacht.
Gruß
Der Dani

du meinst 1 arduino ist mit der Berechnung überfordert?
mich hindert auch nichts dran 2 Arduinos zu verwenden :slight_smile:

ich sollte nurnoch wissen wie ich das anstelle:

In loop solltest du allerdings während des Zugriffs auf time die Interrups zumachen

sonst könnte es doch so funktionieren:

const int pinInj = 2;
const int pinVSS = 3;
const int InjSize = 0.440;
const int InjNumber = 4;
const double kmPulses = 5085.3;
const long VSSfactor = 0.70792; // km/h / f

volatile unsigned long startP =0;
volatile unsigned long endP =0;
volatile unsigned long lasttime =0;
volatile unsigned long time =0;
volatile unsigned long tVSSnow=0;
volatile unsigned long tVSSlast=0;
volatile unsigned long tVSS=0;
volatile unsigned long time2=0;
volatile long VSScount=0;

unsigned long timestamp =0;
unsigned long lasttimestamp =3000;

double liter; // Liter 
double liter2s; // Liter der letzten 2sec 
double km; // Entfernung in km
double lkm; // l/100km
double alkm; // durchschnitts l/100km
double v;  // Geschwindigkeit





void setup() {
  
pinMode(pinInj, INPUT);
pinMode(pinVSS, INPUT);
  

attachInterrupt(0, Injection, CHANGE);
attachInterrupt(1, Speed, RISING);
}








void loop () {

liter=time/1000/60*InjSize*InjNumber; 

v=1/tVSS*VSSfactor;

km=VSScount/kmPulses;

alkm=liter/km/100;

timestamp=millis();

if(timestamp-lasttimestamp>=2000){
liter2s=time2/1000/60*InjSize*InjNumber;
time2=0;
lasttimestamp=timestamp;
}

lkm=liter2s*1800/v;



}









void Injection(){

 
  
if(pinInj==HIGH){
    startP = millis();
}

else {
   endP = millis();
   lasttime = endP - startP;
   time= time + lasttime;
   time2= time2 +lasttime;
   
   
}
}


void Speed(){
  tVSSnow=millis();
  tVSS=tVSSnow-tVSSlast;
  tVSSlast=tVSSnow;
  VSScount=VSScount+1;

}

1 arduino ist mit der Berechnung überfordert?

Quatsch, selbst float Berechnungen sind flotter als wenn 2 Arduinos Rechenergebnisse austauschen müssen.
Klar, opamps brauchen "keine" Zeit, Controller arbeiten seriell. ( so eine ähnliche Diskussion hatten wir erst, da ging es aber eher darum, braucht man einen µC, wenn ein opamp ausreicht. Hier soll der ja nur zusätzlich float - Berechnungen analog "simulieren" :wink:

Aber 5 msec ( 200/sec = 12.000 / Minute ) sind 5.000 µs, eine lange Zeit...
Und du kannst das ganze auch so schreiben, dass loop() nur so schnell sein muss wie zur Display-Aktualisierung sinnvoll (> 100 msec ... )

Pulsdauer messen ist einfacher als jede Pulslänge regeln...

Und neben millis() gibts auch micros() und man kann die Hardware-Timer direkt lesen und die Hardware oft passend programmieren...

Interrupte zumachen geht so:

void loop () 
{
  noInterrupts();
    liter=time/1000/60*InjSize*InjNumber;
    ...  // nur das nötigste hier
    unsigned long VssCount_ = VssCount; // als Demo: nur Umkopieren

    time2=0; // was ist mit VssCount und time - Überlauf?
  interrupts();

  // jetzt kann mit liter und VssCount_ weitergerechnet werden, falls diese Berechungen zu lange dauern würden ( rein theoretisch ) ... 
  km=VSScount_/kmPulses;
  alkm=liter/km/100;
// ...

Vielen Dank! So sollte es funktionieren!

vllt noch eine Idee wie ich Sicherstelle dass nach dem Ausschalten des Motors und bevor ich den Schlüssel abziehe und somit die Spannungsversorgung unterbreche, die Daten in das externe Interface geschrieben werden?

Ausschaltverzögerung?

bzw. wie lange dauert dass die Daten zu übertragen? reichen die wenigen msec? Vllt muss ich mir darüber überhaupt keine Gedanken machen?

Ich hatte einen Multifunktions Ladedruck Controller da habe ich halt Displayfunktionen,
Musste den Ladedruck regeln und Einspritzzeiten überwachen Lambda überwachung, Ladrucküberwachung. daher musste ich ein bisschen auf mein Timming achten.
Für den Ladedruck würde ich den Freescale MPX4250AP nehmen.
Damit kannst du sowohl unterdruck als auch überdruck messen (bis 1,5bar Ladedruck).
Gruß
Der Dani

Für den Ladedruck würde ich den Freescale MPX4250AP nehmen.

habe ich.

Wenn man was Steuert, besonders so etwas sensibles, ist es was anderes. Ich nehme nur Daten auf.

vllt noch eine Idee wie ich Sicherstelle dass nach dem Ausschalten des Motors und bevor ich den Schlüssel abziehe und somit die Spannungsversorgung unterbreche, die Daten in das externe Interface geschrieben werden?

Ausschaltverzögerung?
bzw. wie lange dauert dass die Daten zu übertragen? reichen die wenigen msec? Vllt muss ich mir darüber überhaupt keine Gedanken machen?

In mein Uhren-RAM schreiben geht "sofort". Da reicht der Kondensator im Arduino dicke.

Externe Archivierung (auf SD Card ?) würde ich seltener machen und das Schreiben selbst per LED signalisieren. Dann kann man auch Fehler per Dauer-LED anzeigen. Bei Motor aus wird dann ein letztes Mal geschrieben.
Die LED zeigt wie schnell das geht (muss evtl. sogar künstlich verlängert werden, damit man es sicher sieht),
und würde dich psychologisch hindern, den Schlüssel zu schnell abzuziehen :wink: