Bytearray bitweise shiften

OP #7753796
Lesenswert?

Im Rahmen eines Projektes mit Schieberegistern (es werden definitiv mehr als 4 Schieberegister werden) benötigte ich eine bitweise Schiebefunktion für Arrays und ich habe doch tatsächlich keine Funktionen im Netz dafür gefunden (okay, soooo arg lange habe ich nicht gesucht).

Also selbst machen.

Das Ergebnis möchte ich hier posten für den Fall, dass das jemand ebenfalls gebrauchen kann. Der Einfachkeit halber habe ich das als Konsolenprogramm für den PC zuerst geschrieben, um die Funktionen zu testen. Das Ergebnis kann im Anhang gesehen werden, die Funktionen:

1
   uint8_t shiftarray_left(uint8_t anz, uint8_t carryin, uint8_t *parray);
2
   uint8_t shiftarray_right(uint8_t anz, uint8_t carryin, uint8_t *parray);

habe ich für AVR, STM32, STM8 und MCS51 getestet, sollten jedoch - weil reines C - auch mit jedem anderen in C programmierbaren Controller funktionieren.

Eine gute (Sonntags)nachtruhe,

JJ

Angehängte Dateien:
#7754034
Lesenswert?

Ralph S. schrieb:

habe ich für AVR, STM32, STM8 und MCS51 getestet, sollten jedoch - weil reines C - auch mit jedem anderen in C programmierbaren Controller funktionieren.

Naja, rein logisch mögen dein Funktionen korrekt sein, aber besonders auf kleinen uCs ohne Barrelshifter sind viele deiner Operationen eher "langsam".

1
cf= (*parray >> 7) & 0x01;

Sowas braucht 7 Schiebeoperationen auf dem AVR, je nach Compilerschlauheit auch noch ne Zählvariable für die Anzahl der Schiebungen. Man muss das Carrybit nicht zwingend in Bit 0 schieben und verarbeiten, das geht auch anders. Z.B. so.

1
uint8_t shiftarray_left(uint8_t cnt, uint8_t c_in, uint8_t *data) {
2
  uint8_t c_out, tmp;
3

4
  data += (cnt-1);
5
  for (; cnt > 0; cnt--) {
6
    tmp = *data;
7
    c_out= tmp & 0x80;
8
    tmp <<= 1;
9
    if (c_in) tmp |= 1;
10
    *data =  tmp;
11
    c_in = c_out;
12
    data--;
13
  }
14
  return c_out;
15
}

Es reicht, den logischen Wert zu speichern und auszuwerten. Das übersetzt sich in sehr kompaktes und schnelles ASM ohne sinnloses Rumschieben.

#7754043
Lesenswert?

Und so nach rechts. Alles das Gleiche, nur andere Bitmasken, siehe Bitmanipulation.

1
uint8_t shiftarray_right(uint8_t cnt, uint8_t c_in, uint8_t *data) {
2
  uint8_t c_out, tmp;
3

4
  for (; cnt > 0; cnt--) {
5
    tmp = *data;
6
    c_out = tmp & 0x01;
7
    tmp >>= 1;
8
    if (c_in) tmp |= 0x80;
9
    *data =  tmp;
10
    c_in = c_out;
11
    data++;
12
  }
13
  return c_out;
14
}
#7754208
Lesenswert?

Ralph S. schrieb:

Also selbst machen.

Hattest Du tatsächlich eine Anwendung, wo Du durch mehr als 32 Bit durchschieben solltest und die Größe/Gesamtmenge nicht von vornherein feststand? CRC für beliebige Datenströme wäre so ein Fall. Ist aber selten variabel und gleichzeitig >32 Bit.

Schieberegister klingt ehr nach festen Dimensionen, wo der Standardweg ein "Bitpointer" ist, also ein Bit-Set/Get mit Index++/-- ist.

(Firma: 1984now) #7754230
Lesenswert?

Mario M. schrieb:

Falk B. schrieb:

cf= (*parray >> 7) & 0x01;

Sowas braucht 7 Schiebeoperationen auf dem AVR, je nach Compilerschlauheit auch noch ne Zählvariable für die Anzahl der Schiebungen.

Also ist avr-gcc schlau, denn er macht für diesen Spezialfall folgenden Code daraus.

1
>     rol r24
2
>     clr r24
3
>     rol r24
4
>

Nicht wirklich. In Asm braucht man nur:

rol r24

und der Drops ist gelutscht. Da hat man halt Zugriff auf das echte Carryflag. Nunja, zumindest wenn die Architektur überhaupt eins besitzt.

#7754524
Lesenswert?

Ralph S. schrieb:

Für solche Verbesserungsvorschläge keine Ironie - liebe ich dieses Forum

