Hallo,
ich habe da ein kleines Problem. Ich würde gern in meinem mega32 einen
ca. 2000 bit langen Array per uart einlesen und dann ausgeben. Es
handelt sich nur um Bits. Welchen Datentyp nimmt man da? Ein Bitfeld?
Kann das so groß sein?
Jetzt kommt aber die eigentliche Frage: der Array soll an einem Pin dann
ausgegeben werden. Dabei müssen aber alle Bits gleich lang sein.
Zielbitrate ist ca 1 Mb pro Sekunde. Also ca 16 Takte.
Wie würdet ihr das angehen?
Hatte ein ganz ähnliches Problem. Ich habe einen Struct angelegt mit
einem Array von uint64_t Werten. Dann einen Timer-CTC-Interrupt welcher
im gewünschten Interval auslöst (bei mir nur alle 10ms). Dort wird eine
Variable (die Bitposition) hochgezählt. Die Bitposition DIV 64 ergibt
den jeweilige Arrayindex, und die Bitposition MOD 64 ergibt das Bit
innerhalb der 64 Bit Variable. Die Frage bei dir ist halt ob diese
Methode für dich schnell genug ist. 1 Mbit/s finde ich sehr sportlich.
Aber du kannst optimieren. Wenn du meine ganzen Constraints nicht hast,
dann kannst du bestimmt viel schneller mit einem Zeiger über den RAM
fahren.
gruß cyblord
Schneller wird es nicht - aber 1MBit/s wird da auch nicht rauskommen und
dein Controller ist während der Ausgabe blockiert.
1
unsignedchararray[25];
2
registerunsignedcharbufferasm("r6");
3
registerunsignedcharcounterasm("r7");
4
5
/*...*/
6
buffer=array[24];
7
/*...*/
8
9
ISR(TIMER1_COMPA_vect)
10
{
11
unsignedcharindex=24;
12
/* clear flag */
13
TIFR=(1<<OCF1A);
14
cli();
15
while(1)
16
{
17
if(TIFR&(1<<OCF1A))
18
{
19
/* clear flag */
20
TIFR=(1<<OCF1A);
21
/* ausgabe an D0. Rest von PORTD unbelegt oder Eingang */
22
PORTD=(buffer&1)?1:0;
23
buffer>>=1;
24
counter<<=1;
25
if(!counter){
26
buffer=array[--index];
27
counter=1;
28
if(index==~0)
29
{
30
sei();
31
return;
32
}
33
}
34
}
35
}
36
}
Wer stellt solche hirnrissigen Aufgaben an den falschen Controller?
Was ist das für eine merkwürde Bit-Kette, die asynchron mit ungefähr
1MBit/s rauskommen soll?
Mir schwant, du willst eigentlich etwas völlig anderes...
Netiquette: Klare Beschreibung des Problems.
Silvan König schrieb:> if( array[c]&(1<<c2) )> ...> Ich vermute allerdings, dass das kein Mb/s wird :-(
Als erstes wird wegen der variablen Shiftoperation kein konstanter
Ausgabetakt herauskommen. Den Zirkus würde man sich mit SPI sparen.
Werner schrieb:> Als erstes wird wegen der variablen Shiftoperation kein konstanter> Ausgabetakt herauskommen. Den Zirkus würde man sich mit SPI sparen.
Bei 1MHz Busrate sind die Register kaum gerettet, ist die Bitzeit schon
um - keine Chance, da einen Interrupt-Sprung unterzubringen.
cyblord ---- schrieb:> Ich habe einen Struct angelegt mit> einem Array von uint64_t Werten.
Wenn Du möglichst viel Flash und CPU-Zeit verheizen willst, eine gute
Idee.
Soll es aber effizient sein, ist ein Byte-Array am sinnvollsten.
Um nun das Bit (0..7) auszugeben, schiebt man einmal das Byte bei jeder
Ausgabe. Und nach 8 Bits wird der Byte-Index hochgezählt.
16 Zyklen dürfte allerdings auch in Assembler zu knapp sein.
Peter
Peter Dannegger schrieb:> 16 Zyklen dürfte allerdings auch in Assembler zu knapp sein.
Ja. Meine Routine von oben braucht 20 bis zur ersten Verzweigung, 30
wenn das nächste Byte geladen wird. Weit weg von 1MBit/s bei 16MHz...
Trotzdem:
> Mir schwant, du willst eigentlich etwas völlig anderes...> Netiquette: Klare Beschreibung des Problems.
Es geht dabei um einen Laser-Exposér, wie er zur Zeit im
Platinenunterforum diskutiert wird. Der Controller soll eine Zeile mit
schwarz-weiß-Werten laden, und diese dann auf die Platine brutzeln Da
müssen natürlich die Bitabstände gleich sein. Die Zeit, die pro Bit
bleibt ist leider sehr begrenzt, da ja ein schnell drehender
Polygonspiegel verwendet wird.
das Logging der IRMP hatte ich auch als Bitarray imlementiert.
wenn sich dort zuviel geäändert hat, der kann sich die IRMP aus der
Wordclock 0.10 oder 0.11 anschauen.
wunder schrieb:> Es geht dabei um einen Laser-Exposér, wie er zur Zeit im> Platinenunterforum diskutiert wird.
Aber nicht ohne Grund haben Laserdrucker keine 8-bitter verbaut. Wenn du
keinen schnelleren Prozessor nehmen willst, dann dreh das Spiegelrad und
den Vorschub doch langsamer.
Vlad Tepesch schrieb:> das Logging der IRMP hatte ich auch als Bitarray imlementiert.> wenn sich dort zuviel geäändert hat, der kann sich die IRMP aus der> Wordclock 0.10 oder 0.11 anschauen.
Bis auf ein paar kosmetische Änderungen ist das Logging noch das
Gleiche. Hier werden jeweils 8 Bit in einem Byte abgelegt.
wunder schrieb:> Hallo,> ich habe da ein kleines Problem. Ich würde gern in meinem mega32 einen> ca. 2000 bit langen Array per uart einlesen und dann ausgeben. Es> handelt sich nur um Bits. Welchen Datentyp nimmt man da? Ein Bitfeld?> Kann das so groß sein?> Jetzt kommt aber die eigentliche Frage: der Array soll an einem Pin dann> ausgegeben werden. Dabei müssen aber alle Bits gleich lang sein.> Zielbitrate ist ca 1 Mb pro Sekunde. Also ca 16 Takte.> Wie würdet ihr das angehen?
Hallo miteinander,
habe ich die Problemstellung falsch verstanden?
Eine vorher ins SRAM geschriebene Bitfolge über einen Pin auszugeben,
schafft sogar ein kleiner ATtiny85, und zwar mit bis zu 3 Mbit/s.
Der etwas betagtere ATmega32 schafft immer noch 2 Mbit/s. Wenns etwas
schneller sein soll, dann würde ich den neueren ATmega324A verwenden.
Ich glaub, ich bin im falschen Film. :-)
Hi
>Wenns etwas>schneller sein soll, dann würde ich den neueren ATmega324A verwenden.
Was heisst 'etwas' schneller? Der ATMega324 hat zwei USARTs, die im SPI
Mode bis zum halben Controllertakt laufen können. Also bis zu 10MHz.
Durch den Sendepuffer sollte der Bitstrom auch schön gleichmäßig werden.
MfG Spess
spess53 schrieb:> Durch den Sendepuffer sollte der Bitstrom auch schön gleichmäßig werden.
Stimmt, mit der UART als SPI geht das auch in C bequem. Man hat dann pro
Byte 128 Zyklen Zeit zum Nachladen.
Beim einfachen SPI lassen sich dagegen Lücken nicht vermeiden.
Ich würde die alten Mega8/16/32 eh nicht mehr für neue Projekte
verwenden.
Peter
3Mbit ist schon recht optimistisch.
dh 6,6Takte pro bit
ROR 1 // ins carry schieben
BRCC 2 // branch if carry set
OUT 1 // output setzen
*8
LD 2 // dann das nächste Byte laden
IJMP 2 // an den anfang springen
die eigentlich ausgabe braucht zwar nur 4 Takte, aber das Nachladen und
springen braucht nochmal 4, da ja die Bits alle gleich lang sein sollen
wirds schwierig.
Wenn man vielfache von 2 Byte ausgibt könnte man das ganze duplizieren.
R1 als ARbeitsregister
ROR, BRCC, OUT, NOP, NOP 7mal
ROR, BRCC, OUT, LD R2
R2 als Arbeitsregister
ROR, BRCC, OUT, NOP, NOP 6mal
ROR, BRCC, OUT, LD R1
ROR, BRCC, OUT, jump start
hier ist natürlich das Problem, dass die Abbruchbedingung nicht getestet
wird. Dies würde aber auch 2 aufeinanderfolgende Operationen benötigen,
die aber nicht in das enge Raster passen.
Theoretisch hat man zwar noch .6Takte pro bit, aber dann hat man einen
relativ großen Jitter auf dem Ausgabestrom, wenn man sich nach jedem 2.
Bit einen zusätzlichen Takt genehmigt.
cleverer Ansatz wäre, die SPI das machen zu lassen, die schafft dann
auch 10Mbit
>Eine vorher ins SRAM geschriebene Bitfolge über einen Pin auszugeben,>schafft sogar ein kleiner ATtiny85, und zwar mit bis zu 3 Mbit/s.
Bist Du Dir da sicher? Das Problem dürfte doch die geforderte
Gleichmäßigkeit sein. Spätestens wenn der erste Counter 255 erreicht,
dürfte es eng werden mit gleichem Bittakt. Oder wie meinst Du das?
Warum muss das ein kleiner 8-bit Prozessor sein? Für etwas, das
taktgenau ausgegeben werden muss, würde ich ein FPGA nehmen. Auch ein
Uart zum Empfangen ist bei diesem leicht zu erstellen. Und dann sollte
es keine Probleme geben, dass Prozessor noch nicht bereit ist, weil er
irgendwo anders im Interrupt hängt.
Also FPGA würde wohl schon was ganz kleines reichen.
wunder schrieb:> Es geht dabei um einen Laser-Exposér, wie er zur Zeit im> Platinenunterforum diskutiert wird.
Dann ist das Konzept aber Käse.
Geschickter ist, den PC rechnen zu lassen und dem AVR nur die Zeit zum
nächsten Compare-Match vom Timer zu übermitteln. Oder von mir aus noch
eine Liste von Compare-Werten.
Den Compare-Match stellst du dann noch auf Toggle Pin und schon ist das
fertig.
Hmm, das funktioniert aber nur, wenn man nicht zu viele s/w Übergänge
auf der Platine hat, da die ja alle als 14 bit Werte gespeichert werden
müssen. Die Idee oben mit den festen Pixeln hätte durchaus Vorteile. Man
könnte tatsächlich jedes Pixel abwechselnd S/W machen, ohne dass man
irgendwo ein Speicher-Problem bekommt, wenn denn der Speicher zumindest
für eine Zeile reicht! Man braucht ca. 0.1 mm Pitch, also 1600 Pixel
oder 200 Byte für eine Eurokarte. Geht man an die Auflösungsgrenze des
Resists, braucht man 3200-6400 bit, also 400-800 Byte. Das geht mit nem
Atmega.
Schon bei 0.1 mm Pitch, kann man aber theoretisch 500*2 Toggles haben,
bei 2 byte pro Toggle sind das 2 kB, bei 0.025 mm schon 8 kB. D.h. man
könnte den Controller schon bei minimaler Auflösung crashen und alles
hängt vom Leiterplattendesign ab. Kann man machen, hört sich für mich
aber auch nicht solider an, als die Idee des TO hier, Pixel auszugeben!
Peter Dannegger schrieb:> cyblord ---- schrieb:>> Ich habe einen Struct angelegt mit>> einem Array von uint64_t Werten.>> Wenn Du möglichst viel Flash und CPU-Zeit verheizen willst, eine gute> Idee.
Nun bei mir gehts nicht um Effizienz in der Ausführung sondern ich will
so wenig wie möglich Array-Elemente haben.
gruß cyblord
Hallo nochmal,
nachdem für mache (für mich auch) kaum zu glauben war, dass man ohne
Tricks und ohne USI, UART usw. (sogar mit einem ATtiny13A) Bitfolgen mit
mehr als 3 Mbit/s rausschieben kann, hab ich auf die Schnelle ein
Programm zusammengetippt. Sind wahrscheinlich noch ein oder zwei Fehler
drin, aber das Prinzip wird sicher deutlich.
Die Zahlen ganz links sind die Ausführungszeiten der jeweiligen Befehle
(bei "breq" nur für den Fall, dass nicht gesprungen wird).
cyblord ---- schrieb:> Nun bei mir gehts nicht um Effizienz in der Ausführung sondern ich will> so wenig wie möglich Array-Elemente haben.
Wozu das denn?
Der Speicherplatz ist doch genau gleich groß.
Nur die Ausführung legt nen Affenzahn zu und der Code wird erheblich
kleiner.
Peter
@ Markus W.
Das BST,BLD ändert ja kein anderes Flag. Du kannst also das CP,CPC über
2 Bits splitten, ergibt dann 5 Zyklen je Bit.
Ich würde aber trotzdem eine UART als SPI nehmen und in C programmieren.
Dann kann man gleichzeitig mit der 2.UART schon die neuen Daten
empfangen.
Peter
Peter Dannegger schrieb:> Ich würde aber trotzdem eine UART als SPI nehmen und in C programmieren.> Dann kann man gleichzeitig mit der 2.UART schon die neuen Daten> empfangen.
Und wenn man dann noch eine Maschine mit DMA hat muss die Software kein
einziges Bit "in die Hand" nehmen.
Peter Dannegger schrieb:> Ich würde aber trotzdem eine UART als SPI nehmen und in C programmieren.> Dann kann man gleichzeitig mit der 2.UART schon die neuen Daten> empfangen.
Wenn man schon einen anderen Controller nimmt, dann doch lieber gleich
einen ATXMega mit DMA - macht die Sache nicht nur wesentlich einfacher,
sondern auch garantiert noch schneller.
Peter Dannegger schrieb:> @ Markus W.>> Das BST,BLD ändert ja kein anderes Flag. Du kannst also das CP,CPC über> 2 Bits splitten, ergibt dann 5 Zyklen je Bit.
Sehr gute Idee! Wenn man die Befehle dann noch ein bisschen besser
verteilt, kommmt man sogar auf 4 Zyklen. Jedenfalls, falls ich mich
jetzt nicht vertan habe.
Die Ausgabe läuft dann sogar mit 5 Mbit/s!
1
.def bits=r17 ; aktuell zu schreibendes Byte
2
.def bits_prefetch=r17 ; im Voraus abgeholtes zu schreibendes Byte
3
.def iobits=r19 ; Zustand von PORTB
4
5
...
6
7
1 ldi XH,high(Anfangsadresse)
8
1 ldi XL,low(Anfangsadresse)
9
1 ldi YH,high(Enddresse)
10
1 ldi YL,low(Enddresse)
11
1 in iobits,PORTB
12
2 ld bits_prefetch,X+
13
2 rjmp start
14
15
loop:
16
1 bld iobits,5
17
1 out PORTB,bits
18
;
19
1 bst bits,6
20
1 bld iobits,6
21
1 bst bits,7 ; hierhin vorgezogen
22
1 out PORTB,bits
23
;
24
2 ld bits_prefetch,X+
25
1 bld iobits,7
26
1 out PORTB,bits
27
;
28
start:
29
1 mov bits,bits_prefetch
30
1 bst bits,0
31
1 bld iobits,0
32
1 out PORTB,bits
33
;
34
1 cp XL,YL ; 1. Teil der Ende-Prüfung
35
1 bst bits,1
36
1 bld iobits,1
37
1 out PORTB,bits
38
;
39
1 cpc XH,YH ; 2. Teil der Ende-Prüfung
40
1 bst bits,2
41
1 bld iobits,2
42
1 out PORTB,bits
43
;
44
1 breq ende ; 3. Teil der Ende-Prüfung
45
1 bst bits,3
46
1 bld iobits,3
47
1 out PORTB,bits
48
;
49
1 bst bits,4
50
1 bld iobits,4
51
1 bst bits,5 ; hierhin vorgezogen
52
1 out PORTB,bits
53
;
54
2 rjmp loop
55
end:
> Ich würde aber trotzdem eine UART als SPI nehmen und in C programmieren.> Dann kann man gleichzeitig mit der 2.UART schon die neuen Daten> empfangen.
Aber sicher! Ich wollte damit nur zeigen, dass sich ein derartiges
Problem auch mit einem popeligen 8-Bit-ATtiny13A lösen lässt – wenn man
denn will. So Sonderfälle gibt es natürlich, wenn der Platz nicht reicht
oder man auf den letzten Cent schauen muss, weil die Schaltung mit einer
Auflage von 20.000 Stück gefertigt werden soll.
Komfortabler ist die Lösung mit UART aber auf alle Fälle!
Habe mir überlegt, ob das auch mit einem FT2232H geht. Schnell genug ist
der allemal. Ein Test zeigt aber, dass der Takt nach jedem Byte ein
kleines Päuschen macht. Sowas aber auch!
Thomas K. schrieb:> Einige Fehler sind mir noch aufgefallen.
Oha, da war ich vorhin doch wieder zu schnell und hab beim Generalumbau
einiges übersehen. Danke!
Der Ordnung halber hier die korrigierte Version:
1
.def bits=r17 ; aktuell zu schreibendes Byte
2
.def bits_prefetch=r18 ; im Voraus abgeholtes zu schreibendes Byte
wunder schrieb:> Zielbitrate ist ca 1 Mb pro Sekunde. Also ca 16 Takte.> Wie würdet ihr das angehen?
Mit einem externen seriellen SRAM. Den kann man beliebig langsam mit
Werten befüllen, zum Rauspusten muss man nur einmal die Startadresse
geben, den Output anmachen und den Clock toggeln.
mfg mf
PS: Der hier...
http://dangerousprototypes.com/2011/04/06/new-prototype-logic-shrimp-logic-analyzer/
...hat mich inspiriert. 256K * 1Bit ist doch schon gut Holz :)
Mini Float schrieb:> zum Rauspusten muss man nur einmal die Startadresse> geben, den Output anmachen und den Clock toggeln.
Dann brauchst Du trotzdem einen MC, der den Clock erzeugt und auf das
Ende testet. Du hast also garnichts gewonnen, aber ein zusätzliches
Bauteil benötigt.
Peter
Peter Dannegger schrieb:> Dann brauchst Du trotzdem einen MC, der den Clock erzeugt und auf das> Ende testet. Du hast also garnichts gewonnen, aber ein zusätzliches> Bauteil benötigt.
Stimmt... da hilft nur eins - mehr Bauteile! Nur ein Zähler mehr und
schon hat man das Problem wieder "behoben" =)
Peter Dannegger schrieb:> Mini Float schrieb:>> zum Rauspusten muss man nur einmal die Startadresse>> geben, den Output anmachen und den Clock toggeln.>> Dann brauchst Du trotzdem einen MC, der den Clock erzeugt und auf das> Ende testet. Du hast also garnichts gewonnen, aber ein zusätzliches> Bauteil benötigt.
Auch wenn es etwas spät ist:
Kann man da bei nicht nen Timer zu Hilfe nehmen? Damit sollte man doch
eine abgezählte Reihe Impulse raushauen können und somit auch an die
native Geschwindigkeit des Prozessors rankommen. Vielleicht muss ich den
Clock noch anständig gaten. Aktives Abzählen der Impulse brauch ich
allerdings nicht mehr wirklich.
mf
Mini Float schrieb:> Kann man da bei nicht nen Timer zu Hilfe nehmen?
Ja. Hab ich direkt über deinem Post schon geschrieben. Ob der Zähler
jetzt extern oder intern ist, ist egal.