Hallo!
Ich bin kompletter Neuling auf dem Gebiet Microcontroller und habe
dementsprechend die eine oder andere Frage. Fangen wir einfach mal mit
der ersten an. ;)
Hintergrund: Ich würde gerne einen 8 Kanal AD-Wandler auslesen
(MAX1270).
Das Board mit dem ich arbeite hat einen ATMega128 und läuft mit einem
3,6864 MHz Baudratenquarz. Der Takteingang des AD-Wandlers ist mit PA0
verbunden.
Nun experimentiere ich gerade mit verschiedenen Möglichkeiten herum
diesen Takteingang anzusprechen, u.a. auch indem ich mithilfe eines
Timers den Port toggel, was mich zu meiner eigentlichen Frage führt.
Um mal die maximale Porttoggelgeschwindigkeit zu ermitteln hab ich mir
gedacht ich schreibe einfach:
int main(void)
{
init_devices();
while(1)
{
PORTC ^= ( 1 << PINC3 );
}
return 0;
}
PINC3 ist einfach mit dem Oszilloskop zu erreichen, deshalb wird der
verwendet und nicht wie oben PA0.
Hier wird janun nichts anderes abgearbeitet als das Toggeln, aber egal
wie schnell ich meinen Timer konfiguriere, ich komme nicht über eine
Frequenz von ca. 370 kHz. Ich denke mal, dass ich das prinzipiell schon
richtig mache, denn wenn ich den Timer z.B. auf 1000 Hz einstelle, dann
erreiche ich auch die zu erwartenden 500 Hz. Nur scheint irgendwie nach
oben hin Schluß zu sein.
Kann es sein, dass die ganze Abarbeitung in etwa 20 Takte dauert und ich
deshalb in etwa 1/10 meiner Quarzfrequenz erhalte?
Gruss, Rüdiger
Das Toggeln, was Du da machst, hat überhaupt nichts mit dem Timer zu
tun! Die Geschwindigkeit ist durch die Ausführungszeit der Schleife
bestimmt.
Wenn Du mit dem Timer einen Portpin toggeln willst, dann musst Du den
Timer entsprechend konfigurieren (sinnvolerweise CTC-Betrieb) und einen
Pin, den der Timer auch hardwaremäßig ansteuern kann, benutzen. Da muss
man aber nicht viel ausprobieren: Die maximale Toggelfrequenz per
Hardware ist F_CPU / 2.
Rüdiger B. wrote:
> Kann es sein, dass die ganze Abarbeitung in etwa 10 Takte dauert und ich> deshalb in etwa 1/10 meiner Quarzfrequenz erhalte?
Wenn du ohne Optimierung arbeitest, kann das gut sein.
Selbst mit Optimierung ist es nicht sonderlich schnell, da du ja
den Compiler anweist, dass er jeweils den Wert des Ports erst wieder
einlesen muss -- obwohl das für deine Funktion (Emulieren des SPI-
Taktes) gar nicht notwendig wäre. Eine entrollte Schleife wäre
nach der Hardware-SPI (weiß der Geier, warum du die nicht gleich
genommen hast) die schnellste Lösung:
1
#include<avr/io.h>
2
3
voidsend_spi_clk(void)
4
{
5
uint8_tv,nv;
6
v=PORTC|(1<<3);
7
nv=v&~(1<<3);
8
PORTC=v;PORTC=nv;
9
PORTC=v;PORTC=nv;
10
PORTC=v;PORTC=nv;
11
PORTC=v;PORTC=nv;
12
PORTC=v;PORTC=nv;
13
PORTC=v;PORTC=nv;
14
PORTC=v;PORTC=nv;
15
PORTC=v;PORTC=nv;
16
}
Mit Optimierung macht der Compiler daraus:
1
.global send_spi_clk
2
.type send_spi_clk, @function
3
send_spi_clk:
4
/* prologue: frame size=0 */
5
/* prologue end (size=0) */
6
in r24,53-0x20
7
ori r24,lo8(8)
8
mov r25,r24
9
andi r25,lo8(-9)
10
out 53-0x20,r24
11
out 53-0x20,r25
12
out 53-0x20,r24
13
out 53-0x20,r25
14
out 53-0x20,r24
15
out 53-0x20,r25
16
out 53-0x20,r24
17
out 53-0x20,r25
18
out 53-0x20,r24
19
out 53-0x20,r25
20
out 53-0x20,r24
21
out 53-0x20,r25
22
out 53-0x20,r24
23
out 53-0x20,r25
24
out 53-0x20,r24
25
out 53-0x20,r25
26
/* epilogue: frame size=0 */
27
ret
d. h. nach dem Setup der Werte wird mit f[CPU]/2 getaktet.
Johannes M. wrote:
> @A. K.:> Warum sollte die "&="/"|="-Kombination schneller sein als ein EXOR?
Weil es dafür die cbi/sbi Befehle gibt. Für XOR gibt es keine
Entsprechung, daher wird load/alu/store draus. Schonmal mindestens 1
Takt mehr, evtl. 2.
Angenehmer wird es mit der neueren Generation, hier also Mega1284 oder
so, denn da geht dann
PINC = (1<<PINC3);
als Hardware-Toggle.
A. K. wrote:
> Johannes M. wrote:>> @A. K.:>> Warum sollte die "&="/"|="-Kombination schneller sein als ein EXOR?>> Weil es dafür die cbi/sbi Befehle gibt.
Stimmt natürlich. Wenn der Compiler die dann auch benutzt....
Oha, so schnell so viele Antworten, danke schonmal dafür. :)
Als erstes: da hab ich mich etwas unglücklich ausgedrückt natürlich. Ich
habe mit dem Timer (im CTC Betrieb) experimentiert und da dann beim
Compare Match getoggelt. Dabei fiel mir aus, dass das "langsamer"
abläuft als ich eigentlich erwartet habe, und habe dann angefangen
testweise im main zu toggeln. Die Geschichte mit dem Timer wird
natürlich überhaupt nicht klar aus meinem obrigen Beitrag, sorry wenns
Misverständnisse gab.
@A.K.
Deine erste Variante hab ich gerade selbst herausgefunden (stolzbin)
;), die zweite werd ich mir auf jeden Fall mal merken.
@Jörg
Opimierung ist auf s gestellt, möglicherweise könnte man da vielleicht
noch etwas rausholen. Aber ist ja eh nur zum generellen Verständnis
erstmal.
Das mit der entrollten Schleife (lese ich so zum ersten mal) bringt mich
eigentlich erst hierher. Ich habe ein Funktion in der der ADC
angesprochen und ausgelesen werden soll. Das hatte ich in meinem ersten
Eifer dann einfach mal so "runterprogrammiert", nach dem Motto:
Ports setzen, Takt simulieren mittels toggeln, Ports neu setzen, nochmal
toggeln usw.
Das funktionierte dann nicht und ich fing an mir Gedanken zu machen, ob
das eventuelle am Takt liegen könnte und ich in der Form vielleicht
einfach zu schnell war, oder der Takt zu unsymmetrisch ist. Dann hab ich
erst mit delay experimentiert, und letztendlich dann mit Timern, wo mir
dann auffiel, dass ich wohl ganz und gar nicht zu schnell bin (der
MAX1270 verträgt 0,1 ... 2 MHz als externen Takt) und der Fehler
woanders liegen muss.
Aber dann denke ich mal, dass das Prinzip mit der entrollten Schleife
wohl funktionieren sollte. Da teste ich nochmal etwas rum und melde mich
dann ggf. morgen in einem anderen Thread. ;)
Vielen dank nochmal, Rüdiger