ich hab mal eine Frage zum Thema Interrupts. Im Rahmen eines Projekts muss ich alle 0,8ms einen Sensor auslesen und nach draussen kommunizieren. Um die Taktung zu erreichen war meine Idee mit Interrupts zu arbeiten. Nachdem ich mich in die Thematik eingelesen hatte und meinen ersten Versuch ausprobieren wollte hatte ich mein erstes Problem. Zuerst wollte ich während des Interrupts den Sensorwert auslesen und direkt an den Serial Monitor schicken, was nicht funktionierte. Dann habe ich versucht nur den Sensorwert während des Interrupts auszulesen und in der loop Funktion dann den Wert per Serial.println nach draussen zu geben. Auch wieder nix. Ich habe mit dem Vergleichswert rumgespielt und die Abstände zwischen den Interrupts größer gemacht. Auch das war keine Lösung.
Nun glaube ich dass evtl etwas mit meinen Interrupt Einstellungen nicht stimmt, finde aber partout keinen Fehler. Hat jemand eine Idee woran das sonst liegen könnte, oder bin ich mit meinem Lösungsansatz komplett auf dem Holzweg.
Grüße und dankeschön im Vorraus!
Hier mein Code
int sensorValue = 0;
int sensorPin = A0;
int i;
void setup()
{
cli(); // Interrupt abschalten
//Timer 0 zurücksetzen
TCCR0A = 0; // TCCR0A auf Null setzen
TCCR0B = 0; // TCCR0B auf Null setzen
TCNT0 = 0; // Zählerwert auf NUll setzen
// Vergleichswert setzen
OCR0A = 50; //
//Prescaler einstellen
TCCR0B |= (1 << CS02) | (1 << CS00); // CS02 & CS00 Bits setzen um 1:1024 Prescaler einzustellen
TCCR0A |= (1 << WGM01); // TCCR0A ist das Timer/Counter Kontroll Register, ist jetz auf CTC Modus eingestellt
TIMSK0 |= (1 << OCIE0A); // Vergleichs-Interrupt A ist aktiviert
Serial.begin(115200);
sei(); // Interrupt wieder aktivieren
}
ISR(TIMER0_COMPA_vect)
{
//das geschiet bei einem Interrupt
//sensorValue = analogRead(sensorPin);
Serial.println("A");
}
void loop ()
{
//Serial.println(sensorValue);
Serial.println("B");
//i++;
}
man soll generell keine komplizierten Sachen in einer ISR erledigen, weil sie so kurz wie möglich/nötig gehalten werden sollte. Den sie könnte und wird andere ISR's während ihrer Ausführung behindern. Darum klappt das mit serial in einer ISR nur rein zufällig, je nach zeitlicher Beanspruchung.
Versuch mal eine status Variable in der ISR zu schalten und diese in der loop auszuwerten. An "volatile" denken.
bensch247:
... oder bin ich mit meinem Lösungsansatz komplett auf dem Holzweg.
Das nun nicht, aber wenn Du in loop alle 800 Milli Mikrossekunden den Wert abfragst, geht das viel einfacher. Schau mal in Beispiele/02.Digital/BlinkWithoutDelay. Anstelle LED blinken zu lassen, den Wert abfragen und ausgeben.
EDIT: Sorry, ich meinte natürlich 800 Mikrosekunden, also die Funktion micros(), benutzt wie millis() im Beispiel.
agmue:
Das nun nicht, aber wenn Du in loop alle 800 Millisekunden den Wert abfragst, geht das viel einfacher. Schau mal in Beispiele/02.Digital/BlinkWithoutDelay. Anstelle LED blinken zu lassen, den Wert abfragen und ausgeben.
das Problem ist ja, dass ich alle 0,8ms (!) den Wert abfragen muss. Aber ich werde mir die anderen Stichworte und Hinweise mal angucken und ein Feedback geben.
wenn es nur ungefähr genau sein soll, dann kann man das wie schon erwähnt wurde in der loop mit micros() machen. Die Wiederholgenauigkeit hängt dann ab wieviel die loop noch so an Code abarbeiten muß. delay() ist hier sowieso verboten.
Wird der Code noch größer? Von welchen Toleranzen reden wir?
Wenn es ganz präzise sein soll, dann gehts nur mit Timer ISR. Und weil Klaus das anspricht, die Quarzabweichung kann man dann nachträglich nach ausmessen anpassen mittels dem Timer.
Ok, also im Endeffekt sollen in der loop später 6 Sensoren ausgelesen werden. Ob analog oder digital ist noch nicht raus. Allerdings hatte ich mal mit der Micros() abgeschätzt dass eine analoge Messung ca 0,1ms dauert. Das könnte also machbar sein. Ich würde das Programm mal eine definierte Zahl an Sekunden laufen lassen und über die Anzahl an Messungen die Dauer mitteln. Je nachdem was dann dabei herauskommt kriege ich ja eine Abschätzung wie lange eine durchschnittliche Messung dauert und wie groß die Abweichung zu den anvisierten 0,8ms is
das hier ist mein gerade funktionierender Code den ich ma testen möchte wie lange das tatsächlich dauert:
unsigned long zeitVergangen = 0;
const long intervall = 800; //Abstand zwischen 2 Messungen in Microsekunden
int sensorPin = A0;
int sensorValue = 0;
int i = 0;
void setup()
{
Serial.begin(115200);
}
void loop()
{
unsigned long zeitAktuell = micros();
zur Loop Zeitmessung hatte mal GuntherB eine geniale Idee.
Denn deine serielle Ausgabe verfälscht dir deine eigentliche Zeitmessung. Außer man will bedingte seriellen Ausgaben mit messen.
Funktion:
/*********************************************************************************
** 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.println(" µs");
Serial.print(F("Durchschnitt "));Serial.print(Avg);Serial.println(" µs");
Serial.print(F("Maximal "));Serial.print(Max);Serial.println(" µs");
Min = 0xFFFF;
Max = 0;
Avg = 0;
Messung = 0;
Index = 0;
}
}
und aufgerufen wird diese 2x. Einmal genau am Anfang der loop und einmal genau am Ende der loop.
Wenn AL auf 200 gesetzt ist, bedeutet das, es wird 100 mal hintereinander die loop vermessen und danach die Werte ausgespuckt und dann wieder 100 mal hintereinander gemessen. Je nach RAM kannste das auch erhöhen.
millis oder micros sind immer bis auf 4 genau.
Das schlägt sich aber negativ auf die Genauigkeit der Messung nieder.
Wenn man es per Hand macht kann man auch mit einem Timer direkt den ADC triggern (entweder per Compare Match oder Overflow). Ohne den Umweg über eine ISR und vollständig in Hardware. Aber meistens wird es wohl reichen die Messung in einer ISR zu starten.
Wichtig ist eben dass dir klar wird, dass du Dinge wie Serial oder auch delay() nicht in einer ISR ausführen kannst. Statt dessen musst du eine Variable setzen um loop() mitzuteilen dass ein Ereignis eingetreten ist.
Das schlägt sich aber negativ auf die Genauigkeit der Messung nieder.
Wenn man es per Hand macht kann man auch mit einem Timer direkt den ADC triggern (entweder per Compare Match oder Overflow). Ohne den Umweg über eine ISR und vollständig in Hardware. Aber meistens wird es wohl reichen die Messung in einer ISR zu starten.
Wichtig ist eben dass dir klar wird, dass du Dinge wie Serial oder auch delay() nicht in einer ISR ausführen kannst. Statt dessen musst du eine Variable setzen um loop() mitzuteilen dass ein Ereignis eingetreten ist.
ah ok. kannst du mir nen link schicken bei der das mit den variablen zufällig erklärt wird? eine kurze google recherche hat mir grad nich sooo viel erklärt
ansonsten schon einmal dickes dankeschön für die hilfe hier
Darf ich mal eine Frage dazwischenwerfen? Was willst Du mit den Daten tatsächlich machen? Doch wohl nicht in so kurzen Abständen auf den Monitor malen. Zumindest ich kann da nicht mitlesen.
Serenifly:
Aber ja, alle 1ms einen Messwert auf Serial zu schreiben ist so oder so Unsinn
Das meine ich, ein analoger Messwert muß ja irgendwie verarbeitet werden.
Ich habe in loop ohne ISR alle 800 µs sechs analoge Meßwerte erfaßt und dabei einen digitalen Ausgang geschaltet. Mein Schätzeisen zeigt mir Werte zwischen 798 und 802 µs. Das geht also. Mit Serial.print wird es dann aber kritisch.
Es geht darum Daten von einem Teststand auszuwerten. Die Daten sollen vom Teststand nach aussen (drahtlos) gesendet werden, wo sie dann gespeichert werden sollen. Der Plan war das ganze mit einem Bluetooth Modul zu realisieren und das kommuniziert nunmal mit einer (max.) Baudrate von 115200 seriell mit dem Arduino. Bevor ich das ganze mit dem Bluetooth Modul probiere wollte ich erst einmal gucken ob das von der "Leistung" bzw. Programmstruktur überhaupt funktioniert. Ich werde mir die hier beherzigten Ratschläge angucken (muss mich allerdings in das eine oder andere erst einmal kurz einlesen) und meinen Ansatz dementsprechend anpassen. Haltet ihr das Vorhaben für überhaupt realistisch so weit?