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
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?
- 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
>if (pin 32 is set) > value|=(1<<32); Buhaha;)
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?
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.
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?
> 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.
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?
> gibt es eine Verknüpfung die sowohl > ein Bit als 0 aus 1 setzen kann? Nochmal auf deutsch bitte.
man kann erst alles auf 0 setzen und dann verodern
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.
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.
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 &=~.
Da wäre vielleicht die Möglichkeit, Bits in einem Byte per const uint32_t a[256] den Zielpositionen zuzuordnen.
>> 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.
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.
>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.
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...
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
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
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.
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.
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 | }
|
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
Ich betrachte das mal als Beweis für die Möglichkeit, beliebig simple Sachverhalte beliebig kompliziert auszudrücken ;)
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
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
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.
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.
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.
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.
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
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.
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
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.
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.
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.
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
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;
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)
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?
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
Falls noch jemand wissen wollte, warum C Programmierer auch ein bisschen Assembler können sollten: Dieser Thread zeigt es.
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.
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.
> es hilft, solchen Code lesen zu können
Das meinte ich ja.
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.
> C fehlt ein Set/Unset Operetor.
Ich glaube, du brauchst hier nicht die Programmiersprache zu erklären.
Die kennen wir alle gut genug.
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
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.
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.
> 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.
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.
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/
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.
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.
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.
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.
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.
... gilt natürlich auch für die Ausgabe per BSRR o.ä., aber um die ging es hier überhaupt nicht.
W.S. schrieb: > kann er (der Compiler!) die zugrundeliegende Hardware der Peripherie > nicht kennen. Und wer kennt sie dann?
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
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
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; |
@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.
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)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.