Forum: Mikrocontroller und Digitale Elektronik 24 Einzelne Bits zu 3 Byte zusammen fügen in c++?


von Markus S. (markussch)


Lesenswert?

Hallo,

ich benutze einen 32Bit MC und habe 24 I/O Pins die nicht in einer Reihe 
sind definiert als SIG1-SIG24 (liefern also den Pin Zustand 0/1).

Ich benötige sie aber in der richtigen Reihenfolge in einem entweder 
3Byte Array oder in einer 4Byte Long Variable jenachdem was besser geht.

Wie stelle ich das am besten an?

Gruß
Markus

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

1
uint32t value=0;
2
if (pin 32 is set)
3
  value|=(1<<32);
4
if (pin 31 is set)
5
  value|=(1<<31);
6
...
7
if (pin 0 is set)
8
  value|=(1<<0);

Die Ausdrücke in den Klammern kriegst du selber hin, oder?

von Logik Anal Ysator (Gast)


Lesenswert?

- Definiere die "richtige" Reihenfolge der Bits
- Lese die einzelnen Bits und schiebe sie in die
richtige Position
- ver-odere alle einzelnen Bits in eine Long Variable

von holger (Gast)


Lesenswert?

>if (pin 32 is set)
>  value|=(1<<32);

Buhaha;)

von Logik Anal Ysator (Gast)


Lesenswert?

holger schrieb:
> Buhaha;)

Hauptsache Stefanus kann als erster seinen Mist beitragen.

von Markus S. (markussch)


Lesenswert?

Ne variante ohne 'IF' wird dann wohl nicht gehen oder?

value|=(1<<32);    setzt ein Bit
value|=(0<<32);    löscht aber kein Bit oder?

von Logik Anal Ysator (Gast)


Lesenswert?

Markus S. schrieb:
> und habe 24 I/O Pins die nicht in einer Reihe
> sind definiert

Stefanus kennt auch schon die Reihenfolge der Bits. Seine
Glaskugel arbeitet hervorragend.

von Logik Anal Ysator (Gast)


Lesenswert?

Markus S. schrieb:
> value|=(0<<32);    löscht aber kein Bit oder?

Lässt sich in jedem C-Tutorial oder C-Lehbuch nachlesen.

Bist du zu faul das zu tun?

von Stefan F. (Gast)


Lesenswert?

> Buhaha;)

Nach 6 Wochen weichgekochtem Hirn bei 28-36°C solltest auch du solche 
Flüchtigkeitsfehler verzeihen können. Egal ob du noch im Kindergarten 
spielst oder älter bist.

von Markus S. (markussch)


Lesenswert?

Nein geht nicht, hätte besser geschrieben "value|=(0<<32);  geht ja 
leider nicht"

Frage gibt es eine Verknüpfung die sowohl ein Bit als 0 aus 1 setzen 
kann?

von Stefan F. (Gast)


Lesenswert?

> gibt es eine Verknüpfung die sowohl
> ein Bit als 0 aus 1 setzen kann?

Nochmal auf deutsch bitte.

von sdfg (Gast)


Lesenswert?

man kann erst alles auf 0 setzen und dann verodern

von Stefan F. (Gast)


Lesenswert?

Man kann auf die if Ausrücke verzichten, indem man ellenlange wahnwitzig 
verschachtelte Ausdrücke benutzt. Aber dieser Code wäre sehr schlecht 
lesbar und vermutlich nicht einmal schneller.

von Stefan F. (Gast)


Lesenswert?

Man könnte Markos benutzen, finde ich aber auch nicht gut:
1
#define isset(port,bit) (((port) >> (bit)) & 1)
2
3
uint32_t value =
4
  (isset(registerX,24) << 31) | 
5
  (isset(registerX,7) << 30) |
6
  (isset(registerX,0) << 29) |
7
  ...
8
  (isset(registerX,12) << 0);

Wenn da ein Tippfehler passiert, spuckt der Compiler sicher eine 
herrlich unverständliche Fehlermeldung aus.

von Nop (Gast)


Lesenswert?

Markus S. schrieb:
> Ne variante ohne 'IF' wird dann wohl nicht gehen oder?
>
> value|=(1<<32);    setzt ein Bit
> value|=(0<<32);    löscht aber kein Bit oder?

Nein. Entweder, Du initialisierst Deinen uint32 immer mit 0 und setzt 
dann nur die nötigen Bits (wie Stefan demonstriert hat), oder Du machst 
das mit &=~.

von (prx) A. K. (prx)


Lesenswert?

Da wäre vielleicht die Möglichkeit, Bits in einem Byte per
  const uint32_t a[256]
den Zielpositionen zuzuordnen.

von holger (Gast)


Lesenswert?

>> Buhaha;)

>Nach 6 Wochen weichgekochtem Hirn bei 28-36°C solltest auch du solche
>Flüchtigkeitsfehler verzeihen können.

