Forum: Mikrocontroller und Digitale Elektronik AVR Assembler - IO Port-Switching-Zeiten nicht berechenbar


von Christian (dragony)


Lesenswert?

Hallo,

ich versuche einen IO-Port zu schalten, brauche dafür ein auf 
1-Clock-Zyklus genaues Timing und genau da liegt das Problem. Leider 
kann ich noch kein Assembler, deshalb versuche ich es in C. Damit es so 
unkompliziert wie möglich ist, sieht mein Code (gekürzt) so aus:

uint8_t off = PORTB;
uint8_t on = PORTB | (1<<PORTB4);

if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;
if (bla) PORTB = on; else PORTB = off;

Das funktioniert auch alles soweit, solange ich 01010101 setze. Setze 
ich allerdings 00001111 fällt mir auf, dass er anscheinend so "klug" 
ist, bei den sich wiederholenden Bits weniger Zyklen zu brauchen als bei 
dauernd wechselnden Bits, als ob es intern so nach 
"IF(differentStatus)Switch" ablaufen würde. Das ist natürlich 
prinzipiell sehr intelligent, aber in meinem Fall müssen die Timings 
halt genau stimmen. Laut ASM macht er es so:

sbrs r24, 1
rjmp .+4
out 0x18, r25
rjmp .+2
out 0x18, r18

Sieht doch für mich als Laie eigentlich genau richtig aus. Dennoch 
brauchen die outs je nach Bitmuster verschieden lange....

von Peter II (Gast)


Lesenswert?

Christian S. schrieb:
> Das funktioniert auch alles soweit, solange ich 01010101 setze. Setze
> ich allerdings 00001111 fällt mir auf, dass er anscheinend so "klug"
> ist, bei den sich wiederholenden Bits weniger Zyklen zu brauchen als bei
> dauernd wechselnden Bits, als ob es intern so nach
> "IF(differentStatus)Switch" ablaufen würde.

zeige dafür bitte brauchbaren code - wo wird denn 01010101 gesetzt?

> Dennoch brauchen die outs je nach Bitmuster verschieden lange....
nein, ein out brauch immer gleich lange. Die Frage ist ob ein out immer 
das richtige ist.

Zeige eine komplette Funktion und den Aufruf dafür.

von Amateur (Gast)


Lesenswert?

>Sieht doch für mich als Laie eigentlich genau richtig aus. Dennoch
>brauchen die outs je nach Bitmuster verschieden lange...

Out ist out! Ob jetzt 8-mal nix 0x00 oder 8-mal was 0xff rausgeschaufelt 
wird, ist dem Befehl gleich.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian S. schrieb:
> uint8_t off = PORTB;
> uint8_t on = PORTB | (1<<PORTB4);
>
> if (bla) PORTB = on; else PORTB = off;
> if (bla) PORTB = on; else PORTB = off;

Dieser Code ist Schwachsinn. Die Variable off enthält den aktuellen 
Zustand von PORTB, die Variable on enthält den aktuellen Zustand, wobei 
zusätzlich PB4 gesetzt ist. Wenn aber das Bit einmal gesetzt wurde, ist 
es auch beim nächsten mal in der Variablen off gesetzt.

Wenn, dann muss das so heißen:
1
if (bla) PORTB |= (1<<PORTB4); else PORTB &= ~(1<<PORTB4);
2
if (bla) PORTB |= (1<<PORTB4); else PORTB &= ~(1<<PORTB4);
3
...

Aber zurück zur eigentlichen Frage, nämlich zu den verschieden langen 
Abarbeitungszeiten: Es liegt nicht an der Portzuweisung, sondern an der 
Abarbeitung der if-Statements. Je nachdem, welche Bedingung TRUE ist, 
sind verschieden viele ifs durchlaufen worden. Das braucht 
unterschiedlich lange Zeiten.

Abhilfe: Eine Tabelle. Vielleicht auch ein switch.

: Bearbeitet durch Moderator
von Mitlesa (Gast)


Lesenswert?

1
uint8_t off = PORTB;
2
uint8_t on = PORTB | (1<<PORTB4);
3
4
if (bla) PORTB = on; else PORTB = off;

Im else-Zweig ergibt sich immer PORTB = PORTB;
Soll das so sein?

von Stefan (Gast)


