Hallo, ich muss einen Drehgeber mit 100kHz simulieren. Vormals waren es 15kHz aber die Anforderungen wurden wiedermal geändert. Aus diesem Grund ist ein Atmega16 bestückt (16MHz Quarz). Ich habe eigentlich nur vier Zustände, die regemlmäßig durchlaufen werden: /A/B A/B AB /AB Dann geht das Rondo von vorne los. Ursprünglich war angedacht, über RS232 die Frequenz änderbar zu gestalten. Daher hatte ich per TWI den Teiler für den Überlauf übertragen und im Timer Overflow Interrupt aus einem Array die Werte für die Ausgänge ausgelesen und gesetzt. Mit dieser Methode komme ich aber nicht ansatzweise an meine 100kHz heran. Jetzt habe ich den Quelltext mal komplett zusammengestrichen (nurnoch einen Zähler von 1-4 und Ausgabe der Werte auf den Port). Damit erreiche ich bei maximaler Optimierung gerade einmal 120kHz. Wieso ist das so langsam? Das würde ja bedeuten, dass der Prozessor für diese lapidare Aufgabe über 100 Takte benötigt?! Habe ich einen prinzipiellen Denkfehler oder ist der Prozessor wirklich so langsam? Danke für Input!
Wenn der Quelltext so zusammengestrichen wurde, wird wohl auch nur noch wenig drinstehen, was der Geheimhaltung bedarf. Wird der Grau-Code über eine Tabelle erzeugt? Oder über Shift-Operationen mit XOR? Sind die Port/Pin-Adressen fest oder Variablen? Es gibt schon ein paar Möglichkeiten, das langsam zu bekommen.
Hi Wer kümmert sich um die RS232-Kommunikation? Vll sind dort Deine 100 Takte zu finden. MfG
Wer wird da gleich... schrieb: > Wieso ist das so langsam? Das würde ja bedeuten, dass der Prozessor für > diese lapidare Aufgabe über 100 Takte benötigt?! Das wird an deinem Programm liegen. Der Prozessor kann es auf jeden Fall schneller. Wenn es also wirklich schnell sein soll, nimmst du den ganzen Timer- und Interrupt-Kram raus und lässt den Prozessor die Pulse direkt mit Zeitsteuerung z.B. über NOP-Befehle ausgeben.
Sind die Fuse-Bits richtig gesetzt? Default läuft der AtMega16 mit internem Oszillator mit 1 MHz. Das wären dann 8 Takte für 125 kHz.
Sorry, konnte nix mehr schreiben weil ein Bug in der Forensoftware das verhindert hat GRML Der Quelltext ist recht primitiv (is ja nix meghr da):
1 | #define _XTAL_FREQ 16000000
|
2 | #include <avr/io.h> |
3 | #include <avr/interrupt.h> |
4 | #include <stdint.h> |
5 | #include <stdlib.h> |
6 | #include <util/delay.h> |
7 | #include "TWI_Slave.h" |
8 | #include "avr/sleep.h" |
9 | |
10 | unsigned int overflowCounter,byte0,byte1,byte2,byte3,i, overflowcounter2 = 0; |
11 | unsigned int encodervalue[4]; |
12 | |
13 | int main( void ) |
14 | {
|
15 | DDRC = 0x3C; |
16 | |
17 | encodervalue[0]=0x0C; |
18 | encodervalue[1]=0x18; |
19 | encodervalue[2]=0x30; |
20 | encodervalue[3]=0x24; |
21 | |
22 | while(1) |
23 | {
|
24 | |
25 | i++; |
26 | if (i==4) |
27 | {i=0;} |
28 | PORTC=((PORTC&0xC3)|encodervalue[i]); |
29 | }
|
30 | }
|
Fuses sind auf "High Speed Ext. Crystal 16 MHz" (oder so ähnlich) gesetzt. Oder muss ich andere Fuses auch noch setzen????
Bubbi B. schrieb: > Oder muss ich andere Fuses auch noch setzen???? CLKDIV8 darf nicht gesetzt sein, das ist iirc bei Auslieferung gesetzt.
Felix U. schrieb: > Bubbi B. schrieb: >> Oder muss ich andere Fuses auch noch setzen???? > > CLKDIV8 darf nicht gesetzt sein, das ist iirc bei Auslieferung gesetzt. Hat der Atmega16 nicht.
Bubbi B. schrieb: > if (i==4) > {i=0;} Damit erzeugst du dir einen (vermutlich) ungewollten Jitter im Signal, i.e. der Zustand bei i=3 ist um die Ausführungszeit für das 0-setzen von i länger. Gleichmäßiger läuft das ab, wenn du einfach immer ein
1 | i &= 0x03; |
durchläufst. Die ganze Indizierung mit i=0..3 kann man durch linearen Code ersetzen, das würde noch die Zeit für den indizierten Array-Zugriff sparen.
PORTC=((PORTC&0xC3)|encodervalue[i] ... Da musst Du den Port erstmal lesen, mit c3 verunden, encodervalue lesen, mit dem vorangegangenen verodern und dann in Port schreiben, das sind schon einige Schritte ... schreib doch einfach direkt in Port, sind ja nur 4 Zustände, ergo 4 Zeilen Code, zur Not in ASM out PORTC, 0xirgendwas
Bubbi B. schrieb: > Hat der Atmega16 nicht. Sorry, das habe ich verwechselt. Schneller geht es aber auch, wenn du die encodervalues in 8-bit statt 16-bit ints schreibst, weil der Compiler die dann in Registern behalten kann. Dasselbe gilt natürlich für i. Dann noch das, was der Weinbauer gesagt hat, dann bist du wohl schon doppelt so schnell.
:
Bearbeitet durch User
Wolfgang schrieb: > Das wird an deinem Programm liegen. Vermutlich. Ich hab das Programm jetzt komplett reduziert und setze stur den Port. 2,6 MHz... Irre, wieviel so wenig Befehle und ein paar optimierungsprobleme (16- statt 8bit Zugriff etc.) den Prozessor extrem verlangsamen o.O Selbst wenn ich alles bis ultimo optimiere; ich weiß nicht, ob der Prozessor das wirklich packt, SPI auch noch zu machen, wenn er 100.000 Hz erreichen soll! Danke euch!
:
Bearbeitet durch User
Bubbi B. schrieb: > SPI auch noch zu machen, Wenn SPI oder UART per Interrupt zuschlagen wird nur in dem Moment der Übertragung der Output gestört. Aber das man doch sicher nicht ständig machen, also läuft die Ausgabe genauso schnell wenn man die Vorgabe per Schnittstelle einbaut. ok, fast genauso schnell, es muss ja noch die einstellbare Verzögerung in die Schleife.
Bubbi B. schrieb: > Vermutlich. Ich hab das Programm jetzt komplett reduziert und setze stur > den Port. > > 2,6 MHz... Zyklus oder Schrittrate? Wenn Zylus, dann hast du das ist das Optimum für diese Busy-Loop erreicht. Sowas kann C gut. Allerdings: Jitter schon ohne Störungen durch ISRs: 300%. Das kommt durch das Schließen der Schleife. Kann man aber kompensieren, indem man nach jeder der ersten drei Portausgaben 2x nop einfügt. Dann ist der Jitter weg. Asm rules... Allerdings sinkt dadurch natürlich die Zyklusfrequenz von 16M/6 auf 16M/12, also auf die Hälfte. Egal, auch dann: Wenn da nur eine einzige in C geschriebene ISR reingrätscht, bist du locker bei einigen 1000% Jitter... Nö, nö, der Ansatz mit einer Timer-ISR war schon OK, bloß die Sprache war falsch. Mit Asm kann man 1MHz ohne jeden Hauch von Jitter erreichen und auch konkurrierende ISRs wirken sich längst nicht so schädlich aus (wenn sie ebenfalls liebevoll Asm-optimiert werden). Falls es dich interessiert, so würde dein Problem in einer richtigen Sprache gelöst werden:
1 | .EQU ENC_A_BIT = 0 ;nur als Beispiele |
2 | .EQU ENC_B_BIT = 1 |
3 | .EQU ENC_PORT = PORTC |
4 | |
5 | .EQU ENC_A_MSK = 1<<ENC_A_BIT |
6 | .EQU ENC_B_MSK = 1<<ENC_B_BIT |
7 | |
8 | .DEF ENC_STAT = R16 ;optional, ansonsten +4 ticks für register save/restore |
9 | |
10 | .CSEG |
11 | .ORG OVF0addr ;[4][4][4][4] "in place" auf dem Vektor, ansonsten +2 ticks for rjmp |
12 | sbrc ENC_STAT,ENC_A_BIT ; 2 2 1 1 |
13 | rjmp _isr_a_high ; 2 2 |
14 | nop ; 1 1 optional: sorgt nur für Jitterfreiheit (jedenfalls so lange ohne ISR-Konkurrenz) |
15 | |
16 | sbrc ENC_STAT,ENC_B_BIT ; 2 1 |
17 | ldi ENC_STAT,ENC_A_MSK|ENC_B_MSK ; 1 |
18 | sbrs ENC_STAT,ENC_B_BIT ; 1 2 |
19 | ldi ENC_STAT,ENC_B_MSK ; 1 |
20 | out ENC_PORT,ENC_STAT ; 1 1 |
21 | reti ; 4 4 |
22 | |
23 | _isr_a_high: |
24 | sbrs ENC_STAT,ENC_B_BIT ; 2 1 |
25 | ldi ENC_STAT,0 ; 1 |
26 | sbrc ENC_STAT,ENC_B_BIT ; 1 2 |
27 | ldi ENC_STAT,ENC_A_MSK ; 1 |
28 | out ENC_PORT,ENC_STAT ; 1 1 |
29 | reti ; 4 4 |
30 | ;-- -- -- -- |
31 | ;16 16 16 16 |
Wer wird da gleich... schrieb: > Damit erreiche ich bei maximaler Optimierung gerade einmal 120kHz. Nimm einfach den Timer T1 mit Toggle Output on Compare, da sollten bis 4MHz gehen und ganz ohne Interruptlast.
:
Bearbeitet durch User
Peter D. schrieb: > Nimm einfach den Timer T1 mit Toggle Output on Compare, da sollten bis > 4MHz gehen und ganz ohne Interruptlast. Das kann man natürlich auch machen und ist für dieses einfache Problem die bei weitem effizienteste Lösung. Aber ganz so einfach ist sie auch nicht umzusetzen. In der Nähe von Vmax werden sowohl Änderungen der Schrittfrequenz als auch Richtungswechsel zum Alptraum. Jedenfall, wenn sie einigermassen glitchfrei erfolgen sollen. Und schon wieder heisst es: Assembler rules...
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.