Kein Problem. War halt lustig;)

>Egal ob du noch im Kindergarten
>spielst oder älter bist.

Das hättest du dir jetzt auch verkneifen können.

> value|=(1<<32);    setzt ein Bit
> value|=(0<<32);    löscht aber kein Bit oder?

OMG, nicht noch mehr 32. Das geht nicht! Dieses Bit fliegt raus.
Passt nicht in 32 Bit.

von Markus S. (markussch)


Lesenswert?

Dann müsste folgendes ja funktionieren:

uint32t value=0;
value|=(SIG24<<23);
value|=(SIG23<<22);
value|=(SIG22<<21);
...
value|=(SIG1<<0);

Wenn ein SIG.. Port auf 0 ist dann passiert ja nix oder?

Beitrag #5515596 wurde vom Autor gelöscht.
von holger (Gast)


Lesenswert?

>Wenn ein SIG.. Port auf 0 ist dann passiert ja nix oder?

Ja, 0 oder 0 ist 0. So ein bisschen Digitaltechnik solltest du dir
schon mal reinziehen.

von Markus S. (markussch)


Lesenswert?

Meinte nur weil du vorher explizit über die IF den 1 Zustand abgefragt 
hast, das kann man sich ja dann sparen.

Besten Dank an alle bis auf Logic... Durch solche Leute wird dieses 
Forum immer unbeliebter. Wenn du so Anal fixiert bist schau doch lieber 
y o u p o r n...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

eine wilde Idee bei der Wärme.  29°C in der Bude.
Wenn noch nicht geschehen, Pinreihenfolge in einem Array definieren.
Danach sollte es in einer for Schleife zu erledigen sein die von 0 bis 
23 durchläuft und dabei mit den Indexnummern arbeitet?

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Ich hab das nicht so ganz verstanden:

Markus S. schrieb:
> ich benutze einen 32Bit MC und habe 24 I/O Pins die nicht in einer Reihe
> sind definiert als SIG1-SIG24 (liefern also den Pin Zustand 0/1).

Was sind SIG1-SIG24? Variablen, die einfach 0 oder 1 sind? Oder 
Bitnummern in einem Register?

Markus S. schrieb:
> Ne variante ohne 'IF' wird dann wohl nicht gehen oder?

Doch, geht schon. Dann muss man das Bit maskieren und an die richtige 
Stelle schieben.
Beispiel: Dein Bit ist bit 20 von Register X und soll in bit 15 der 
Variable:
1
variable |= (RegisterX >> 20 & 0x01) << 15;

Sofern SIG1-SIG24 einfach Variablen sind, die 0 oder 1 sind, reicht 
natürlich sowas wie:
1
variable |= SIG20 << 20;

> value|=(1<<32);    setzt ein Bit
> value|=(0<<32);    löscht aber kein Bit oder?

Nein.

: Bearbeitet durch User
von Rudolph (Gast)


Lesenswert?

Markus S. schrieb:
> uint32t value=0;
> value|=(SIG24<<23);
> value|=(SIG23<<22);
> value|=(SIG22<<21);
> ...
> value|=(SIG1<<0);

Mit den Variablen wird das doch zur Laufzeit geschoben.
Du füllst doch irgendwo die SIGn Variablen, pack da doch gleich den Wert 
rein statt Null oder Eins.