Lesenswert?

Noch besser:
Verwendung von Timer-Output-Compare. Das geht zwar nur mit dezidierten 
Timer-Pins, dafür kannst Du Dir aber auch sicher sein, daß da Timing 
passt.

Bei Deiner Software-Lösung darfst Du entweder keine Interrupts verwenden 
oder Du musst sie für die Dauer des Pulses ausschalten.

Gruß, Stefan

von Amateur (Gast)


Lesenswert?

Es gibt die Möglichkeit den Versatz durch die unterschiedlichen 
Laufzeiten durch das auffüllen mit NOPs auszugleichen. In der Praxis 
artet das aber schnell in einen Job für den guten, alten Sisyphos aus.

Soweit mir bekannt benötigen die einfachen out-Befehle 1 Tackt Zyklus, 
ein bedingter Sprung ohne Verzweigung ebenfalls einen Tackt und bei 
Verzweigung 2 Tackte.

von Christian (dragony)


Lesenswert?

Mittlerweile habe ich das Problem gefunden.

Hier nochmal der Code, auf den es ankommt:

sbrs r24, 1
rjmp .+4
out 0x18, r25
rjmp .+2
out 0x18, r18

Das Problem ist, dass die rjmps manchmal vor und manchmal nach dem out 
ausgeführt werden, was das Timing zerstört. Ich habe es jetzt durch nops 
gelöst, aber das kostet mich insgesamt 3 Takte. Kann man sich diesen 
Iftest irgendwie sparen?

Irgendwie sowas wäre schön:

PORTB = Bit 0 von byte X an Stelle PORTB4 packen
PORTB = Bit 1 von byte X an Stelle PORTB4 packen
PORTB = Bit 2 von byte X an Stelle PORTB4 packen
PORTB = Bit 3 von byte X an Stelle PORTB4 packen
PORTB = Bit 4 von byte X an Stelle PORTB4 packen
PORTB = Bit 5 von byte X an Stelle PORTB4 packen
PORTB = Bit 6 von byte X an Stelle PORTB4 packen
PORTB = Bit 7 von byte X an Stelle PORTB4 packen

Es geht einfach darum, einen UART Sender in Software zu implementieren. 
Dient für mich als Übung.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian S. schrieb:
> Es geht einfach darum, einen UART Sender in Software zu implementieren.

Benutze doch einen Timer dafür. Oder willst Du gar 115200 Baud oder mehr 
in Software hinbekommen?

von uwe (Gast)


Lesenswert?

> PORTB = Bit 4 von byte X an Stelle PORTB4 packen
Sollte dir diese Zeile nicht zu denken geben?!

von Peter II (Gast)


Lesenswert?

mich wunder das es überhaupt ein out gibt. Du willst doch nur ein bit 
setzen. Mit out blockierst du den ganzen Port für andere dinge.

Und bei einen Soft-Uart brauchst du eh pausen. Damit spielt das Timing 
gar keine so grosse rolle.
1
uint8_t data = 0x12;
2
for( uint8_t i = 0; i < 8; ++i ) {
3
   if ( data & 1 ) {
4
       PORTB |= (1<<PB4)
5
   } else {
6
       PORTB &= ~(1<<PB4)
7
   }
8
   data = data >> 1;
9
   us_dalay(xxx);
10
}

damit sollte es schon funktionieren, die schleife könnte man auch noch 
aufdröseln aber da man eh warten muss sehen ich kaum sinn drin. In 
diesem code sollte auch kein OUT mehr vorhanden sein.

Wie schnell willst du denn übertragen?

von Klaus (Gast)


Lesenswert?

Christian S. schrieb:
> Mittlerweile habe ich das Problem gefunden.

Das Problem ist klar erkennbar. Wenn ich Dir ungebeten einen Rat geben 
darf, fange erst mal an C zu lernen, eine LED zu toggeln und das 
Datenblatt zu lesen. So in einem halben Jahr vielleicht schreibst Du mal 
eine Software UART. Das bisher gezeigt ist, mit Verlaub, grosser Murks. 
:-)

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Christian S. schrieb:
> Es geht einfach darum, einen UART Sender in Software zu implementieren.
> Dient für mich als Übung.

Hmmm... und warum schiebst du die Bits nicht einfach direkt raus?

