Forum: Mikrocontroller und Digitale Elektronik Assemblertrick "Delaybreaker" gesucht


von Christian (dragony)


Lesenswert?

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.

von oszi40 (Gast)


Lesenswert?

So ganz einfach wirds wohl nicht,
wenn die Laufzeiten der Befehle verschieden sind.

von Peter D. (peda)


Lesenswert?

In Idle gehen und den Interrupt in Assembler schreiben.

von Christian (dragony)


Lesenswert?

@Peter: Nicht möglich, da das Betreten der ISR alleine schon 4 Takte 
kostet und da habe ich noch nichtmal einen Register gesichert.

von Peter D. (peda)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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.

von Mike A. (Gast)


Lesenswert?

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.

von Heinz V. (heinz_v)


Lesenswert?

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

von Stefan1234 (Gast)


Lesenswert?

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...

von Stefan1234 (Gast)


Lesenswert?

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)

von Christian (dragony)


Lesenswert?

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.

von oszi40 (Gast)


Lesenswert?

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

von Martin S. (led_martin)


Lesenswert?

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

von Leo B. (luigi)


Lesenswert?

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ß!

von spontan (Gast)


Lesenswert?

>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.

von Jim (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Peter D. (peda)


Lesenswert?

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 Michael S. (stroggi)


Lesenswert?

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 ?

von uwe (Gast)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

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

von Ben (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
von Alexander S. (esko) Benutzerseite


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von uwe (Gast)


Lesenswert?

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.

von P. M. (o-o)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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.

von Christian (dragony)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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 ?

von Karl H. (kbuchegg)


Lesenswert?

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?

von Gerd E. (robberknight)


Lesenswert?

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.

von Christian (dragony)


Lesenswert?

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 ^^

von Oldie (Gast)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Oliver S. (oliverso)


Lesenswert?

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

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Jonas B. (jibi)


Lesenswert?

>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

von Christian (dragony)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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.

von Christian (dragony)


Lesenswert?

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
Noch kein Account? Hier anmelden.