Dann vielleicht ohne manuelle Carry-Bit-Auswertung wenn es 16-Bit-Register gibt?

1
uint8_t shiftarray_right(uint8_t cnt, uint8_t c_in, uint8_t *data) {
2
uint16_t s; 
3

4
  while(cnt-->0) {
5
    s = (c_in<<8)|*data;
6
    c_in = *data;
7
    *data++ = (uint8_t) (s>>1);
8
  }
9
  return c_in&1;
10
}

Anmerkungen: Das Schieben um 8 sollte von nahezu allen Compilern in direkten Byte-Zugriff umgesetzt werden.

Ein Erweitern auf uint16 vor dem Schieben ist nicht notwendig, (integral promotion)

Wenn der Prozessor 16-Bit-Register hat, sollte 16-Bit schieben nicht langsamer sein als 8bit. Für reine 8-Bit-Prozessoren kann ein Bitdangeling schneller sein.

sollte für links ähnlich kurz gehen.

OP #7754536
Lesenswert?

Bruno V. schrieb:

attest Du tatsächlich eine Anwendung, wo Du durch mehr als 32 Bit durchschieben solltest und die Größe/Gesamtmenge nicht von vornherein feststand? CRC für beliebige Datenströme wäre so ein Fall. Ist aber selten variabel und gleichzeitig >32 Bit.

Momentan geht es um eine - für meine Begriffe - sehr sehr große Modelleisenbahnanlage, gemischt aus alten und neuen Teilen eines Bekannten. Das ist, für mich der Wahnsinn in Tüten, denn er hat 167 verschiedene Lokomotiven (Sammler halt), die zum Teil sogar älter sind als ich (das Modell, das Original sowieso) und baut diese Lokomotiven auf Digitalsteuerung um, die Weichen (momentan sind es 72) jedoch nicht.

Die Anzahl der Weichen kann noch wachsen. Für dieses Ansteuern werden Schieberegister verwendet (hier braucht es noch kein bitweises Schieben über ein Array).

Er baut jedoch auch einen kleinen Rummelplatz auf (natürlich nicht so groß und schön wie im Miniaturwunderland), deren Beleuchtung und Lichtspiele mit SMD-Leds realisiert und mittels Schieberegistern angesteuert werden sollen. Die Lauflichter dieser Lichtspiele werden geshiftet und es steht noch nicht fest, wieviele es für einzelne Modelle es werden. Das bisher größte Modell hat 96 LED's. Mindestens hierfür bedarf es des Schiebens über ein Array.

#7754728
Lesenswert?

Ralph S. schrieb:

Die Lauflichter dieser Lichtspiele werden geshiftet und es steht noch nicht fest, wieviele es für einzelne Modelle es werden. Das bisher größte Modell hat 96 LED's. Mindestens hierfür bedarf es des Schiebens über ein Array.

Mag sein, dass es ein Missverständnis ist: ich sehe noch nicht, warum man über das Array schieben sollte.

Eine typische Implementierung: Ich habe 12 Bytes, in die ich die neuen Muster schreibe und die ich regelmäßig (x Mal pro Sekunde) alle en Block raushaue. Oder ich takte ein Muster zyklisch durch die Kette, jeweils eine LED weiter.

In beiden Fällen shiftet man nicht durchs Array. Stell Dir einfach die Frage, ob Du es auch bei 1000 Bytes machen würdest. Oder ob es auch auf festen Mustern (aka ROM) funktionieren sollte.

Beispiel

1
uint8_t LEDs[12];
2
// "on == 0": LED aus. "on != 0": an
3
extern void shift_LED(uint8_t on);
4

5
void rauspusten(uint8_t *LEDs, int n)
6
{
7
    while(n-->0)
8
    {
9
    uint8_t c = *LEDs++;
10
 
11
        // ausgerollt, weil alles andere den Aufwand hier nicht lohnt
12
        shift_next(c|0x80);
13
        shift_next(c|0x40);
14
        shift_next(c|0x20);
15
        shift_next(c|0x10);
16
        shift_next(c|0x08);
17
        shift_next(c|0x04);
18
        shift_next(c|0x02);
19
        shift_next(c|0x01);
20
    }
21
}
22

23
/* eine beliebige LED setzen/löschen */
24
void SetLED(uint8_t *LEDs, uint8_t idx)
25
{
26
     LEDs[idx/8]| |= 1<<(idx%8);      
27
}
28
void ClrLED(uint8_t *LEDs, uint8_t idx)
29
{
30
     LEDs[idx/8] &= ~(1<<(idx%8));      
31
}

(wenn man sizeof übergibt, kann man auch die Grenzen abfragen. Oder man arbeitet auf festen Arrays)

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