BST
BLD
OUT
... usw, insgesamt 8 Mal

Dauert immer genau gleich lang, es sind keine Abfragen und keine Sprünge 
drin.

: Bearbeitet durch User
von Christian (dragony)


Lesenswert?

Ich bin jetzt erfolgreich bei 1 MBit mit dem internen 8 MHz Resonator 
und will mal sehen ob 2 möglich sind. Dazu muss jedoch der IF-Test weg. 
Ich denke, aufgrund mancher Äusserungen kann ich auf diesem hohen Niveau 
keine Hilfe erwarten. Kein Problem, hab bisher alle meine Problem auch 
selbst lösen können. Dauert nur etwas länger ^^

Das mit BST und BLD könnte echt mal eine Möglichkeit sein. Habe mich 
immer gefragt, was dieser blöde 1 Bit Speicher soll. Danke :)

: Bearbeitet durch User
von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Christian S. schrieb:
> Ich bin jetzt erfolgreich bei 1 MBit mit dem internen 8 MHz Resonator
> und will mal sehen ob 2 möglich sind.

Sollte mit BST,BLD,OUT locker klappen bis 2,666 Mbps.

von Martin S. (led_martin)


Lesenswert?

Wenn Du solche Geschwindigkeiten anstrebst (1 Bitlänge = 4 Taktzyklen), 
solltest Du dich mit (Inline-)Assembler beschäftigen. Die von Markus 
Weber vorgeschlagene BST / BLD -Lösung schafft die Geschwindigkeit. Der 
Empfang wird noch lustig, da Du die fallende Flanke des Startbits auf 
einen Taktzyklus genau erkennen must, und nur 6 Takte bis zum Abtasten 
des ersten Datenbits hast.

Mit freundlichen Grüßen - Martin

von Klaus (Gast)


Lesenswert?

Christian S. schrieb:

Das wirst Du sicher schaffen. Scheinst genügend hartnäckig zu sein. Du 
gehst halt einen langen, steinigen Umweg. Schneller kämst Du zum Ziel, 
wenn Du von vorne anfangen würdest.

Aber gut. Jedem seine Herausforderung. Viel Erfolg.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Es geht übrigens auch noch schneller:

LSR
OUT
... 8 Mal. -> 4 Mbps

Aber dann wirds etwas schmutzig, weil du vom betreffenden Port nur Bit 0 
für die Ausgabe benutzen kannst. Die übrigen Pins tragen nur Bitabfälle 
nach draußen. :-)

Was natürlich nichts macht, wenn die übrigen Pins des Ports als Eingänge 
verwendet werden. Dann schaltet man nur die Pullups – wenn überhaupt, 
denn bei manchen neueren AVR gibts dafür ja ein eigenes Pullup-Register.

Ach so: Manche AVR können auch mit 16 MHz internem Takt betrieben werden 
(z.B. ATtiny85, ATtiny861A). Vielleicht hilft dir das, die 
Geschwindigkeit noch weiter zu erhöhen...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Christian S. schrieb:
> Ich bin jetzt erfolgreich bei 1 MBit mit dem internen 8 MHz Resonator

Ist das eine Art Wettbewerb?

Ansonsten ist es eher unverständlich, warum man für derartige
Geschwindigkeiten nicht auf die vorhandene Hardware zurückgreift.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Jörg Wunsch schrieb:
> Ansonsten ist es eher unverständlich, warum man für derartige
> Geschwindigkeiten nicht auf die vorhandene Hardware zurückgreift.

Öhm, ja, seh ich genauso. :-)

Christian schreibt oben ja: "Dient für mich als Übung".
Vielleicht plant er auch irgendwas Seltsames und will Worte mit 4 oder 
10 bit Länge senden, dann wirds mit der Standard-Hardware auch wieder 
kompliziert.

von c-hater (Gast)


Lesenswert?

Christian S. schrieb:

> Irgendwie sowas wäre schön:
>
> PORTB = Bit 0 von byte X an Stelle PORTB4 packen
> PORTB = Bit 1 von byte X an Stelle PORTB4 packen
> PORTB = Bit 2 von byte X an Stelle PORTB4 packen
> PORTB = Bit 3 von byte X an Stelle PORTB4 packen
> PORTB = Bit 4 von byte X an Stelle PORTB4 packen
> PORTB = Bit 5 von byte X an Stelle PORTB4 packen
> PORTB = Bit 6 von byte X an Stelle PORTB4 packen
> PORTB = Bit 7 von byte X an Stelle PORTB4 packen
>
> Es geht einfach darum, einen UART Sender in Software zu implementieren.
> Dient für mich als Übung.