if(pinset(x) SIG12 = 4096;

value = SIG1 + SIG2 + SIG3 ...

Ein if(SIG6) ist dann immer nuch nur wahr oder falsch.

von Operator S. (smkr)


Lesenswert?

Markus S. schrieb:
> Ich benötige sie aber in der richtigen Reihenfolge in einem entweder
> 3Byte Array oder in einer 4Byte Long Variable jenachdem was besser geht.

"Besser" in welchem kontext?

Schau dir aber einmal std::bitset an, das könnte dir bieten worauf du 
hinaus willst.

Ansonsten wäre auch ein Union von 24 bool Variabeln, oder gleich eines 
bool arrays der Grösse 24 möglich.

von Markus F. (mfro)


Lesenswert?

Scheint wirklich heiß zu sein, da draussen. Der Index in mix gibt an, 
wo's hin soll, der Wert, wo's herkommt.
1
uint32_t shuffle(uint32_t raw)
2
{
3
    uint8_t mix[] = { 1, 12, 18, 0, 24, 11 };
4
    uint32_t ret = 0;
5
6
    for (int i = 0; i < sizeof(mix); i++)
7
        ret |= ((raw >> mix[i]) & 1) << i;
8
9
    return ret;
10
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Da der Thread eh schon entgleist ist, hier noch die übertriebene 
template-Variante:
1
#include <cstddef>
2
#include <iostream>
3
#include <iomanip>
4
#include <climits>
5
6
template <std::size_t iPort, std::size_t iPin>
7
struct HWPin {
8
  static constexpr std::size_t port = iPort;
9
  static constexpr std::size_t pin = iPin;
10
};
11
12
template <typename... T>
13
struct List {};
14
15
template <typename HwPort, std::size_t nHwPorts, typename LogPort, std::size_t... Ports, std::size_t... Pins, std::size_t... I>
16
void readPorts2 (LogPort& out, HwPort (&in) [nHwPorts], List<HWPin<Ports, Pins>...>, std::index_sequence<I...>) {
17
  out = 
18
    ((((in [Ports] >> Pins) & 1) << I) + ... );
19
}
20
21
template <typename HwPort, std::size_t nHwPorts, typename LogPort, std::size_t... Ports, std::size_t... Pins>
22
void readPorts (LogPort& out, HwPort (&in) [nHwPorts], List<HWPin<Ports, Pins>...> l) {
23
  readPorts2 (out, in, l, std::make_index_sequence<sizeof...(Ports)> {});
24
}
25
26
27
int main () {
28
  // Array mit Eingabe-Portdaten
29
  uint32_t inPorts [4] { GPIOA->IDR, GPIOB->IDR, GPIOC->IDR };
30
  uint32_t packed;
31
  readPorts (packed, inPorts, List<
32
    // Definition der Pins in logischer Reihenfolge
33
    HWPin<0,1> , HWPin<0,7>, HWPin<0, 3>, HWPin<1, 4>
34
  > {});
35
  std::cout << "0x" << std::hex << std::setw(CHAR_BIT * sizeof(packed) / 4) << std::setfill('0') << packed << std::endl;
36
}

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Ich betrachte das mal als Beweis für die Möglichkeit, beliebig simple 
Sachverhalte beliebig kompliziert auszudrücken ;)

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
>> ich benutze einen 32Bit MC und habe 24 I/O Pins die nicht in einer Reihe
>> sind definiert als SIG1-SIG24 (liefern also den Pin Zustand 0/1).
>
> Was sind SIG1-SIG24? Variablen, die einfach 0 oder 1 sind? Oder
> Bitnummern in einem Register?

Wenn sie symbolisch für IO-Pins stehen, und sich diese Bits gebündelt 
auf wenige Ports verteilen, aber hauptsächlich die Reihenfolge nicht 
passt, dann lassen sich Bytes dieser Ports wie oben erwähnt über 
Tabellen umcodieren.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Also ehrlich, der erste Code von Stefan ist klar, sauber, sauschnell, 
lesbar und maximal flexibel. Bei einem Controller mit wenigen 100 Pins 
macht was "schlaues" wenig Sinn.

Alternativ wäre statt bitbanging noch bitfelder in gleicher Weise 
möglich, wenn man bitfelder nicht für Teufelszeug hält.

Ergänzung: statisch analysierbar, dann fallen die meisten 
Flüchtigkeitsehler auch auf

von W.S. (Gast)


Lesenswert?

Markus S. schrieb:
> ich benutze einen 32Bit MC und habe 24 I/O Pins die nicht in einer Reihe
> sind

..aber du verrätst niemandem, welcher µC das ist. Dann mußt du dein 
Problem eben allein lösen.

Tip: bei manchen 32 Bittern aus der Cortex-Riege gibt es mehrere 
Zugriffsmöglichkeiten für die Ports. Eine davon ist ein Alias-Feld 
(offizieller Name fällt mit grad nicht ein), wo jedes Portpin als eine 
Boolean-Variable dargestellt ist. Und das wahlweise als 8, 16 oder 32 
Bit longbool.

Wo sowas vorhanden ist (z.B. LPC11E12), kannst du etwa so schreiben:
1
#define GPPB    0x50000000
2
#define W0  (*((volatile unsigned long*) (GPPB + 0x1000)))
3
#define W1  (*((volatile unsigned long*) (GPPB + 0x1004)))
4
...
5
6
MyVector =
7
  (W0  << 1) |
8
  (W27 << 2) |
9
  (W3  << 3) |
10
   ... usw.
Wo sowas nicht geht, mußt du zwangsläufig auf
((PortX >> nummer) &1) << bitpos) |
zurückgreifen.

W.S.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

W.S. schrieb:
> offizieller Name fällt mit grad nicht ein

Bit-Banding.

von Markus S. (markussch)


Lesenswert?

Danke nochmal für die Rege Diskussion, welchen MC ich benutze spiel 
keine Rolle (funktioniert mit jedem) und es ging ja darum, es möglichst 
effizient zu machen.

