Ich habe ein Gerät, welches ein PWM (250Hz) Singnal ausgibt. Dieses möchte ich auswerten. Das Singnal wird mit einem Transitor erzeugt der GND Durchschaltet. Jetzt habe ich denn Port als eingang Deklariert und denn internen Pull up eingeschaltet. gebe ich nun das singnal auf den Port macht der AVR einen internen Reset!! Woran könnte das liegen. Ziehe ich den eingang manuell auf GND oder + passiert nichts. Bin für jede Idee dankbar.
//Port A und D Ausgang DDRA = 0xFF; DDRD = 0x1F; // PortC als eingang alle Pull up Widerstände acktiviert DDRC = 0x00; PORTC = 0xFF; PORTD = 0xE0; Könnte es an der Init der Ausgänge liegen. PortD 7 soll der Eingang sein ???
Hallo Micha, wie Benedikt schon sagte, handelt es sich vermutlich um einen Software-Fehler -- es sei denn, der Reset-Pin wird auf 0 gezogen. Ursachen können sein: 1. undefinierter Interrupt-Vektor (PC springt an eine Adresse ohne Interrupt-Handler) 2. Stack-Überlauf 3. PC zeigt (z.B. wegen "2.") auf die "NOP"-Zellen nach dem Ende des Programms, läuft über und fängt wieder bei 0 (=Reset-Vektor) an. Du musst schon mehr von Deinem Code zeigen, um effiziente Hilfe zu bekommen! Gruß risu
Sobald ich an Pin 7 das Singnal lege, macht er einen Reset.
if((!(PIND & (1<<PIND7)))) // Wenn PinD 7 -> 0 ist dann { Pause++; } Wenn ich diese Zeile weglasse geht es. Jedoch kann ich dann die Pausenlänge nicht mehr messen.
kann es sein das solange PinD7 0 ist er in der Interupt Rotine bleibt. Und ein neuer Interupt vom timer Ausgelöst wird, wärend der Alte noch bearbeitet wird ?
Hi Von welchem Controller redest du eigentlich? Weder hier noch im Programm ist eine Angabe dazu. MfG Spess
Hi, 1. Die Frage von Spess53 habe ich auch. 2. Die Variablen "PWM" und "Pause" müssen "volatile" sein. Gruß risu
//----- int volatile PWM; int volatile Pause; habe ich gemacht ändert garnix. :-(
Woraus schließt Du, dass es zu einem "internen Reset" kommt?
Das PWM Singnal verschwimmpt. Also wird es wird überlagert. Dies passiert nur wenn ich: 1.) if((!(PIND & (1<<PIND7)))) // Wenn PinD 7 -> 0 ist dann { Pause++; } das ins Programm einfüge egal wo ! 2.) ich extern einen Reset oft hindereinander auslöse 3.) das PWM Singnal anschliese
Das Ganze macht einen ziemlich wirren Eindruck:
1. Das gepostete Programm nicht das sein, was auf deinem Controller
läuft, weil es einen Syntaxfehler enthält (schließende Klammer am
Ende von main() fehlt).
2. Du greifst auf die Variable PWM sowohl im Interrupthandler als auch
im Hauptprogramm lesend und schreibend zu. Hast du dir Gedanken
gemacht, was passiert, wenn an der ungeschicktesten Stelle im
Hauptprogramm dieses unterbrochen wird und der Interrupthandler PWM
ändert? Da PWM aus zwei Bytes besteht, kann die Unterbrechung sogar
innerhalb eines Zugriffs auf die Variable passieren.
3. Die Zeile
OCR1A = 64;
hat wenig Sinn, da du den Timer im Normalmodus (nicht CTC-Modus)
betreibst, wo er immer bis zum Maximalwert von 65535 hochzählt. So
dürftest du nur eine Interrupfrequenz von 244Hz und damit eine
PWM-Frequenz von etwa 0,5Hz erhalten. Oder habe ich da etwas
übersehen? Wahrscheinlich möchtest du aber den CTC-Modus. Dazu müssen
in auch TCCR1A die entsprechenden Bits gesetzt werden.
> Das PWM Singnal verschwimmpt. Also wird es wird überlagert.
Kannst etwas genauer beschreiben, wie das Signal aussieht?
CH2 input Singnal das ich messen will !! Messung also abfrage if((!(PIND & (1<<PIND7)))) // Wenn PinD 7 -> 0 ist dann { Pause++; } ist acktiviert
Singnal liegt weiterhin am Eingang !! Jedoch habe ich jetzt die Abfrage deaktiviert. Was mache ich nur falsch. Kann ich das PWM Singnal auch anders erzeugen. Es muß 250 HZ haben !! Oder wie könnte ich die Auswertung anders machen. Ist irgendwas mit dem Interrupt Aufruf falsch ??
//Timer einstellen //TCCR1B = (1<<CS10) | (1<<CS11); //setzt den Prescaler 64 TCCR1B = (1<<CS10) | (0<<CS11); //setzt den Prescaler 1 TCCR1A = (1<<WGM12) | (1<<WGM13); //CTC MODE OCR1A = 400; //setzt den Vergleichswert TCNT1 = 0; //startewert des Counters TIMSK = 1<<OCIE1A; //Compare-Int aktivieren sei(); //globale Ints aktivieren Wenn ich denn Vergleichswert erhöhe geht es ab ca. 400. Warum?
> Was mache ich nur falsch.
Wenn der Timer im CTC-Modus läuft und OCR1A=64 ist, generiert er alle 64
Taktzyklen einen Interrupt. Der Aufruf und die Ausführung des
Interrupthandlers dauert im worst Case etwa 59 Zyklen. Somit bleiben dem
Hauptprogramm zwischen zwei Interrupts gerade einmal 7 Zyklen. In dieser
Zeit kann aber nur ein Bruchteil eines vollen Durchlaufs der
Hauptschleife stattfinden. D.h. zwischen der Ausführung der Zeile
1 | if (PWM == Prozent) // 0 bis 500 |
in zwei aufeinanderfolgenden Schleifendurchläufen werden gleich mehrere Interrupts ausgelöst, von denen jeder zur Inkrementierung der Variable PWM führt. Bei der Auswertung der Bedingung PWM==Prozent hat also PWM in einem Schleifedurchlauf bspw. den Wert 100, im nächsten (nach vielleicht fünf Interrupts) aber schon 105. Liegt der Wert von Prozent dazwischen, wird die ==-Bedingung nicht wahr, und es erfolgt in der aktuellen PWM-Periode kein Wechsel von low nach high. Deswegen erhältst du im Ausgangssignal die vielen Aussetzer. Der Wechsel von high nach low passiert dagegen immer, weil du dort klugerweise auf PWM>=500, und nicht auf PWM==500 abfragst. Um das Problem etwas einzudämmen, kannst du auch in der ersten Abfrage ein >= anstelle des == verwenden, also
1 | if (PWM >= Prozent) // 0 bis 500 |
Das löst aber nicht das eigentliche Problem, nämlich die fast vollständige Auslastung der CPU mit den Interrupts. Außerdem besteht immer noch das Problem des simultanen Zugriffs auf 16-Bit-Variablen im Hauptprogramm und im Interrupthandler. Die kann man zwar durch Einfügen von Interruptsperren an den entsprechenden Stellen im Hauptprogramm beseitigen, dadurch wird aber das erste Problem noch verschärft, und es kann zu Interruptstaus kommen, weil der jeweils nächste Interrupt schon eintrifft, bevor der aktuelle abgearbeitet ist. > Wenn ich denn Vergleichswert erhöhe geht es ab ca. 400. Warum? Dann bleiben dem Hauptprogramm 341 statt 7 Zyklen bis zum jeweils nächsten Interrupt. In dieser Zeit schafft es wahrscheinlich sogar mehrere Schleifendurchläufe. Eigentlich sollte es schon mit weniger als 400 funktionieren. Hast du beim Compiler vielleicht die Optimierung nicht eingeschaltet? > Kann ich das PWM Singnal auch anders erzeugen. Es muß 250 HZ haben !! > > Oder wie könnte ich die Auswertung anders machen. Zum Glück hat der ATmega8535 sowohl für die PWM-Generierung als auch die Pulsbreitenmessung des Eingangssignals spezielle Hardwareeinheiten, die diese Aufgaben (fast) ohne das Mitwirken der CPU erledigen. Sie heißen "Input Capture Unit" (für die Zeitmessung) und "Output Compare Unit" (für die PWM-Generierung) und sind jeweils in einem eigenen Abschnitt im Datenblatt beschrieben. Damit kannst du alle der beschriebenen Probleme auf einen Schlag beseitigen, und die CPU hat wieder Luft, um noch andere nützliche Dinge zu tun. Dazu musst du allerdings das Eingangssignal nicht an PD7, sondern an PD6 (ICP1) anschließen und das Ausgangssignal nicht von PD1, sondern von PD4 (OC1B) abnehmen. Vielleicht kannst du das in deiner Schaltung ja noch ändern.
Ja,PD7 auf PD6 legen ginge eventuell mit ner Zinnbrücke. Aber die AVR Kontroller sind doch schnell !! Der AVR läuft mit 16 Mhz! Das sind 16000000 Befehle pro Sekunde. Und ich will nur ein Signal von 250 Hz erzeugen. Und gleichzeitig eins messen. Das muss doch zu schaffen sein! Könntest du mir ein Beispiel geben, wie ich das einstelle mit PD6.
Micha wrote: > Der AVR läuft mit 16 Mhz! > Das sind 16000000 Befehle pro Sekunde. Nein, sind es nicht.
> Und ich will nur ein Signal von 250 Hz erzeugen. Die 250Hz sind, für sich gesehen, nicht das Problem, aber die 250Hz in Verbindung mit der PWM-Auflösung von 500 Schritten. Um diese Auflösung softwaremäßig zu erreichen, muss die Software 250*500=125000 Mal pro Sekunde aktiv werden. Dann hat sie genau 128 Taktzyklen Zeit, das Eingangssignal zu verarbeiten und das Ausgangssignal zu generieren. Wie Niels andeutete, benötigen viele Befehle nicht einen, sondern zwei Zyklen, einige sogar noch länger. Wenn man's geschickt anstellt, geht das trotzdem, auch softwaremäßig. Ich vermute aber, dass das Programm, bis es fertig ist, noch ein paar Dinge mehr tun soll. Bei der Softwarelösung bist du dann ständig am Zyklenzählen, um sicherzustellen, dass der Zeittakt auch unter den ungünstigsten Bedingungen eingehalten wird. Was soll dein Programm denn außer der Pulslängenmessung und der PWM-Generierung sonst noch tun? Irgendetwas muss ja bspw. mit der gemessenen Pulslänge getan werden. Und du hättest sicher keinen 40-Füßler genommen, wenn mit den anderen Ports nicht auch noch etwas gemacht würde. > Könntest du mir ein Beispiel geben, wie ich das einstelle mit PD6. Ich habe gerade nichts fertiges im Zugriff, schon gar nicht für den ATmega8535. Es sollte aber genügend Beispiele hier im Forum, in den Tutorials und sonstwo im Netz geben, die du entsprechend anpassen kannst. Da fällt mir gerade ein: Wieso möchtest du überhaupt die PWM in 500 Schritten auflösen? Das Tastverhältnis wird doch, wenn ich das richtig gesehen habe, von Port C als ganze Zahl von 0 bis 100 eigelesen. Damit genügt es, nur 100*250=25000 Mal den Interrupt zu generieren und du hast 640 CPU-Zyklen zwischen zwei Interrupts zur Verfügung. So dürfte es auch bei softwaremäßiger PWM keine Probleme geben.
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.