Ich habe vor einigen Monaten einen Arduino Uno gekauft und mit dem programmieren begonnen.
Dabei habe ich viel übers Programmieren und Elektronik gelernt, sodass ich mir ein größeres Projekt vorgenommen habe,
das kurz vor der Vollendung steht: eine programmierbare Zündung für Einzylinder-Motoren.
Kern des Programms ist die Bestimmung der Drehzahl des Motors mittels Hallsensors;
Auf Basis der Drehzahl wird dann der Zündzeitpunkt bestimmt. Der "richtige" Zündzeitpunkt bei der jeweiligen Drehzahl ist in einem Array hinterlegt, damit die CPU nicht so viel rechnen muss. Den ZZP wandle ich in Microsekunden Wartezeit nach einem Hallsignal um; bei Erreichen dieses Wertes wird ein Timer1-CTC-interrupt ausgelöst, ein Pin auf high gesetzt und damit dann die Peripherie-Elektronik durchgesteuert.
Wie ihr euch vorstellen könnt, ist diese Anwendung, besonders bei sehr hohen Drehzahlen zeitkritisch. Daher möchte ich bei der angesprochenen Wartezeit bis Timer-interrupt die Programmlaufzeit als Korrekturfaktor berücksichtigen.
Meine Frage: wie kann ich die Ausführungszeit bestimmen? Kann man das aus dem Code herausrechnen?
Herausrechnen nein. Jedoch kannst du dir ein entsprechend großes Array anlegen, welches du auf Tastendruck über UART ausgeben kannst. UART Ausgabe während des Interupts ist nicht sinnvoll.
Also nochmal langsam: Zeitstempel (micros(); ) beim hallsignal erfassen und dann nochmal unmittelbar vor dem timer-interrupt. Differenz dann per Serial ausgeben?
Wenn zwischendurch noch ein Interrupt auftritt, stimmt der Wert aber nicht mehr, oder?
Eine frage noch:
Delaymicroseconds() funktioniert ja selbst in der interruptroutine- micros(); aber nicht oder?
Huj89:
Also nochmal langsam: Zeitstempel (micros(); ) beim hallsignal erfassen und dann nochmal unmittelbar vor dem timer-interrupt. Differenz dann per Serial ausgeben?
Das wird ja wohl schlecht gehen, den Zeitpunkt VOR einem eintretenden Interrupt-Ereignis zu timen.
Wenn Du den zeitlichen Abstand von zwei Signalen messen möchtest, dann nimmst Du dafür normalerweise ein Oszilloskop mit entsprechender Bandbreite oder einen Logiktester, triggerst auf das erste Signal und liest ab, wann danach das zweite Signal auftritt.
So wie Du den Programmablauf beschreibst, wird der Jitter beim zweiten Signal aber stets +/-4µs betragen.
Beim Arduino läuft ständig Timer0 mit und erzeugt Timer-Interrupts für die Bereitstellung von Zeitfunktionen in millis, micros und delay. So ein Timer-Interrupt benötigt mit allem Overhead alles in allem ca. 4µs und Du kannst nie wissen, wann der nach Erkennung eines bestimmten Signals auftreten und dazwischenhauen wird.
Mir gehts vor allem darum zu wissen, wie lange der Arduino zwischen dem Auftreten des Hallsignals und dem Start des Timers braucht. Dazu bräuchte ich irgendeine Möglichkeit der Messung oder Berechnung.
Kann man nicht auf Basis des Codes die dafür nötigen Arbeitsschritte des Controllers ermitteln?
Huj89:
Mir gehts vor allem darum zu wissen, wie lange der Arduino zwischen dem Auftreten des Hallsignals und dem Start des Timers braucht. Dazu bräuchte ich irgendeine Möglichkeit der Messung oder Berechnung.
Kann man nicht auf Basis des Codes die dafür nötigen Arbeitsschritte des Controllers ermitteln?
Das soll jetzt wohl soviel heißen wie:
1, Ich habe kein geeignetes Oszilloskop und
2, Ich habe auch keinen geeigneten Logiktester
um den Zeitunterschied zwischen beiden Signalen zu testen.
???
Per Software messen ist insofern schwierig, weil die Ausführung von Software selbst Zeit kostet, und wenn Du nun zusätzlichen Code schreibst, um Zeiten zu messen, dauert auch dieser Code Zeit, Und um diese Zeit wird dann die "gemessene" Zeit länger.
wie die anderen schon schreiben wäre es besser Du könntest mit einem Logic Analyzer messen. Mit Code geht das auch, nur wie auch schon geschrieben kommt die Zeit der Codeausführung dazu.
Um erstmal einen Anhaltspunkt zubekommen wo Du liegst, das wäre dann alles in allen die maximal Werte, kannst Du folgenden Code verwenden.
Du mußt nur noch ganz am Anfang der loop und am Ende der loop die Funktion
LoopTiming(); // Loop Time Messung
millis habe ich schon durch micros ersetzt. Aktuell werden 200 loop Durchläufe erfasst und dann ausgewertet, dann beginnt die Messung erneut mit 200 Durchläufen. Du siehst auch ob die Codezeit schwankt. Die Ungenauigkeit bleibt bei +/- 4µs und die noch nicht ausgemessen Ausführungsdauer der Messfunktion selbst die man abziehen müßte.
/*********************************************************************************
** LoopTiming() v06 by GuntherB & Doc_Arduino @ german Arduino Forum **
**********************************************************************************
** Funktion um die Dauer der Loop-Zeiten zu ermitteln **
** wird in der Loop am Anfang und Ende aufgerufen **
** benötigt ca (AL * 4 + 16) Byte RAM **
*********************************************************************************/
void LoopTiming()
{
const int AL = 200; // Arraylänge, NUR GERADE Zahlen verwenden!
static unsigned long LoopTime[AL];
static unsigned int Index=0, Messung=0, Min=0xFFFF, Max, Avg;
if (Messung % 2 == 0) // wenn Messung X gerade (0,2,4,6 usw.), entspricht immer Anfang der Loop
{
LoopTime[Index] = micros();
Messung++;
Index++;
return; // Funktion sofort beenden, spart bestimmt Zeit
}
if (Messung % 2 == 1) // wenn Messung X ungerade (1,3,5,7 usw.), entspricht immer Ende der Loop
{
LoopTime[Index] = micros();
LoopTime[Index-1] = LoopTime[Index] - LoopTime[Index-1]; // Loopdauer einen Index niedriger einspeichern wie aktuell
Messung++;
}
if (Index >= AL) // Array voll, Daten auswerten
{
for (int i = 0; i<AL; i++)
{
Min = min(Min, LoopTime[i]);
Max = max(Max, LoopTime[i]);
Avg += LoopTime[i];
}
Avg = Avg / AL;
Serial.print(F("Minimal "));Serial.print(Min);
Serial.print(F("Durchschnitt "));Serial.print(Avg);
Serial.print(F("Maximal "));Serial.print(Max);
Min = 0xFFFF;
Max = 0;
Avg = 0;
Messung = 0;
Index = 0;
}
}
Was eventuell möglich wäre ist auf zwei oder mehreren Pins den Status digital auszugeben (Low oder High) und dann mit einem zweiten Arduino die Zeit dazwischen zu messen. Kann aber ein Oszi nicht wirklich ersetzten da schon die Zeitbasis Toleranzen hat.
Huj89:
Mit beiden Signalen meinst du das Hallsignal einerseits und den Pin den ich auf High schalte andererseits, richtig?
Ja, das ist doch der Wert, der Dich im Endeffekt interessiert, oder?
Definierst Du eine Variable zum Speichern einer Zeit:
volatile unsigned long time;
Wenn der Hallsensor auslöst, setzt Du die Variable auf den aktuellen Stand des Mikrosekundenzählers:
time=micros();
Und wenn der andere Pin HIGH schaltet, ermittelst Du die Zeitdifferenz:
time=micros()-time;
Dann steht in "time" die Zeitdifferenz, plusminus vier Mikrosekunden, solange bis Du die Variable beim nächsten Auslösen des Hallsensors wieder auf eine neue Startzeit setzt.
wie genau muß das denn nun wirklich sein. Kannst Du eine Aussage treffen?
Am Moped mit elektronischer Zündung wurde das mit einem Stroboskop eingestellt. Da spielten µs keine Rolle.
Danke für eure Antworten - die helfen wir schon mal weiter.
Ich versuchs jetzt erstmal mit der von jurs vorgeschlagenen Variante, weil sie am einfachsten umzusetzen ist.
Bezgl. der Genauigkeit.. Da kann ich auch nur Schätzen:
Bei 10000 Umdrehungen dauert es ca. 17 Mikrosekunden, die Kurbelwelle um 1 Grad zu drehen.
Wenn ich nun das Hallsignal bei 40 grad vor dem oberen Totpunkt habe und bei 20 Grad zünden will, muss die CPU bis zur Zündung 20*17=340 Mikrosekunden warten. Wenn ich aber noch 100 Mikrosekunden für die Codeausführung brauche und die Zündung entsprechend später kommt, kann man ruck zuck ein Detos bekommen (Loch im Kolben).
Wenn ich von max. Drehzahlen von 15000 Umdrehungen ausgehe, glaube ich, dass es gut wäre auf ca. 20 Mikrosekunden genau zu timen.