Forum: Mikrocontroller und Digitale Elektronik ATXmega: Compare Werte nach Überlauf ohne Funktion


von Matthias S. (matze11)


Angehängte Dateien:

Lesenswert?

Hallo,

ich erstelle gerade ein Programm, welches ein Signal an einem Pin und 
definiert Zeitvesetzt an einem anderem Pin ausgeben soll.

Basis ist AVR Studio 6 und das XPLAINED-Board (ATXmega128A1), welches 
ich mit C programmiere.

Der Timer0 läuft mit 1MHz Takt. Den Zeitversatz möchte ich in µs 
Schritten vorgeben. Die Grundfunktion ist, dass ich den Zeitversatz / 
2^16 teile, dies ist dann die Anzahl der Überläufe des Timers die ich 
abwarten muss, um dann die Compare Werte (Rest der Division durch 2^16) 
für die Timer zu setzten und freizugeben.

Soweit so gut. Das Programm funktioniert solange richtig, bis der erste 
Compare Wert nach einem Überlauf statt finden soll. Danach werden die 
CCn Werte quasi nicht mehr beachtet.

Anbei ist das Programm und ein Bild. Das Bild zeigt, wie ich 100ms 
Zeitversatz zwischen den steigenden Flanken vorgebe: also 1 Überlauf 
abwarte und dann den Compare Wert von
ca. 35.000 für die Taktflanke nutzen möchte. Leider kommt wie ihr seht 
die Flanke schon beim Überlauf. An der Umrechnung liegt es leider nicht, 
wenn ich von Hand die Werte den Registern direkt zuweise hat das 
ebenfalls keinen Einfluss auf das Signal.

Ich hoffe jemand kann mir irgendwie helfen!

Vielen Dank im Voraus.

Gruß,
Matze

von Thomas F. (tomasf)


Lesenswert?

Ich sehe nur einen problematischen Punkt: Du veränderst das 
INTCTRLB-Register in der Schleife und in den Interrupts, diese Operation 
ist aber nicht elementar, so dass sich die Zuweisungen gegenseitig 
unterbrechen und garantiert nicht das gewünschte rauskommt.

Ansonsten versuche das Problem mal nur mit einem Compare-Channel zu 
reproduzieren, da findet sich die Ursache dann sicher leichter.

von Peter D. (peda)


Lesenswert?

Wenn Du einen Interrupt erst später im Programm enablest, solltest Du 
vorher dessen Pending-Bit löschen.

Einige AVRs haben asynchrone Timer, z.B. ATtiny261. Da muß man nach dem 
Löschen und vor dem Enable noch 3 NOPs einfügen, ehe es wirklich als 
gelöscht angekommen ist.

Achso, Löschen durch 1 reinschreiben (sollte ja bekannt sein).

von Matthias S. (matze11)


Angehängte Dateien:

Lesenswert?

Hallo,

Vielen Dank schon einmal für die Antworten.
Das zurücksetzten der IR Flags mittels Oder-Maske
1
TCC0.INTFLAGS |= 0b00010000;
hat mich auf jeden Fall weiter gebracht.

Leider ist gerade am Anfang gerade im kleinen Bereich noch irgendetwas 
nicht ganz sauber. Also, wenn sich der erste Überlauf anbahnt. Später 
wenn alle Teile des Signals sich in den Überläufen zu befinden scheint 
es zu funktionieren.

Ich habe an allen erdenklichen Stellen schon _delay_us(5); einfügt 
gehabt, um asynchrone Abhandlungen abzufangen, das hatte aber leider 
keine Auswirkungen auf das Ergebnis. Das Ausführen wenigerer Compare IR 
hatte für mich kein Unterschied auf das Verhalten gemacht.

Habt ihr noch eine Idee?

Anbei nochmal das jetzige C-File.

Gruß und ein schönes Wochenende!

Matze

von Peter D. (peda)


Lesenswert?

Matthias Stottmeister schrieb:
> Das zurücksetzten der IR Flags mittels Oder-Maske

Das setzt alle Flags zurück.
Du mußt die 1 direkt reinschreiben.

von Matthias S. (matze11)


Lesenswert?

Peter Dannegger schrieb:
> Das setzt alle Flags zurück.
> Du mußt die 1 direkt reinschreiben.

Aber das ist doch eine Oder-Maske, welche das Bit setzt, falls es noch 
nicht gesetzt ist.

von Peter D. (peda)


Lesenswert?

Das hat sich ein Scherzkeks bei Atmel eben so ausgedacht, damit jeder 
Anfänger darauf hereinfällt.

Überlege mal, was 1 OR irgendwas aus der 1 macht.
Folgende Ausdrücke machen daher exakt dasselbe:
1
  TCC0.INTFLAGS |= irgendwas;
2
// oder
3
  TCC0.INTFLAGS = 0xFF;
4
// oder
5
  TCC0.INTFLAGS = TCC0.INTFLAGS;
Sie löschen alle gesetzten Flags.

Um z.B. Flag 5 zu löschen geht nur das:
1
  TCC0.INTFLAGS = 1<<5;
2
// oder
3
  TCC0.INTFLAGS &= 1<<5;

von Matthias S. (matze11)


Lesenswert?