Dann übe es bitte in einer Sprache, mit der du volle Kontrolle über das 
Timing hast. C ist keine solche Sprache, Assembler schon. Übrigens hast 
du einen Assembler, ein C-Compiler ist schließlich auch nur ein 
aufgebohrter Assembler, kann aber immer noch native 
Assembler-Instruktionen verstehen.

Was nun dein Problem betrifft, geht die schnellste Lösung so (Byte x sei 
in Register R24, R16 sei unbenutzt/gesichert):

cli                ; 1
in R16,PORTB       ; 1
cbr R16,1<<PORTB4  ; 1 Startbit
out PORTB,R16      ; 1

bst R24,0          ; 1
bld R16,4          ; 1
out PORTB,R16      ; 1
bst R24,1          ;...
bld R16,4
out PORTB,R16
bst R24,2
bld R16,4
out PORTB,R16
bst R24,3
bld R16,4
out PORTB,R16
bst R24,4
bld R16,4
out PORTB,R16
bst R24,5
bld R16,4
out PORTB,R16
bst R24,6
bld R16,4
out PORTB,R16
bst R24,7
bld R16,4
out PORTB,R16

sbr R16,1<<PORTB4  ; 1 Stopbit
sei                ; 1
out PORTB,R16      ; 1


Jedes Bit dauert drei Takte, die gesamte Routine 31 Takte. Die Bitrate 
entspricht einem Drittel des Systemtakts.

Und, bevor du fragst: langsamer geht immer, das ist leicht. Schwierig 
wäre bloß noch schneller. Das geht, zumindest beim AVR8, nichtmal über 
die eingebaute UART-Hardware. Naja, bei den neueren kann man sie im 
SPI-Mode betreiben und erreicht dann bei der Bitrate sogar die Hälfte 
des Systemtaktes und das sogar ohne jegliche Interuptsperre.

von Peter II (Gast)


Lesenswert?

c-hater schrieb:
> Und, bevor du fragst: langsamer geht immer, das ist leicht. Schwierig
> wäre bloß noch schneller.

auch kein Problem. Wenn genug Flash vorhanden ist.
1
switch( data ) {
2
   case 0:
3
       PORTB |= (1<<pb4); //start
4
       PORTB &= ~(1<<pb4); //bit0
5
       PORTB &= ~(1<<pb4); //bit1
6
       PORTB &= ~(1<<pb4); //bit2
7
       PORTB &= ~(1<<pb4); //bit3
8
       PORTB &= ~(1<<pb4); //bit4
9
       PORTB &= ~(1<<pb4); //bit5
10
       PORTB &= ~(1<<pb4); //bit6
11
       PORTB &= ~(1<<pb4); //bit7
12
       PORTB |= (1<<pb4); //stop
13
       break;
14
   case 1:
15
       PORTB |= (1<<pb4); //start
16
       PORTB |= (1<<pb4); //bit0
17
       PORTB &= ~(1<<pb4); //bit1
18
       PORTB &= ~(1<<pb4); //bit2
19
       PORTB &= ~(1<<pb4); //bit3
20
       PORTB &= ~(1<<pb4); //bit4
21
       PORTB &= ~(1<<pb4); //bit5
22
       PORTB &= ~(1<<pb4); //bit6
23
       PORTB &= ~(1<<pb4); //bit7
24
       PORTB |= (1<<pb4); //stop
25
       break;
26
    //usw
27
    //....
28
    case 255:
29
    //...
30
}

: Bearbeitet durch Moderator
von c-hater (Gast)


Lesenswert?

Peter II schrieb:

> [c]

Da lach ich drüber...

