Forum: Projekte & Code Bytearray bitweise shiften


von Ralph S. (jjflash)


Angehängte Dateien:

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

von Mario M. (thelonging)


Lesenswert?

Je nach Einsatzzweck und Schieberegisterlänge kann es sinnvoller sein, 
mit Pointer(n) und Bitmaske(n) nur das aus-/eingehende Bit zu 
bearbeiten. Just my 2 ct.

von Ralph S. (jjflash)


Lesenswert?

Mario M. schrieb:
> Je nach Einsatzzweck und Schieberegisterlänge kann es sinnvoller sein,
> mit Pointer(n) und Bitmaske(n) nur das aus-/eingehende Bit zu
> bearbeiten. Just my 2 ct.

Na ja, ich brauchte aber die "verschobenen" Bits im Array und nicht nur 
die Carryflags

von Falk B. (falk)


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.

von Falk B. (falk)


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
}

von Ralph S. (jjflash)


Lesenswert?

Das werde ich glatt ausprobieren (und für solche Verbesserungsvorschläge 
- keine Ironie - liebe ich dieses Forum).

Vielen Dank Falk

von Bruno V. (bruno_v)


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.

von Mario M. (thelonging)


Lesenswert?

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

von Ob S. (Firma: 1984now) (observer)


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.

von Mario M. (thelonging)


Angehängte Dateien:

Lesenswert?

Ähm, es geht aber um AVR. Dessen "rotate"-Befehle sind reine 
Shift-Operationen.

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Mario M. schrieb:

> Ähm, es geht aber um AVR.

Nicht nur. Der gepostetete Code ist C mit dem ausdrücklich genannten 
Ziel der Portabilität (siehe z.B. im OT genannte, bereits evaluierte 
Testfälle).

Die explizit genannten haben alle ein Carryflag, aber es gibt eben auch 
andere Architekturen, die keins besitzen.

von Kurt (sommerwin)


Lesenswert?

Mit den Rotations Befehlen (links oder rechts) geht das ganz einfach

von Bruno V. (bruno_v)


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.

von Ralph S. (jjflash)


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.

von Bruno V. (bruno_v)


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)

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

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

... ist es

von 900ss (900ss)


Lesenswert?

Ralph S. schrieb:
> ... ist es

Ich denke nicht :)
Sein Vorschlag: denke dir du müsstest das aus dem Rom lesen und ins 
Schieberegister schreiben. Das Rom kann man schlecht schieben ;)

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.