Wie ich oben geschrieben habe liefern SIG1-SIG24 (per #define) direkt 
den Bitzustand des entsprechenden Pins am MC.
Zitat:
> ich benutze einen 32Bit MC und habe 24 I/O Pins die nicht in einer Reihe
> sind definiert als SIG1-SIG24 (liefern also den Pin Zustand 0/1).

Die wohl einfachste/schnellste/übersichtlichste Lösung ist diese:

uint32t value=0;
value|=(SIG24<<23);
value|=(SIG23<<22);
value|=(SIG22<<21);
...
value|=(SIG1<<0);

Habe sie jetzt auch fertig programmiert und sie funktioniert 
einwandfrei!

Gruß
Markus

: Bearbeitet durch User
Beitrag #5516819 wurde von einem Moderator gelöscht.
von A. S. (Gast)


Lesenswert?

Markus S. schrieb:
> Habe sie jetzt auch fertig programmiert und sie funktioniert
> einwandfrei!

Das ist gut.

Im einer kontemplatuven Stunde nach ein paar Tagen empfehle ich dennoch, 
mal zu überleben, wo die Vorteile von Stefans Version mit If liegen. Für 
den Compiler und den Leser.

von Vincent H. (vinci)


Lesenswert?

Niklas G. schrieb:
> Da der Thread eh schon entgleist ist, hier noch die übertriebene
> template-Variante

Daran ist gar nichts übertrieben. Das ist nämlich die einzige Variante 
die Lauf- und Compilezeit Werte unterstützten könnte. Sprich ist der 
Wert für "out" bekannt bekommt man 2 Instruktionen und nicht ein paar 
hundert...

Von Reuse brauch ich gar nicht erst anfangen.

von (prx) A. K. (prx)


Lesenswert?

Markus S. schrieb:
> Die wohl einfachste/schnellste/übersichtlichste Lösung ist diese:

Wenn sich die Inputs nicht über zu viele Ports verteilen, wird eine 
Umcodierung über ein paar Tabellen vmtl schneller sein.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Die wohl einfachste/schnellste/übersichtlichste Lösung ist diese:
1
uint32t value=0;
2
value|=(SIG24<<23);
3
value|=(SIG23<<22);
4
value|=(SIG22<<21);
5
...
6
value|=(SIG1<<0);

einfach: ja
übersichtlich: ja
schnellste: nein

Denn so werden immer alle 32 Makros ausgeführt, dann das Bit geschoben, 
dann mit value verodert. Mein Vorschlag mit der if-kette verkürzt die 
Sache etwas bei den Bits, die 0 sind.

von Joachim B. (jar)


Lesenswert?

Stefanus F. schrieb:
> Mein Vorschlag mit der if-kette verkürzt die
> Sache etwas bei den Bits, die 0 sind.

einverstanden,

Stefanus F. schrieb:
> uint32t value=0;
> if (pin 32 is set)
>   value|=(1<<32);
> if (pin 31 is set)
>   value|=(1<<31);
> ...
> if (pin 0 is set)
>   value|=(1<<0);

dann machen wir es doch richtig (rem 24 I/O)

uint32t value=0;
if (pin 24 is set)
  value|=(1<<23);
if (pin 23 is set)
  value|=(1<<22);
...
if (pin 1 is set)
  value|=(1<<0);

sorry aber als Letztes sollte es richtig stehen

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Jetzt sollte man natürlich noch diese SIGxx Makros verkürzen, dass sie 
bei Low Pegel eine 0 liefern und bei High pegel irgendeine höhere Zahl.

Momentan hat er die Makros ja so geschrieben, dass 0 oder 1 bei raus 
kommt. Für diese if-Kette würden sie damit eine unnötige 
Schiebe-Operation enthalten.

von Niklas Gürtler (Gast)


Lesenswert?

Das wird doch eh alles wegoptimiert. Lieber das Übersichtlichste nehmen 
und schauen ob der generierte Code nicht vielleicht sogar optimal ist...
Auf >= Cortex-M3 wären das die Instruktionen UBFX und BFI, wenn die Bits 
komplett durcheinander sind.

von Niklas Gürtler (Gast)


Lesenswert?

Wobei ich die Variante mit "if" nicht so schön finde - die Ziel Variable 
wird mehrfach verändert (mutable), wodurch das ganze relativ weit weg 
von funktionaler Ausdrucksweise ist und daher m.E. unübersichtlicher. 
Sollte es tatsächlich nicht wegoptimiert werden, sind bedingte Sprünge 
je nach Prozessor auch ineffizient.

von (prx) A. K. (prx)


Lesenswert?

Stefanus F. schrieb:
> Mein Vorschlag mit der if-kette verkürzt die
> Sache etwas bei den Bits, die 0 sind.

Ganz so einfach ist es nicht. Es hängt ein wenig vom Prozessor ab, 
inwieweit ein
  if (reg1 & in_bit)
     reg2 |= out_bit;
als Bit-Test und bedingtes Bit-Or übersetzt wird, z.B. beim ARM7. Mit 
Sprung spielt die Laufzeit bedingter Sprünge mit hinein, die je nach 
Core und Anbindung vom Programmspeicher hoch ausfallen kann.

Und wenn die 24 Inputs alle von einem 32-Bit Port stammen, dann ist
  out = a3[input >> 24 & 0xFF]
      | a2[input >> 16 & 0xFF]
      | a1[input >>  8 & 0xFF]
      | a0[input >>  0 & 0xFF];
deutlich schneller.

: Bearbeitet durch User
von Crazy Harry (crazy_h)


Lesenswert?

Das ist zwar kein C, aber geht bestimmt auch damit.

Procedure ReadStatus;             // Einlesen und aufbereiten der 
C&RStatus-Variablen
  Var D1, D2 : Byte;
  Begin
    D1:= not PinB;                // --> C5 R5 C6 R6 C7 R7 C8 R8
    D2:= not PinF;                // --> R1 C1 R2 C2 R3 C3 R4 C4
    RStatus:=$00;
    RStatus:=((D1 and %00000001) shl 7) or ((D1 and %00000100) shl 4) or
             ((D1 and %00010000) shl 1) or ((D1 and %01000000) shr 2) or
             ((D2 and %00000010) shl 2) or ((D2 and %00001000) shr 1) or
             ((D2 and %00100000) shr 4) or ((D2 and %10000000) shr 7);

    CStatus:=$00;
    CStatus:=((D1 and %00000010) shl 6) or ((D1 and %00001000) shl 3) or
              (D1 and %00100000)        or ((D1 and %10000000) shr 3) or
             ((D2 and %00000001) shl 3) or  (D2 and %00000100)        or
             ((D2 and %00010000) shr 3) or ((D2 and %01000000) shr 6);
  End ReadStatus;

von Einer K. (Gast)


Lesenswert?

Stefanus F. schrieb:
> Mein Vorschlag mit der if-kette verkürzt die
> Sache etwas bei den Bits, die 0 sind.
Nur wenn der betreffende µC keinen "Barrel Shifter" hat.
(Aber das dürfen wir ja nicht erfahren)

von Niklas Gürtler (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Nur wenn der betreffende µC keinen "Barrel Shifter" hat.

Welcher 32 Bit Prozessor hat denn keinen? Und außerdem auch noch keine 
Pipeline, welche durch bedingte Sprünge ausgebremst wird?

von (prx) A. K. (prx)


Lesenswert?

Niklas Gürtler schrieb:
> Welcher 32 Bit Prozessor hat denn keinen? Und außerdem auch noch keine
> Pipeline, welche durch bedingte Sprünge ausgebremst wird?

Ein Problem gibts eher bei einem Portzugriff pro Bit, also zB
   #define SIGxx (... PORT2 ...)
   ... SIGxx << 13 ...
oder dem analogen Bitbanging, weil so ein Portzugriff ein paar Takte 
dauern kann und dank volatile nicht optimiert wird. Bei 24 davon läppert 
sich das.

Eine generische Methode, die völlig unabhängig von der Verteilung der 
Input-Pins auf Ports ist, die ist zwar elegant, aber sehr wahrscheinlich 
nicht laufzeitoptimal.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Falls noch jemand wissen wollte, warum C Programmierer auch ein bisschen 
Assembler können sollten: Dieser Thread zeigt es.

von Niklas Gürtler (Gast)


Lesenswert?

Das stimmt allerdings... Bei manchen Cortex-A kann das 50 Takte pro 
Zugriff dauern, weil hier der Cache umgangen werden muss und der 
Peripherie Bus viel langsamer als der Core sein kann.

von (prx) A. K. (prx)


Lesenswert?

Stefanus F. schrieb:
> Falls noch jemand wissen wollte, warum C Programmierer auch ein bisschen
> Assembler können sollten: Dieser Thread zeigt es.

Man muss dazu nicht unbedingt in Assembler programmieren können. Aber es 
hilft, solchen Code lesen zu können, und ein wenig die Maschine zu 
verstehen.

von Stefan F. (Gast)


Lesenswert?

> es hilft, solchen Code lesen zu können

Das meinte ich ja.

von Operator (Gast)


Lesenswert?

C fehlt ein Set/Unset Operetor.

Mit # als Operatorzeichen etwa so:

port # 1 # 2;

Setzt das LSB und loescht das Bit darueber.


Traditionell ginge das wohl so:

port = (port | 1) & ~2;


oder:

result = var # 1 # 2;


Bei einem:

port ## 2 oder: port #1 #

koennte der Compiler das jeweils unnoetige schonmal herausoptimieren.

Beitrag #5516903 wurde vom Autor gelöscht.
von Stefan F. (Gast)


Lesenswert?

> C fehlt ein Set/Unset Operetor.

Ich glaube, du brauchst hier nicht die Programmiersprache zu erklären. 
Die kennen wir alle gut genug.

von (prx) A. K. (prx)


Lesenswert?

Operator schrieb:
> koennte der Compiler das jeweils unnoetige schonmal herausoptimieren.

Solange der Compiler pro Bit auf einen Port zugreifen muss, hast du die 
Zugriffszeit daran an der Backe. Unabhängig von der Syntax.

Auf Register angewandt sieht das mit diesem Operator nur optisch anders 
aus, an möglicher Optimierung ändert das nichts. Jeder Compiler wird |0 
oder <<0 ohnehin streichen, wenn optimiert übersetzt.

: Bearbeitet durch User
von STK500-Besitzer (Gast)


Lesenswert?

A. K. schrieb im Beitrag #5516903:
> ... und jetzt bitte nochmal für Normalsterbliche.

Man kann in manchen Sprachen den Zugriff auf als public definierte class 
member steuern, indem man den get- und set-Operator verwendet.
Dabei wird bei einer Zuweiung der set-Operator verwendet und beim 
Auslesen der get-Operator.
Um willkürlichem Schreibzugriff entgegenzuwirken, kann man den 
set-Operator private definieren und den get als public.
Dann kann von überall lesend auf die Variable zugegriffen werden, aber 
nur innerhalb der Klasse schreibend.
Die Operatoren arbeiten wie Funktionen (get endet mit einem return, set 
hat als quasi unsichtbares Argument die Variable "value").
Innerhalb dieser Operatoren kann man auch noch Programmcode abarbeiten 
lassen.

von (prx) A. K. (prx)


Lesenswert?

STK500-Besitzer schrieb:
> Man kann in manchen Sprachen den Zugriff auf als public definierte class
> member steuern, indem man den get- und set-Operator verwendet.

Yep, damit lassen sich Programme eleganter formulieren. Wenn es primär 
auf die Optik ankommt, kann das sehr nützlich sein. Wenn indes die 
Laufzeit wichtig ist, dann muss man in der hiesigen Aufgabe von der 
Abstraktion runter auf die Realität, und die konkrete Pinverteilung 
betrachten. Das ist nicht annähernd so schön, kann aber sehr viel 
schneller sein.

Wobei ich eine Weile brauchte, bis ich verstand, wer "Operator" ist. 
Dessen Hirn windet sich allerdings völlig anders als meines. ;-)

: Bearbeitet durch User
Beitrag #5516918 wurde vom Autor gelöscht.
Beitrag #5516920 wurde vom Autor gelöscht.
von Stefan F. (Gast)


Lesenswert?

> Wenn SIG1..SIG24 nur den Zustand 0 und 1 haben

Leider spielt der TO nicht mit offenen karten, so daß wir alle nicht 
wissen, wie seine SIGxx Makros aussehen.

Das man kein if "braucht", habe wir längst festgestellt. Wir sind schon 
einen Schritt weiter und vergleichen die Performance diverser Varianten.

Was aber - wie nun klar ist - ohne die Makros und den µC zu kennen zu 
keiner konkreten Empfehlung führen kann.

Es wurde alles dazu gesagt, nur nicht nicht von jedem. Mal sehen, wie 
viele Leute noch die selben Vorschläge wiederholen und dann die selben 
Bedenken dazu äußern.

von Niklas Gürtler (Gast)


Lesenswert?

Operator schrieb:
> C fehlt ein Set/Unset Operetor.

Da es ja um C++ geht, steht es dir frei das selbst zu implementieren... 
# ist schon belegt. Aber so etwas könnte man sich bauen:
1
RegisterXY[7] = 0;
2
RegisterYZ.BitBlubb =1;
Mit einer cleveren Umsetzung ist das dann auch genau so effizient wie 
das direkte Hinschreiben. C(++) sind halt Hochsprachen und nicht primär 
für Treiber Entwicklung gemacht, weshalb solche Dinge nicht direkt 
kompakt unterstützt werden. Nach dem Grundsatz:
Oft benutzte Dinge einfach machen, alle Dinge möglich machen.

von c++ (Gast)


Lesenswert?

1
#include <cstdint>
2
#include <biset>
3
4
uint32_t read_io()
5
{
6
    std::bitset<24> bits
7
8
    bits.set( 0, SIG1 );
9
    bits.set( 1, SIG2 );
10
    // ...
11
    bits.set(23, SIG24);
12
13
    return bits.to_ulong();
14
}
http://www.cplusplus.com/reference/bitset/bitset/

von W.S. (Gast)


Lesenswert?

Vincent H. schrieb:
> Daran ist gar nichts übertrieben. Das ist nämlich die einzige Variante
> die Lauf- und Compilezeit Werte unterstützten könnte.

Es ist die einzige Variante, die geradezu blind ist für die 
zugrundeliegende Hardware. Man bedenke mal, daß das Zusammenklauben von 
wild in der Peripherie verstreuten Bits prinzipiell derart hardware- und 
projekt-nah ist, daß man mit Templates und dergleichen an dieser Stelle 
eigentlich GARNICHTS Gutes bewirken kann.

Will man einen hochästhetisch aussehenden Quellcode oder einen 
hocheffektiven Maschinencode?


Stefanus F. schrieb:
> Falls noch jemand wissen wollte, warum C Programmierer auch ein bisschen
> Assembler können sollten: Dieser Thread zeigt es.

Wenigstens ein wenig Assembler sollte JEDER µC-Programmierer können. 
Aber das reicht nicht. Man sollte auch die Peripherie des betreffenden 
µC soweit kennen, daß man sie auch benutzen kann.

Wer mit irgendwelchen Makros versucht, Portpins in eine generalisierte 
Form zu bringen, vergeigt fast immer wesentliche Möglichkeiten, die der 
betr. µC bietet.


Operator schrieb:
> Bei einem:
> port ## 2 oder: port #1 #
> koennte der Compiler das jeweils unnoetige schonmal herausoptimieren.

siehe oben. Das ist ein glatter Fehlgedanke. Der Compiler kann beim 
gestellten Thema überhaupt nichts herausoptimieren, denn es geht 
darum, 24 wild in der Prärie liegende Pinzustände in eine einzige long 
Variable zu befördern. Also müssen alle 24 Zustände irgendwie abgefragt 
werden und man kann keinen davon mal eben wegoptimieren.

Schlußendlich habe ich den Eindruck, daß hier mal wieder eine völlig 
falsche Aufgabenstellung zelebriert wird. Der TO hat genau so etwas vor 
und fragt nur nach, wie er das am besten kann, aber wie schon so oft in 
diesem Forum erweist sich wohl auch dieses als falscher - weil 
uneffektiver - Denkansatz, der bei besserer Gesamtplanung des 
zugrundeliegenden Projektes schlichtweg überflüssig wäre.

W.S.

von Niklas Gürtler (Gast)


Lesenswert?

W.S. schrieb:
> Man bedenke mal, daß das Zusammenklauben von wild in der Peripherie
> verstreuten Bits prinzipiell derart hardware- und projekt-nah ist, daß
> man mit Templates und dergleichen an dieser Stelle eigentlich GARNICHTS
> Gutes bewirken kann.

Der Code wird ggf. übersichtlicher. Die Definition der Funktion ist 
etwas kryptisch, dafür ist der Aufruf ziemlich kurz und es ist leicht 
ersichtlich, wie die Pins definiert sind.

W.S. schrieb:
> Will man einen hochästhetisch aussehenden Quellcode oder einen
> hocheffektiven Maschinencode?

Compiler sind heutzutage sehr gut im Optimieren. Der abstrakte, lesbare 
Code wird in einen anwendungsspezifischen optimalen Maschinencode 
übersetzt. Man muss ggf. noch ein bisschen mit "restrict" und 
"always_inline" nach helfen.

Siehe z.B. die im Artikel Serialisierung vorgestellte Bibliothek. 
Diese ermöglicht sehr abstrakten, übersichtlichen und wartbaren Code. 
Dieser wird aber sehr effizient übersetzt. Genau das ist der Spagat, für 
den es C++ gibt.

von W.S. (Gast)


Lesenswert?

Niklas Gürtler schrieb:
> Compiler sind heutzutage sehr gut im Optimieren.

Du hast kein einziges Wort verstanden.

Ganz egal, ob ein Compiler den ihm vorgesetzten Code optimieren kann 
oder nicht, kann er (der Compiler!) die zugrundeliegende Hardware der 
Peripherie nicht kennen. Er weiß daher nicht, ob dessen Peripherie nicht 
nur eine einzige Art Zugriff auf die Portpins kennt, sondern ggf. auch 
noch andere Zugriffsarten, die dann eben weitaus effizienter sind für 
den gegebenen Fall.

Siehe z.B. auch das hier in diesem Forum ewige Thema "Setzen eines 
Portpins zu hi oder lo", was bei manchen Chips auf dramatisch bessere 
Weise (BSRR Register o.a.) als sonst üblich getan werden kann. So etwas 
kann kein Compile dieser Welt optimieren, denn dafür ist hardware- und 
projekt-nahes Wissen vonnöten.

W.S.

von S. R. (svenska)


Lesenswert?

Markus S. schrieb:
> Danke nochmal für die Rege Diskussion, welchen MC ich benutze spiel
> keine Rolle (funktioniert mit jedem) und es ging ja darum, es möglichst
> effizient zu machen.

Wenn es für alle möglichen Controller mit jeder möglichen Pinbelegung 
und jeder beliebigen Definition für deine SIGnn-Makros funktioniert, 
dann ist das Ergebnis alles andere als "möglichst effizient".

Niklas Gürtler schrieb:
>> Nur wenn der betreffende µC keinen "Barrel Shifter" hat.
>
> Welcher 32 Bit Prozessor hat denn keinen? Und außerdem auch noch keine
> Pipeline, welche durch bedingte Sprünge ausgebremst wird?

Der erzwungene Barrelshifter ist eine Spezialität von ARM, auf allen 
anderen Architekturen ist er eine Optimierung, entsprechend muss er 
nicht auf jeder MIPS- oder RISCV-Implementation vorhanden sein.

Da wir vom TO eine absolute Unkenntnis der Realität verordnet bekommen 
haben, kann es sich durchaus um einen PicoRV32 handeln: Keine Pipeline, 
optionalen Barrelshifter. Oder um eine Hochschul-Kursimplementation von 
MIPS-I.

von Niklas Gürtler (Gast)


Lesenswert?

W.S. schrieb:
> Du hast kein einziges Wort verstanden

Und du meinen Code nicht gelesen. Da ist überhaupt kein Peripherie 
Zugriff drin. Den macht der Aufrufer selbst, so wie es optimal ist. 32 
Bits auf einmal Lesen dürfte ziemlich optimal sein (besser als Bit 
Banding). Und wenn man es drauf anlegt, kann man das selbstverständlich 
auch mit Templates modellieren - wenn nur genau 1 Bit gebraucht wird und 
das an Stelle 0 stehen soll, automatisch auf die Bit Banding Region 
zugreifen. In anderen Fällen dürfte das sonst keinen Vorteil bieten. Bei 
anderen Architekturen gilt das dann analog.

von Niklas Gürtler (Gast)


Lesenswert?

... gilt natürlich auch für die Ausgabe per BSRR o.ä., aber um die ging 
es hier überhaupt nicht.

von A. S. (Gast)


Lesenswert?

W.S. schrieb:
> kann er (der Compiler!) die zugrundeliegende Hardware der Peripherie
> nicht kennen.

Und wer kennt sie dann?

von (prx) A. K. (prx)


Lesenswert?

Achim S. schrieb:
>> kann er (der Compiler!) die zugrundeliegende Hardware der Peripherie
>> nicht kennen.
>
> Und wer kennt sie dann?

Der Fragesteller. Vielleicht. Sofern es keine Lern-Aufgabe ist.

: Bearbeitet durch User
von Crazy Harry (crazy_h)


Lesenswert?

Ach falls das noch wer wissen will: meine Procedure (Pascal) oben 
braucht auf einem 32MHz XMega 2.98us. Sind aber nur 2 Bytes.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

Weil's so schön und etwas anders ist ;-)
1
    uint32_t value = 0;
2
    value = (value<<1) | !!SIG24;
3
    value = (value<<1) | !!SIG23;
4
    value = (value<<1) | !!SIG22;
5
    ...
6
    value = (value<<1) | !!SIG3;
7
    value = (value<<1) | !!SIG2;
8
    value = (value<<1) | !!SIG1;
9
    return value;

von S. R. (svenska)


Lesenswert?

@foobar: Das hat den Vorteil, dass es mit jedem C-style Boolean 
funktioniert, nicht nur mit exakt 0 und 1. Schön. :-)