> switch( data ) {

OMG. Switch mit 256 Zweigen. Selbst intelligente Compiler können das 
bestenfalls auf 8 Verzweigungen eindampfen (binärer Baum). Verzweigungen 
sind der Tod jeglichen getimeten Codes, wenn man sie einen C-Compiler 
machen läßt. Abgesehen davon kosten sie auch immer überproportional viel 
Rechenzeit.

>        PORTB |= (1<<pb4); //start
>        PORTB &= ~(1<<pb4); //bit0
>        PORTB &= ~(1<<pb4); //bit1
>        PORTB &= ~(1<<pb4); //bit2
>        PORTB &= ~(1<<pb4); //bit3
>        PORTB &= ~(1<<pb4); //bit4
>        PORTB &= ~(1<<pb4); //bit5
>        PORTB &= ~(1<<pb4); //bit6
>        PORTB &= ~(1<<pb4); //bit7
>        PORTB |= (1<<pb4); //stop

Gute Compiler werden das als sbi/cbi übersetzen, also zwei Takte pro Bit 
brauchen. Schlechte Compiler oder auch gute Compiler mit "ungünstig" 
eingestellter Optimierung können da leicht auch mal 3 oder sogar 5 Takte 
pro bit draus machen...
Abgesehen von diesen Betrachtungen ist der Code aber auch noch falsch. 
Das Startbit muß natürlich Low sein, aber das würde ich hier gern als 
Flüchtigkeitsfehler verbuchen wollen und nicht weiter diskutieren.


Worüber ich eher diskutieren würde: der Ansatz mit dem präparierten Code 
natürlich auch in Assembler möglich. Bloß würde ein gelernter 
Asm-Programmierer natürlich keinen binären Baum verwenden, sondern einen 
berechneten Sprung.

Das wäre natürlich auch in C möglich. Aber der Vergleich der beiden 
Routinen zeigt dann ganz deutlich, was ich an C so sehr hasse, den 
Bombast an Syntax, den man benötigt, um einfachste Sachverhalte 
auszudrücken, ohne dass man in der Compilerausgabe vor lauter Warnungen 
die Fehler nicht mehr sehen kann...

von Peter II (Gast)


Lesenswert?

c-hater schrieb:
> OMG. Switch mit 256 Zweigen. Selbst intelligente Compiler können das
> bestenfalls auf 8 Verzweigungen eindampfen (binärer Baum).

sie machen einfach eine Sprungtabelle draus, habe ich schon beim GCC 
gesehen. Sind dann bloss 2 oder 4 takte.

> Gute Compiler werden das als sbi/cbi übersetzen, also zwei Takte pro Bit
sbi/cbi brauchen doch nur 1 Takt?

> Schlechte Compiler oder auch gute Compiler mit "ungünstig"
Compiler die nicht mal cbi/sbi hinbekommen, kann man eh nicht ernsthaft 
einsetzten, damit würde ein Großteil der Programme nicht mehr laufen. 
Man kann sich schon darauf verlassen sie es können.

> Abgesehen von diesen Betrachtungen ist der Code aber auch noch falsch.
> Das Startbit muß natürlich Low sein, aber das würde ich hier gern als
> Flüchtigkeitsfehler verbuchen wollen und nicht weiter diskutieren.
ich war mir auch wegen der Reihenfolge der bits nichts sicher. Es ging 
ja nur ums Prinzip.

von Martin S. (led_martin)


Lesenswert?

Peter II schrieb:
> sbi/cbi brauchen doch nur 1 Takt?

Hier liegst Du falsch, es sind 2 Takte. Hast es vermutlich mit SBR / CBR 
verwechselt, die brauchen tatsächlich nur einen Takt, arbeiten aber nur 
mit den CPU-Registern, könnten aber auch verwendet werden, ohne daß es 
langsamer wird:
1
CBR r16, 1 << PB4
2
OUT PORTB, r16
3
SBR r16, 1 << PB4
4
OUT PORTB, r16
5
SBR r16, 1 << PB4
6
OUT PORTB, r16
7
CBR r16, 1 << PB4
8
OUT PORTB, r16
9
CBR r16, 1 << PB4
10
OUT PORTB, r16
11
CBR r16, 1 << PB4
12
OUT PORTB, r16
13
CBR r16, 1 << PB4
14
OUT PORTB, r16
15
SBR r16, 1 << PB4
16
OUT PORTB, r16
17
SBR r16, 1 << PB4
18
OUT PORTB, r16
19
SBR r16, 1 << PB4
20
OUT PORTB, r16

-> 2 Takte pro ausgegebenem Bit

Wer's richtig schnell will, berechnet die auszugebenden Bitmuster im 
Vorraus, und schreibt sie nur noch hintereinander raus:
1
OUT PORTB, r6
2
OUT PORTB, r7
3
OUT PORTB, r8
4
OUT PORTB, r9
5
OUT PORTB, r10
6
OUT PORTB, r11
7
OUT PORTB, r12
8
OUT PORTB, r13
9
OUT PORTB, r14
10
OUT PORTB, r15

-> 1 Takt pro ausgegebenem Bit

Mit freundlichen Grüßen - Martin

von Peter II (Gast)


Lesenswert?

Martin Schlüter schrieb:
> Hier liegst Du falsch, es sind 2 Takte. Hast es vermutlich mit SBR / CBR
> verwechselt,

http://www.atmel.com/images/doc0856.pdf
seite 123

Cycles : 2
Cycles XMEGA: 1
Cycles Reduced Core tinyAVR:1

naja, wir hatte beide recht.

von c-hater (Gast)


Lesenswert?

Peter II schrieb:

> sie machen einfach eine Sprungtabelle draus, habe ich schon beim GCC
> gesehen. Sind dann bloss 2 oder 4 takte.

Vier mag ich vielleicht noch glauben, zwei nimmer. Bis zum Beweis des 
Gegenteils würde ich das als offensichtliche Schutzbehauptung eines 
C-Gäubigen einstufen.

Bitte Code vorlegen, der diese Behauptung beweisen kann. Bitte mit 
exakter Angabe von Compilerversion und verwendeten Optimierungsoptionen. 
Womit wir gleich beim nächsten Problem wären, der 
C-Kompatibilitätslüge...

> sbi/cbi brauchen doch nur 1 Takt?

Nein, zwei. Ziehe bitte die Lektüre des AVR "instruction set reference 
manual" in Betracht, bevor du Aussagen über das Timing von Instruktionen 
triffst. Es ist ja wirklich nicht schwer, die Instruktion des Interesses 
im Inhaltsverzeichnis anzuklicken und dann bis ganz unten 
runterzuscrollen, wo Atmel schamhaft die wichtigsten Infos 
hinschreibt...

von Peter II (Gast)


Lesenswert?

c-hater schrieb:
>> sbi/cbi brauchen doch nur 1 Takt?
>
> Nein, zwei. Ziehe bitte die Lektüre des AVR "instruction set reference
> manual" in Betracht, bevor du Aussagen über das Timing von Instruktionen
> triffst. Es ist ja wirklich nicht schwer, die Instruktion des Interesses
> im Inhaltsverzeichnis anzuklicken und dann bis ganz unten
> runterzuscrollen, wo Atmel schamhaft die wichtigsten Infos
> hinschreibt...

habe ich gemacht und geschrieben, das wir beide recht haben!

von Martin S. (led_martin)


Lesenswert?

@Peter II (Gast):

Da muß ich Dir Recht geben. Hatte es selber nicht nachgesehen, mache am 
meisten mit den ATmegas, und da sind es halt 2 Takte. Beim Xmega ist da 
wohl Einiges gemacht worden, da gibt es recht viele Unterschiede, z.B. 
auch bei den SRAM-Zugriffen.

Mit freundlichen Grüßen - Martin

von Nomo (Gast)


Lesenswert?

c-hater schrieb:
> OMG. Switch mit 256 Zweigen. Selbst intelligente Compiler können das
> bestenfalls auf 8 Verzweigungen eindampfen (binärer Baum).

Sprungtabelle mit Funktionspointers ist eine andere Variante.

von c-hater (Gast)


Lesenswert?

Peter II schrieb:

> habe ich gemacht und geschrieben, das wir beide recht haben!

Ja, ich wiederum habe das geschrieben, bevor ich deine diesbezüglich 
Antwort gelesen hatte.->Typisches Problem asynchroner Kommunikation, 
kein böser Wille.

Übrigens: Ich wußte tatsächlich nicht, daß die XMegas die Sache in einem 
Takt abfackeln können, bei den Dingern stellt sich ja dank reichlich 
verfügbarer und potenter Hardware nur äußerst selten das Problem, etwas 
derartiges in Software abhandeln zu müssen, bei mir bisher halt noch 
nie.

Trotzdem eine schöne Sache, denn ich habe wirklich etwas neues in diesem 
Thread gelernt, was ich vielleicht irgendwann mal brauchen kann. Und sei 
es nur zur Herstellung der Kompatibilität für Stücken gebrauchter 
Software...

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

c-hater schrieb:
> Vier mag ich vielleicht noch glauben, zwei nimmer. Bis zum Beweis des
> Gegenteils würde ich das als offensichtliche Schutzbehauptung eines
> C-Gäubigen einstufen.

Gell, das einzig Gute an C ist der Inline-Assembler. ;-)
Ich geb zu, es gibt Momente, da seh ich das genauso wie du.

