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

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.