Ich mache das seit jeher mit der Oder Maske und das funktioniert ja 
auch.
1
var =  0b10101010;
2
var |= 0b11110000; 
3
//     0b11111010; //Ergebnis in var
1 mit Irgendwas verodert ergibt an der Stelle immer eine 1 und die 
möchte ich ja auch an der Stelle jeweils haben. Warum soll der Befehl 
nicht funktionieren?

Ich habe die Möglichkeiten von dir getestet und leider keine 
Unterschiede zu meinen vorherigen Befehlen festgestellt.

Gruß,
Matze

von MWS (Gast)


Lesenswert?

Matthias Stottmeister schrieb:
> Warum soll der Befehl
> nicht funktionieren?

Weil mit jedem gesetztem Flag per "Oder" eine 1 auf sich selbst 
geschrieben und damit alles gelöscht wird, was vorher gesetzt war. Also 
nicht nur das, was in der Oder-Maske 1 ist.

von Malte S. (maltest)


Lesenswert?

Kann's grad nicht testen aber GCC übersetzt |= ziemlich sicher in ein 
sbi und damit stellt sich der gewünschte Effekt ein. Bis du auf die Idee 
kommst, mehrere Flags auf einmal löschen zu wollen. Also besser nicht 
blind drauf verlassen...

von MWS (Gast)


Lesenswert?

Malte S. schrieb:
> aber GCC übersetzt |= ziemlich sicher in ein
> sbi

Bei 'nem XMega und TCC0.Intflags auf Adresse 2060?
Nicht wirklich...

von Malte S. (maltest)


Lesenswert?

Ja, sorry habe auch nach dem Posten gesehen, dass ich mich in der 
Familie geirrt habe. Der Threadtitel war auf dem Handy nicht so 
aufdringlich zu sehen ;)

von Matthias S. (matze11)


Lesenswert?

Ich habs noch einmal mit nem Port ausprobiert:
1
i=  0b10101010;
2
i|= 0b11110000;
3
PORTE_OUT=i;
jetzt leuchten 2 LEDs (Pin0 und Pin2) -> 0b11111010 (LEDs hängen an VCC 
Low-aktiv).
Was soll daran jetzt falsch sein?

Außerdem habe ich es doch getestet, und es hätte dann ja einen 
Unterschied geben müssen, oder?

von Simon K. (simon) Benutzerseite


Lesenswert?

Matthias Stottmeister schrieb:
> Außerdem habe ich es doch getestet, und es hätte dann ja einen
> Unterschied geben müssen, oder?

Die INTFLAG Register verhalten sich halt anders. Ein Flag wird 
zurückgesetzt, indem eine 1 reingeschrieben wird.
Wenn du jetzt das INTFLAG Register ausliest, mit einem Bit veroderst und 
zurückschreibst, werden alle gesetzten Interrupt Flags automatisch 
wieder gelöscht, weil sie eben eine 1 an der entsprechenden Position 
hatten.

Was ist denn daran so schwer?

von MWS (Gast)


Lesenswert?

Matthias Stottmeister schrieb:
> Ich habs noch einmal mit nem Port ausprobiert:

Ein Port ist kein Flag-Register.
Beschäftige Dich mal mit den Grundlagen.

von Matthias S. (matze11)


Lesenswert?

Ich habe die unterschiedlichen Befehle für CCA bis CCD getestet :
TCC0.INTFLAGS |= 0b10000000;
TCC0.INTFLAGS &= 1<<7;
es macht keinen Unterschied!

von Peter D. (peda)


Lesenswert?

Matthias Stottmeister schrieb:
> es macht keinen Unterschied!

Das bedeutet nur, daß die anderen Flags zu diesem Zeitpunkt entweder 
nicht gesetzt sind oder egal sind.

Ich habe nie behauptet, daß das eine Rolle spielt, sondern nur, daß 
dieser Code alle Flags des Registers löscht.

von Matthias S. (matze11)


Lesenswert?

Okay, dann hat man auf jeden Fall ein definiertes Verhalten. Ich habe 
das jetzt mit deinem Code gemacht. Das ist auf jeden Fall gut zu wissen 
und eine verdammt verflixte Sache. Da muss man erst einmal drauf kommen. 
Danke!

Betrifft dieses Verhalten nur die Flagregister, oder auch die 
Kontrollregister wie z.B. INTCTRLB?

von MWS (Gast)


Lesenswert?

Matthias,

das Problem wurde Dir hier bereits gelöst:

Peter Dannegger schrieb:
> Wenn Du einen Interrupt erst später im Programm enablest, solltest Du
> vorher dessen Pending-Bit löschen.

Das Verhalten das Du feststellst, ist ganz normal, denn Interrupt-Flags 
werden im AVR bereits dann gesetzt, wenn die betreffende Bedingung 
eingetreten ist, unabhängig davon ob der betreffende Interrupt erlaubt 
ist oder nicht.

Das passiert bei den Compare-Ints das erste Mal innerhalb des Bereichs 
bis zum ersten Timerüberlauf. Das betreffende Compare-Flag ist also dann 
bereits gesetzt. Sobald nun der entsprechende Compare-Int freigegeben 
wird, löst dieser sofort aus und wird dann in Folge gesperrt. Damit hat 
die Höhe des Comparewertes keinerlei Wirkung.

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
Noch kein Account? Hier anmelden.