Anscheinend klappt es tatsächlich 1:1 mit dem CPU-Takt. Allerdings sind 
dann die Stoppbits jeweils mehr als 10 Takte lang, was natürlich die 
DÜ-Geschwindigkeit in den Keller zieht. Oder gibt es eine 
bessere/schnellere Lösung?
1
init:
2
clr r0
3
clr r1
4
inc r1  ; Annahme: Portpin 0 wird zum Senden verwendet
5
6
next:
7
... ; zu sendendes Byte in r30 erwartet
8
swap r30
9
mov r31,r30
10
andi r31,0x0f
11
ori r31,0x10  ; Startadresse der Ausgabeprozeduren (high)
12
andi r30,0xf0
13
IJMP
14
15
.org 0x1000
16
out PORTB,r0  ; Startbit
17
out PORTB,r0
18
out PORTB,r0
19
out PORTB,r0
20
out PORTB,r0
21
out PORTB,r0
22
out PORTB,r0
23
out PORTB,r0
24
out PORTB,r0
25
out PORTB,r1  ; Stopbit
26
jmp next
27
28
.org 0x1010
29
out PORTB,r0  ; Startbit
30
out PORTB,r1
31
out PORTB,r0
32
out PORTB,r0
33
out PORTB,r0
34
out PORTB,r0
35
out PORTB,r0
36
out PORTB,r0
37
out PORTB,r0
38
out PORTB,r1  ; Stopbit
39
jmp next
40
41
... usw ...