Beitrag #5517324 wurde vom Autor gelöscht.
von Carl D. (jcw2)


Lesenswert?

Niklas Gürtler schrieb:
> ... gilt natürlich auch für die Ausgabe per BSRR o.ä., aber um die ging
> es hier überhaupt nicht.

Wenn man unbedingt will, dann kann C++17 sogar die Umsetztabelle "Bits 
die ausgegeben werden sollen" in "BSSR-Patterns" als constexpr 
generieren. Zumindest für den Fall "alle Bits beliebig auf eine Port 
verstreut" und bis zu einer in der Source einstellbaren Anzahl von Bits, 
da dies ja ein uint32_t Array wird und es noch ins ROM passen muß. Für 
typische 4-Bit+RS+E-LCD-Schnittstellen braucht das 256-Byte Flash und 
ein zusätzliches Laden aus der Tabelle.
Und wenn irgend eine Bedingung (Port/Anzahl Pins/kein BSSR) nicht 
eingehalten wird, dann wird per constexpr if eine andere Variante 
benutzt. Der Anwedungs-Code ändert sich nicht, die Templates muß 
zuallererst mal der Compiler verstehen. STL ist auch nichts leicht 
lesbares, aber sehr zuverlässig.

(ist als Ergänzung zu Niklas zu verstehen)

von Carl D. (jcw2)


Lesenswert?

W.S. schrieb:
>
> Siehe z.B. auch das hier in diesem Forum ewige Thema "Setzen eines
> Portpins zu hi oder lo", was bei manchen Chips auf dramatisch bessere
> Weise (BSRR Register o.a.) als sonst üblich getan werden kann. So etwas
> kann kein Compile dieser Welt optimieren, denn dafür ist hardware- und
> projekt-nahes Wissen vonnöten.
>

Das kann der Compiler auch nicht wissen, das bekommt er vom 
Template-Programmierer gesagt. So der das kann.

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.