Hallo liebe Community
Ich stehe vor folgendem problem:
Ich muss in meiner Anwendung einen Rechteck Takt erzeugen. DutyCycle
vorzugsweise 1/1
Die Geschwindigkeit sollte 500kHz + sein... (Je mehr desto besser)
Ich habe mir dazu mal folgenden Chip genauer angeschaut:
DS1085http://www.maxim-ic.com/datasheet/index.mvp/id/3491
Dieser erzeugt selbstständig Signale im bereich von
8.1kHz - 133MHz bei einer genauigkeit von 0.75%
Soweit so gut....
Das problem ist, ich sollte den Takt in meinem uC programm definiert
Starten können. Also genau wissen das der Takt jetzt mit der Steigenden
Flanke beginnt etc.
deshalb dachte ich daran, Das Taktsignal mit dem AVR zu erzeugen. Dieser
muss während der Signal Erzeugung nichts weiteres tun ausser die
erzeugung und nach einer definierten zeit (zähler stand) wieder stoppen.
Ist es sinnvoll solch ein Signal mit dem AVR mit ein bisschen Assembler
zu erzeugen?
Mit welchen Maximalen Taktraten kann ich in etwa rechnen bei einem 12Mhz
bzw. einem 20MHz AVR Takt?
Danke schonmal
Claudio Hediger schrieb:> Ist es sinnvoll solch ein Signal mit dem AVR mit ein bisschen Assembler> zu erzeugen?
Und was erhoffst du dir dabei von Assembler?
Deinen Takt kann der Timer des AVR ganz alleine erzeugen. Ein paar
Konfigurationsbits richtig setzen und du hast deinen Takt an einem Pin.
(Das ist eine PWM mit einstellbarem TOP Wert und einem Comparewert genau
in der Mitte).
> Mit welchen Maximalen Taktraten kann ich in etwa rechnen bei einem 12Mhz> bzw. einem 20MHz AVR Takt?
theoretisch müsste man auf die Halfte der Taktfrequenz kommen.
Die eigentliche Frage ist aber eine andere: Wie genau kannst du von der
Maximalfrequnz ausgehend abstufen und ist es erlaubt bei hohen
Frequenzen von 1:1 abzuweichen.
Karl heinz Buchegger schrieb:> theoretisch müsste man auf die Halfte der Taktfrequenz kommen.> Die eigentliche Frage ist aber eine andere: Wie genau kannst du von der> Maximalfrequnz ausgehend abstufen und ist es erlaubt bei hohen> Frequenzen von 1:1 abzuweichen.
Die Maximalfrequenz ist eigentlich irrelevant. Es spielt also keine
rolle ob es 1MHz 2 oder 5MHz sind. Wobei 5MHz ein idealer Wert sein
dürfte.
Ich nehme an, das ich durch Setzen eines Flags den Timer direkt Starten
kann oder?
Claudio Hediger schrieb:> Ich nehme an, das ich durch Setzen eines Flags den Timer direkt Starten> kann oder?
So ist es.
Vorteiler setzen -> der Timer läuft
Vorteiler wegnehmen -> der Timer steht
Karl heinz Buchegger schrieb:> So ist es.> Vorteiler setzen -> der Timer läuft> Vorteiler wegnehmen -> der Timer steht
Ok sehr schön :)
Und damit ich weiss wie viele Takte bereits ausgegeben wurden, wäre es
meiner meinung nach die beste lösung eine Variable in der Interrupt
Funktion des Timers zu erhöhen. Was meinst du?
Claudio Hediger schrieb:> Karl heinz Buchegger schrieb:>> So ist es.>> Vorteiler setzen -> der Timer läuft>> Vorteiler wegnehmen -> der Timer steht>> Ok sehr schön :)>> Und damit ich weiss wie viele Takte bereits ausgegeben wurden, wäre es> meiner meinung nach die beste lösung eine Variable in der Interrupt> Funktion des Timers zu erhöhen. Was meinst du?
Dass du dann bei hohen Taktfrequenzen Schwierigkeiten kriegen wirst,
weil auch der ISR Aufruf und dessen Abarbeitung Zeit braucht.
Karl heinz Buchegger schrieb:> Dass du dann bei hohen Taktfrequenzen Schwierigkeiten kriegen wirst,> weil auch der ISR Aufruf und dessen Abarbeitung Zeit braucht.
Ja, doch anderst ist es ja kaum zu lösen oder?
Karl heinz Buchegger schrieb:> Dass du dann bei hohen Taktfrequenzen Schwierigkeiten kriegen wirst,> weil auch der ISR Aufruf und dessen Abarbeitung Zeit braucht.
Wäre es denn im ISR Aufruf sinnvoll direkt mit Assembler befehlen den
eigenen Zähler zu erhöhen und zu prüfen, ob der gewünschte wert bereits
erreicht ist?
Claudio Hediger schrieb:> Wäre es denn im ISR Aufruf sinnvoll direkt mit Assembler befehlen den> eigenen Zähler zu erhöhen und zu prüfen, ob der gewünschte wert bereits> erreicht ist?
In dem Fall bin ich tatsächlich geneigt 'ja' zu sagen.
Nicht weil der Compiler erhöhen und Abfrage nicht genausogut hinkriegst
wie du selber.
Sondern, weil du höchst wahrscheinlich einen besseren Funktion
Prolog/Epilog hinkriegst als der Compiler.
Karl heinz Buchegger schrieb:> In dem Fall bin ich tatsächlich geneigt 'ja' zu sagen.> Nicht weil der Compiler erhöhen und Abfrage nicht genausogut hinkriegst> wie du selber.> Sondern, weil du höchst wahrscheinlich einen besseren Funktion> Prolog/Epilog hinkriegst als der Compiler.
Ok sehr schön....
Vielen Dank für deine Hilfe...
Ich werde es mal versuchen :)
nur noch eine kleine frage, was ist mit prolog und epilog genau gemeint?
Claudio Hediger schrieb:> Karl heinz Buchegger schrieb:>> Dass du dann bei hohen Taktfrequenzen Schwierigkeiten kriegen wirst,>> weil auch der ISR Aufruf und dessen Abarbeitung Zeit braucht.>> Wäre es denn im ISR Aufruf sinnvoll direkt mit Assembler befehlen den> eigenen Zähler zu erhöhen und zu prüfen, ob der gewünschte wert bereits> erreicht ist?
Das kann ebenfalls die Harware erledigen. Einfach das Signal extern auf
einen Eingang zurückgeben und einen zweiten Timer damit hochzählen
lassen.
Johann L. schrieb:> Das kann ebenfalls die Harware erledigen. Einfach das Signal extern auf> einen Eingang zurückgeben und einen zweiten Timer damit hochzählen> lassen.
Ahh das ist ja perfekt :)
Vielen Dank...
Ich verwende einen Atmega8 soweit ich weiss hat dieser einen 16bit
counter..
Ich muss nämlich auf 32768 Zählen... somit sollte das ja bei diesem
Controller kein problem sein oder?
Claudio Hediger schrieb:> nur noch eine kleine frage, was ist mit prolog und epilog genau gemeint?
Solange du das fragen musst .... lass Assembler Assembler sein und
programmiere alles in C.
Karl heinz Buchegger schrieb:> Solange du das fragen musst .... lass Assembler Assembler sein und> programmiere alles in C.
Das hatte ich nach dem tipp, die hardware zählen zu lassen auch vor :)
Trozdem Danke
Hi
>Den Burst einfach durch Pintoggeln in einer Schleife generieren.
Pintoggeln geht bei neueren AVRs durch das Schreiben einer 1 in das
entsprechende Bit des PIN-Registers.
'Writing a logic one to PINxn toggles the value of PORTxn, independent
on the value of DDRxn.'
MfG Spess
Vuvuzelatus schrieb:> Andere unkomplizierte Möglichkeit: Den Burst einfach durch Pintoggeln in> einer Schleife generieren.> GenerateBurst:> ldi r16, 0> ldi r17, 1> out DDRB, r17>> rjmp LoopH>> LoopL:> out PORTB, r16>> subi ZL, 1> sbci ZH, 0> breq Fertig>> LoopH:> out PORTB, r17>> nop> rjmp LoopL>> Fertig:> ret>> Vor dem Aufruf muss das Z-Registerpaar auf die gewünschte Anzahl der> Pulse gesetzt werden, z. B. erzeugt...> ldi ZL, Low (32768)> ldi ZH, High(32768)> rcall GenerateBurst>> ...einen genau 32768 Pulse langen Burst mit der Taktfrequenz FOSC/4 auf> Pin 0 von Port B.
Wow vielen Dank!
Das ist ja fantastisch :)
Ich nehme an, das ich bevor ich diesen Code ausführe, die register auf
dem Stack sichern mus mittels push oder?
Ich weiss ja nicht wo der Compiler welches register verwendet
Zum ende muss ich sie wieder mit pop herstellen oder?
Besten Dank
Karl heinz Buchegger schrieb:> Das kriegt dir der C-Compiler genauso hin.> Aber ganz so einfach ist es dann auch wieder nicht.> Du willst ja die Frequenz auch einstellen können.
Oh das tut mir leid... anscheinend habe ich da was falsches
geschrieben...
Die frequenz muss nicht unbedingt einstellbar sein.
Trozdem Danke für den Hinweis :)
Claudio Hediger schrieb:> Oh das tut mir leid... anscheinend habe ich da was falsches> geschrieben...> Die frequenz muss nicht unbedingt einstellbar sein.
Okay. Kann auch sein, dass ich im Eröffnungsposting etwas rausgelesen
habe, was nicht dort steht.
Das ändert die Situation allerdings.
D.h. du willst nur eine bestimmte Anzahl an Pulsen möglichst schnell
rausblasen.
Karl heinz Buchegger schrieb:> wird übersetzt zu (wenn man den Compiler am inlining hindert)
Was genau meinst du mit ihn am inlining hindern?
Karl heinz Buchegger schrieb:> D.h. du willst nur eine bestimmte Anzahl an Pulsen möglichst schnell> rausblasen.
Ja und ich muss genau wissen wie lange das die clocks sind also welche
Taktrate sie haben.
Karl heinz Buchegger schrieb:> und das ist doch nicht schlecht. Den 2ten sbiw könnte man noch> einsparen. Aber sonst sehe ich nicht mehr viel Potential.
Wie lange dauert denn in diesem fall der assembler code?
und ist es ein duty cycle cvon 1/1?
Claudio Hediger schrieb:> Was genau meinst du mit ihn am inlining hindern?
Bei dir passt irgendwie gar nichts zusammen.
Auf der einen Seite willst du alles unbedingt in Assembler machen, auf
der anderen Seite schimmert immer wieder durch, dass du davon (sagen
wirs mal freundlich) keine Ahnung hast.
Die Frage gehört auch dazu
> Wie lange dauert denn in diesem fall der assembler code?
genauso wie die hier
> und ist es ein duty cycle cvon 1/1?
die letzte lässt sogar darauf schliessen, dass du von Programmieren
nicht allzuviel Ahnung hast.
Natürlich ist der Duty Cycle 1:1. In jedem Durchlauf wird der Pin
getoggelt (in den anderen Zustand gebracht). 1 Durchlauf dauert immer
gleich lang (bis auf ev. den ersten bzw. den letzten), daher kann der
Duty Cycle nur 1:1 sein.
Karl heinz Buchegger schrieb:> Bei dir passt irgendwie gar nichts zusammen.> Auf der einen Seite willst du alles unbedingt in Assembler machen, auf> der anderen Seite schimmert immer wieder durch, dass du davon (sagen> wirs mal freundlich) keine Ahnung hast.
Hmmm also Ich habe früher mal mit Assembler die AVRs programmiert... das
ist jedoch schon ein weillchen her.
Alles möchte ich bestimmt nicht in assembler machen. Mir geht es hier
wirklich nur um diese kleine zeitkritische stelle.
Zudem ist es meiner meinung nach nicht schlimm etwas nicht zu wissen.
Wenn ich ja der Profi in assembler wäre, würde ich ja wohl kaum ein
Topic in diesem Forum verfassen, oder etwa nicht?
Leider weiss ich immer noch nicht was du damit gemeint hast :)
Karl heinz Buchegger schrieb:>> und ist es ein duty cycle cvon 1/1?>> die letzte lässt sogar darauf schliessen, dass du von Programmieren> nicht allzuviel Ahnung hast.>> Natürlich ist der Duty Cycle 1:1. In jedem Durchlauf wird der Pin> getoggelt (in den anderen Zustand gebracht). 1 Durchlauf dauert immer> gleich lang (bis auf ev. den ersten bzw. den letzten), daher kann der> Duty Cycle nur 1:1 sein.
Ich weiss das beim Pin Toggeling ein Tastverhältnis von 1:1 entsteht,
jedoch war ich irritiert, da beim ASM Code lediglich eine 0 mit sbi
geschrieben wird
1
6c: 02 c0 rjmp .+4 ; 0x72 <Burst+0x6>
2
6e: b0 9a sbi 0x16, 0 ; 22 <------------
3
70: 01 97 sbiw r24, 0x01 ; 1
4
72: 00 97 sbiw r24, 0x00 ; 0
5
74: e1 f7 brne .-8 ; 0x6e <Burst+0x2>
6
76: 08 95 ret
Falls sbi 0x16, 0 den Pin automatisch bei jedem durchgang toggelt, ist
es für mich auch absolut verständlich das es ein Duty Cycle von 1:1
ergibt
Claudio Hediger schrieb:> Hmmm also Ich habe früher mal mit Assembler die AVRs programmiert...> das ist jedoch schon ein weillchen her.
Das muss schon sehr lange her sein. Du scheinst so gut wie alles
vergessen zu haben. Daher wäre es besser, wenn du von der Prämisse
ausgehst: Ich steig wieder bei 0 ein.
> jedoch war ich irritiert, da beim ASM Code lediglich eine 0 mit sbi> geschrieben wird
Schau dir bitte im Instruction Set an, was der Befehl sbi wirklich
macht!
Und was bei neueren AVR ein Bitsetzen am PIN Register bewirkt.
Claudio Hediger schrieb:> Zudem ist es meiner meinung nach nicht schlimm etwas nicht zu wissen.
Da hast du recht.
Das ist nicht schlimm.
Warum es geht: Wenn du in C schreibst, dann schreib auch in C
Die meisten, die das das erste mal versuchen, wissen gar nicht worauf
sie sich einlassen, wenn sie Assembler mit C mischen wollen. Die meisten
denken auch, dass sie den Compiler ja leicht übertrumpfen. So nach dem
Motto: Ich krieg gerade mal eine for-Schleife im 3ten Anlauf fehlerfrei
hin, aber hey: so schwer kann das doch nicht sein, den Compiler
auszustechen.
Wenn du Laufzeit Probleme hast, dann sieh erst mal zu, dass du sie mit
dem Compiler lösen kannst. Viele Codestellen, die als ineffizienter
Assemblercode vom Compiler erzeugt werden, haben ihre Ursache darin,
dass der Programmierer C nicht beherrscht und daher dem Compiler
entweder zuviele Freiheitsgrade lässt oder dem Compiler Nebenbedingungen
aufs Auge drückt die ihn am Optimieren hindern.
Auf Assembler weicht man als C-Programmierer nur dann aus, wenn man
tatsächlich über jeden Takt die Kontrolle braucht und das auch nur dann,
wenn man sich angesehen hat, was der Compiler erzeugt und man einen Weg
hat, wie man das besser machen kann.
Gut: Dein Problem ist von der Sorte - jeder Taktzyklus zählt.
Aber auch dann, schreibt man sich immer erst eine entsprechende
C-Routine, sieht sich an, wie der Compiler das umsetzt und tauscht dann
Funtkionsinhalte durch Assembler Code aus.
Und dann stellt sich die Frage nach: Wie kriege ich da Argumente rein,
wie mache ich Dinge variabel so gar nicht mehr. Der Compiler hat das
Framework erzeugt und nur noch die innersten Konstrukte tausch ich aus.
Als C-Programmiere fängt man so gut wie nie damit an, gleich erstmal
einen Assemblerteil zu postulieren nur weil man das Gefühl hat, das man
den brauchen wird.
Wenn dein Auto (deiner Meinung nach) zuviel Sprit braucht, dann kann
dein erster Gedanke nicht sein, wie du die Verbrennung in den
Brennkammern optimieren kannst. Sondern du schaust dir zb erst einmal
den Reifendruck an, nimmst die Dachträger vom Dach etc.
Vielen Dank für dein langes Posting :)
Ich stimme dem in allen punkten zu....
Ich werde mich nun also mal an die sache ranmachen und erstmal mit C
weiterfahren....
Danke nochmals :)
Claudio hediger schrieb:> Leider kommt damit kein Clock raus :(
Das war auch kein komplettes vollständiges Beispiel, sondern es ging nur
um die Fragestellung: Was macht der Compiler aus der Schleife in der
Funktion Burst.
Um das im Assembler Listing zu sehen, muss ich zb keinen Port auf
Ausgang schalten. Ich kann auch davon ausgehen, dass der µC tatsächlich
einer von der Sorte ist, der mittels Bitsetzen im Pin Register den
Ausgabeport toggelt (das hängt aber vom tatsächlichen Typ des AVR-µC ab.
Ältere können das nicht)
Die älteren Atmel beherrschen das Toggeln bei Schreiben auf den
Eingangspin nicht, der ATM8 könnte dazugehören. Außerdem muss das DDRx
Register auf Ausgang gesetzt werden, sonst geht auch nix.
Karl heinz Buchegger schrieb:> (das hängt aber vom tatsächlichen Typ des AVR-µC ab.> Ältere können das nicht)
Ich verwende einen Atmega8 wie bereits weit(er) oben geschrieben.
Diese kann es leider nicht :(
Somit würde mir nur noch die Assembler variante bleibe oder sehe ich da
was nicht?
Claudio hediger schrieb:> Somit würde mir nur noch die Assembler variante bleibe oder sehe ich da> was nicht?
Und wie würdest du das dann in Assembler schreiben?
(Die C Variante sieht genau gleich aus wie die Assembler Variante. Wenn
du es in C nicht kannst, kriegst du es auch in Assembler nicht hin. Ich
kann ehrlich dieses ewige sofortige 'dann muss ichs in Assembler machen'
schon nicht mehr hören. Für dich scheint Assembler die Lösung aller
Probleme zu bedeuten)
Schreibs komplett in Assembler, dann stellt sich die Frage schon nicht
mehr.
Karl heinz Buchegger schrieb:> Die C Variante sieht genau gleich aus wie die Assembler Variante
Ok jetzt hab ichs endlich kapiert :P sorry für die soooo lange leitung
ich habs nun also so programmiert:
1
voidfast_clock(void)
2
{
3
unsignedcharucR16=0x02;
4
unsignedcharucR17=0x00;
5
6
while(1)
7
{
8
PORTC&=ucR17;
9
PORTC|=ucR16;
10
}
11
}
Einfach mal als kleiner test
Ich komme jedoch auf 1Mhz getaktet ist der AVR mit 12Mhz
Müsste das nicht flotter sein?
>> Einfach mal als kleiner test> Ich komme jedoch auf 1Mhz getaktet ist der AVR mit 12Mhz> Müsste das nicht flotter sein?
Du möchtest hier keine Variablen benutzen.
Denn dann bleibt dem Compiler nichts anderes übrig, als tatsächlich die
Operationen so auszuführen, wie du sie hingeschrieben hast
Wert vom Port holen
Variablenwert holen
miteinander verunden
Wert an den Port schreiben
1
#define CLOCK_PIN PC1
2
#define CLOCK_PORT PORTC
3
4
voidfast_clock(void)
5
{
6
while(1)
7
{
8
CLOCK_PORT&=~(1<<CLOCK_PIN);
9
CLOCK_PORT|=(1<<CLOCK_PIN);
10
}
11
}
1 << CLOCK_PIN ist eine Konstante (0x02). Und erst jetzt ermöglichst du
dem Compiler zu optimieren was nur geht (sofern der Optimizer
eingeschaltet ist).
Der Umweg über die #define hat nur den Zweck, die komplette
Hardwarekonfiguration an einer Stelle beisammen zu haben. Ändert sich
der Pin, brauchst du das nur beim CLOCK_PIN an einer Stelle ändern und
neu kompilieren.
1
voidfast_clock(void)
2
{
3
while(1)
4
{
5
PORTC&=~(1<<PC1);
6
PORTC|=(1<<PC1);
7
}
8
}
würde genau dasselbe machen, aber die Hardwareinfo ist über mehrere
Stellen verstreut.
AVR-GCC-Tutorial
Karl heinz Buchegger schrieb:> void fast_clock(void)> {> while(1)> {> PORTC &= ~( 1 << PC1 );> PORTC |= ( 1 << PC1 );> }> }
Ok vielen dank...
Also ich hab das mal soweit getestet....
Ich komme damit auf etwa 1.2Mhz
Merkwürdigerweise ist das signal alles andere als stabil!
Es schwankt zwischen 1Mhz und 1.5Mhz hin und her!
Claudio hediger schrieb:> Karl heinz Buchegger schrieb:>> void fast_clock(void)>> {>> while(1)>> {>> PORTC &= ~( 1 << PC1 );>> PORTC |= ( 1 << PC1 );>> }>> }>> Ok vielen dank...>> Also ich hab das mal soweit getestet....> Ich komme damit auf etwa 1.2Mhz
Assembler Code ansehen.
Das müsste in eine Schleife
cbi ...
sbi ...
rjmp ...
übersetzt werden. Das sind in Summe 6 Takte, d.h du bist bei 12/6 = 2Mhz
Durch Loop Unrolling ginge da noch was, aber wesentlich schneller wirds
nicht mehr. Und du willst ja auch noch Pulse zählen.
Claudio hediger schrieb:> Merkwürdigerweise ist das signal alles andere als stabil!> Es schwankt zwischen 1Mhz und 1.5Mhz hin und her!
Da zweifle ich mal die Verwendung des 12Mhz Quarzes an.
Interne 8Mhz würden bedeuten:
8 / 6 = 1.33Mhz
und das passt ziemlich gut zu deinen 1.2Mhz. Und auch die Schwankungen
würden sich dadurch erklären.
-> Fuse-Bits ansehen, ob der Mega tatsächlich auf Quarz gefused ist.
Claudio hediger schrieb:> Hmmm also wenn ich den Quarz entferne läuft nix mehr
OK. Das genügt mir um den Quarz zu akzeptieren. Das ist ein deutliches
Indiz.
Aber ein Quarz schwingt nicht so unregelmässig (ausser er ist defekt)
Irgendwelche Interrups aktiv?
Karl heinz Buchegger schrieb:> Claudio hediger schrieb:>> Hmmm also wenn ich den Quarz entferne läuft nix mehr>> OK. Das genügt mir um den Quarz zu akzeptieren. Das ist ein deutliches> Indiz.> Aber ein Quarz schwingt nicht so unregelmässig (ausser er ist defekt)>> Irgendwelche Interrups aktiv?
Es sind keine Interrupts aktiv nach dem main geht direkt in die
Endlosschleofe mit der Takterzeugung.
Ich habe den quarz kurzerhand ausgetauscht... Leider das selbe ergebnis
Claudio hediger schrieb:> Ich habe den quarz kurzerhand ausgetauscht... Leider das selbe ergebnis
Hmm.
Gibts doch gar nicht.
Das Messverfahren ist über jeden Zweifel erhaben?
Karl heinz Buchegger schrieb:> Hmm.> Gibts doch gar nicht.>> Das Messverfahren ist über jeden Zweifel erhaben?
hmmm merkwürdig es geht nun plötzlich :S
nun ja... vielleicht hab ich es nicht richtig kompiliert
das duty cycle ist 8:2 (2 ist low)
Kann man das noch etwas optimieren?
Claudio hediger schrieb:> das duty cycle ist 8:2 (2 ist low)>> Kann man das noch etwas optimieren?
Jetzt ist der Zeitpunk, an dem man auf Assembler muss
Aus
1
4c: a9 98 cbi 0x15, 1 ; 21
2
4e: a9 9a sbi 0x15, 1 ; 21
3
50: 01 97 sbiw r24, 0x01 ; 1
4
52: 00 97 sbiw r24, 0x00 ; 0
5
54: d9 f7 brne .-10 ; 0x4c <fast_clock+0x2>
6
56: 08 95 ret
muss
1
cbi 0x15, 1 ; 21
2
sbiw r24, 0x01 ; 1
3
sbi 0x15, 1 ; 21
4
brne .-8
5
ret
werden. Dann sollte sich ein 1:1 Verhältnis einstellen.
Das kriegen wir nicht mehr in C gebacken, weil wir ihm nicht
vorschreiben können, dass er Teile des Schleifenkonstrukts mitten
zwischen die Setz und Lösch Anweisungen reinziehen soll.
Ich bin in inline_Assembler aber zu schwach. Mit diesen Clobber Listen
hab ich so meine liebe Mühe :-)
Aber ich probiers mal, wenn ich zu Hause bin.
Karl Heinz, will Dir jetzt den Spaß nicht verderben ;-)
> Jetzt ist der Zeitpunk, an dem man auf Assembler muss
Nein, muss man nicht.
Man muss nur geschickt umstellen.
Das hier braucht 8 Zyklen, entsprechend 1,5MHz bei genau 1:1
Tastverhältnis:
1
unsigned int Ctr = 65535;
2
while(Ctr)
3
{
4
PORTC |= ( 1 << PC1 );
5
Ctr--;
6
PORTC &= ~( 1 << PC1 );
7
}
Und der Compiler baut auch den Murks nicht rein, daß er ein SBIW 0
verwendet, zumindest hier bei mir nicht. :-)
MWS schrieb:> Karl Heinz, will Dir jetzt den Spaß nicht verderben ;-)
Ganz im Gegenteil.
Wenn Assembler vermeidbar ist, dann umso besser
>
1
unsigned int Ctr = 65535;
2
> while(Ctr)
3
> {
4
> PORTC |= ( 1 << PC1 );
5
> Ctr--;
6
> PORTC &= ~( 1 << PC1 );
7
> }
Kopfklatsch.
Manchmal ist alles so naheliegend und man siehts trotzdem nicht.
Ich war so sehr auf
while( Ctr-- )
fixiert, dass ich in die Richtung gar nicht nachgedacht habe.
Danke.
Man kann sogar einfach meine Assemblerroutine von oben so in C abbilden,
dass der Compiler wieder den gleichen Maschinencode daraus macht. Der
"Trick": Man lässt das 'breq' und das 'rjmp' zu 'goto's werden - der
Compiler übersetzt sie dann brav wieder in 'breq' und 'rjmp' zurück. Im
Ergebnis bekommt man damit wieder die mit dieser Methode maximal
erreichbare Pulsfrequenz FOSC/4.
Vuvuzelatus schrieb:> Im> Ergebnis bekommt man damit wieder die mit dieser Methode maximal> erreichbare Pulsfrequenz FOSC/4.
Fantastisch :) Vielen Dank!
Aber klappt das auch wenn man nur einen Pin ändern möchte und nicht den
gesamten Port?
also mit
>Aber klappt das auch wenn man nur einen Pin ändern möchte und nicht den>gesamten Port?>>also mit>>PORTC &= ~( 1 << PC1 );
Klar, nur die Pulsfrequenz ist dann etwas geringer, nämlich FOSC/10
statt FOSC/8, weil dieses Konstrukt in sbi/cbi übersetzt wird, die je
zwei Takte benötigen. PORTB = 0 und PORTB = 1 wird über out erledigt,
das nur einen Takt verbraucht.
>Eher FOSC/8.
Jooo... danke, Spess :-)
Meine "FOSC/4" ist falsch, ich korrigiere: Von jeder Flanke bis zur
nächsten sind es 4 Takte, aber da ein Puls aus zwei Flanken besteht
(L-->H und H-->L), ist die resultierende Frequenz FOSC/8.
Noch eine verbesserte Version. Wieder FOSC/8, aber ohne Nebenwirkung auf
den Pins PB1 bis PB7. Alle Pins außer PB0 bleiben auf den Zuständen, die
sie beim Aufruf der Routine haben. Falls anderer Ausgabepin als PB0
gewünscht, einfach im Code ändern.
Hi
Mal ehrlich. Mit Assembler wäre man schon zehn mal fertig. Statt dessen
wird hier tagelang mit C Problemen herum gedoktert, die man ohne C nicht
hätte.
MfG Spess
spess53 schrieb:> Hi>> Mal ehrlich. Mit Assembler wäre man schon zehn mal fertig. Statt dessen> wird hier tagelang mit C Problemen herum gedoktert, die man ohne C nicht> hätte.>
Übertreib nicht.
Ein C-Kundiger hat die Schleife genauso schnell, wie du das alles in
Assembler (ok. bei der Schleife hab ich etwas geschlafen um sie auf 1:1
zu bringen)
Und vom ganzen Rest rundherum reden wir mal nicht :-)
Wenn sich Vuvuzelatus spielen will, soll er das tun.
Für den Rest ist
1
voidBurst(unsignedintCtr)
2
{
3
while(Ctr)
4
{
5
PORTC|=(1<<PC1);
6
Ctr--;
7
PORTC&=~(1<<PC1);
8
}
9
}
perfekt und spielt alle Stückchen die man braucht.