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
Dein Codeausschnitt berücksichtig in keiner Weise den Timer --> Ergo ist die Toggelgeschwindigkeit immer gleich.
Erstens solltest mit Optimierung übersetzen, wenn's dir drauf ankommt. Zweitens geht es auch schneller:
1 | while(1) |
2 | {
|
3 | PORTC |= ( 1 << PINC3 ); |
4 | PORTC &=~( 1 << PINC3 ); |
5 | PORTC |= ( 1 << PINC3 ); |
6 | PORTC &=~( 1 << PINC3 ); |
7 | PORTC |= ( 1 << PINC3 ); |
8 | PORTC &=~( 1 << PINC3 ); |
9 | PORTC |= ( 1 << PINC3 ); |
10 | PORTC &=~( 1 << PINC3 ); |
11 | }
|
gibt etwas mehr als 4 CPU-Takte pro Pin-Takt. Nocht schneller:
1 | uint8_t portc_1 = PORTC & ~(1<<PINC3); |
2 | uint8_t portc_h = PORTC | (1<<PINC3); |
3 | while(1) |
4 | {
|
5 | PORTC = portc_l; |
6 | PORTC = portc_h; |
7 | PORTC = portc_l; |
8 | PORTC = portc_h; |
9 | PORTC = portc_l; |
10 | PORTC = portc_h; |
11 | PORTC = portc_l; |
12 | PORTC = portc_h; |
13 | }
|
mit gut 2 CPU-Takten pro Pin-Takt.
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.
@A. K.: Warum sollte die "&="/"|="-Kombination schneller sein als ein EXOR?
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 | void send_spi_clk(void) |
4 | {
|
5 | uint8_t v, 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
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.