Lange Bitcodes speichern und ausgeben

Gast #2811593
Lesenswert?

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?
#2811604
Lesenswert?

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
#2811628
Lesenswert?

Schneller wird es nicht - aber 1MBit/s wird da auch nicht rauskommen und 
dein Controller ist während der Ausgabe blockiert.
1
unsigned char array[25];
2
register unsigned char buffer asm("r6");
3
register unsigned char counter asm("r7");
4

5
/*...*/
6
buffer = array[24];
7
/*...*/
8

9
ISR(TIMER1_COMPA_vect)
10
{
11
   unsigned char index = 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.
#2811700
Lesenswert?

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
#2811723
Lesenswert?

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.
Angehängte Dateien:
Gast #2811731
Lesenswert?

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.
(Firma: guloshop.de) #2811940
Lesenswert?

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. :-)
Gast #2811954
Lesenswert?

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
#2811996
Lesenswert?

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
#2812001
Lesenswert?

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
#2812006
Lesenswert?

>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?
Gast #2812106
Lesenswert?

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.
#2812118
Lesenswert?

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.
#2812626
Lesenswert?

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!
(Firma: guloshop.de) #2812640
Lesenswert?

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

1
    .def bits=r17  ; aktuell zu schreibendes Byte
2
    .def iobits=r18  ; Zustand von PORTB
3

4
    .macro write_bit ; Bit-Nummer
5
1     bst bits,@0
6
1     bld iobits,@0
7
1     out PORTB,bits
8
      .endmacro
9

10
    .macro nop_nop_nop
11
2     rjmp PC  ; (zweimal nop)
12
1     nop
13
      .endmacro
14

15
    ...
16

17
1     ldi XH,high(Anfangsadresse)
18
1     ldi XL,low(Anfangsadresse)
19
1     ldi YH,high(Enddresse)
20
1     ldi YL,low(Enddresse)
21
1     in iobits,PORTB
22

23
    loop:
24
3     write_bit 6
25
3       nop_nop_nop
26
3     write_bit 7
27
1       nop
28
2       ld bits,X+
29
    start:
30
3     write_bit 0
31
3       nop_nop_nop
32
3     write_bit 1
33
3       nop_nop_nop
34
3     write_bit 2
35
3       nop_nop_nop
36
3     write_bit 3
37
1       cp XL,YL
38
1       cpc XH,YH
39
1       breq ende
40
3     write_bit 4
41
3       nop_nop_nop
42
3     write_bit 5
43
1       nop
44
2       rjmp loop
45
    end:
#2812845
Lesenswert?

@ 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
(Firma: guloshop.de) #2812888
Lesenswert?

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!
#2812970
Lesenswert?

Wow!

Einige Fehler sind mir noch aufgefallen.

a) bits_prefetch darf nicht das gleiche Register sein wie /bits/:
1
    .def bits=r17  ; aktuell zu schreibendes Byte
2
    .def bits_prefetch=r18  ; im Voraus abgeholtes zu schreibendes Byte

b) die Ausgabe sollte wohl immer auf den gleichen Pin gehen. Dazu muss 
in iobits immer dasselbe Bit verändert werden:
1
      bld iobits,0  ; diese Bitnummer definiert den Pin und bleibt konstant!

c) für die Ausgabe muss das Register iobits in das PortB-Register 
geschrieben werden:
1
      out PORTB,iobits
(Firma: guloshop.de) #2812993
Lesenswert?

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
3
      .def iobits=r19  ; Zustand von PORTB
4

5
1     ldi XH,high(Anfangsadresse)
6
1     ldi XL,low(Anfangsadresse)
7
1     ldi YH,high(Enddresse)
8
1     ldi YL,low(Enddresse)
9
1     in iobits,PORTB
10
2     ld bits_prefetch,X+
11
2     rjmp start
12

13
    loop:
14
1     bld iobits,0
15
1     out PORTB,iobits
16
      ;
17
1     bst bits,6
18
1     bld iobits,0
19
1     bst bits,7  ; hierhin vorgezogen
20
1     out PORTB,iobits
21
      ;
22
2     ld bits_prefetch,X+
23
1     bld iobits,0
24
1     out PORTB,iobits
25
      ;
26
    start:
27
1     mov bits,bits_prefetch
28
1     bst bits,0
29
1     bld iobits,0
30
1     out PORTB,iobits
31
      ;
32
1     cp XL,YL  ; 1. Teil der Ende-Prüfung
33
1     bst bits,1
34
1     bld iobits,0
35
1     out PORTB,iobits
36
      ;
37
1     cpc XH,YH  ; 2. Teil der Ende-Prüfung
38
1     bst bits,2
39
1     bld iobits,0
40
1     out PORTB,iobits
41
      ;
42
1     breq ende  ; 3. Teil der Ende-Prüfung
43
1     bst bits,3
44
1     bld iobits,0
45
1     out PORTB,iobits
46
      ;
47
1     bst bits,4
48
1     bld iobits,0
49
1     bst bits,5  ; hierhin vorgezogen
50
1     out PORTB,iobits
51
      ;
52
2     rjmp loop
53
    end:
#2813067
Lesenswert?

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 :)
#2829571
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren