Forum: Compiler & IDEs Auf Bit array zugreifen


von Peter Z. (Gast)


Lesenswert?

Hallo,
Mein µC ist ein ATtiny85
Ich benutze AVRSTudio 6
Ich benötige für einen Ringbuffer in C ein 1024 Bit langes Bitarray.
ist das so:
1
char buffer[128];
2
unsigned short int my_ptr;
3
4
if(buffer[my_ptr >> 3] & (1 << (my_ptr & 0x07)))  //Abfrage
5
6
buffer[my_ptr >> 3] |=  (1 << (my_ptr & 0x07));   //Setzen
7
8
buffer[my_ptr >> 3] &= ~(1 << (my_ptr & 0x07));   //Löschen
9
10
my_ptr = (my_ptr + 1) & 0x03FF;                   //my_pointer++
Die Abfrage, Setzen, Löschen müssen sehr schnell sein.
Kann man das besser schreiben?
Das Thema wurde bestimmt schon öfters behandelt,
leider konnte ich auf die Schnelle nix geeignetes finden...

von Lutz H. (luhe)


Lesenswert?

wenn es schnell sein soll,
Jedes Bit als  ein Byte oder 2 Byte oder 4 Byte speichern,
dann wird der Speicherbedarf zwar etwas größer,
aber das Testen der einzelnen Bits fällt weg.

von Karl H. (kbuchegg)


Lesenswert?

Erst mal machst du aus dem char einen unsigned char, oder gleiche einen 
uint8_t

Den Datentyp char reservierst du ausschliesslich für Dinge, die mit 
Textverarbeitung zu tun haben und sonst nichts anderes. In allen anderen 
Fällen bist du explizit darüber, ob du einen char mit Vorzeichen oder 
einen char ohne Vorzeichen haben willst und überlässt es nicht dem 
Compiler, was er sich bei einem char so denkt.

Zum anderen willst du diese Operation
1
    1 << x
