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
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.
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
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.
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 | }
|
Das werde ich glatt ausprobieren (und für solche Verbesserungsvorschläge
- keine Ironie - liebe ich dieses Forum).
Vielen Dank Falk
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.
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
|
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.
Ähm, es geht aber um AVR. Dessen "rotate"-Befehle sind reine
Shift-Operationen.
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.
Mit den Rotations Befehlen (links oder rechts) geht das ganz einfach
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.
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.
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)
Bruno V. schrieb:
> Mag sein, dass es ein Missverständnis ist: ich sehe noch nicht, warum
> man über das Array schieben sollte.
... ist es
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.
|