Hallo,
AVR Mega2560
ich verzweifel so langsam am Timer 1.
Er läuft im Normalmode und soll alle 3 Kanäle am Toggle Pin schalten.
Die takten eigentlich auch verschieden, nur das rücksetzen klappt nicht.
Und dann schalten die zwischendurch nochmal um. Warum weis ich nicht.
Siehe Screenshot. Die Beschriftung ist das was ich denke, ob das stimmt
weis ich nicht. Nur bei der ersten Taktung je Kanal bin ich mir sicher,
weil das rechnerisch hinkommt. Was an den anderen Flanken passiert ...
keine Ahnung.
Mein Verständnis nach, zählt TCNT1 munter hoch, wenn das mit einem OCR1x
übereinstimmt toggelt der dazugehörige Pin. Wenn ich nun den Compare ISR
vom langsamsten Takt nehme und darin das TCNT1 einfach erhöhe um
zeitiger einen Overlow zu manipulieren, sollte sich das TCNT1 damit
zeitiger auf 0 setzen und meine Toggle Pins wieder beim nächsten Compare
toggeln.
Irgendwie toggelt es aber nicht wie gewünscht.
Meine Idee zum ganzen ist mit einem Timer und seinen 3 Kanälen 3
verschiedene Takte zu erzeugen.
Wo liegt mein Fehler?
Veit D. schrieb:> Meine Idee zum ganzen ist mit einem Timer und seinen 3 Kanälen 3> verschiedene Takte zu erzeugen.
Geht nicht, kannst nur verschiedene Tastverhältnisse über PWM erzeugen,
der Takt bleibt aber immer gleich.
> TIMSK1 |= (1 << TOIE1); // enable Timer1 Overflow Interrupt> TIMSK1 |= (1 << OCIE1A); // enable Timer Compare Interrupt A> TIMSK1 |= (1 << OCIE1B); // enable Timer Compare Interrupt B
Warum funktioniert dein Programm überhaupt, ohne einen Interrupt Handler
für den Overflow vom Timer und für Compare A/B?
> TCNT1H = 0xFF;> TCNT1L = 0xFF;
Kann auch durch TCNT1 = 0xFFFF ersetzt werden
> sei();
Würde ich in der ISR gar nicht machen.
Hallo,
ich habe viele Seiten gelesen, auch das Datenblatt und auch hier das AVR
Timer Tutorial. Das vorweg.
Wofür braucht man einen extra Interrupt Handler? Ich denke das braucht
man nur wenn man bei dem Ereignis noch irgendwas machen möchte außer der
Reihe. Die Flags und Interrupts setzt der µC doch selbst wieder zurück
im CTC Mode und hier möchte/muß ich das beim Compare.C. Oder nicht?
Und wegen dem TCNT1 Register. Laut dem Tutorial hier muß man das High
und Low Byte getrennt beschreiben und dazu die Interrupts auschalten.
Stimmt das nicht?
Und wegen meinen Takten. Ich kann doch aber den Takt nochmal immer
halbieren von A zu B und B zu C.
Nur was macht der µC beim ersten und zweiten Fragezeichen im Screen?
Veit D. schrieb:> Wofür braucht man einen extra Interrupt Handler?
Nun. du hast den Compare Interrupt ja aktiviert. Also muss dafür auch
eine ISR definiert sein. Ist dem nicht so, resettet der AVR sich, weil
an dem Interrupthandler keine Adresse einer gültigen Funktion steht.
Ich glaube aber eher dass du nicht das komplette C-File gepastet hast...
der Anfang fehlt irgendwie.
> Und wegen dem TCNT1 Register. Laut dem Tutorial hier muß man das High> und Low Byte getrennt beschreiben
Macht der Compiler
> und dazu die Interrupts auschalten.
(Macht der Compiler nicht, aber...)
> Stimmt das nicht?
Du befindest dich zu dem Zeitpunkt in einem Interrupt Handler. Andere
Interrupts sind da gesperrt bis zum Ende der ISR.
(Ausnahme: man aktiviert sie manuell wieder. Braucht man selten und muss
man aufpassen bei. Hier wird es nicht benötigt)
Bei welcher Frequenz läuft das ganze eigentlich?
Zumindestens von 392.26ms bis 393.34ms sieht das ganze für mich halbwegs
logisch aus. Du hast ja nur COM1x0 gesetzt, d.h. von sich aus toggelt
der AVR bei nem Match nur. Zurückgesetzt bei nem Overflow wird da bei
deinen Einstellungen nichts.
Bei ca. 392.81ms dürfte ein zweiter Overflow stattfinden. Den siehst du
natürlich im Diagramm nicht, weil da nichts passiert (der Timer ist eben
nicht so konfiguriert, dass er beim Overflow OC1x löscht...)
Hallo,
das läuft mit Prescaler 8 mit 16MHz CPU Takt.
Kanal A von Timer 1 mit Compare Einstellung 99 entpricht 50µs.
So, hab den Code gleich nochmal geändert.
Jetzt habe ich folgendes siehe Screenshot 1.
Ohne ISR(TIMER1_OVF_vect) { }
Was macht der hier noch für einen Toggle bei ca. 0,8ms und kurz danach
auf Kanal B und dann Kana C? Dann wieder 3 kurze LOW Pulse? Verstehe ich
noch nicht.
sieht das ähnlich wie im alten Screenshot 2.
Mit ISR(TIMER1_OVF_vect) { }
taktet die wie sie wollen.
Du meinst die Interrupts sind noch falsch gesetzt?
Der Overflow Interrupt ist jedoch zusätzlich gesetzt.
Veit D. schrieb:> Jetzt habe ich folgendes siehe Screenshot 1.> Ohne ISR(TIMER1_OVF_vect) { }
Was heisst 'ohne'.
Noch mal für dich zum mitmeisseln:
Wenn du einen Interrupt freigibst, und das tust du hier
dann musst du auch eine entsprechende ISR im System haben.
Keine entsprechende ISR zu haben, ist ein FEHLER. Denn das gcc System
ist so aufgesetzt, dass dieser Fall zu einem Programmneustart führt!
ALso:
Wenn du einen Interrupt brauchst, dann
* gibst du ihn frei
* schreibst eine zugehörige ISR
Wenn du auf einen spezifischen Interrupt nicht reagieren willst, dann
* gibst du ihn nicht frei
* und brauchst auch keine ISR dafür schreiben.
Es gibt nur diese beiden Möglichkeiten. Die Kombination
* Interrupt freigeben
* aber keine ISR dafür schreiben
ist so erst mal nicht vorgesehen. Macht auch keinen Sinn.
> Was macht der hier noch für einen Toggle bei ca. 0,8ms und kurz danach> auf Kanal B und dann Kana C? Dann wieder 3 kurze LOW Pulse?
Den Screenshot kannst du vergessen. Ohne die zum Overflow gehörende ISR,
sind da Programmneustarts drinnen, die man jetzt in diesem Licht
analysieren müsste. Ist aber vergebene Liebesmüh, denn im Grunde
interessiert dieser Fall nicht, weil du einen schweren Programmfehler
machst, wenn du keine passende ISR für einen freigegebenen Interrupt
hast.
Das andere Bild passt perfekt zum geposteten Programm. Genau dieses
Ergebnis würde man erwarten.
Komplett ohne ISR(TIMER1_OVF_vect) { } resettet der AVR sich bei jedem
Overflow, weil du den Overflow Interrupt aktiviert, aber nicht mit Code
versehen hast.
Das siehst du im ersten Screenshot an dem kurzen Low-Puls (eigentlich:
Pin auf Input ohne Pullup). Während der Zeit ist der im Reset.
Was du im ersten Screenshot nicht siehst, ist der zweite Reset, der z.B.
bei +0.75ms stattfindet. Da die Pins da sowieso low sind, fällts da
nicht auf, dass die durch den Reset kurz low werden.
Der zweite Screenshot sieht genau so aus wie ich ihn erwarte. Von
"Taktet wie sie wollen" kann da keine Rede sein :) Compare C lässt den
Timer überlaufen, dadurch läuft er von vorne los.
Verwechselst du eventuell den Normal Mode (den hast du eingestellt) mit
dem CTC Mode? Nur letzterer löscht dir beim Overflow automatisch OC1x.
Dann sieht das ganze nämlich auch nach PWM aus.
Der CTC Mode verwendet allerdings immer OCR1A, d.h. du müsstest 299 (den
höchsten Wert) da reinschreiben, ansonsten werden OCR1B/C nie erreicht.
> Meine Idee zum ganzen ist mit einem Timer und seinen 3 Kanälen 3 verschiedene
Takte zu erzeugen.
Denk an eine Uhr. Der Sekundenzeiger braucht 60 Sekunden um das
Ziffernblatt einmal zu umrunden. Bringst du Kontakte bei den Sekunden
15, 30 und 38 an, dann schalten die zwar zu unterschiedlichen Zeiten,
aber der Sekundenzeiger braucht nach wie vor 60 Sekunden um einmal
rundum zu kommen.
Genauso hier:
Du kriegst keine unterschiedlichen Frequenzen. Auf allen 3 Pins wirst du
immer die gleichen Frequenzen haben. Lediglich in der Phase sind sie
verschoben (d.h. die Pins gehen zu unterschiedlichen Zeitpunkten von 0
auf 1 bzw. umgekehrt, aber der zeitliche Abstand zwischen den Flanken
ist bei allen 3 Kanälen identisch).
Wenn du das willst, dann kannst du bei der Systematik bleiben. WEnn du
aber unterschiedliche Frequenzen willst, dann hast du mit Zitronen
gehandelt.
Hallo,
teilweise verstehe ich Eure Erklärungen, teilweise aber auch nicht.
Das mit den ISR's habe ich jetzt verstanden. Danke.
Nur das das 2. Diagramm so normal sein soll verstehe ich nicht.
Die takten alle mit der gleichen Frequenz 3,2kHz und alle mit einem
Tastverhältnis von 50/50%. Dürfte laut meiner Logik nicht sein.
Start.
TCNT1 läuft bis 99 > Compare.A löst aus, Pin toggelt von 1 auf 0.
TCNT1 läuft weiter bis 199 > Compare.B löst aus, Pin toggelt von 1 auf
0.
TCNT1 läuft weiter bis 299 > Compare.C löst aus, Pin toggelt von 1 auf
0.
Beim Comp.C erhöhe ich künstlich den TCNT Wert. Overflow tritt ein und
setzt alles zurück. Alles beginnt von vorn mit gemeinsamer Startflanke 0
auf 1.
Macht es aber nicht. Irgendwo muß ich wohl einen Denkfehler haben.
Veit D. schrieb:> Start.> TCNT1 läuft bis 99 > Compare.A löst aus, Pin toggelt von 1 auf 0.> TCNT1 läuft weiter bis 199 > Compare.B löst aus, Pin toggelt von 1 auf> 0.> TCNT1 läuft weiter bis 299 > Compare.C löst aus, Pin toggelt von 1 auf> 0.>> Beim Comp.C erhöhe ich künstlich den TCNT Wert. Overflow tritt ein und> setzt alles zurück.
Was genau verstehst du unter 'alles'?
Die Pins werden nicht zurück gesetzt. Die hast du mittels COM Bits so
eingestellt, dass sie vom Compare Match getoggelt werden. Und genau das
machen sie auch. VOn eiem 0-Setzen bei Timer Overflow steht da nichts.
Du liest Dinge aus dem Datenblatt heraus, die da definitiv nicht drinnen
stehen!
Der Timer läuft weiter, bis er bei 99 den Compare A erreicht. Pin wird
von 0 auf 1 getoggelt.
läuft weiter bis 199, Compare B erreicht, Pin wird von 0 auf 1 getoggelt
läuft weiter bis 299, Compare C erreicht, Pin wird von 0 auf 1 getoggelt
Overflow forciert. Timer beginnt bei 0
99, COmpare A erreicht, Pin toggelt von 1 auf 0
199, Compare B erreicht, Pin toggelt von 1 auf 0
299, .... naja, so geht das jetzt immer weiter.
Veit D. schrieb:> Die takten alle mit der gleichen Frequenz 3,2kHz und alle mit einem> Tastverhältnis von 50/50%. Dürfte laut meiner Logik nicht sein.
Das ist das Signal was du siehst. Der Timer läuft aber mit der doppelten
Frequenz über.
> Alles beginnt von vorn mit gemeinsamer Startflanke 0> auf 1.> Macht es aber nicht.
Warum sollte es? Hast du nirgendwo so eingestellt.
Der Normal Mode setzt die Pins beim Overflow nicht auf 1 zurück.
Entweder du nimmst den CTC Mode (der macht das automatisch) oder du
machst es von Hand zusammen mit dem Überlaufenlassen vom Timer (PORTB =
.... nach TCNT1 = 0xffff)
Hallo,
dann habe ich echt die ganze Zeit was völlig fehl interpretiert. Danke
für die Erklärung. Durch den mir unbekannten Reset dachte ich genau so
soll es werden, ich bin nah dran.
Ich muß also dann beim Compare Interrupt selbst die Pins zurücksetzen.
Jetzt verstehe ich auch warum meine Idee/Vorhaben so nicht funktionieren
kann.
Ich müßte beim Compare.B den Pin von A manuell umschalten. Sodass "A"
mit der doppelten Frequenz von B taktet. Und das gleiche beim Compare.C
mit Pin "B". Dann habe ich zwar Taktdifferenzen drin, weil der ISR
Aufruf auch Zeit kostet. Aber theoretisch müßte das so funktionieren?
Dann brauche ich keinen künstlichen Overflow?
Wegen den aktivierten Interrupts nochmal. Im CTC Modus muß ich keinen
Overflow ISR Handler schreiben?
Pins A und B umschalten? Ich bin mir gerade nicht sicher was du vorhast.
Wie sollen die drei Kurven aussehen? Was ist dein Ziel? Mal die Kurven
mal auf
Veit D. schrieb:> Wegen den aktivierten Interrupts nochmal. Im CTC Modus muß ich keinen> Overflow ISR Handler schreiben?
Du musst gar nix wenn du nicht willst. Im Normal Mode nicht und auch in
sonst keinem Modus. Falls das, was du vorhast, sich irgendwie mit den
Flags vom Timer bewerkstelligen lässt ("Toggle ... on ..." etc) brauchst
du auch gar keine Interrupts und kannst den Timer auch so laufen lassen.
Das einzige was du brauchst ist. Wenn du einen Interrupt aktiviert hast
(das entsprechende Bit in TIMSK/... gesetzt hast) dann muss dafür auch
eine ISR definiert werden, sonst gibts einen Reset wenn der Interrupt
auslöst.
> Der Timer läuft weiter, bis er bei 99 den Compare A erreicht. Pin wird> von 0 auf 1 getoggelt.> läuft weiter bis 199, Compare B erreicht, Pin wird von 0 auf 1 getoggelt> läuft weiter bis 299, Compare C erreicht, Pin wird von 0 auf 1 getoggelt> Overflow forciert. Timer beginnt bei 0> 99, COmpare A erreicht, Pin toggelt von 1 auf 0> 199, Compare B erreicht, Pin toggelt von 1 auf 0> 299, .... naja, so geht das jetzt immer weiter.
Hallo,
Moment nochmal bitte. Entweder bin ich wieder im alten falschen
Denkmuster oder ... ?
Wenn A/B/C nach verschiedenen Takten comparen und umschalten, warum sind
dann die Perioden alle gleich lang? Die sind nur verschoben.
Veit D. schrieb:> Wegen den aktivierten Interrupts nochmal. Im CTC Modus muß ich keinen> Overflow ISR Handler schreiben?
Welche ISR's du schreiben mußt, hängt davon ab, was du eigentlich
erreichen willst. Im Idealfall garkeine. Generell gültig ist aber: Wenn
du keine ISR für eine beliebige Interruptquelle schreibst, dann darfst
du diese Interruptquelle auch nicht enablen. Das brauchst du auch nicht,
denn auf die Funktion des Timers selber hat es überhaupt keinen
Einfluß, ob eine der durch den Timer gefütterten Interruptquellen
enabled ist oder nicht. Diesen simplen Sachverhalt hast du ganz
offensichtlich nicht begriffen...
Da du auch sonst die Möglichkeiten und Grenzen eines Timers
offensichtlich nicht begreifst, wäre vielleicht eine Annäherung aus der
anderen Richtung sinnvoll: Du versuchst einfach mal aufzuschreiben (am
besten in Kombination mit einer kleinen Grafik des gewünschten
Signalverlaufs), was du eigentlich erreichen willst...
Soweit ich das übersehen kann, hast du das im Verlauf des gesamten
bisherigen Threads nicht zu Stande gebracht. Und wenn man schon nicht
genau formulieren kann, was man eigentlich erreichen will, dann ist
jeder Versuch einer Implementierung des nicht durchdachten Scheißdrecks
von vornherein zum Scheitern verurteilt. So einfach ist das.
Veit D. schrieb:> Wenn A/B/C nach verschiedenen Takten comparen und umschalten, warum sind> dann die Perioden alle gleich lang? Die sind nur verschoben.
Immer an den roten Linien findet ein Overflow statt.
Die Perioden sind gleich lang, weil sich der Wert von A immer 99 Takte
nach jedem Overflow ändert. Da die Overflows 300 Takte
auseinanderliegen, ändert sich A also alle 300 Takte.
Genauso wie B, nur dass das immer 199 Takte nach jedem Overflow
stattfindet. Also auch alle 300 Takte, nur eben zu A verschoben...
Veit D. schrieb:> Wenn A/B/C nach verschiedenen Takten comparen und umschalten, warum sind> dann die Perioden alle gleich lang? Die sind nur verschoben.
Weil ein Sekundenzeiger immer 60 Sekunden zum umrunden eines
Zifferblattes braucht.
Hast du einen Kontakt, der schaltet, wenn der Sekundenzeiger bei 15
angelangt ist, und hast du einen Kontakt der bei 38 schaltet, dann
schalten sie zu verschiedenen Zeitpunkten während einer Umrundung. Aber
von einem Schaltvorgang an einem Kontakt bis zum nächsten Schaltvorgang
an demselben Kontakt dauert es immer 60 Sekunden. Nämlich genau so lang,
wie der Sekundenzeiger zum Umrunden des Zifferblattes braucht.
D.h. deine beiden Kontakte schalten zwar während einer Zeigerumrundung
zu verschiedenen Zeitpunkten, aber egal wo du den Kontakt anbringst, es
dauert für jeden Kontakt IMMER 60 Sekunden, bis der Sekundenzeiger
wieder bei ihm ist. Denn auch für die Sekundenmarkierung 38 muss der
Sekundenzeiger einmal komplett um das Zifferblatt rum, bis er wieder bei
der 38 angelangt ist. Und eine derartige Umrundung dauert nun mal 60
Sekunden)
Jetzt klarer?
(Sekundenzeiger entspricht dem Timer Register TCNT1. Die Schaltkontakte
entsprechen den OCR Compare Registern)
Hallo,
@ c-hater: es ging die ganze Zeit friedlich und konstruktiv ab im
Thread, trotz meiner fatalen Missverständnisse zum Timer. Da mußt du
jetzt nicht noch nachträglich frech werden wo fast alles geklärt ist.
Das habe ich foo und Karl Heinz zu verdanken.
Die eingemalten Overflow Linien machen das Bild klarer. Ich mach mal
eine Diagramm was ich vorhabe.
Hallo,
also. vorgestellt habe ich mir das so wie im Bild. Mit 2 Takten wäre es
sicherlich einfacher. Mit 3 müßte es nach neuen Erkenntnissen und
aktuellen Überlegungen dennoch möglich sein.
Start, TCNT bei 0 und zählt los ...
Compare.A > Pin.A geht automatisch von 0 auf 1 (leerer ISR)
Compare.B > Pin.B geht automatisch von 0 auf 1 und im ISR
setzte ich Pin A manuell zurück auf 0
und setzte das Compare Register neu.
Compare.A > Pin.A geht automatisch von 0 auf 1 (leerer ISR)
Overflow tritt ein > Pin.C geht automatisch von 0 auf 1 und im ISR setze
ich manuell die Pins A und B zurück auf 0.
Soweit meine Theorie. Sollte doch funktionieren?
Man könnte das eigentlich auch im CTC Modus machen. Da müßte ich
allerdings die Reihenfolge A/B/C umdrehen auf C/B/A, weil A den TCNT
nullt. ALso A muß der langsamste Takt sein.
Meinungen?
Edit:
Wobei ich für A sicherlich kein 50/50 Tastverhältnis hinbekommen werde.
Wie wichtig ist es dir, dass der Timer selbst die Pins schaltet?
Im Moment hast du einen Mischmasch aus manuellem Pin Toggeln und
timergestütztem Pin Toggeln.
Die Antwort auf die Frage hängt hauptsächlich davon ab, was sonst noch
so auf dem AVR läuft, vor allen Dingen welche sonstige Interrupts noch
im Spiel sind. Wenn sonst nichts auf dem AVR läuft, dann hat man eine
kleine Unsicherheit, weil es davon abhängt welchen Befehl der AVR gerade
ausführt, wenn ein Interrupt auftritt. D.h. die Flanken sind dann unter
Umständen nicht ganz Taktgenau, die Frequenz würde aber stimmen.
Langer Rede kurzer Sinn: wenn es von den restlichen Umständen vertretbar
ist, dann würde ich das ganz simpel mit einem CTC Modus machen, in
dessen Compare Interrupt ich ein 3 Bit 'Wort' an 3 Pins ausgebe und dann
das 3 Bit Wort um 1 erhöhe.
Man kann die ganze Sache auch rein in Hardware machen, in dem man für
jeden Compare Match ein ISR nachschiebt, die den jeweils korrekten OCR
Wert berechnet, wann der Compare das nächste mal auftreten soll. Der
Timer toggelt dann nach wie vor die Pins beim Compare Match
(wenn ich mir das jetzt so überleg, so kompliziert ist das eigentlich
gar nicht)
1
ISR(TIMER1_COMPA_vect){// Timer 1.A Interrupt
2
OCR1A+=99;
3
}
4
5
ISR(TIMER1_COMPB_vect){// Timer 1.B Interrupt
6
OCR1B+=199;
7
}
8
9
ISR(TIMER1_COMPC_vect){// Timer 1.C Interrupt
10
OCR1C+=299;
11
}
fettich.
wie ich immer sage: wenn man am TCNT Register rumpfuschen muss, dann
stimmt meistens was nicht.
Machbar.
Möglich sind auch ganzzahlige Verhältnisse, also z.B. A/B = 3/2, das
Tastverhältnis sollte dabei auch etwa 50 % betragen können:
Den Grundtakt des Interrupts auf das kleinste gemeinsame Vielfache
einstellen, und in der ISR für jeden Kanal einen Zähler laufen lassen,
welcher bei Erreichen des gewünschten Wertes den Kanal umschaltet
(Pin-toggle).
c-hater kennt sicher eine einfachere Lösung.
Mir fällt da gerade spontan keine Lösung ein wie das ein einzelner Timer
eigenständig hinbekommen könnte. Ohne Interrupthandler geht das hier
vermutlich nicht. Mir fallen da gerade nur mäßig schöne Lösungen ein,
die ich mal trotzdem hier lasse.
Idee 1 Lass den Timer mit dem für A gewünschten Takt laufen (CTC Mode,
OCR1A passend einstellen). Lass einen Interrupt auslösen wenn er von
vorne anfängt (Compare A oder overflow? Bin mir gerade nicht sicher.)
Da drin toggelst du Pin A.
Merk dir wie Pin B gerade steht.
Wenn Pin A jetzt 0 ist, dann toggelst du auch Pin B.
War B vorher 1 und ist jetzt 0, toggelst du auch Pin C.
Idee 2 lass den Timer von 0-65536 laufen, setze OCR1A = 16384, OCR1B =
32768, OCR1C = 49152, schalte alle Compare Interrupts und den Overflow
Interrupt an.
Pin A toggelst du in allen vier Interrupthandlern
Pin B nur in B und Overflow
Pin C nur bei Overflow
Karl H. schrieb:> fettich.
Nicht ganz.
Ich denke die additiven Konstanten müssten 100, 200 und 300 sein.
Die Initialwerte sind 99, 199 und 299. Aber addiert werden immer ganze
Hunderter.
Karl H. schrieb:> (wenn ich mir das jetzt so überleg, so kompliziert ist das eigentlich> gar nicht)
Läuft das dann nicht so langsam "auseinander" - durch die Zuweisungen
und Prolog/Epilog der ISR's, die ja unterschiedlich oft durchlaufen
werden?
Dieter F. schrieb:> Karl H. schrieb:>> (wenn ich mir das jetzt so überleg, so kompliziert ist das eigentlich>> gar nicht)>> Läuft das dann nicht so langsam "auseinander" - durch die Zuweisungen> und Prolog/Epilog der ISR's, die ja unterschiedlich oft durchlaufen> werden?
DIeter.
Das solltest du aber schon wissen, dass es dem Timer Register völlig
wurscht ist, was ich nebenher im µC mache. Das Timer Register zählt
unabhängig davon einfach weiter.
Selbst wenn mal ein Interrupt ein wenig zu spät kommt (d.h. die ISR
etwas zu spät aufgerufen wird), dann wird der nächste Interrupt trotzdem
zeitgerecht ausgelöst.
In seinem Fall hat er 8 * 99 Takte Zeit, bis die ISR für den Compare
Match A den neuen OCR Wert ins Register geschrieben haben muss. Das wird
ja wohl doch reichen! Bei den anderen Compare Matches hat er noch viel
länger Zeit.
Karl H. schrieb:> Das solltest du aber schon wissen, dass es dem Timer Register völlig> wurscht ist, was ich nebenher im µC mache. Das Timer Register zählt> unabhängig davon einfach weiter.
Ja, weiß ich :-)
Und 8*99 Takte reichen auch für 3 ISR ...
Du hast Recht, ich war auf dem Holzweg ....
Hallo,
ich war immer überzeugt das es funktionieren muß, nur mein Ansatz und
Verständnis zum Timer war total daneben.
Das ist mein Code den ich laut vorheriger Überlegung umgesetzt habe.
B taktet langsamer wie A, wie gewünscht. Nur C taktet genau wie B. ???
Und dann dachte ich die takten synchron. Nicht verschoben. Irgendwas
wird noch nicht stimmen.
Soll ich Euren Code testen oder wie machen wir weiter?
In Assembler und für einen ATmega48, 2*fA = 3*fB:
Der Timer1 wird eingestellt auf den Modus 'CTC auf ICR1', Interrupt auf
OCR1A; dann sieht die ISR so aus:
Veit D. schrieb:> Soll ich Euren Code testen oder wie machen wir weiter?
Ja klar.
Darum hab ich ihn ja reingeschoben.
Das ganze ist viel einfacher, als du denkst.
Die Technik, die zum Ziel führt, ist den Timer einfach lufe zu lassen
und die Matches anzupassen
Hallo,
das ist ja echt genial Karl Heinz. Das ist genau das was ich machen
wollte. Alle 3 Takte absolut synchron und immer um die Hälte langsamer.
Ich wußte das es machbar ist. :-) Auf die Idee das so in die ISR
Handler zu schreiben wäre ich nicht gekommen. Auch der OCR1x Register
Überlauf wird sich zu nutze gemacht damit es synchron bleibt.
Vielen Dank.
Das unten ist der Code dazu - zur Vollständigkeit.
Wo ist an meinem Code eins drüber der Denkfehler?
Veit D. schrieb:> Wo ist an meinem Code eins drüber der Denkfehler?
Hab jetzt nicht alles durchdacht.
Aber ein Fehler ist:
Wenn der Timer die Kontrolle über die Pins hat, dann hat er auch die
volle Kontrolle über die Pins!
d.h. du kannst dann nicht einfach mit Portzugriffen den Pin umschalten.
Erst mal musst du mittels COM Bits dem Timer die Kontrolle wieder
entziehen, dann kannst du den Portpin umschalten, dann gibst du die
Kontrolle wieder mit den COM Bits an den Timer zurück
Karl H. schrieb:> Wenn der Timer die Kontrolle über die Pins hat, dann hat er auch die> volle Kontrolle über die Pins!>> d.h. du kannst dann nicht einfach mit Portzugriffen den Pin umschalten.> Erst mal musst du mittels COM Bits dem Timer die Kontrolle wieder> entziehen, dann kannst du den Portpin umschalten, dann gibst du die> Kontrolle wieder mit den COM Bits an den Timer zurück
Hallo,
ach'so. Woher weis man das alles? Steht das im Datenblatt? Hab ich nicht
rausgelesen bisher.
Wegen Deiner Frage oben. Ich habe angefangen mir einen Frequenzgenerator
zu programmieren. Dabei dann umgesetzt wie man den Prescaler und den
passenden Compare Match Wert automatisch berechnen lassen kann. Sodass
man dem ganzen nur noch die Wunschfrequenz übergibt. Als das fertig war,
dachte ich mir, warum nicht noch die beiden anderen Kanäle B/C vom Timer
1 verwenden bevor die nutzlos sich langweilen. Dabei bin ich dann
abgestorben.
In dem Sinne kommen nicht noch mehr Interrupts dazu. Außer ich baue
einen Frequenzzähler. :-)
Hier mein Code zur automatischen Berechnung. Mein ganzer Stolz. :-)
1
voidcalculate_Timer1_(floatFrequency)
2
{// berechnet den best möglichen Compare Match Wert und Prescaler von Timer 1
Solange du bei 3 Frequenzen überall denselben Prescaler benutzen kannst
UND die jeweiligen Compare Werte nicht den erlaubten Bereich verlassen,
kannst du auch unterschiedliche Frequenzen mit den 3 OCR Registern
realisieren.
du könntest zb immer dem A Kanal die höchste Priorität einräumen, so
dass diese Frequenz erzeugt werden kann. Beim B Kanal bzw. C Kanal
könnte es dann sein, dass
* es sich mit den Werten für deren zu realsierende Frequenz so ausgeht,
dass die jeweilige Frequenz (im Rahmen der Timergenauigkeit) exakt
machbar ist
* es eine kleine Abweichung gibt
* die nicht machbar ist
Aber: Option 1 ist der Idealfall. Mit Option 2 kann man oft leben.
Option 3 wäre praktisch dasselbe wie jetzt: dein Funktionsgenerator kann
1 Frequenz erzeugen und nicht mehr.
Hallo,
ja, der Prescaler muß für alle unverändert bzw. gleich sein. Sonst macht
das auch alles keinen Sinn. Habe gerade mit Prescaler 1 probiert und den
obigen Compare Werten, dass Timing ist dann zu kurz. Da stimmt nichts
mehr und C ist völlig aus dem Takt. Ich vermute die ISR Aufrufe
verbraten mehr Zeit als die Compare Match's. Irgendwo ist immer eine
Grenze, ist klar.
Hab dann alle Compare Werte verdoppelt und es taktete wieder sauber.
Hier sieht man dann eine leichte Verschiebung der Flanken. Was man
sonst nicht sah. Wegen der ISR Aufrufe und Addition, nehme ich an.
Das mit Kanal A ist doch jetzt der Fall sein. Oder meinst Du das extra
manuell priorisieren wenn noch andere ISR dazu kommen sollten?
Hallo,
damit taktet noch alles synchron und sauber. A hat dann 53kHz.
Irgendwo zwischen 100 und 150 ist die Schmerzgrenze. :-)
Ich bedanke mich bei allen die geholfen haben.
Recht herzliches Danke! :-)
Hi, mal so als Denkanstoss aus dem Datenblatt:
• Bit 7 – FOCnA: Force Output Compare for Channel A
• Bit 6 – FOCnB: Force Output Compare for Channel B
• Bit 5 – FOCnC: Force Output Compare for Channel C
The FOCnA/FOCnB/FOCnC bits are only active when the WGMn3:0 bits
specifies a non-PWM
mode. When writing a logical one to the FOCnA/FOCnB/FOCnC bit, an
immediate compare
match is forced on the waveform generation unit. The OCnA/OCnB/OCnC
output is changed
according to its COMnx1:0 bits setting. Note that the FOCnA/FOCnB/FOCnC
bits are implemented
as strobes. Therefore it is the value present in the COMnx1:0 bits that
determine the
effect of the forced compare.
A FOCnA/FOCnB/FOCnC strobe will not generate any interrupt nor will it
clear the timer in Clear
Timer on Compare Match (CTC) mode using OCRnA as TOP.
habe das zwar noch nicht benutzt aber sollte die Kontrolle über die
Ausgänge erleichtern
Viel Erfolg, Uwe
Karl H. schrieb:> Wenn du auf einen spezifischen Interrupt nicht reagieren willst, dann> * gibst du ihn nicht frei> * und brauchst auch keine ISR dafür schreiben.>> Es gibt nur diese beiden Möglichkeiten. Die Kombination> * Interrupt freigeben> * aber keine ISR dafür schreiben> ist so erst mal nicht vorgesehen. Macht auch keinen Sinn.
Es mag Situationen geben wo das schon einen Sinn macht oder es eben
keinen Sinn macht, uC gleich zu resetten.
Deswegen legen normale Compiler eine Interrupt Tabelle mit RETI an.
Falls eine ISR definiert ist, wird RETI mit entspr. Adresse ersetzt.
Auch beim programmieren in Assembler macht man das meistens so, vor
allem bei grösseren uC wo das überhaupt nicht ins Gewicht fällt.
TINY13 und ähnliche sind eine andere Geschichte, aber hier geht es um
MEGA2560. gcc bietet zwar ISR(BADISR_vect) aber das ist nur ein Behelf.
c ist und bleibt eben unlogisch - anstatt nur eine Warnung auszugeben,
versucht der Compiler klüger zu sein als der Programmierer...
Marc V. schrieb:> Es mag Situationen geben wo das schon einen Sinn macht oder es eben> keinen Sinn macht
So?
Dann nenn doch mal 1 Szenario, bei dem es Sinn macht einen Interrupt,
den man offensichtlich gar nicht behandeln will, freizugeben. In 99%
aller Fälle ist das ein Fehler, der nur Rechenzeit kostet. Es ist also
sinnvoll, dass so ein Fehler nicht unentdeckt bleibt.
> c ist und bleibt eben unlogisch
Das hat mit C an sich nicht das geringste zu tun. Das haben die
Implementierer der Runtime Lib beim gcc so entschieden.
Bitte nicht schon wieder: ein Implementierungsdetail einer spezifischen
Implementierung herauspicken (das man auch ganz anders hätte
implementieren können), und das dann zum Anlass zu nehmen um auf C
loszugehen. Das eine hat mit dem anderen nichts zu tun!
Eisenbahnen sind auch nicht generell unkomfortabel, nur weil beim ICE ab
einer bestimmten Temperatur die Klimaanlage nicht mehr mitkommt.
Veit D. schrieb:> also. vorgestellt habe ich mir das so wie im Bild.
Das sieht für mich wie das Signalspiel eines dreistufigen Binärzählers
aus.
Das würde man natürlich in einer(!) ISR in Software realisieren mit
einem einzigen Timerkanal als Antrieb, denn das wäre die bei weitem
effizienteste Lösung.
Die ISR müßte in etwa so aussehen (ohne Nutzung weiterführender
Optimierungen):
Beispielwerte für Bits 0..2 von OUTPORT
.EQU OUTMASK=7
.EQU INCRMNT=1
ISR: ;(4)
push R16 ; 2
push R17 ; 2
push R18 ; 2
in R18,SREG ; 1
in R16,OUTPORT ; 1
andi R16,~OUTMASK ; 1
lds R17,COUNTER ; 2
or R16,R17 ; 1
out OUTPORT,R16 ; 1
subi R17,-INCRMNT ; 1
andi R17,OUTMASK ; 1
sts COUNTER,R17 ; 2
out SREG,R18 ; 1
pop R18 ; 2
pop R17 ; 2
pop R16 ; 2
reti ; 4
;--
;32
Damit kannst du drei beliebige "benachbarte" Portpins in der gewünschten
Weise toggeln lassen, wobei die Flanken der drei Signale zueinander
perfekt synchron sind, als Jitter fällt allerdings natürlich die
variable Interruptlatenz an, die vor allem davon abhängt, was sonst noch
an ISRs im System läuft.
Die maximale Ausgabefrequenz für das schnellste der drei Signale beträgt
1/64 des Systemtaktes, die beiden anderen dementsprechend 1/128 und
1/256. Wie gesagt: ohne weitere Optimierungen. Mit Optimierungen sind
noch deutlich höhere Frequenzen erreichbar. Wie oft bei kurzen und sehr
häufig durchlaufenen ISRs ist das Reservieren von Registern die
Optimierung, die mit Abstand am meisten bringt. Hier würden dadurch 12
Takte für push/pop entfallen und damit die möglichen Maximalfrequenzen
auf 1/40, 1/80, 1/160 des Systemtaktes steigen.
Hallo,
schon erstaunlich was so eine Frage zum Timer auslöst. Sehr schön. :-)
@ Uwe:
das mit dem FOC Bit hatte ich mir auch schon überlegt, als ich noch
nicht zu Rande kam mit dem Timer. Nur habe ich dann nicht den gleichen
Effekt wie jetzt mit dem Compare Match Interrupt? Die FOC Bits muß ich
auch irgendwie zu bestimmten Zeiten ändern.
@ c-hater:
aha, Assembler Freak, wie S. Landholt. :-)
Ja, sieht aus wie ein Binärzähler, soll es aber nicht werden.
Ich wollte nur den einen Timer voll ausnutzen wenn der schon 3 Kanäle
hat.
Falls man mal einen 2. Takt benötigt der synchron zum ersten laufen
soll.
Da ich leider von Assembler überhaupt nichts verstehe, kann ich den Code
leider nicht lesen. Wenn Du das nochmal in C schreiben würdest, kann ich
mitlesen. Wie reserviert man Register? Die Register vom Timer sind doch
schon da.
Karl H. schrieb:> In 99% aller Fälle ist das ein Fehler, der nur Rechenzeit kostet.
Eben. Also nur eine Warnung vom Compiler.
> Es ist also sinnvoll, dass so ein Fehler nicht unentdeckt bleibt.
Naturlich, (falls es es ein Fehler ist) aber, wie gesagt, eine einfache
Warnung reicht vollkommen. Es ist kein fataler Fehler, kann keinen
Schaden anrichten, warum gleich uC resetten ?
> So?> Dann nenn doch mal 1 Szenario, bei dem es Sinn macht einen Interrupt,> den man offensichtlich gar nicht behandeln will, freizugeben.
Hmmm:
Angenommen, ich will mal sehen, ob schon der nackte Interrupt als
solches meinen Programm irgendwie durcheinander bringt. Muss ja nicht
gerade ATMEL sein, es kann sich um einen uC mit verschachtelten und
priorisierten Interrupts handeln.
Will aber nicht seitenlang auskommentieren, mache das also mit #ifdef
in irgendeinem .h file ('debug.h' z.B.).
Bleibt alles ubersichtlich und leicht zu debuggen.
> Bitte nicht schon wieder: ein Implementierungsdetail einer spezifischen> Implementierung herauspicken (das man auch ganz anders hätte> implementieren können), und das dann zum Anlass zu nehmen um auf C> loszugehen. Das eine hat mit dem anderen nichts zu tun!
Tu ich nicht, nur mag ich halt c nicht weil es einerseits ganz
unsinnige Sprachkonstrukte zulässt, aber andererseits etwas, was der
Programmierer vielleicht bewusst so gemacht hat, mit Reset bestraft.
Für mich ist das eben unlogisch.
Auf jeden vermeintlichen Fehler soll hingewiesen werden, aber KEIN
Fehler soll in einem Reset (und das auch noch ohne Warnung) enden.
Zugegeben, es ist nur gcc und nicht c als solches, aber...
Marc V. schrieb:> Zugegeben, es ist nur gcc und nicht c als solches, aber...
Es ist noch nicht einmal gcc.
Es ist die Art und Weise, wie die Designer der für einen AVR
spezifischen Runtime Library gedacht haben, dass die Dinge laufen
sollen.
Mit so einer Aussage wie der getätigten diskreditierst du dich nur
selber.
Du schimpfst auf Autos generell, weil die Entscheidungsträger bei Audi
entschieden haben, die Batterie unter dem Rücksitz einzubauen. Man kann
diese Entscheidung natürlich kritisieren. Aber die Kritik muss sich an
die Entscheidungsträger bei Audi richten. Aber aus dieser
Detailentscheidung eine generelle Kritik an Autos abzuleiten, zeigt
einfach nur mangelnde Sachkenntnis.
Genauso auch hier.
Natürlich kann man die Entscheidung kritisieren, dass eine fehlende ISR
zu einem Reset führt. Aber du musst deine Kritik an den richtigen
Adressaten richten. C kann hier nicht das Geringste dafür.
> Will aber nicht seitenlang auskommentieren, mache das also> mit #ifdef in irgendeinem .h file ('debug.h' z.B.).> Bleibt alles ubersichtlich und leicht zu debuggen.
Sorry. Aber das seh ich jetzt als ein eher schwaches Argument an. Denn
wenn ich zu Performance Zwecken einen Interrupt freigebe, dann hab ich
auch die 15 Sekunden Zeit um eine leere ISR dafür zu schreiben.
Und so oft kommt der Fall ja dann auch wieder nicht vor.
In meinen Augen muss man sich schon am Normalfall richten und Ausnahmen
als das sehen was sie sind: Ausnahmen. Dein konstruiertes Szenario ist
sicherlich nicht der Normalfall. Noch sehe ich nicht den Sinn darin, zu
Testzwecken einen Interrupt zuzulassen, der im regulären Programm
überhaupt nicht vorkommt. Testest du auch, ob eine UART-Fifo dir dein
Programm lahmlegt, obwohl du bei einem Lauflicht gar keine UART hast?
> Tu ich nicht, nur mag ich halt c nicht
Ist dein gutes Recht.
Ist aber noch lange kein Grund unsachlich zu werden und mit falschen
Argumenten zu kommen.
Sowas nennt man in Diskussionen 'einen Strohmann bauen'. Weil man keine
Argumente vorbringen kann, die die eigene Position stützen, greift man
eben etwas anderes in diesem Umfeld an und tut dann so, als ob die
Argumente auch auf das ursprüngliche Thema greifen würden. Esoteriker
benutzen diese Technik gerne. Für Globuli hat man keine Argumente, also
konstruiert man einen Strohmann der da lautet: in der Medizin ist auch
nicht alles korrekt. Ist völlig richtig, in der Medizin gibt es auch
schwarze Schafe. Aber das ist ja schliesslich nicht relevant, wenn die
Fragestellung lautet: Funktionieren Globuli besser als Plazebo? Selbst
wenn alle Krankenkassen korrupt sind, selbst wenn alle Pharmafirmen nur
Geld scheffeln wollen, selbst dann hat Wasser immer noch kein
Gedächtnis.
Karl H. schrieb:> Sorry. Aber das seh ich jetzt als ein eher schwaches Argument an. Denn> wenn ich zu Performance Zwecken einen Interrupt freigebe, dann hab ich> auch die 15 Sekunden Zeit um eine leere ISR dafür zu schreiben.
Die ISR ist schon geschrieben, nur soll die vom Compiler eben nicht
compiliert werden wenn ich es nicht will. Will aber auch nicht, dass
dadurch mein Programm zum Ringelspiel wird.
> Und so oft kommt der Fall ja dann auch wieder nicht vor.
Natürlich.
> Dein konstruiertes Szenario ist> sicherlich nicht der Normalfall. Noch sehe ich nicht den Sinn darin, zu> Testzwecken einen Interrupt zuzulassen, der im regulären Programm> überhaupt nicht vorkommt. Testest du auch, ob eine UART-Fifo dir dein> Programm lahmlegt, obwohl du bei einem Lauflicht gar keine UART hast?
Wenn ich 2 Versionen habe, eine mit DMX und eine ohne, schon.
> Ist aber noch lange kein Grund unsachlich zu werden und mit falschen> Argumenten zu kommen.> Sowas nennt man in Diskussionen 'einen Strohmann bauen'. Weil man keine> Argumente vorbringen kann, die die eigene Position stützen, greift man
Ich will hier bestimmt keine Diskussion über Vor- und Nachteile der C
Sprache führen. Ich benutze c nicht (oder nur sehr selten), habe aber
nichts gegen Leute die es tun. Auch Arduino oder jede andere Sprache
ist für mich in Ordnung.
Jedem das Seine.
Ihn dünkt', er säh' ein Teufelchen,
das dreht' am Timer rum.
Er guckt' noch mal und merkt', es war
gar ein Politikum.
"Ob c, ob asm", sprach er,
" die Streiterei ist dumm."
Veit D. schrieb:> Ja, sieht aus wie ein Binärzähler, soll es aber nicht werden.
Was denn dann?
> Ich wollte nur den einen Timer voll ausnutzen wenn der schon 3 Kanäle> hat.
Ist das ein Designziel? Oder geht es darum, den eigentlich
angestrebten Zweck mit minimalem Aufwand zu erreichen?
> Da ich leider von Assembler überhaupt nichts verstehe, kann ich den Code> leider nicht lesen.
Keine Arme, keine Kekse.
> Wenn Du das nochmal in C schreiben würdest, kann ich> mitlesen.
Ausnahmsweise, weil es sowieso zu warm ist, um irgendwas wirklich
Sinnvolles zu tun, schreibe ich auch mal C-Style, ohne dafür bezahlt zu
werden.
Außerdem musste heute sowieso schon den ganzen, gefühlt endlos langen,
Tag C# schreiben, da habe ich den Kotze-Geschmack des C-Style sowieso
schon dermaßen brennend im Hals, da kommt's dann auf die paar Zeilen
mehr wohl auch nicht mehr an...
#define OUTMASK 7
#define INCRMNT 1
#define OUTPORT PORTC
volatile uint8_t COUNTER;
ISR(TIMER1_OVF_vect) //oder was gerade geeignet erscheint
{
uint8_t tmp=OUTPORT; //tmp entspricht R16 in meinem Asm-Code
tmp &= ~OUTMASK;
tmp |= COUNTER;
OUTPORT = tmp;
COUNTER += INCRMNT;
COUNTER &= OUTMASK;
}
> Wie reserviert man Register?
Man benutzt sie einfach ausschließlich in der zeitkritischen ISR.
> Die Register vom Timer sind doch> schon da.
Das sind IO-Register der Peripherie, das ist was anderes. Die Register,
die ich meine, sind die Arbeitsregister der MCU, also das, was in meinem
Code beispielhaft als R16, R17 und R18 erscheint. Kannst du dir ungefähr
als sehr schnelle lokale Variablen vorstellen.
Der verschissene C-Compiler, der dich von allem abschirmt, was wirklich
wichtig ist, muß intern auch mit diesen Dingern arbeiten. Und er macht
das gerade bei ISRs oft nicht wirklich intelligent. Aber diese wirklich
sehr primitive Routine sollte ein heutiger C-Compiler aus eigener Kraft
einigermaßen brauchbar umgesetzt bekommen, d.h. sie sollte nicht
wesentlich mehr als die 32 Takte brauchen, die meine
straightforward-Routine braucht, vielleicht sogar wirklich exakt diese
32. Müßte ich direkt mal probieren, ob der gcc das schafft...
Bloß die ultimative Optimierung, Register exklusiv zu reservieren, die
nötig wäre, um die Sache nochmals merklich schneller zu machen, die
kannst du in C halt nur sehr eingeschränkt bzw. auch garnicht
realisieren.
S. Landolt schrieb:> 3 Takte ließen sich noch einsparen, denn der OUTPORT ist ja bereits der> COUNTER.
Da hast du natürlich absolut Recht. Große Peinlichkeit.
Verdammt, ich bin ganz offensichtlich bereits wesentlich mehr
Hochsprachen-geschädigt, als ich es selber wahr haben will. Ich bin ja
schon fast genau so doof wie irgendsoein hergelaufener verschissener
Compiler, wenn ich solche überaus naheliegenden Optimierungen nicht mehr
ad hoc erkenne.
Und noch 16 Jahre bis zur Rente, ich werde wohl in dieser Zeit noch
völlig verblöden und am Ende doch noch vollständiger C-Legastheniker
werden...
Finstere Aussichten...
Entschuldigung, es lag nicht in meiner Absicht, eine solche Reaktion
hervorzurufen; im Gegenteil dachte ich, es freue Sie, wenn jemand bei
Ihnen mitdenkt.
Hallo,
irgendwie bist du ganz schön schräg drauf, trotz deines älteren
Jahrgangs. Da sollte man doch ruhiger und gelassender sein.
Da du dich erbarmt hast in C zu schreiben, möchte ich Dir natürlich
dafür danken.
Nochmal Danke an alle. Habe viel gelernt von Euch.
Hallo,
ich glaube Du hast das mit dem ISR Handler doch irgendwie falsch
verstanden. Mit Deinem Assembler schalten doch 3 Pins gleichzeitig,
takten also im selben Takt. Oder nicht? Das war nicht mein Wunschziel.
Ich möchte die anderen beiden jeweils mit halben Takt haben.
Siehe meinem Beitrag vom 2.7. 21:53 Uhr.
Das geht laut meiner Logik nicht in einem ISR.
Das war bestimmt ein Missverständnis.
Noch eine Frage wegen der benötigten Interruptzeit.
Wenn ich nur einen Compare ISR verwende zum ausloten wieviel Takte
Minimum sind, dann komme ich auf 51 Takte. Darunter kommt der Takt außer
Tritt.
Im ISR erfolgt aber nur eine einfache Addition. Die der Compiler ja
optimal umsetzen können sollte, denke ich. Jetzt stellt sich mir die
Frage, warum in Assembler mehr gemacht werden kann mit weniger Takten?
Geht doch eigentlich gar nicht. Oder habt ihr in der Rechnung was
unterschlagen? Da kommt sicherlich noch was an Takten dazu was nichts
mit dem Code im ISR zu tun hat. Aber drum herum mit dem ISR aus µC
Sicht. Register sichern usw.
1
TCCR1A|=(1<<COM1A0);// set Toggle OCnA Pin on compare match
2
3
OCR1A=50;// Compare Match Register A (Precaler 1)
4
5
TCCR1B|=(1<<CS10);// set Prescaler 1
6
TIMSK1|=(1<<OCIE1A);// enable Timer Compare Interrupt A
Hi
>und das hier taktet mit nur 122Hz auf Kanal A. ???
Passt doch. Der 16-Bit-Timer hat bei 16 MHz eine Overflowtime von 4,096
ms. Das ergibt beim togglen von OCR1A eine Periode von 8,192 ms oder
eine Frequenz von 122 Hz.
MfG Spess
Veit Devil schrieb:
> Mit Deinem Assembler schalten doch 3 Pins gleichzeitig,> takten also im selben Takt. Oder nicht? Das war nicht mein> Wunschziel. Ich möchte die anderen beiden jeweils mit> halben Takt haben.
Weder weiß ich, wer angesprochen ist, noch was eigentlich das Wunschziel
ist. Wie auch immer:
B0 liefert die Frequenz f, B1 f/2, B2 f/4, synchron auf die fallende
Flanke. Möchte man die steigende, dann muss
subi r17,-INCRMNT durch subi r17,INCRMNT ersetzt werden.
Die Frequenz f selbst wird bestimmt durch den Prescaler des Timers und
den verwendeten Interrupt; für ein möglichst großes f wird man '/1'
einstellen und den Modus 'CTC' mit sehr klein besetztem OCR bzw. ICR
wählen.
Veit D. schrieb:> ich glaube Du hast das mit dem ISR Handler doch irgendwie falsch> verstanden.
Ich? Eher: Du.
> Mit Deinem Assembler schalten doch 3 Pins gleichzeitig,> takten also im selben Takt. Oder nicht?
Nein. Sie schalten zwar gleichzeitig, aber eben nur wenn sie überhaupt
schalten. D.h.: Flanken erfolgen gleichzeitig, aber nur für den höchsten
Takt erfolgt bei jedem Aufruf der ISR eine Flanke, bei dem zweithöchsten
nur bei jedem zweiten usw.
> Im ISR erfolgt aber nur eine einfache Addition.
Hmm? Inzwischen hast du doch auch den C-Code und damit sollte selbst für
dich sichtbar sein, das da neben der Addition durchaus noch einige
(bit-)logische Operationen passieren...
S. Landolt hat korrekterweise darauf hingewiesen, daß die Verwendung
einer zusätzlichen Variablen für den Zähler (der im Kern allein für das
gewünschte Signalspiel sorgt) überflüssig war (was sowohl meinen Asm-
als auch meinen C-Code betrifft), aber die binäre Logik ist natürlich
weiterhin nötig, wenn man bei der Wahl der Ausgangsbits einigermaßen
frei bleiben will.
Bei kluger Wahl der benutzten Portbits ergeben sich allerdings wieder
zusätzlich Optimierungsmöglichkeiten. Wählt man nämlich die Bits 7..5
eines Ports zur Ausgabe, entfällt die Bitlogik vollständig und dadurch
wird auch noch ein Register weniger benötigt, was es erspart, eben
dieses zu retten/wiederherzustellen...
1
.EQU INCRMNT=$20
2
.EQU OUTPORT=PORTC
3
4
ISR: ;(4)
5
push R16 ; 2
6
push R17 ; 2
7
in R17,SREG ; 1
8
in R16,OUTPORT ; 1
9
subi R16,-INCRMNT ; 1
10
out OUTPORT,R16 ; 1
11
out SREG,R17 ; 1
12
pop R17 ; 2
13
pop R16 ; 2
14
reti ; 4
15
;--
16
;21
bzw.
1
#define INCRMNT 0x20
2
#define OUTPORT PORTC
3
4
volatileuint8_tCOUNTER;
5
6
ISR(TIMER1_OVF_vect)//oder was gerade geeignet erscheint
7
{
8
uint8_ttmp=OUTPORT;
9
tmp+=INCRMNT;
10
OUTPORT=tmp
11
}
Derselbe Vorteil ergibt sich übrigens, wenn du statt drei acht Takte
haben willst, also einen Port komplett benutzt. Die Routine ändert sich
dadurch nicht weiter, nur die Definition der einen Konstanten:
#define INCRMNT 1
Oder allgemein gesprochen kannst du mit der optimierten Routine jede
beliebige Zahl von :2-Takten erzeugen, wenn du nur immer die benötigten
Ausgabebits mit dem höchstwertigen Portbit beginnst.
> Die der Compiler ja> optimal umsetzen können sollte, denke ich. Jetzt stellt sich mir die> Frage, warum in Assembler mehr gemacht werden kann mit weniger Takten?
Weil man in Asm keinerlei Rücksicht auf Erfordernisse und Konventionen
der Runtime-Umgebung eine Compilers nehmen muß.
> Da kommt sicherlich noch was an Takten dazu was nichts> mit dem Code im ISR zu tun hat.
Ja, natürlich. Selbst mein Asm-Code enthält ja noch etliches für die
eigentliche Operation unnötige Zeug, in C siehst du diesen Overhead nur
nicht im Quelltext, er ist aber natürlich trotzdem da und das oft noch
in "fetterer" Form.
Hallo,
@ Spess:
ich nutze keinen Overflow Interrupt sondern den Compare Match Interrupt.
Und an den 122 Hz ändert sich auch nichts, egal welchen Counter ich
ändere.
@ Landholt:
ich habs eigentlich schon mehrfach geschrieben inkl. gemalten
Taktdiagramm. Weis nicht was ich noch besser erklären soll. Echt nicht.
@ hater:
tut mir leid, ich raff nicht was Dein Code machen soll. Also ich weis
schon was er machen soll mit dem Port laut Deiner Beschreibung, Art
Binrzähler, aber der Code sagt mir nichts was dazu passen könnte. Das
Ding taktet Kanal A immer mit 122Hz oder 3,8Hz und die anderen sind
außer dem Takt. Oder Kanal A ist nicht 50/50 im Tastv.
Du liest den OUTPORT aus, addierst 32 (0x20) hinzu und gibts das wieder
auf den OUTPORT. Die Logik dahinter raff ich nicht.
Wenn ich dem Compare Match ISR nutze muß der sich ja nach dem richten.
Also wenn Du noch Lust hast und gute Laune, schauste Dir bitte den
ganzen Code an und korrigierst ihn bitte, viel ist das ja nicht oder wir
brechen hier ab und gut ist. Oder schreibst meinetwegen alles in
Assembler und sagst mir welcher Wert den Haupttakt ändert. Nur das es
erstmal funktioniert.
> Du liest den OUTPORT aus, addierst 32 (0x20) hinzu und gibts das wieder
auf den OUTPORT. Die Logik dahinter raff ich nicht.
Naja, die oberen 3 Bit sollen durchgezählt werden:
000xxxxxx + 00100000 =
001xxxxxx + 00100000 =
010xxxxxx + 00100000 =
011xxxxxx + 00100000 =
100xxxxxx + 00100000 =
101xxxxxx + 00100000 =
110xxxxxx + 00100000 =
111xxxxxx + 00100000 =
000xxxxxx + 00100000 =
...
Die x-en bleiben dabei was auch immer sie sind.
Hi
>@ Spess:>ich nutze keinen Overflow Interrupt sondern den Compare Match Interrupt.>Und an den 122 Hz ändert sich auch nichts, egal welchen Counter ich>ändere.
Dein Timer läuft aber im Normal Mode. Da zählt er bis 65535 und fängt
dann von 0 wieder an. Der Inhalt vom OC-Registers bestimmt nur den
Zeitpunkt des Interrupts. Der Abstand bleibt aber 65536 Timertakte. Da
kannst du in das Register reinschreiben was du willst.
MfG Spess
Veit D. schrieb:> Das> Ding taktet Kanal A immer mit 122Hz oder 3,8Hz und die anderen sind> außer dem Takt. Oder Kanal A ist nicht 50/50 im Tastv.
Du mußt natürlich an PortB5..7 messen und nicht an den OCx-Ausgängen.
Wenn du das nicht erkannt hast...
> Du liest den OUTPORT aus, addierst 32 (0x20) hinzu und gibts das wieder> auf den OUTPORT. Die Logik dahinter raff ich nicht.
Dabei ist die Logik überaus trivial: Wenn man was haben will, was ein
Signalspiel wie ein Binärzähler erzeugt, dann ist die einfachste
Methode, tatsächlich einen Binärzahler zu benutzen...
Und genau das wird tmp in den oberen drei Bits, wenn man fortgesetzt
0x20 addiert. Und wenn man vor dem addieren den Port nach tmp liest und
nach dem addieren das Ergebnis wieder auf den Port ausgibt, hat man
nicht nur die gewünschten Signale auf den drei Portbits, sondern dann
dient das Portregister selber auch noch als Speicher der
Zwischenergebnisse zwischen den einzelnen ISR-Aufrufen.
> und sagst mir welcher Wert den Haupttakt ändert.
Keiner bzw. höchstens die Wahl des Prescalers.
Du benutzt den Timer einfach im falschen Modus. Du nimmst den
Normalmodus bei dem immer der volle Zählumfang durchlaufen wird, müßtest
ihn aber z.B. im CTC-Modus benutzen, bei dem beim Erreichen von OCxA
TCNTx auf Null resettet wird.
Und sofort funktioniert das wie gewünscht.
> volatile uint8_t COUNTER;
Kannst du übrigens ersatzlos streichen, ist bei der letzten Änderung
überflüssig geworden (mir selber allerdings beim C-Code ebenfalls
durchgerutscht, das kommt vom C&P-"Programmieren").
S. Landolt schrieb:
> B0 liefert die Frequenz f, B1 f/2, B2 f/4
So war es für die erste Programmversion von c-hater. Was war daran nicht
zu verstehen?
c-hater schrieb:
> Du mußt natürlich an PortB5..7 messen und nicht an den OCx-Ausgängen.
Das gilt nun für die zeitoptimierte Version.
c-hater schrieb:
> Wenn du das nicht erkannt hast...
Da kommen mir allerdings auch Zweifel...
Veit Devil schrieb:
> ...meinetwegen alles in Assembler...> Nur dass es erstmal funktioniert.
Dem Manne kann geholfen werden (wenn ihm denn zu helfen ist):
1
.include "m644def.inc"
2
3
.equ outDDR = DDRC ; Ausgabe auf PORT C (nix OCn !!)
Hallo,
so Leute, hab das wegen der Addition nicht erkannt das als Effekt eine
Art Bit schieben herauskommt. Danke für die Info.
Jetzt habe ich den CTC Mode aktiviert und nur den Compare Match
Interrupt aktiv. Damit man die Pins Port.B, welche auch gleich OC1x
sind, selbst schalten kann. Deshalb stimmte das alles schon noch mit den
Pins. Ganz blöd bin ich auch nicht. :-)
Ich habe das an Port B und Port C probiert. Habe aber den Effekt, dass
die Takte kein 50% Tastverhälnis haben. Jetzt wird doch der ISR aller
150 CPU Takte aufgerufen und damit der Port.C geändert. Damit müßte aber
alles im 50% Tastv. laufen. ???
> so Leute, hab das wegen der Addition nicht erkannt das als Effekt> eine Art Bit schieben herauskommt.
Wieso Bit schieben? Ich dachte, Sie wollen einen Binärzähler.
Hallo,
oh man, 20 und 0x20 ist natürlich ein Unterschied. Das kommt vom wilden
rumprobieren. Wollte dich gerade loben für dein Assembler, funktioniert
nämlich 1a. Anbei ein Bild davon.
Ich hatte geschrieben eine Art Bit schieben! Oder eine Art
Lauflichtmuster.
Nenn es wie du möchtest. Binärzähler ist natürlich treffend für das was
es macht.
Hallo,
jetzt klappt das auch mit dem C Code. :-)
Man war das eine schwere Geburt, begleitet von Missverständnissen und
eigenen Schusselfehlern.
Ich bedanke mich für Eure Geduld. c-hater und Landholt.
Alle anderen natürlich auch.
Ihn dünkt', er sähe ein Problem,
das war so zäh wie Brei.
Er guckt' noch mal und merkt', es war
ein Fall für mehr als zwei.
"Potz Tsakalotos auch", sprach er,
"jetzt klappt das einwandfrei!"
S. Landolt schrieb:> "Potz Tsakalotos auch", sprach er,> "jetzt klappt das einwandfrei!"
Who, the fuck, is "Tsakalotos"?
Die Leute, die Wikipedia ausspuckt, scheinen mir allesamt nicht ganz zum
Thema zu passen und meine sowieso weitgehend vergessene klassische
Bildung bezüglich der griechischen Antike hilft mir auch nicht weiter.
Also wen, zum Teufel, meinst du?
Vorläufig würde ich "Tantalus" als Ersatz vorschlagen. Der paßt in dein
schräges Versmass und obendrein auch logisch in den Text. Und er hat den
Vorteil, dass er halt eine der wenigen Gestalten der griechischen
Klassik ist, die ich nicht vergessen habe...
Ich finde nur den Namen des Neuen so witzig, klingt wie 'Donnerwetter',
'Dunnerschlach', 'Potz Blitz', oder wie das 'Donizetti' von Danny Wilde
aus 'Die 2', falls Sie sich noch an diese Fernsehserie Anfang der
Siebziger erinnern können.
Sehen Sie, das ist der Unterschied zwischen uns beiden: Sie denken an
Tantalos und seine Qualen, ich an Danny Wildes schrägen Witze.
Versmaß und Schema sind übrigens geklaut aus 'Sylvie & Bruno' von
Charles Lutwidge Dodgson alias Lewis Carroll (in der Übersetzung von
Sabine Hübner).
S. Landolt schrieb:> Ich finde nur den Namen des Neuen so witzig
Nachdem ich jetzt die Nachrichten gesehen habe, weiß ich auch, wen du
meinst.
> klingt wie 'Donnerwetter',> 'Dunnerschlach', 'Potz Blitz'
Keine besonders clevere Idee, den "Klang" fremder Namen in der eigenen
Sprache zu instrumentalisieren, für welchen Zweck auch immer. Findest du
nicht auch?
Hi,
das Thema ist nun zwar anders, besser geklärt aber ich will trotzdem
noch mal auf die FOC-Bits zurückkommen. Wenn man Ausgänge via COMnx1:0
bits
an Timer bindet, sind die Ausgänge nicht mehr über Out/SBI/CBI
erreichbar es sei denn du setzt COMnx1:0 auf 0, beeinflusst den Ausgang
und setzt COMnx1:0 erneut auf das gewünschte. Wenn wir mal bei deinen 3
ISR's bleiben wollen wäre es möglich in jeder ISR die anderen Ausgänge
über FOCx zu toggeln ohne die "COMnx1:0" rumschreiberei.
Viel Erfolg, Uwe
an c-hater
Wieso clever? Spaßig allenfalls, aber selbst das ist zugegebenermaßen
Geschmackssache. Ich hatte mich einfach gefreut, dass unser junger
Mensch jetzt ans Ziel gekommen ist, und in dieser Laune musste ich eben
über den Namen lachen, wie so allmählich über die ganze neugriechische
Tragödie.
Suchen Sie sich heute Abend Ihren eigenen, ganz persönlichen Grund für
gute Laune, und sei es nur, dass der Erfolg hier zu großen Teilen Ihnen
zu verdanken ist.
Hallo,
das mit den FOC Bits guck ich mir später mal an, hab zur Zeit andere
Dinge im Kopf.
Was ich noch fragen möchte ist, woher weis man wie Definitionsdatei für
seinen µC heißt?
Du hattest für Deinen .include "m644def.inc" geschrieben.
Die Zeile hatte ich weggelassen und das hat für mich Atmel Studio
passend erledigt. Beim Projekt anlegen hab ich ja den 2560 ausgewählt.
Wenn ich raten müßte sollte das m2560def.inc lauten bei mir. Nur wo kann
man gezielt nachschauen?
Edit:
habs gefunden:
C: ... Atmel\Atmel Toolchain\AVR Assembler\Native\2.1.1175\avrassembler
Veit D. schrieb:> Wenn ich raten müßte sollte das m2560def.inc lauten bei mir. Nur wo kann> man gezielt nachschauen?>> Edit:>> habs gefunden:>> C: ... Atmel\Atmel Toolchain\AVR Assembler\Native\2.1.1175\avrassembler
Die Namen der Includes sind ziemlich regulär aufgebaut. Die Megas
beginnen mit einem "m", die Tinys mit "tn". Dann kommt 1:1 die
Kennnummer, mit der man das Teil auch bestellen würde, also "2560" oder
"1284p" oder "2313a". Und danach einheitlich "def.inc".
Man braucht also eigentlich nicht das Verzeichnis zu durchforsten,
sondern kann den Namen der Include-Datei rein formal aus der
Device-Bezeichnung ableiten.
Ausnahmen bestätigen wie immmer die Regel.
Hallo,
ich möchte die Interrupt Routine in Assembler ins restliche C Programm
einbinden. Wegen der höheren Geschwindigkeit. Ich habe mit Inline
Assembler probiert und mit ausgelagerter .h und .S Datei. Ich bekomme es
einfach nicht hin. Egal was ich versuche zu verstehen. Atmel Studio
meckert laufend rum mit "constant value required" oder "expected comma
after incrmnt" oder "unknown pseudo-op .def" und in Zeilennummern die es
gar nicht gibt.
Könnte mir jemand zeigen was ich falsch mache?
S. Landolt schrieb:> Nun wird noch der Overhead der c-ISR drinstecken, wie Sie diesen> wegbekommen, muss ein c-Programmierer beantworten.
Das ist relativ einfach.
Man kann die ISR mit dem Attribut NAKED markieren, dann obliegt es dem
Programmierer alle Register zu sichern und wieder herzustellen.
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
S. Landolt schrieb:> Und so betrachtet sollte reines c doch eigentlich genauso schnell sein,> oder?
Nicht ganz.
Der gcc setzt in einer ISR einen Standard-Prolog und Standard-Epilog
ein. In dem werden ein paar Register gesichert und wieder hergestellt.
Es findet dabei keine Analyse statt, ob dies tatsächlich für alle im
Prolog gesicherten Register notwendig wäre.
D.h. unter Umständen macht der Compiler hier etwas zuviel
Sicherungsarbeit.
S. Landolt schrieb:> Nun wird noch der Overhead der c-ISR drinstecken, wie Sie diesen> wegbekommen, muss ein c-Programmierer beantworten.
Es ist viel schlimmer: C-Kenntnisse helfen da garnicht. Es hängt allein
vom verwendeten Compiler ab (konkret: von dessen Codegenerator für das
Zielsystem), nicht von der Sprachdefinition. Und gerade der wegen seines
günstigen Preises so beliebte avr-gcc kackt hier gar fürchterlich ab.
Der kann das nämlich schlicht garnicht. Der ist wie die C-Fanbois
allgemein: alles an sich raffen, alles muß C sein, sonst taugt's nix.
Aber auch die kommerzielle Konkurrenz mit etwas pragmatischerer
Grundmotivation kann es nur eingeschränkt. D.h.: es können dort immerhin
bestimmte Register vor dem Codegenerator sozusagen "versteckt" werden.
Im konkreten Fall mit R2 und R16 als Delinquenten sollte es z.B. mit
Keil durchaus gehen.
Damit das insgesamt funktioniert, darf natürlich nix binär hinzugelinkt
werden, sondern der gesamte Code muß als Quelltext vorliegen und mit den
entsprechenden Optionen übersetzt werden.
Fazit: Das einzige, was wirklich immer problemlos das Optimum erlaubt,
ist Assembler. Pur, ohne jeden C-Rotz drumrum. Nur dann hat man die
volle Kontrolle.
Hallo,
ich habe derweile noch weiter probiert. Irgendwie sollte das schon
funktionieren. Siehe den beiden Bsp. wo ich aber leider nur die Hälfte
verstehe.
http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.htmlhttp://www.avrfreaks.net/forum/how-write-assembly-interrupts-within-c-program-solved
In meinem C Programm dachte ich, ich habe oben die 3 Register vorher
definiert, damit der Assembler ISR weis womit er arbeiten soll.
Vielleicht muß das auch so aussehen, habe ich noch nicht probiert.
register uint8_t isr_SREG asm("r2");
register uint8_t isr_tmp asm("r16");
register uint8_t tmp0 asm("r17");
# define outPORT PORTC
# define incrmnt 0x20
Nur warum sieht jetzt eurer Assembler Code anders aus als der originale?
Mit einbinden durch asm ( ... ); geht wohl doch nicht so einfach wie
überall beschrieben?
> Nur warum sieht jetzt eurer Assembler Code anders aus als der originale?
Mein Fehler: ich wollte schnell helfen und dabei sicher sein, dass es
läuft. Ich kann kein C und schon gar keine C-Tricks.
Veit D. schrieb:> Hallo,>> ich habe derweile noch weiter probiert. Irgendwie sollte das schon> funktionieren. Siehe den beiden Bsp. wo ich aber leider nur die Hälfte> verstehe.> http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html> http://www.avrfreaks.net/forum/how-write-assembly-interrupts-within-c-program-solved>> In meinem C Programm dachte ich, ich habe oben die 3 Register vorher> definiert, damit der Assembler ISR weis womit er arbeiten soll.
Das Problem ist, dass du dem Compiler nicht einfach so 3 Register klauen
kannst!
Der restliche Code arbeitet doch mit diesen Registern. Woher soll denn
der Compiler, wenn er ein anderes C-File compiliert, wissen, dass du
diese 3 Register gerne zu deiner ausschliesslichen Verwendung haben
willst?
Du agierst momentan wie jemand, der sich einfach ein Werkzeug aus dem
Lager holt. Klar, in einer 1 Mann Bude kannst du das gerne machen. Aber
in einer etwas größeren Firma geht das nicht mehr. Da gibt es einen
Verantwortlichen, und dem muss man Bescheid geben. Da ist kooperatives
Verhalten angesagt, oder man fliegt.
Hallo,
es kommt immer noch "constant value required"
Oder schreibt man im Definitionsabschnitt mit anderer Syntax für
Assembler?
register uint8_t isr_SREG asm("r2"); zum Bsp.? Wobei das so auch
Fehlermeldungen hakelt.
Hallo,
so wie ich das verstehe klauen die anderen doch auch in ihren Bsp.
Register und es soll bei denen funktionieren, nehme ich an. Wie sagt man
das dem Compiler richtig?
Hallo,
aha, okay, habs nochmal umgebaut. Leider bekomme ich weiterhin "constant
value required" angezeigt und diesmal zeigen die Zeilennummern in die
set_Timer1 Funktion. Warum auch immer. Die ist schon lange fehlerfrei.
Veit D. schrieb:> Leider bekomme ich weiterhin "constant> value required"
Das bezieht sich auf SREG und PORTC. Das kannst du so innerhalb von
Inline-Assembler nicht verwenden, weil der Preprocessor keine
Ersetzungen innerhalb von String-Literalen durchführt.
Compiler drüber jagen, LSS File studieren
(Allerdings ist das mit der heissen Nadel gestrickt. Mit den Clobber
Listen kenn ich mich selbst nicht wirklich aus)
Hallo,
man muß auch mal dünnes Eis betreten dürfen, mit Sicherheitsleine. :-)
Also, Euer beider Code wird fehlerfrei kompiliert. Das macht Hoffnung.
Auch wenn ich da nun Code lese technisch raus bin.
Bei dem von Karl-Heinz takten sie jedoch nicht jeweils mit der halben
Frequenz der vorherigen. 2 takten gleich.
Bei dem von S.Landholt takten die 3 wie sie sollen. Aber leider ist das
Gesamtpaket nicht schneller, eher minimal langsamer als reiner C Code.
Was aber auffällt ist, dass es bei zu kurzem Compare Wert NICHT zu
Taktaussetzer kommt wie unter reinem C. Wie wenn der ISR nicht
hinterkommt oder so ähnlich. Der max. Takt hängt hier im Gemischtpaket
bei 195kHz fest. Mit reinem C sind noch 220kHz möglich. In reinem
Assembler ca. 500kHz.
Kann man vielleicht noch etwas optimieren wenn jetzt der richtige Weg
eingeschlagen wurde? Bin fasziniert von euren Code Künsten.
Was bedeutet KHBs oder NAKED?
Wie komme ich an das LSS File?
Habe Atmel Studio.
Veit D. schrieb:> Bei dem von S.Landholt takten die 3 wie sie sollen. Aber leider ist das> Gesamtpaket nicht schneller, eher minimal langsamer als reiner C Code.
Logisch. Weil seine ISR nicht NAKED ist.
Sieht man sich das Listing File an, dann sieht man dass der Compiler
beim Einstieg in die ISR ein paar Register sichert (zb das SREG) und
dann sein Code nochmal.
> Was bedeutet KHBs oder NAKED?
Hab ich oben den Link vergessen? Mag sein.
NAKED bedeutet, dass sich der Compiler aus der ISR raushält. Er sichert
nichts. Der Programmierer ist komplett alleine eigenverantwortlich.
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html> Wie komme ich an das LSS File?
Das ist in einem deiner Projektverzeichnisse. Aus dem Kopf raus: ich
denke irgendwo im Debug Subverzeichnis
Hallo,
die Beteiligung wird niedrig sein, weil das "Unterthema" im falschen
Thread steckt. Nehme ich an. Soll/darf ich einen neuen Thread eröffnen?
Wenn das Sinn macht.
Dein Code geht jetzt bis 235kHz. Schon einmal schneller als reines C.
Ist aber komischerweise immer noch Meilenweit von reinem Assembler
entfernt. Ich ging davon aus, dass man mit eingebauten Assembler Code
genau die gleiche Geschwindigkeit erreicht für den Code Teil wie mit
reinem Assembler. Da lag ich bestimmt falsch.
Jedenfalls vielen Dank an Euch für Eure Freizeit Opferung für mich und
das Code schreiben. Hätte ich nie hinbekommen.
Seltsam, da muss jetzt doch ein C-Spezialist ran.
Ich hatte gedacht, Aufruf und Routine haben 16 Takte, dazwischen noch
ein rjmp mit 2 Takten, könnte aber auch ein jmp mit 3 sein, macht
maximal 19. Bei 16 MHz käme ich dann auf 421 kHz.
Schauen Sie doch mal in diesem LSS-File nach, was der Compiler
produziert.
In reinem Assembler könnte man 2 Arbeitsregister reservieren, das sparte
2 out/in mit insgesamt 4 Takten, das rjmp ist immer möglich, also
nochmal 1 weniger, macht 14 entsprechend 571 kHz. Dies hatten wir schon
einmal viel weiter oben. Wenn die Interrupts OC1B und OC1C nicht
benötigt werden, ließe sich noch das rjmp sparen, das wären dann 12
Takte entsprechend 666 kHz (immer mit 16 MHz gerechnet).
> Freizeit Opferung
Gar so uneigennützig ist es nicht, man lernt auch beim Antworten etwas
(z.B. wenn ich jetzt auf einen Fehler hingewiesen würde).
Korrektur: der Einsprung in die ISR benötigt einen Takt mehr, und bei
den großen Controllern auch der Rücksprung. So erreicht man mit einem
ATmega16 615 kHz, mit einem ATmega1284 nur 571, bei reinem Assembler.
Das erklärt aber nicht die große Differenz zu Ihren 235 kHz bei C mit
Assembler.
S. Landolt schrieb:> Korrektur: der Einsprung in die ISR benötigt einen Takt mehr, und bei> den großen Controllern auch der Rücksprung.
Nein. Wenn man die ganz kleinen Tinys mit ihrer irregulärer Architektur
mal wegläßt, also beim Tiny13 aufwärts mit der Analyse beginnt, gibt es
folgende Fälle:
22Bit-PC (ja/nein)
Stack im externen RAM (ja/nein)
ISR-Zieladresse (InPlace/per rjmp erreichbar/per jmp erreichbar)
Insgesamt also zwölf denkbare Fälle.
Da "Stack im externen RAM" zwar technisch möglich, aber eine krasse
designerische Fehlentscheidung ist, kann man das wohl schonmal komplett
ignorieren.
Bleiben sechs Fälle.
ISR-Zieladresse "InPlace" ist nur bei recht speziellen Anwendungen
möglich (ist dann allerdings oft eine durchaus attraktive Optimierung).
Für den Normalfall kann man es aber vorläufig in den Skat drücken.
ISR-Zieladresse nur per far jmp erreichbar ist wiederum eine krasse
designerische Fehlentscheidung, muß man also nicht haben. Selbst mit der
Krücke C hat man ja die Macht, die ISRs auf eine "naheliegendere"
Adresse bezüglich der Vektortabelle zu drücken.
Bleiben also effektiv zwei Fälle:
22Bit-PC (ja/nein)
Nur darauf hat man echt absolut keinen Einfluß. Kostet halt entweder 8
oder 12 Takte für den ISR-Frame, weil entweder 2 oder 3 Byte für das
reti auf den Stack gelegt und von dort wieder zurück geholt werden
müssen.
Zum Glück gibt es eigentlich nur ein AVR8-Device (Mega2560), bei dem man
mit dieser Scheiße überhaupt konfrontiert wird. Und man muß den ja nicht
benutzen. Zumal das Teil eigentlich außer dem großen Flash rein garnix
wirklich attraktives zu bieten hat...
Hallo,
den Mega2560 habe ich wegen dem vielen SRAM und großen Flash genommen,
dass ich mir für den Anfang nicht so einen großen Kopf machen muß ums
Speicher sparen usw. hat 3 hardware Serial dabei, 3 Timer usw. Ideal rum
rumspielen. Die kleineren µC wird man dann nehmen wenn man genau weis
was man damit macht. Ich bin noch in der Findungsphase ...
Ich habe jedenfalls von Euch viel gelernt, auch wenn ich noch nicht
alles verstanden habe. Dann werde ich in paar Tagen das Thema C und
Assembler nochmal in einen neuen Thread eröffnen. Dürfte ja nicht unter
Crossposting fallen. Hoffe ich.
Danke @ all.
Veit D. schrieb:> den Mega2560 habe ich wegen dem vielen SRAM und großen Flash genommen,> dass ich mir für den Anfang nicht so einen großen Kopf machen muß ums> Speicher sparen usw. hat 3 hardware Serial dabei, 3 Timer usw. Ideal rum> rumspielen.
Mega1284P wäre die weitaus weisere Wahl gewesen. Doppelt soviel SRAM,
mehr Timer, viele Fähigkeiten der erneuerten AVR8-Architektur (z.B.: 2
SPI-Master-fähige UARTs, toggelnde PINx-Schreibzugriffe, jeder Pin
PCINT-fähig), obendrein billiger und auch in DIL für Raster-Layouts für
Versuchszwecke verfügbar.
Und auch die 128k Flash des Mega1284P mußt du erstmal mit Code voll
bekommen. Konstante Daten im Flash zählen nämlich i.d.R. nicht wirklich
mit, die kann man (wenn's wirklich mal eng werden sollte im Flash-Space)
größtenteil sehr leicht auf seriellen Zusatzflash auslagern. Der ist
spottbillig, leicht verfügbar und leicht anzubinden.
>Doppelt soviel SRAM,>mehr Timer, viele Fähigkeiten der erneuerten AVR8-Architektur (z.B.: 2>SPI-Master-fähige UARTs, toggelnde PINx-Schreibzugriffe, jeder Pin>PCINT-fähig), obendrein billiger und auch in DIL für Raster-Layouts für>Versuchszwecke verfügbar.
Eigentlich hat der ATMega2560 insgesamt 6 Timer. Die vier USARTs können
theoretisch auch SPI. Allerdings haben dir Arduino-Jungs durch fehlende
XCK-Anschlüsse die USARTs so kastriert, das das nicht nutzbar ist. PCINT
wird überschätzt.
MfG Spess
Hallo,
oh ja, der 1284P hat 16kB SRAM. Upps. Man muß eben wie immer im Leben
abwägen was man braucht. Viele Timer oder viel SRAM oder viele I/O Pins
oder viele UARTs oder ... dafür gibts den 1284P im DIP Gehäuse. Hat
seinen Reiz.
Beim nächstenmal. :-)
Nur warum soll man die UARTs Pins als SPI verwenden, wenn SPI doch schon
vorhanden ist. Man benötigt doch nur einen SPI Bus.
Hi
>Nur warum soll man die UARTs Pins als SPI verwenden, wenn SPI doch schon>vorhanden ist.
Weil USART im SPI-Mode universeller ist:
- Gepufferter Transmitter und Receiver
- Flexiblere Baudrateneistellung durch UBRR
- Die Problematik mit dem SS-PIN entfällt
>Man benötigt doch nur einen SPI Bus.
Es gibt aber 4 verschiedene SPI-Modes. Und es ist wesentlich entspannter
dafür unterschiedliche Hardware zu nutzen.
MfG Spess
Veit D. schrieb:> Nur warum soll man die UARTs Pins als SPI verwenden, wenn SPI doch schon> vorhanden ist. Man benötigt doch nur einen SPI Bus.
Im Gegensatz zur dedizierten SPI-Hardware sind die USARTs double
buffered. Das ermöglicht es, problemloser höhere Geschwindigkeiten zu
erreichen und vor allem auch stabil zu halten. Außerdem ermöglicht es
die Erzeugung einer wirklich kontinuierlichen Waveform, was zwar für die
SPI-Anwendung recht unwichtig ist, aber für viele andere Zwecke sehr
nützlich.