mit einem variablen x auf einem AVR nicht haben, weil das eine teure 
Operation ist, da der Compiler das in eine Schleife auflösen muss. Da 
bist du mit einem 8 Stück-Array von vordefinierten Konstanten noch 
besser bedient
1
uint8_t BitMasksSet[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
2
uint8_t BitMasksClr[] = { ~0x01, ~0x02, ~0x04, ~0x08, ~0x10, ~0x20, ~0x40, ~0x80 };
3
4
   buffer[my_ptr >> 3] & BitMasksSet[ my_ptr & 0x07 ]
5
...

von B. S. (bestucki)


Lesenswert?

Peter Zz schrieb:
> Die Abfrage, Setzen, Löschen müssen sehr schnell sein.
> Kann man das besser schreiben?

Evt. kannst du dir die Schiebereien sparen, wenn du eine Lookup Tabelle 
verwendest. Den "Pointer" könntest du in eine Struktur packen und dort 
Byte und Bit unterscheiden. Z.B. sowas (ungetestet):
1
uint8_t buffer[128];
2
3
struct my_ptr_t{
4
  uint8_t Byte;
5
  uint8_t Bit;
6
  uint8_t Mask[8];
7
}my_ptr={0,0,{0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}};
8
9
if(buffer[my_ptr.Byte] & (my_ptr.Mask[my_ptr.Bit])  //Abfrage
10
11
buffer[my_ptr.Byte] |=  (my_ptr.Mask[my_ptr.Bit]);   //Setzen
12
13
buffer[my_ptr.Byte] &= ~(my_ptr.Mask[my_ptr.Bit]);   //Löschen
14
15
my_ptr.Bit=(my_ptr.Bit+1)&0x07;                      //my_pointer++
16
if(!my_ptr.Bit){
17
  my_ptr.Byte=(my_ptr.Byte+1)&0x7F;
18
}

Ob das wirklich schneller ist, musst du aber selber testen.

von Falk B. (falk)


Lesenswert?

@ Peter Zz (bastelboy)

>Ich benötige für einen Ringbuffer in C ein 1024 Bit langes Bitarray.

>Die Abfrage, Setzen, Löschen müssen sehr schnell sein.

Du hast mal sicher NICHT 1024 einzelne Statusbist, sondern eher ein 
Array mti Daten für LEDs etc., die schnell per Soft-SPI irgendwie 
ausgegeben werden sollen? ist das so?

Dafür gibt es schnelle Soft-SPI Routinen. Ansonsten, siehe 
Bitmanipulation bzw.

Beitrag "Re: Bits aus einem unsigned char array extrahieren"

Beitrag "Re: AtMega 48 Timmer"

von Peter Z. (Gast)


Lesenswert?

Falk Brunner schrieb:
> Du hast mal sicher NICHT 1024 einzelne Statusbist, sondern eher ein
> Array mti Daten für LEDs etc., die schnell per Soft-SPI irgendwie
> ausgegeben werden sollen? ist das so?

Ne, ich will einen Netzsynchronen 50Hz Rechteck definiert verzögern.
Die Verzögerungszeit soll 0 bis 51,15ms betragen.
Das sind 1024 mögliche Werte die mit einem 10-Gang-Poti eingestellt 
werden.
Nein, es soll kein Dimmer werden.

von chris (Gast)


Lesenswert?

Du sprichst von Ringbuffer.
Anstelle der Bitschieberei, solltest du zwei Byte Variablen fuer den 
Index haben, also sowas

#define idx_inc if(!(idx_l<<=1)) idx_h++,idx_l++

und dann einfach idx_h im bytearray nehmen und idx_l als and/or maske.

von Christoph W. (wasle)


Lesenswert?

Wenn ich das richtig verstehe willst du mit dem so eine art 
schiberegister (als ringpuffer) bauen und indem du die länge veränderst 
eine verzögerung einbringen.

Des weiteren hört es sich so an als währen im puffer maximal ~2 
netzperioden also 4/5 flanken enthalten.

Falls das so ist würde ich den puffer sozusagen kompimieren und nur 
flanken speichern.
Also zb. einen ringpuffer von
1
struct buffElem
2
{
3
uint8_t value:1;
4
uint16_t count:15;
5
}

Beim insert wird dann falls value gleich bleibt nur count erhöht.
Beim entnehmen solange wird dekrementiert bis count 0  ist und dann der 
eintrag gelöscht.

Ist sicher nicht die schnellste version. Braucht aber vermutlich weniger 
speicher.

von Karl H. (kbuchegg)


Lesenswert?

Peter Zz schrieb:
> Falk Brunner schrieb:
>> Du hast mal sicher NICHT 1024 einzelne Statusbist, sondern eher ein
>> Array mti Daten für LEDs etc., die schnell per Soft-SPI irgendwie
>> ausgegeben werden sollen? ist das so?
>
> Ne, ich will einen Netzsynchronen 50Hz Rechteck definiert verzögern.
> Die Verzögerungszeit soll 0 bis 51,15ms betragen.
> Das sind 1024 mögliche Werte die mit einem 10-Gang-Poti eingestellt
> werden.
> Nein, es soll kein Dimmer werden.

d.h. es wird squentiell eine 0 oder eine 1 in einem bestimmten Takt 
eingeschrieben.

Dann braucht es aber doch die allgemeinen Zugriffsfunktionen gar nicht. 
Alles was du willst ist doch nichts anderes als das nächste 'Bit' auf 
entweder 0 oder 1 zu setzen. Das lässt sich aber recht einfach erreichen
1
uint8_t writeIndex = 0x00;
2
uint8_t  bitMask = 0x01;
3
uint8_t buffer[128];
4
5
void IncrementToNextBit()
6
{
7
  bitMask <<= 1;
8
  if( bitMask == 0 )
9
  {
10
    bitMask = 0x01;
11
    writeIndex++;
12
    if( writeIndex == 128 )
13
      writeIndex = 0;
14
  }
15
}
16
17
void writeZero()
18
{
19
  buffer[writeIndex] &= ~bitMask;
20
21
  incrementToNextBit();
22
}
23
24
void writeOne()
25
{
26
  buffer[writeIndex] |= bitMask;
27
28
  incrementToNextBit();
29
}

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Peter Zz (bastelboy)

>Ne, ich will einen Netzsynchronen 50Hz Rechteck definiert verzögern.
>Die Verzögerungszeit soll 0 bis 51,15ms betragen.
>Das sind 1024 mögliche Werte die mit einem 10-Gang-Poti eingestellt
>werden.

Das mit den 1024 Bit als Signalspeicher ist eine Möglichkeit, das zu 
machen. Man kriegt das auch recht schnell und effizient hin.
Wenn du bis zu 51,15 ms verzögern willst, sind das bei dir 50us / 20 kHz 
Zeitauflösung. In der Zeit kann ein uC fast beliebig schlecht ein Bit 
aus einem Array lesen, das ist eine kleine Ewigkeit. Wenn man es clever 
macht, sind die Funktionen aber sehr einfach und schnell, wie von den 
anderen Schreibern angedeutet.

Für die Einstellung würde ich heute kein 10 Gang Poti mehr nehmen, 
sondern einen Drehgeber. Der ist billiger und kann die Einstellung 
mit digitaler Genauigkeit und Reproduzierbarkeit eingeben. Ein Poti hat 
immer das Problem, dass der Messwert am ADC geringfügig wackelt.

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.