Hallo zusammen, ich habe ein Problem, welches ich bisher nicht lösen konnte. Ich habe jetzt einen "schmutzigen" Ansatz, aber vielleicht gibt es einen besseren Weg. Problem: Ich brauche ein taktgenaues Timing. Deshalb rede ich ab jetzt nicht in Microsekunden, sondern in Takten. Takt 0 ist quasi jetzt und Takt 50 ist 50 Takte in der Zukunft. Gegeben ist ein Pin, an dem irgendwann zwischen Takt 0 und Takt 50 ein Signal anliegt. Genau 10 Takte später muss ich diesen Pin abtasten und habe dafür auch kein Zeitfenster. Wenn also bei Takt 17 das Signal anliegt, muss ich den selben Pin an Takt 27 abtasten. Takt 26 ist zu früh, Takt 28 ist zu spät. Der erste naive Ansatz ist (abstrakt formuliert): while (pin0 == 0); delay(6 takte); read pin0; Geht leider nicht, da die while Schleife zwar immer 4 Takte braucht, aber das Signal ja an verschiedenen Stellen im Loop aufgetreten sein kann, ich also beim Assembler sbic nicht weiss, wie lang dort das Signal schon anlag. Es kann ja auch im jmp gekommen sein und das sind ja auch zwei Takte. Mein Ansatz ist jetzt folgender, von dem ich weder weiss, wie man es macht, noch weiss ich, ob es eine gute Idee ist. Zeile 1: PCINT0 Sprungziel sichern, überschreiben mit neuem Sprungziel auf Zeile 4 Zeile 2: Interrupts aktivieren Zeile 3: delay(1000ms) (eh wurscht); Zeile 4: delay(6 takte); Zeile 5: read pin0; Zeile 6: Interrupts aus und altes Sprungziel wiederherstellen. Kann mir jemand bei meinem Problem helfen? Danke.
So ganz einfach wirds wohl nicht, wenn die Laufzeiten der Befehle verschieden sind.
@Peter: Nicht möglich, da das Betreten der ISR alleine schon 4 Takte kostet und da habe ich noch nichtmal einen Register gesichert.
Christian S. schrieb: > Genau 10 Takte später muss ich diesen Pin abtasten Christian S. schrieb: > Nicht möglich, da das Betreten der ISR alleine schon 4 Takte > kostet Da kommt noch der RJMP zum Handler hinzu. Also hast Du doch noch 4 Takte Zeit. Christian S. schrieb: > da habe ich noch nichtmal einen Register gesichert. Deswegen sagte ich ja Assembler.
Christian S. schrieb: > @Peter: Nicht möglich, da das Betreten der ISR alleine schon 4 Takte > kostet und da habe ich noch nichtmal einen Register gesichert. Du kannst in den verbleibenden 6 Takten 3 Register sichern. Oder einfach 6 nop machen.
Christian S. schrieb: > Gegeben ist ein Pin, an dem irgendwann zwischen Takt 0 und Takt 50 ein > Signal anliegt. Genau 10 Takte später muss ich diesen Pin abtasten Warum liest du nicht laufend den Wert vom Pin in einen Ringpuffer ein. Dann hast du das Signal und den 10 Abtasttakte späteren Pegel beide zur Verfügung. Irgendwann wirst du allerdings mal den Schritt tun müssen, deinem abstrakten Takt eine Abtastfrequenz zuzuordnen.
Lösungsansatz mit min. Zusatzhardware: Das Signal kommt sagen wir an Port A.0 an, geht zusätzlich auf Din eines D Flipflop, dessen Q an Port A.1, Port B.0 liefert dem Flipflop CLK und Reset. Dann wäre der Zyklus folgender: Signal wurde an A.0 detektiert FF zurücksetzen ;Takt '0' verzögerung ;Takt '1-8' (loop, nops) Port B.0 High ;Enable des FF Eingangs, Takt '9' In Port A.1 ;Testen des FF Ausgang
Ich gehe von einer "normalen" CPU aus, die für einen Sprung mehr als einen Takt benötigt. Mike A. schrieb: > Christian S. schrieb: >> Gegeben ist ein Pin, an dem irgendwann zwischen Takt 0 und Takt 50 ein >> Signal anliegt. Genau 10 Takte später muss ich diesen Pin abtasten > > Warum liest du nicht laufend den Wert vom Pin in einen Ringpuffer ein. Das Problem dabei ist, dass dann zwar viele Werte im Ringpuffer stehen, aber die CPU nicht weiß, welcher Wert dem Zeitpunkt des 10. Taktes entspricht. Der Grund dafür ist dass die CPU den Zeitpunkt der Startflanke (Takt 0) nicht taktgenau bestimmen. Die Abfrage while(Pin == 0); wird immer zu einer Abfrage + Sprung übersetzt. @Christian S. Darfst du in der Aufgabe Peripherie benutzen? Ich denke da an flankengetriggerten Timer oder ähnliches...
Christian S. schrieb: > Mein Ansatz ist jetzt folgender, von dem ich weder weiss, wie man es > macht, noch weiss ich, ob es eine gute Idee ist. > > Zeile 1: PCINT0 Sprungziel sichern, überschreiben mit neuem Sprungziel > auf Zeile 4 > Zeile 2: Interrupts aktivieren > Zeile 3: delay(1000ms) (eh wurscht); > Zeile 4: delay(6 takte); > Zeile 5: read pin0; > Zeile 6: Interrupts aus und altes Sprungziel wiederherstellen. Nicht jede CPU reagiert sofort auf einen Interrupt. Manche CPUs arbeiten erst den aktuellen Befehl - der auch mal 10, 20 oder noch mehr Takte dauern kann - ab. Je nach Befehl in der main()-Schleife kann die ISR auch später kommen (=> Jitter)
Es handelt sich um den ATtiny13A. Es wäre schön, wenn ich keinen zusätzlichen Chip dafür nehmen müsste. Der Timer wäre noch frei, aber ich weiss nicht, wie man den dafür missbrauchen könnte. Der kann zwar seinen Takt ausgeben, aber nicht auf ein eingehendes Signal den Timer starten, was eine Lösung wäre, wenn es denn gehen würde.
Wahrscheinlich endet die Geschichte mit einen flinken HW-Dezimalzähler der bei Bedarf auf NUll gesetzt wird und dann nach dem 10. Takt Werte einsammelt?? http://www.elektronik-kompendium.de/sites/dig/0210222.htm
Muß es der ATtiny13A sein? Bei einem ATmega (Auch ein ATtiny mit USI-Einheit sollte gehen) könnte man den UART im SPI-Modus laufen lassen, und das Signal damit immerhin mit halber Taktfrequenz abtasten, 8 Abtastungen landen dann in einem Byte und können ausgewertet werden. Wenn das noch zu langsam ist, könnte man ein Seriell In/Parallel Out Schieberegister mit dem CLKOUT Pin betreiben. Dann hat man eine Abtastung bei jedem Taktzyklus. Die Auswertung wird dann aber, bei dieser Datenflut, doch etwas haarig. Das Schieberegister auf 16 Stufen zu verlängern könnte das etwas entschärfen. Mit freundlichen Grüßen - Martin
Ich würde den Ansatz mit PCINT0 weiter verfolgen. Wenn du den Interrupt in Assembler oder zumindest inline-assembler schreibst, brauchst du nicht mal Register sichern, da du eines der vielen vom GCC gar nicht verwenden Register nehmen kannst. (bin mir nicht mehr sicher, glaube r3-r7 sind frei) Und für eine konstante Reaktionszeit musst du deinen Tiny in den Sleep-Mode schicken und per Interrupt aufwecken... Dann kannst du noch ausloten wie viele Takte du ggf. trödeln musst und deinen Sample-Punkt setzen. Viel Spaß!
>Ich habe jetzt einen "schmutzigen" Ansatz, aber vielleicht gibt es einen >besseren Weg. So wie es aussieht, hast du überhaupt keinen Ansatz. Deine Forderungen sind unvollständig. Es ist egal, wann du dieses Signal erfaßt, es gibt keinen Bezug zu irgendwas. Wenn auf die Änderung des Eingangssignals mit einem bestimmten Timing ein Ausgangssignal generiert werden soll, dann dann macht das Timing Sinn. So wie von dir bisher beschrieben, ist es völlig egal zu welchem Zeitpunkt das Eingangssignal erkannt wird. Es ist sogar völlig egal, ob es überhaupt erkannt wird.
1 | wait: sbic PINB,0 ;2 1 |
2 | rjmp wait ; 2 |
3 | nop ;1 |
4 | nop ;1 |
5 | nop ;1 |
6 | nop ;1 |
7 | nop ;1 |
8 | nop ;1 |
9 | nop ;1 |
10 | in r0,PINB ;1 |
Die Warteschleife am Anfang braucht 3 Zyklen, schneller/genauer geht's m.E. nicht.
Christian S. schrieb: > Gegeben ist ein Pin, an dem irgendwann zwischen Takt 0 und Takt 50 ein > Signal anliegt. Genau 10 Takte später muss ich diesen Pin abtasten und > habe dafür auch kein Zeitfenster. Wenn also bei Takt 17 das Signal > anliegt, muss ich den selben Pin an Takt 27 abtasten. Takt 26 ist zu > früh, Takt 28 ist zu spät. > Geht leider nicht, da die while Schleife zwar immer 4 Takte braucht, > aber das Signal ja an verschiedenen Stellen im Loop aufgetreten sein > kann, ich also beim Assembler sbic nicht weiss, wie lang dort das Signal > schon anlag. Es kann ja auch im jmp gekommen sein und das sind ja auch > zwei Takte. Ja genau das ist das Problem bei der Sache, Software ohne Hilfe irgendeiner spezialisierten Hardware taktgenau mit äußeren Ereignissen zu synchronisieren ist nur möglich, wenn diese "periodisch" (zumindest in einem periodischen Raster) auftreten, bei Einzelereignissen geht das nicht, da kommt man maximal auf zwei Takte Genauigkeit. Und das Mittel dazu ist das "Ausrollen" der Warteschleife, so daß der Rücksprung entfällt.
Christian S. schrieb: > habe dafür auch kein Zeitfenster. Wenn also bei Takt 17 das Signal > anliegt, muss ich den selben Pin an Takt 27 abtasten. Takt 26 ist zu > früh, Takt 28 ist zu spät. Das kannst du gleich vergessen. Min. Toleranz beim Abfragen ist 2Cy (sbis/sbic oder rjmp, ist egal). Die einzige Möglichkeit wäre ein Tiny oder MEGA mit ICP und Timer mit CLK = 1:1. Wenn der ICR Wert ungerade ist, wird 9 Cy gewartet, wenn der Wert gerade ist, wird 10 Cy gewartet (nur als Beispiel). Und das Schöne ist, es gibt sogar ein Int dafür.
:
Bearbeitet durch User
Sehr oft werden hier Lösungen versucht, die unnötig kompliziert sind, nur weil der Sinn des Ganzen verheimlicht wird. Es wird eine kleine Teilaufgabe völlig aus dem Zusammenhang gerissen und jeder grübelt, was will der bloß damit.
Von was für einem Takt wird denn hier überhaupt gesprochen, oder habe ich das überlesen? Geht es um den konkreten CPU-Takt, oder um ein externes Taktsignal mit f_ext << f_cpu ?
1. Idee: Mach den takt doppelt oder dreimal oder zehnmal so hoch,d ann hast du ein Zeitfenster. 2. Idee: klatsch ein CPLD an den µC oder löse das Problem Komplett im CPLD
Marc Vesely schrieb: > Die einzige Möglichkeit wäre ein Tiny oder MEGA mit ICP und Timer > mit CLK = 1:1. So ist es. Das ist das einzige System im einem AVR, welches wirklich taktgenau Ereignisse erfassen kann. Interrupts haben ein variable Auslösezeit, da der aktuell laufende Befehl immer erst beendet wird. Oliver
Ich würde warten bis Moby hier auftaucht. Das ist der Assembler-Fachmann des Forums und löst dauernd Aufgaben, die ein exaktes Timing erfordern. Er kann dir sicher weiterhelfen.
Ben schrieb: > Er kann dir sicher weiterhelfen. Das glaube ich nicht. Die Aufgabe ist auf einem AVR so schlicht und ergreifend mit diesen Timingvorgaben ... > Wenn also bei Takt 17 das Signal anliegt, muss ich den selben Pin an > Takt 27 abtasten. Takt 26 ist zu früh, Takt 28 ist zu spät. ... nicht lösbar. Egal welcher Ansatz gewählt wird, es gibt für die Gesamtaufgabe immer eine unbestimmte Latenz. Und dabei wurde der Nebenschauplatz 'Timeout nach 50 Takten' noch gar nicht angesprochen. (Sofern mit 'Takt' der CPU Takt gemeint war, wovon wohl alle ausgehen)
:
Bearbeitet durch User
Wie Peter schon geschrieben hat, beschreibe mal das komplette Problem, nicht nur einen Ausschnitt davon. Was sind das für Signale, woher kommen sie, warum willst du 10 Takte warten, etc. Je mehr Infos, desto besser.
Karl Heinz schrieb: > ... nicht lösbar. Auf dem Tiny nicht. Per ICP und Timer im 1:1 clk in reinem Assembler sollte es gehen, solange nach 10 Takten nur eingelesen werden muß, nicht aber auch reagiert. Allerdings muß die ISR dann in der Vektortabelle stehen, Zeit für einen rjmp ist da nicht mehr. Andere Interrupts sind damit vom Tisch. Die ISR wird nach Minimum 4-5 Zyklen und maximal nach 8-9 Zyklen (oder so, anhängig vom Prozessor) erreicht. Es müssen halt eine ganze Reihe Register ab 16 aufwärts freigehalten werden. ISR: in r16, PINA in r17, PINA in R18, PINA in R19, PINA in R20, PINA in R21, PINA Jetzt anhand des Zählerstanddifferenz zwischen ICP-Register und aktuellem Zählerstand die ISR-Einsprung-Verzögerung ausrechen, und damit bestimmen, in welchen Register der gewünschte Wert steht. Nicht schön, aber machbar. Oliver
Karl Heinz schrieb: > ... nicht lösbar. Egal welcher Ansatz gewählt wird, es gibt für die > Gesamtaufgabe immer eine unbestimmte Latenz. Wenn die gewünschte Auflösung bzw. Genauigkeit 1 Takt ist, dann ist die Aufgabe sehr wohl lösbar, nur nicht mit ATTINY13. Aber mit ATTINY2313 oder ATMEGA ginge es ohne Probleme. Etwa 30 Bytes für alles, schätze ich.
Oliver S. schrieb: > Nicht schön, aber machbar. Es geht auch schöner und mit anderen Interrupts uf dem Tisch, nur blieb die Frage unbeantwortet worum es hier eigentlich geht und warum es genau 10 Takte sein müssen ? Und wie schnell läuft der Tiny überhaupt ?
:
Bearbeitet durch User
Ist so nicht lösbar weil der µC könnte gerade einen 2 Takt Befehl abarbeiten oder auch nicht. Also hat man dann jenachdem einen weiter Takt nicht vorhersehbarer Verzögerung oder auch nicht.
Was willst du überhaupt tun? Mir scheint, dass du entweder etwas murksen willst oder aber dein Prozessor eigentlich zu langsam ist für die Aufgabe.
uwe schrieb: > Ist so nicht lösbar weil der µC könnte gerade einen 2 Takt Befehl > abarbeiten oder auch nicht. Also hat man dann jenachdem einen weiter > Takt nicht vorhersehbarer Verzögerung oder auch nicht. Nicht über etwas reden, worüber man nicht genau Bescheid weisst.
Danke für die Antworten. Ich werde jetzt einfach den Timer16 ICP nehmen. Das hört sich gut an. Das Rumgewackel im MCU ist mir dann auch zu blöd. Wenns nicht sauber lösbar ist, nehme ich halt einen externen Chip dafür.
Christian S. schrieb: > Danke für die Antworten. Ich werde jetzt einfach den Timer16 ICP nehmen. > Das hört sich gut an. Das Rumgewackel im MCU ist mir dann auch zu blöd. > Wenns nicht sauber lösbar ist, nehme ich halt einen externen Chip dafür. Wenn du uns sagst wofür das gut sein soll, kriegst du von mir auch die Lösung. In Ordnung ?
Oliver S. schrieb: > ISR: > > in r16, PINA > in r17, PINA > in R18, PINA > in R19, PINA > in R20, PINA > in R21, PINA Hmm. Ja, so könnte das tatsächlich was werden. > Jetzt anhand des Zählerstanddifferenz zwischen ICP-Register und > aktuellem Zählerstand die ISR-Einsprung-Verzögerung ausrechen, und damit > bestimmen, in welchen Register der gewünschte Wert steht. > > Nicht schön, aber machbar. Jep. Aber mir gehts wie den anderen. Was ist die eigentliche Aufgabenstellung?
Stefan1234 schrieb: >> Warum liest du nicht laufend den Wert vom Pin in einen Ringpuffer ein. > > Das Problem dabei ist, dass dann zwar viele Werte im Ringpuffer stehen, > aber die CPU nicht weiß, welcher Wert dem Zeitpunkt des 10. Taktes > entspricht. Das kann ich nicht ganz nachvollziehen. Wenn Du jeden Takt einen Wert in den Ringpuffer schreibst, kannst Du später beim Auswerten das erste Auftreten des Pulses finden und dann einfach 10 Speicherstellen weiter springen und findest dort das Ergebnis. Natürlich geht das nicht mit einem Attiny, denn der kann nicht gleichzeitig den Ring eines Ringpuffers managen und dennoch jeden Takt weiterhin einen Pin auslesen. Aber auf einem Cortex M mit DMA wäre das mein Lösungsansatz.
Ich wollte die Aufgabe nicht klar hinschreiben, da sie objektiv ziemlich dämlich ist. Mir geht es da eher um das "wie weit kann ich gehen"-Prinzip. Ich bastel immernoch am Software-UART rum und will sehen, wie schnell ich mit einer 8 MHz CPU eine Sende- und Empfangsroutine hinbekomme. Mein persönliches Ziel sind 2 MBit, also Frequenz/4, also 4 Zyklen pro Bit bei 1 Startbit und 2 Stopbits. Die Senderoutine war geradezu ein Witz im Vergleich zur Empfangsroutine. Da gibts Probleme ohne Ende. Warum ich einen Delay-Breaker haben wollte: Ich habe Probleme, mit 2 Stopbits die Daten in einen Ringbuffer zu schreiben, danach die ISR zu verlassen und neu reinzukommen. Deshalb habe ich mal testweise mit CTS gespielt. Nachdem ich CTS aber getriggert habe, dauert es 15-20 Takte, bis die Sequenz reinkommt. Da das Signal aber nur 4 Takte lang anliegt und Takt 1 und Takt 4 wegen der Ansteigszeiten eh tabu sind, kann ich nur Takt 2 oder Takt 3 abtasten. Mittlerweile sage ich aber, dass es unbedingt ohne CTS gehen muss, da es sonst einfach zu unelegant ist und dann lieber gar nicht. Deshalb kämpfe ich derzeit erstmal am ISR Eingangsdelay und versuche mal rauszufinden, wie weit ich den überhaupt optimieren kann. Dazu will ich vor den ganzen pushes erstmal das erste Bit einlesen und will sehen, wie lang dieser Grunddelay ist. Die ganzen Probleme sind ja auch nur deshalb so schlimm, weil man mal entschieden hat, dass UART nur genau 1 Startbit haben darf und das nicht konfigurierbar sein darf. Klar, wenn ich produktiv wäre, würde ich mir jetzt einen 25 MBit UART kaufen, oder noch besser, weg mit dem Scheiss und direkt auf USB Bus gehen, wie es der Rest der Welt schon getan hat. Aber ich bin halt hartnäckig und will wissen, ob es möglich ist oder nicht. Wär halt cool, so ein 2 MBit Software UART ^^
Mit Warteschleife und Interrupt hat man bei ATTiny IMMER eine Unsicherheit von 1 Clk-Takt. Ich habe noch nie marke: rjmp marke ; (braucht 2 Clk-Takte) probiert. Meist ist da noch mindestens ein Befehl mit 1 Clk-Takt davor. In jedem Fall kann das EREIGNIS aber vor dem 2 Clk-Takte-Befehl auftreten (Delay = 2 Clk-Takte), oder mittendrin (Delay = 1 Clk-Takt), oder vor einem Befehl, der nur 1 Clk-Takt dauert (Delay = 1 Clk-Takt). Um ein RJMP, oder BRxx (2 Clk-Takte) kommst du nicht herum! Vergiss es! Oder arbeite mit einer so hohen CLK-Frequenz, dass es nicht mehr auf einen CLK-Takt ankommt. FERTIG.
Oldie schrieb: > Vergiss es! Nein, vergiss es nicht. OK, hier ist es, wie versprochen:
1 | .include "tn2313def.inc" |
2 | |
3 | .def FlagReg = r16 |
4 | .equ ImpPort = PIND ;* ICPI beim Tiny2313 am PinD.6 |
5 | .equ ImpDone = 0 ;* Flag zum Rücksprung |
6 | ;
|
7 | .DSEG |
8 | ImpWert: .byte 1 |
9 | TimWert: .byte 1 |
10 | IcrWert: .byte 1 |
11 | |
12 | .CSEG |
13 | ;*** Timer1 Vorteiler wurde auf CLOCK eingestellt |
14 | ldi FlagReg, 1<<ICES1 ;* Input capture = steigende Flanke |
15 | out TCCR1B, FlagReg |
16 | CaptImp: clr FlagReg |
17 | out TCNT1H, FlagReg |
18 | out TCNT1L, FlagReg |
19 | |
20 | ; +--- Interrupt beim Byte A - Fall A - Zählerstand gerade |
21 | ; | |
22 | ; | +---- Interrupt beim Byte B - Fall B - Zählerstand ungerade |
23 | ; \|/ \|/ |
24 | Wait4Imp: sbrc FlagReg, ImpDone ;* |
25 | ret ;* Impuls wurde in der ICR-routine bearbeitet, zurück |
26 | rjmp Wait4Imp ;* |
27 | ; /|\ /|\ |
28 | ; | +---- Interrupt beim Byte B - Fall B - Zählerstand ungerade |
29 | ; | |
30 | ; +--- Interrupt beim Byte A - Fall A - Zählerstand gerade |
31 | |
32 | ISR_TIMER1_CAPT: |
33 | ;* Takte vergangen bis zum Einsprung |
34 | ;*Fall B Fall A |
35 | ;* 4 5 |
36 | in FlagReg, ICR1L ;* 1 1 |
37 | sbrc FlagReg, 0 ;* 1 2 |
38 | rjmp ImpOdd ;* 2 |
39 | nop ;* 1 |
40 | nop ;* 1 |
41 | ImpEven: in FlagReg, PinD |
42 | sts ImpWert, FlagReg ;* Hier hast du den Portzustand |
43 | in FlagReg, ICR1L ;* |
44 | sts IcrWert, FlagReg ;* Nicht nötig, kann aber auch nicht schaden... |
45 | in FlagReg, TCNT1L ;* |
46 | sts TimWert, FlagReg ;* Nicht nötig, kann aber auch nicht schaden... |
47 | ldi FlagReg, 1<<ImpDone |
48 | reti
|
49 | ImpOdd: rjmp ImpEven ;* 2 |
50 | ;================================================================================= |
51 | ; Takte vergangen bis zum Abtasten: 10 10 |
Aber auch hier musst du mit einem Fehler von (Taktdauer - 1ns) rechnen, selbst bei 20MHz sind es satte 49ns.
:
Bearbeitet durch User
Marc Vesely schrieb: > Es geht auch schöner und mit anderen Interrupts uf dem Tisch, Ist tatsächlich schöner, aber auch ohne andere Interrupts. Auch diese ISR muß direkt in der Interruptabelle stehen, sonst reichen die verfügbaren 10 Zyklen nicht aus. Oliver
Oliver S. schrieb: > Ist tatsächlich schöner, aber auch ohne andere Interrupts. Auch diese > ISR muß direkt in der Interruptabelle stehen, sonst reichen die > verfügbaren 10 Zyklen nicht aus. Nicht unbedingt. 2 Zyklen sind in beiden Fallen übrig, es reicht für einen rjmp:
1 | ;* Takte vergangen bis zum Einsprung |
2 | ;*Fall B Fall A |
3 | ;* 4 5 |
4 | INT_VEKTOR_TABELLE: |
5 | ICP_VEKT: rjmp ISR_TIMER1_CAPT ;* 2 2 |
6 | |
7 | ISR_TIMER1_CAPT: |
8 | in FlagReg, ICR1L ;* 1 1 |
9 | sbrc FlagReg, 0 ;* 1 2 |
10 | rjmp ImpEven ;* 2 |
11 | ImpEven: in FlagReg, PinD |
12 | sts ImpWert, FlagReg ;* Hier hast du den Portzustand |
13 | in FlagReg, ICR1L ;* |
14 | sts IcrWert, FlagReg ;* Nicht nötig, kann aber auch nicht schaden... |
15 | in FlagReg, TCNT1L ;* |
16 | sts TimWert, FlagReg ;* Nicht nötig, kann aber auch nicht schaden... |
17 | ldi FlagReg, 1<<ImpDone |
18 | reti
|
19 | ;================================================================================= |
20 | ; Takte vergangen bis zum Abtasten: 10 10 |
Christian S. schrieb: > Ich bastel immernoch am Software-UART rum und will sehen, wie schnell > ich mit einer 8 MHz CPU eine Sende- und Empfangsroutine hinbekomme. Mein > persönliches Ziel sind 2 MBit, also Frequenz/4, also 4 Zyklen pro Bit > bei 1 Startbit und 2 Stopbits. Die Senderoutine war geradezu ein Witz im > Vergleich zur Empfangsroutine. Da gibts Probleme ohne Ende. Habe es gerade erst bemerkt. Wie willst du 2Mbit mit 10 Zyklen und 8MHz erreichen ? Deine 10 Zyklen * 125ns = 1250ns = 800Kbit Das geht nie und nimmer. 1Mbit wäre noch zu schaffen, aber auch nur mit Tricks und ohne überhaupt Zeit zu haben, die empfangenen Bytes irgendwie zu bearbeiten. Ein ATTiny4313 schafft 2Mbit mit 16MHz und das auch noch in Hardware (3 mal Abtasten pro bit). Wozu das Ganze gut sein soll, warum mit Gewalt irgendetwas versuchen was keinen praktischen Nutzen hat und das in Hardware schon viel besser realisiert ist, wird für mich immer ein Rätsel bleiben.
:
Bearbeitet durch User
>1Mbit wäre noch zu schaffen, aber auch nur mit Tricks und ohne >überhaupt Zeit zu haben, die empfangenen Bytes irgendwie zu bearbeiten. Genau, warum dann das Ganze? Gruß J
Sagte ich bereits. Ich wollte wissen, was möglich ist, wenn man sich mal ernsthaft dran setzt und wo letztendlich der unüberwindbare Flaschenhals liegt. Als Anfänger war es für mich unverständlich, warum ich beim Empfangen nur 1 MBit schaffen soll, wenn ich doch auch problemlos 2 MBit/s senden kann. Ich habe vorher nie mit ISRs gearbeitet und mache MCUs und C auch erst seit 8 Wochen. Ich habe hier viel gelernt und danke euch für die interessanten Beiträge.
Christian S. schrieb: > liegt. Als Anfänger war es für mich unverständlich, warum ich beim > Empfangen nur 1 MBit schaffen soll, wenn ich doch auch problemlos 2 > MBit/s senden kann. Ich habe vorher nie mit ISRs gearbeitet und mache Na, so problemlos wohl auch wieder nicht. Ich mochte gern diese (problemlose) Routine ohne den loop auszurollen sehen. Und mit loop ausrolen geht es sogar noch ein bisschen schneller. Nur muss man ein ganzes Port fur diesen einen bit reservieren.
Der Loop war ausgerollt, das ist richtig. Aber die 16 Befehle sind ja nun nicht wirklich der Speicherfresser :) Lustigerweise geht es auf einer 8 MHz CPU so genau auf, dass man nichtmal Delays braucht, wenn man mit einem Stopbit auskommt.
1 | void SendSerial_2MBit_8MHz(uint8_t byte) { //2000kBit = 500ns pro bit auf 8 MHz MCU. |
2 | SetBitOff(PortBDataRegister, B1SerialOut); //start bit |
3 | asm volatile("bst %[src],0\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
4 | asm volatile("bst %[src],1\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
5 | asm volatile("bst %[src],2\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
6 | asm volatile("bst %[src],3\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
7 | asm volatile("bst %[src],4\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
8 | asm volatile("bst %[src],5\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
9 | asm volatile("bst %[src],6\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
10 | asm volatile("bst %[src],7\n bld %[dest],%[destbit]" : [dest] "+r" (PortBDataRegister) : [src] "r" (byte), [destbit] "n" (B |
11 | SetBitOn(PortBDataRegister, B1SerialOut); |
12 | }
|
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.