von Peter II (Gast)


Lesenswert?

Markus Weber schrieb:
> Gell, das einzig Gute an C ist der Inline-Assembler. ;-)

wobei die Frage ist warum nicht dafür C verwenden, habe jetzt nichts da 
zu testen was da rauskommt.
1
uint8_t r1 = 0;
2
uint8_t r2 = 1;
3
4
switch( data ) {
5
   case 0:
6
       PORTB = r1;
7
       PORTB = r0;
8
       PORTB = r0;
9
       PORTB = r0;
10
       PORTB = r0;
11
       PORTB = r0;
12
       PORTB = r0;
13
       PORTB = r0;
14
       PORTB = r0;
15
       PORTB = r1;
16
       break;
17
18
    //...
19
}

von Christian (dragony)


Lesenswert?

Besten Dank für die vielen umfangreichen Antworten. Das sind ca. 10 mal 
mehr Informationen, als ich erhofft habe. Damit kann ich mich jetzt sehr 
gut auseinandersetzen :)

von Peter II (Gast)


Lesenswert?

könnte das schneller sein?
1
next:
2
... ; zu sendendes Byte in r30 erwartet
3
IJMP;
4
jmp SEND1;
5
jmp SEND2;
6
jmp SEND3;
7
....
8
SEND1:
9
out PORTB,r0  ; Startbit
10
out PORTB,r0
11
out PORTB,r0
12
out PORTB,r0
13
out PORTB,r0
14
out PORTB,r0
15
...

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Peter II schrieb:
> könnte das schneller sein?

Ich glaube, du hast Recht! Quasi zweifach indirekt (indirekt mit 
anschließender Sprungtabelle) ist da wahrscheinlich schneller.

Macht dann eine Stoppbitlänge von mehr als 7 Takten. Immer noch nicht 
gut, aber deutlich besser.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Warum hoffe ich bei solchen Diskussionen immer, dass der Programmierer 
des Flugzeugs, in dem ich gerade sitze, etwas mehr Erfahrung hatte?

