Hallo, ich habe leider keine Loesung fuer mein Problem im Forum gefunden. Sollte ich etwas uebersehen haben, sorry fuer den doppelten Post! Hier zu meinem Problem: Wenn ich aus einer ISR in meinem Programm (siehe Anhang) eine Funktion get_adc aufrufen moechte, bekomme ich vom Compiler (gestartet mit AVR-Studio, Versionen siehe Anhang) immer folgende Fehlermeldung: Build started 16.5.2006 at 00:44:08 avr-gcc.exe -mmcu=atmega32 -Wall -gdwarf-2 -O0 -MD -MP -MT Drehzahlregler.o -MF dep/Drehzahlregler.o.d -c ../Drehzahlregler.c ../Drehzahlregler.c: In function `__vector_11': ../Drehzahlregler.c:20: warning: implicit declaration of function `get_adc' ../Drehzahlregler.c: At top level: ../Drehzahlregler.c:28: error: conflicting types for 'get_adc' ../Drehzahlregler.c:20: error: previous implicit declaration of 'get_adc' was here ../Drehzahlregler.c:42: warning: return type of 'main' is not `int' make: *** [Drehzahlregler.o] Error 1 Build failed with 2 errors and 2 warnings... Wenn ich die Funktion get_adc aber aus der main aufrufe (siehe auskommentierte Zeile im Source) und den Aufruf in der ISR auskommentiere ist der Fehler wieder weg. Da ich annehme, dass es sich um einen stupiden Anfaengerfehler handelt moechte ich um Nachsicht fuer die verschwendete Zeit bitten! ;-) MfG PeQ-Man
du versuchst get_adc aufzurufen bevor es dem Compiler bekannt gemacht wurde. Entweder du verschiebst deine Funktion get_adc vor die ISR, oder du teilst deinem Compiler durch einen Prototypen(=Deklaration) mit dass diese Funktion existiert und sie diese und jene Parameter besitzt. Das geht indem du in deinem Fall "unsigned int get_adc(int kanal);" in deine Datei einfügst, noch vor den Funktionsaufrufen; bei mir finden sich Funktionsprototypen meist direkt hinter den globalen Variablen. Alternativ kannst du die Deklaration auch in einer Headerdatei erledigen. HTH
Da Du keine 256 Kanäle und auch keinen negativen Kanäle hast, solltest Du besser: unsigned int get_adc(unsigned char kanal); schreiben. Man muß ja den Compiler nicht unnötig zwingen, toten Code zu erzeugen. Peter
Funktionsaufrufe aus ISRs sind schlechter Programmierstil, v.a. dann, wenn sie noch Warteschleifen enthalten. Du willst das Programm ja vielleicht mal erweitern, und dann kanns Probleme geben. Grundsätzlich: ISRs so kurz wie möglich. Da Du anscheinend mit nem Mega32 arbeitest: Machs Dir doch nicht unnötig kompliziert. Beschäftige Dich mal mit den ADC Auto Trigger-Quellen (Einstellung über die Bits ADTSx in SFIOR) und nimm Timer0 Overflow als Triggerquelle. Dann den ADC-Interrupt aktivieren und fast alles läuft automatisch. Musst nur dran denken: Wenn Du den Overflow Interrupt dann deaktivierst (eigentlich brauchst Du dann nämlich nur noch den ADC-Interrupt), musst Du das Overflow-Flag in der ADC-ISR manuell löschen, sonst wandelt der nur einmal. Gruß Johnny
Hallo! Erstmal danke fuer die schnellen Antworten! @Matthias Bauch: Danke, hab da wohl gestern den Wald vor lauter Baeumen nicht gesehen... Jetzt geht es ;-) @peter dannegger: Das ist ein gutes Argument, soweit hatte ich noch gar nicht gedacht (stehe da noch ziemlich an den Anfaengen...). Ich habe das jetzt geandert. @johnny.m: Das das mit dem Funktionsaufruf aus der ISR nicht gerade elegant ist, ist mir bewusst. Das mit dem Timing fasse ich als nicht so kritisch auf, da ich in der ISR 2 Messungen machen will mit jeweils einer Dauer von 0,1ms. Oder ist das dann schon zu viel? Der Workaround den ich mir ueberlegt habe, dass ich eine Flag-Variable in der ISR nutze und in der main nach diesem Flag polle, erscheint mir aus energietechnischer Sicht nicht sehr gut. Ich hatte vor den µC schlafen zu schicken und nur auf den Interrupt zu reagieren. Wenn ich polle is er ja quasi immer beschaeftigt oder sehe ich das falsch? Den ADC automatisch anwerfen zu lassen finde ich eine gute Idee. ICh sehe jedoch keine Moeglichkeit da bei jedem Overflow dann spaeter mal 2 Messungen von zwei verschiedenen Kanaelen zu machenn. Oder? MfG PeQ-Man
>Das das mit dem Funktionsaufruf aus der ISR nicht gerade elegant ist, >ist mir bewusst du solltest dir auch bewusst sein, dass das eine Menge Ram auf dem Stack verbraucht, da alle Register gesichert werden. >Ich hatte vor den µC schlafen zu schicken und nur auf den Interrupt zu >reagieren. und bei einem Interrupt, wacht er auf und macht dann in der Mainroutine weiter, wo du die Flags überprüfst und dich wieder schlafen legst. mainroutine: 1:flags pollen 2:tue was 3.leg dich schalfen zu 1.
> Ich hatte vor den µC schlafen zu schicken und nur auf den Interrupt > zu reagieren. Dann ist die Variante, die Messung anzuwerfen, den Controller schlafen zu legen, und den ADC mit einem Interrupt den Controller wieder aufzuwecken sicher ohnehin die sinnvollste. > ICh sehe jedoch keine Moeglichkeit da bei jedem Overflow dann > spaeter mal 2 Messungen von zwei verschiedenen Kanaelen zu > machenn. Oder? Jede Messung braucht sowieso ihre Zeit, also ,,gleichzeitig'' geht's ohnehin nicht. Entweder schiebst du die erste Messung bei deinem Overflow an, legst den Controller schlafen, speicherst nach dem Ende der Wandlung den Wert ab und schiebst die zweite Messung gleichermaßen an. Oder aber, du misst einfach kontinuierlich abwechselnd beide Kanäle nach diesem Prinzip, und spuckst beim Overflow-Interrupt sofort die letzten beiden Messwerte aus.
@Wolfram: >mainroutine: >1:flags pollen >2:tue was >3.leg dich schalfen >zu 1. Wie kann ich dann aus dem Schlaf wieder zu 1 zurueckspringen? Bin da noch recht neu auf dem Gebiet, wie man sicher schon gemerkt hat ;-) @Jörg Wunsch: >Jede Messung braucht sowieso ihre Zeit, also ,,gleichzeitig'' >geht's ohnehin nicht. Das hatte ich wohl falsch ausgedrueckt. Ich wollte die Messungen in der ISR nacheinander anwerfen. Ich ja schliesslich zwischendrin den Kanal wecheln. >Entweder schiebst du die erste Messung bei deinem >Overflow an, legst den Controller schlafen, speicherst nach dem Ende >der Wandlung den Wert ab und schiebst die zweite Messung gleichermaßen >an. Oder aber, du misst einfach kontinuierlich abwechselnd beide >Kanäle nach diesem Prinzip, und spuckst beim Overflow-Interrupt sofort >die letzten beiden Messwerte aus. Das klingt recht vernuenftig. ;-) Aber wie schalte ich dann zwischen den Overflows zwischen den Kanaelen um? Die Messung wird ja dann automatisch angeworfen?! Kann ich das dann einfach ueber eine parallele ISR in der ich jeweils den Kanal wechsle machen? MfG PeQ-Man
Da hilft eine Bitvariablr. Ist sie 0 wird Messwert 1 erfasst und die Bitvariable auf 1 gesetzt. Ist die Bitvariable 1 wird Messwert 2 erfasst und die Variable wird auf 0 gesetzt. So wird jedesmal zwischen Messwert 1 und 2 unterschieden. Zwischen den Overflows wird gar nichts gemacht, sondern nach prüfen der Bitvariable wird das ADMUX-Register gesetzt. Wenn der ADC-ISR kommt, wird anhand der Bitvariable deutlich, welcher Wert gewandelt wurde. Danach zwischen 0 und 1 unterscheiden, Wandlung starten und schlafen. MW
> Aber wie schalte ich dann zwischen > den Overflows zwischen den Kanaelen um? Der ADC-Interrupt vermeldet dir das Ende der Messung (und weckt den Prozessor aus dem Schlaf auf). Danach speicherst du den aktuellen Messwert in einer Variablen ab (aus der ihn der Timer-Interrupt dann abholen kann), wechselst den Kanal, schiebst eine neue Messung an und legst den Prozessor wieder schlafen. Guck mal ins Datenblatt, es gibt eine minimale Zeit, die man verstreichen lassen muss nach dem Beschreiben des ADMUX bis zum Anwerfen der neuen Messung, sonst wird die Messung noch auf dem alten Kanal angeschoben. Im Prinzip kannst du die Umschaltung auf den anderen Kanal auch sofort nach dem Aktivieren der Messung anwerfen (also noch vor dem Schlafen- legen), da sie ohnehin bis zum Ende der laufenden Messung verzögert wird.
...eine Bitvariable brauchste eigentlich nicht. Einfach nach jeder Messung abfragen, was in ADMUX grad drinsteht, dann weißt Du, welchen Kanal Du grad gemessen hast. Dann das Ergebnis in der entsprechenden Variablen speichern und den Kanal umschalten. Nach der nächsten Messung das gleiche Spiel, nur umgekehrt...
Hallo, also nochmals vielen Dank fuer eure Hilfe! Ich habe jetzt mal versucht den ADC ueber den TIMER0_OVF zu starten und die Auswertung in der ISR(ADC_vect) zu machen. Ich habe das schon so weit wie moeglich abgespeckt um meinen Fehler zu finden nur leider scheint der ADC nichts zu tun?! MfG PeQ-Man PS: Ich hoffe, das wird jetzt nicht etwas zu off-topic fuer diese Ueberschrift?!
Der Programmcode liest sich gut weiter so, Wo ist deine Timer ISR Routine?
Ich hatte doch oben geschrieben: Wenn Du für den Timer Overflow den Interrupt nicht aktivierst und die entsprechende ISR weglässt, musst Du in der ADC-ISR das Overflow Flag manuell löschen. Sonst wandelt der nur ein einziges Mal und macht dann gar nix mehr. Also: Irgendwo in der ADC-ISR 'TIFR = 1 << TOV0;' reinschreiben.
Hab grad noch gesehen, dass Du den Overflow Interrupt aktiviert hast, den ADC-Interrupt jedoch nicht! Das haut dann natürlich daneben. Entweder den Overflow Interrupt deaktivieren und das Flag manuell löschen (wie oben beschrieben) oder den Interrupt aktivieren und eine (evtl. leere) Overflow-ISR deklarieren, wobei letzteres allerdings Laufzeit kostet (was in Deinem Programm allerdings wahrscheinlich nichts ausmacht...). Und Du musst natürlich den ADC-Interrupt aktivieren, sonst passiert gar nix...
Hallo, also jetzt funktioniert alles. Vielen Dank nochmal! Ich bin aber im Handbuch noch auf einen Satz gestossne den ich nicht so ganz verstehe (ATMEGA32 Handbuch, Seite 210, "Starting a Conversion"): [...]When a positive edge occurs on the selected trigger signal, the ADC prescaler is reset and a conversion is started.[...] Muss ich das so verstehen, dass ich auch nach jeder Messung den Prescaler neu starten muss oder ist das nur von Belang, wenn ich einen externen Trigger benutze? MfG PeQ-Man
Das bezieht sich nur auf das externe Triggersignal. Heißt auf deutsch: Wenn eine positive Flanke auf dem Triggersignal auftritt (heißt z.B. bei Verwendung eines Timer Overflow oder Compare als Autotrigger, dass das entsprechende Flag von 0 auf 1 wechselt, also gesetzt wird), wird der Prescaler zurückgesetzt und ohne Rücksicht auf Verluste wird eine neue Wandlung gestartet. Der Prescaler selbst läuft eigentlich immer durch. Wenn Du die Wandlung manuell startest (durch Setzen von ADSC) passiert genau dasselbe.
Ich halte das für ein Implementierungsdetail. Du hast ja sowieso keine Möglichkeit, den Prescaler direkt zurückzusetzen.
Hallo, man, ihr seit ja schneller als mein Mailintervall ;-) Es handelte sich hier wohl eher um ein Missverstaendnis meinerseits. Ich dachte der Prescaler an sich is nur das Teilverhaeltnis zum CPU-Takt. MfG PeQ-Man
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.