mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Toggelgeschwindigkeit für Ports


Autor: Rüdiger B. (fenris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Codeausschnitt berücksichtig in keiner Weise den Timer --> Ergo ist 
die Toggelgeschwindigkeit immer gleich.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erstens solltest mit Optimierung übersetzen, wenn's dir drauf ankommt.

Zweitens geht es auch schneller:
while(1)
{
   PORTC |= ( 1 << PINC3 );
   PORTC &=~( 1 << PINC3 );
   PORTC |= ( 1 << PINC3 );
   PORTC &=~( 1 << PINC3 );
   PORTC |= ( 1 << PINC3 );
   PORTC &=~( 1 << PINC3 );
   PORTC |= ( 1 << PINC3 );
   PORTC &=~( 1 << PINC3 );
}
gibt etwas mehr als 4 CPU-Takte pro Pin-Takt.
Nocht schneller:
uint8_t portc_1 = PORTC & ~(1<<PINC3);
uint8_t portc_h = PORTC |  (1<<PINC3);
while(1)
{
   PORTC = portc_l;
   PORTC = portc_h;
   PORTC = portc_l;
   PORTC = portc_h;
   PORTC = portc_l;
   PORTC = portc_h;
   PORTC = portc_l;
   PORTC = portc_h;
}
mit gut 2 CPU-Takten pro Pin-Takt.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A. K.:
Warum sollte die "&="/"|="-Kombination schneller sein als ein EXOR?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
#include <avr/io.h>

void send_spi_clk(void)
{
  uint8_t v, nv;
  v = PORTC | (1 << 3);
  nv = v & ~(1 << 3);
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
  PORTC = v; PORTC = nv;
}

Mit Optimierung macht der Compiler daraus:
.global send_spi_clk
        .type   send_spi_clk, @function
send_spi_clk:
/* prologue: frame size=0 */
/* prologue end (size=0) */
        in r24,53-0x20
        ori r24,lo8(8)
        mov r25,r24
        andi r25,lo8(-9)
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
        out 53-0x20,r24
        out 53-0x20,r25
/* epilogue: frame size=0 */
        ret
d. h. nach dem Setup der Werte wird mit f[CPU]/2 getaktet.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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....

Autor: Rüdiger B. (fenris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.