Jörg Wunsch hatte es mal kurz angesprochen, wie es Profis machen: sie 
lesen das Datenblatt ihres Controllers und nehmen eine 
Hardwarekomponente, die so eine Übertragung ohne Prozessorlast abhandeln 
kann...
Mit dem SPI Interface kann ich z.B. ganz problemlos 4MBit/s bei 8MHz 
Takt erreichen. Und habe nebenher Zeit, das nächste Byte vorzubereiten.

: Bearbeitet durch Moderator
von Peter II (Gast)


Lesenswert?

Lothar Miller schrieb:
> Warum hoffe ich bei solchen Diskussionen immer, dass der Programmierer
> des Flugzeugs, in dem ich gerade sitze, etwas mehr Erfahrung hatte?
>
> Jörg Wunsch hatte es mal kurz angesprochen, wie es Profis machen: sie
> lesen dad Datenblatt ihres Controllers und nehmen eine
> Hardwarekomponente, die so eine Übertragung ohne Prozessorlast abhandeln
> kann...
> Mit dem SPI Interface kann ich z.bB. ganz problemlos 4MBit/s bei 8MHz
> Takt erreichen. Und habe nebenher Zeit, das nächste Byte vorzubereiten.

wenn es danach geht, hätte wir für die ersten PC spiele auch 10Jahre 
länger warten müssen. Zum glück gab es damals schon "kreative" 
Entwickler die alles aus der Hardware geholt haben.

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


Lesenswert?

Peter II schrieb:
> wenn es danach geht, hätte wir für die ersten PC spiele auch 10Jahre
> länger warten müssen. Zum glück gab es damals schon "kreative"
> Entwickler die alles aus der Hardware geholt haben.

 Ich denke, da liegst du aber gewaltig daneben.
 Nicht die Geschwindigkeit war damals das Problem, sondern mangelnder
 RAM, sowohl bei den uC als auch bei der Videokarten.
 Ich glaube mein Vater hat sein Tandy damals 2000 DM bezahlt, die
 Umrüstung von 4KB(!) auf 16KB RAM hat so um die 500DM gekostet.

von Peter II (Gast)


Lesenswert?

Marc Vesely schrieb:
> Ich denke, da liegst du aber gewaltig daneben.
>  Nicht die Geschwindigkeit war damals das Problem, sondern mangelnder
>  RAM, sowohl bei den uC als auch bei der Videokarten.
>  Ich glaube mein Vater hat sein Tandy damals 2000 DM bezahlt, die
>  Umrüstung von 4KB(!) auf 16KB RAM hat so um die 500DM gekostet.

wenn ich das lesen, klingt es aber nicht nach ram:

http://de.wikipedia.org/wiki/Duff%E2%80%99s_Device

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


Lesenswert?

Peter II schrieb:
> wenn ich das lesen, klingt es aber nicht nach ram:
>
> http://de.wikipedia.org/wiki/Duff%E2%80%99s_Device

 ???
 Nix aus der Hardware rausgeholt, einen einfachen loop ausgerollt.
 Und funktioniert auch nur in C, normale Sprachen fallen nicht von
 einem Switch zum nächsten...

 Ihr seid alle so schnell wenns heisst c-hater anzuspucken, aber C
 ist ganz einfach ein Witz von einer Sprache.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter II schrieb:
> wenn ich das lesen, klingt es aber nicht nach ram:

Hat aber nichts mit normalem PC zu tun, sondern mit früher
Videobearbeitung.

https://de.wikipedia.org/wiki/Lucasfilm

Zurück zum Thema des Threads: ich bin gewiss ein großer Verfechter
davon, auch bei Controllern möglichst portable Software in einer
höheren Programmiersprache zu zimmern.  Aber wenn man wirklich auf den
Takt genau etwas machen will, und keinerlei Hardware dafür abstellen
kann (nichtmal einen Timer), dann bleibt in der Tat nur das händische
Auszählen der Takte in einem Stück Assemblercode – egal, ob man das
nun zum eigenen Zeitvertreib oder aus echter Notwendigkeit heraus
tun möchte.

von Peter II (Gast)


Lesenswert?

Marc Vesely schrieb:
> Nix aus der Hardware rausgeholt, einen einfachen loop ausgerollt.

damit konnte mehr Daten als sonst mit der gleichen CPU verarbeitet 
werden. Das verstehe ich schon unter, mehr als der gleichen Hardware 
rausgeholt.

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.