Forum: Mikrocontroller und Digitale Elektronik STM32 Shift operationen


von Jan K. (Gast)


Lesenswert?

Hallo,

ich habe einen Pin welcher definiert wurde z.B. GPIO_Pin_10. Gibt es 
eine Möglichkeit einfach um 10 Pins zu schiften? Mir fällt nur ein 
log(GPIO_Pin_10)/log(2) ein was ziemlich dämlich ist..

…….


Hintergrund:

Ich will Pins einlesen und kenne nur den Startpin per define. So sollte 
es funktionieren, wenn GPIO_Pin_10 einfach eine 10 enthalten würde und 
nicht (1<<10) = 0x0400.
1
#define MASK ( ( (1 << NUM_PINS) - 1) << START_PIN)
1
 uint16_t Input = (GPIO_ReadInputData(PORT) & MASK) >> START_PIN; // read port and mask relevant Pins
Hoffe das war einigermaßen verständlich...

VG
Jan

Oder kann ich schreiben
1
START_PIN_POS = ln(GPIO_Pin_10)/ln(2)
und der Kompiler macht die Arbeit bereits....

von Herlock Scholmes (Gast)


Lesenswert?

Jan K. schrieb:
> START_PIN_POS = ln(GPIO_Pin_10)/ln(2)

Nicht auszuhalten.
Die Freitage kommen immer früher.

Oder war das früher auch schon so schlimm?

von (prx) A. K. (prx)


Lesenswert?

Man kann beim GCC Masken in Bitnummern umrechnen, in dem man den 
Maschinenbefehl CLZ verwendet:
  #define BBitOfMask(mask)  (31 - __builtin_clz(mask))
Bei Konstanten macht der Compiler das selbst.

(aus https://www.mikrocontroller.net/articles/ARM_Bitbanding)

: Bearbeitet durch User
von Jan K. (Gast)


Lesenswert?

Herlock Scholmes schrieb:
> Jan K. schrieb:
>> START_PIN_POS = ln(GPIO_Pin_10)/ln(2)
>
> Nicht auszuhalten.
> Die Freitage kommen immer früher.
>
> Oder war das früher auch schon so schlimm?

Hä ich meine das Ernst. Wie würdest du es denn lösen? Wenn man es über 
ein define macht könnte doch sein, dass der Compiler das erledigt.

von Jan K. (Gast)


Lesenswert?

A. K. schrieb:
> Man kann beim GCC Masken in Bitnummern umrechnen, in dem man den
> Maschinenbefehl CLZ verwendet:
>   #define BBitOfMask(mask)  (31 - __builtin_clz(mask))
> Bei Konstanten macht der Compiler das selbst.
>
> (aus https://www.mikrocontroller.net/articles/ARM_Bitbanding)

Cool Danke, dass schaue ich mir mal an.

Was wäre denn der normale Weg?

sowas wie

(GPIO_ReadInputData(PORT) & GPIO_Pin_10) >> 10;

müsste doch Standard sein. Es würde ja schon reichen, wenn GPIO_Pin_10 
mit dem einfachen Zahlenwert definiert wäre. Die andere Richtung mit 1<< 
x ist ja super einfach.

von (prx) A. K. (prx)


Lesenswert?

Jan K. schrieb:
> Hä ich meine das Ernst. Wie würdest du es denn lösen? Wenn man es über
> ein define macht könnte doch sein, dass der Compiler das erledigt.

Dir ist schon klar, dass
- Fliesskommarechnung schon mal 1-2 LSBs daneben landet,
- double=>int nicht rundet, sondern abschneidet?

von Jan K. (Gast)


Lesenswert?

A. K. schrieb:
> Dir ist schon klar, dass
> - Fliesskommarechnung schon mal 1-2 LSBs daneben landet,
> - double=>int nicht rundet, sondern abschneidet?

Joooo und deine Lösung? Wenn es der Kompiler macht sollte er es 
schaffen, auf dem µC sollte es bitte nicht live berechnet werden....

von A. S. (Gast)


Lesenswert?

Jan K. schrieb:
> ich habe einen Pin welcher definiert wurde z.B. GPIO_Pin_10.
Wie?

>Gibt es
> eine Möglichkeit einfach um 10 Pins zu schiften?
Nein, nur Bits, keine Pins.

Mir fällt nur ein
> log(GPIO_Pin_10)/log(2) ein was ziemlich dämlich ist..

Ja. wenn der wert 0x400 ist, dann kannst Du auch einfach teilen.

von W.S. (Gast)


Lesenswert?

Jan K. schrieb:
> nicht (1<<10) = 0x0400.
> #define MASK ( ( (1 << NUM_PINS) - 1) << START_PIN)
>  uint16_t Input = (GPIO_ReadInputData(PORT) & MASK) >> START_PIN; //
> read port and mask relevant Pins
> Hoffe das war einigermaßen verständlich...

Nö. das war zumindest für mich völlig unverständlich.

Also:
Pins kannst du nicht abfragen. Was du kannst, ist das Auslesen von 
Hardware-Registern, hier vor allem die Register, die zur GPIO-Peripherie 
gehören. Und da gibt es je nach Typ des Chips eine ganze Reihe 
unterschiedlicher Register.

Zunächst erstmal so etwas wie GPIOx_IDR oder GPIOx->IDR (je nach Gusto 
des Schreiberlings der betreffenden .h) Bei den meisten Cortexen (zu 
denen auch die STM32 gehören) sind die diversen Pins des Chips zu Ports 
unterschiedlicher Breite zusammengefaßt. Deren Bezeichnung ist 
herstellerabhängig, z.B. GPIOA..GPIOF oder GPIO0..GPIO5 usw. Guck dazu 
einfach in dein RefManual.

Wenn du also Port D Pin 10 abfragen willst, dann schreibst du einfach
1
bool ergebnis;
2
ergebnis = (GPIOD_IDR>>10)&1;

Klaro?

W.S.

von A. S. (Gast)


Lesenswert?

W.S. schrieb:
> Wenn du also Port D Pin 10 abfragen willst, dann schreibst du einfach

Anscheinend ist es genau umgekehrt.

Er bekommt einen wert (1<<10), also 0x400. Und möchte mit diesen 0x400 
die 10 "zurückgewinnen", um dann 10 Bit zu schieben.

Das (wie schon angemerkt) geht ohne Zwischenritt "Bit-Ermitteln" einfach 
durch Division seines ausgelesenen Wertes durch diese 0x400 (oder 
welchen Wert auch immer).

von Thomas E. (picalic)


Lesenswert?

Naja, es kann schon vorkommen, daß man aus der Definition eines 
Einzelbits als Bitmaske (z.B. in der .h-Datei einer Library) die 
Bitposition erzeugen muss, um die Addresse für's Bitbanding 
auszurechenen.
Das kann man ggf. per Macros machen (z.B. für STM32F303):
1
#define BITNR(x) (x&0x1?0:x&0x2?1:x&0x4?2:x&0x8?3: \
2
                 x&0x10?4:x&0x20?5:x&0x40?6:x&0x80?7: \
3
                 x&0x100?8:x&0x200?9:x&0x400?10:x&0x800?11: \
4
                 x&0x1000?12:x&0x2000?13:x&0x4000?14:x&0x8000?15: \
5
                 x&0x10000?16:x&0x20000?17:x&0x40000?18:x&0x80000?19: \
6
                 x&0x100000?20:x&0x200000?21:x&0x400000?22:x&0x800000?23: \
7
                 x&0x1000000?24:x&0x2000000?25:x&0x4000000?26:x&0x8000000?27: \
8
                 x&0x10000000?28:x&0x20000000?29:x&0x40000000?30:x&0x80000000?31:32)
9
10
#define bbaddr(addr, bit) ((u32*)((((u32)&(addr))+0x02000000 & 0xFF000000) + 32 * (((u32)&(addr)) & 0x00FFFFFF) + 4*(bit)))
11
12
#define bbSetBit(address, bitmask) (*bbaddr(address, BITNR(bitmask)) = 1)
13
#define bbClearBit(address, bitmask) (*bbaddr(address, BITNR(bitmask)) = 0)

von Irgendwer (Gast)


Lesenswert?

So ganz verstehe nicht worauf du hinaus willst. Wenn GPIO_Pin_10 = 
0x0400 ist kannst du den doch direkt verwenden und musst nichts mehr 
durch die Gegend schieben (bei 10 müsstest du das).
1
if (GPIO_ReadInputData(PORT) & GPIO_Pin_10)
2
{
3
  bit 10 gesetzt
4
}
5
else
6
{
7
  bit 10 nicht gesetzt
8
}

von boah ... (Gast)


Lesenswert?

Thomas E. schrieb:
> #define BITNR(x) (x&0x1?0:x&0x2?1:x&0x4?2:x&0x8?3: \
>                  x&0x10?4:x&0x20?5:x&0x40?6:x&0x80?7: \
>                  x&0x100?8:x&0x200?9:x&0x400?10:x&0x800?11: \
>                  x&0x1000?12:x&0x2000?13:x&0x4000?14:x&0x8000?15: \
>                  x&0x10000?16:x&0x20000?17:x&0x40000?18:x&0x80000?19: \
>                  x&0x100000?20:x&0x200000?21:x&0x400000?22:x&0x800000?23:
> \
>                  x&0x1000000?24:x&0x2000000?25:x&0x4000000?26:x&0x8000000?27:
> \
> 
x&0x10000000?28:x&0x20000000?29:x&0x40000000?30:x&0x80000000?31:32)

einfach geil ...

von Thomas E. (picalic)


Lesenswert?

boah ... schrieb:
> einfach geil ...

Um mich aber nicht mit fremden Federn zu schmücken, möchte ich nicht 
versäumen, hier den Link zu posten, wo ich das gefunden habe (Google hat 
es wiedergefunden!):
http://www.keil.com/forum/21105/bitband-macro-to-get-bit-number-from-bit-mask/

von boah... (Gast)


Lesenswert?

War ironisch gemeint. Ist doch nur ein fettes if Konstrukt. ;-)

von Thomas E. (picalic)


Lesenswert?

boah... schrieb:
> War ironisch gemeint. Ist doch nur ein fettes if Konstrukt. ;-)

Ja, aber es erfüllt seinen Zweck. Der Code, der hinten 'rauskommt, ist 
i.d.R. effektiver, als ein Bit im Register mit "Register |= Bitmask" zu 
setzen. Und letzteres ist nicht so einfach als "atomic" machbar.
Wenn Ironie heißen soll, daß die Lösung sch**** ist, dann schlag halt 
'was besseres vor!

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


Lesenswert?

Thomas E. schrieb:
> Naja, es kann schon vorkommen, daß man aus der Definition eines
> Einzelbits als Bitmaske (z.B. in der .h-Datei einer Library) die
> Bitposition erzeugen muss, um die Addresse für's Bitbanding
> auszurechenen.
> Das kann man ggf. per Macros machen (z.B. für STM32F303):

Ja sag mal, was hast du denn da für einen elendigen Bandwurm über 8 
Zeilen gepostet?

Und nein, es kann eben nicht vorkommen, daß man..MUSS. Niemand muß, 
sondern manche Leute stellen sich einfach nur zu dämlich an. Wenn ich im 
Eröffnungspost sehe, daß der TO zu allererst mit seinem #define quasi 
auf den Baum geklettert ist und anschließend nicht mehr heil herunter 
kommt, dann sollte man ihn über's Knie legen, bis er begreift, daß man 
all sowas auf eine sinnvollere Weise macht.

Ich sehe hier im Forum an so unsäglich vielen Stellen, daß die Leute 
bloß eben Angelernte sind, die man mal auf C dressiert hat, anstatt sie 
das Programmieren zu LEHREN. Deshalb halten sie sich selbst an den 
unmöglichsten Stellen genau an ihre Dressur, ohne auch nur im Geringsten 
zu VERSTEHEN, was sie da eigentlich anrichten.

Da gibt es Leute, die für einen bool unbedingt ein uint8_t nehmen, als 
ob sie damit Integerartithmetik mit definierter Bitzahl betreiben 
wollten.

Da gibt es Leute, die für einen String ebenfalls uint8_t nehmen.

Da gibt es Leute, die für jeden Kram unbedingt einen Namen nehmen 
wollen, weil man ihnen eingetrichtert hat, daß ein (1<<xyz) genauso 
bösartig sei wie ein goto - und die dann auf solche Abwege kommen wie 
der TO.

Sind wir hier in der Klapsmühle?

W.S.

von Thomas E. (picalic)


Lesenswert?

W.S. schrieb:
> Und nein, es kann eben nicht vorkommen, daß man..MUSS. Niemand muß,

Klar, niemand MUSS ein Aufgabe auf eine ganz bestimmte Weise lösen. Man 
KANN sich auch ggf. das Datenblatt vornehmen und jedes Bit, das der 
Hersteller in seinen von ihm zur Verfügung gestellten Header-Dateien eh 
schon symbolisch als Bitmaske definiert hat (weil das offenbar der 
Standard  dieses Herstellers ist), nochmal selbst neu mit der Bitnummer 
definieren. Im Quelltext hat man es dann halt mit zwei verschiedenen 
Symbolen für die gleiche Sache zu tun, je nachdem, ob man Maske oder 
Bitnummer braucht. Wenn man den Controller wechselt, muss man dann eben 
u.U. auch seine eigenen, nicht standardisierten Header-Files auch wieder 
überarbeiten. Ob das für Portabilität und Fehlervermeidung hilfreich 
ist?
Von mir aus ersetze das "muss" in meinem Satz durch "will". Ich warte 
aber immer noch (wie wohl auch der Verfasser des original-Beitrags, der 
den elendigen Bandwurm 2012 gepostet hat) auf die kurze und elegante 
Lösung für dieses Problem. Für einen, der die hohe Kunst des 
Programmierens wirklich beherrscht, sollte das ja ein Klacks sein.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

In C++ kann man das stark schön mit constexpr machen, funktioniert dann 
einheitlich für alle Typen. Falls man den GCC nutzt, kann man auch 
automatisch die CLZ builtins nutzen; die werden dann je nach Architektur 
schön optimiert, falls die Berechnung zur Laufzeit erfolgt. Wenn die 
Eingabe dem Compiler bekannt ist, erfolgt die Berechnung immer durch den 
Compiler selbst.
1
#include <iostream>
2
#include <cstddef>
3
#include <limits>
4
5
template <typename T>
6
constexpr std::size_t log2 (const T& val, T bit = (std::numeric_limits<T>::digits-1)) {
7
  return val & (T { 1 } << bit) ? bit : log2 (val, bit-1);
8
}
9
10
#ifdef __GNUC__
11
constexpr std::size_t log2 (unsigned int val) {
12
  return std::numeric_limits<unsigned int>::digits - __builtin_clz (val) - 1;;
13
}
14
15
constexpr std::size_t log2 (unsigned long val) {
16
  return std::numeric_limits<unsigned long>::digits - __builtin_clzl (val) - 1;;
17
}
18
19
constexpr std::size_t log2 (unsigned long long val) {
20
  return std::numeric_limits<unsigned long long>::digits - __builtin_clzl (val) - 1;;
21
}
22
#endif
23
24
int main () {
25
  std::cout << log2(1) << std::endl;
26
  std::cout << log2(2) << std::endl;
27
  std::cout << log2(4) << std::endl;
28
  std::cout << log2(8) << std::endl;
29
  std::cout << log2(9) << std::endl;
30
  std::cout << log2(0x80000000U) << std::endl;
31
}
Ausgabe:
1
0
2
1
3
2
4
3
5
3
6
31

von boah... (Gast)


Lesenswert?

Niklas G. schrieb:
> In C++

gibt es auch templates. eine des unsinnigsten erfindungen, wie auch 
looping loui. ;-)

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Ich sehe hier im Forum an so unsäglich vielen Stellen, daß die Leute
> bloß eben Angelernte sind, die man mal auf C dressiert hat, anstatt sie
> das Programmieren zu LEHREN.

Ja, Dich zum Beispiel. Wenn Du verstanden hättest, wie das funktioniert, 
dann wüßtest Du auch, daß das keinen Laufzeit-Overhead erzeugt, weil der 
Compiler die ganze Kette nämlich zur Compilezeit auswertet. Das ist der 
Sinn dieser Makro-Definition. Beachte den Kontext - es geht darum, daß 
dieses Makro nicht mit einer Variablen gefüttert wird, sondern mit einer 
Bitmaske als #define.

von boah... (Gast)


Lesenswert?

Nop schrieb:
> weil der
> Compiler die ganze Kette nämlich zur Compilezeit auswertet

???

wird immer besser hier: der hellsehende compiler. gute erfindung. 
brauche ich auch.

von Nop (Gast)


Lesenswert?

boah... schrieb:

> wird immer besser hier: der hellsehende compiler.

Lies das Vorposting, speziell den Satz mit dem Kontext. Dann nachdenken.

von Niklas Gürtler (Gast)


Lesenswert?

boah... schrieb:
> gibt es auch templates. eine des unsinnigsten erfindungen, wie auch
> looping loui. ;-)

Wer genau hinschaut sieht dass da sogar eins im Spiel ist. Wirklich so 
unsinnig wenn es tut was der OP möchte?

boah... schrieb:
> wird immer besser hier: der hellsehende compiler. gute erfindung.
> brauche ich auch.

Wenn der Compiler die Eingabe und den Algorithmus kennt und dieser 
"constexpr" ist, kann er die Ausgabe vorberechnen.

von Nop (Gast)


Lesenswert?

Niklas Gürtler schrieb:

> Wenn der Compiler die Eingabe und den Algorithmus kennt und dieser
> "constexpr" ist, kann er die Ausgabe vorberechnen.

C hat kein constexpr, so daß keine Algorithmen ausgewertet werden können 
- Ausdrücke aber schon, weswegen hier der Elvis-Operator mit Vorteil 
genutzt wird.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Nop schrieb:
> C hat kein constexpr, so daß keine Algorithmen ausgewertet werden können
> - Ausdrücke aber schon, weswegen hier der Elvis-Operator mit Vorteil
> genutzt wird.

Ja, daher schrieb ich ja auch C++. Da es ja um ARM geht sind ja auch C++ 
Compiler verfügbar.

von Nop (Gast)


Lesenswert?

Niklas G. schrieb:

> Ja, daher schrieb ich ja auch C++. Da es ja um ARM geht sind ja auch C++
> Compiler verfügbar.

Und offensichtlich nicht im konkreten Projekt benutzt, weil man dann 
keinen veketteten Ausdruck, sondern constexpr genommen hätte. Hat man 
aber nicht. Es sind für ARM auch noch zig andere Sprachen verfügbar, die 
aber hier ebenfalls nicht genutzt wurden. Wollen wir die jetzt auch alle 
noch aufzählen, wie man es da machen würde?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Nop schrieb:
> Und offensichtlich nicht im konkreten Projekt benutzt, weil man dann
> keinen veketteten Ausdruck, sondern constexpr genommen hätte.

Vielleicht ist der Grund dafür ja auch Unkenntnis von constexpr - wozu 
fragt man sonst im Forum?

Nop schrieb:
> Es sind für ARM auch noch zig andere Sprachen verfügbar, die
> aber hier ebenfalls nicht genutzt wurden.
Auf wie viele davon kann man mit so geringem Aufwand von C wechseln?

Nop schrieb:
> Wollen wir die jetzt auch alle
> noch aufzählen, wie man es da machen würde?
Warum nicht, wäre interessant zu sehen welche Sprachen das auch so 
können.

von Nop (Gast)


Lesenswert?

Niklas G. schrieb:

> Auf wie viele davon kann man mit so geringem Aufwand von C wechseln?

Prust.. ja nee, ist klar. Naja, halt die übliche ungefragte C++-Onanie, 
die sich irgendwo zwischen Veganern und Zeugen Jehovas einreiht.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Nop schrieb:
> Prust.. ja nee, ist klar. Naja, halt die übliche ungefragte C++-Onanie,
> die sich irgendwo zwischen Veganern und Zeugen Jehovas einreiht.

Warum so ein Tonfall?

von Nop (Gast)


Lesenswert?

Niklas G. schrieb:

> Warum so ein Tonfall?

Weil man keinen Fetzen C diskutieren kann, ohne daß einem C++-Fans 
reingrätschen und ihre Lieblingssprache wie Sauerbier anpreisen, eine 
Lösung auf der Suche nach einem Problem. Wilhelm ist da auch so ein 
Kandidat. Aber aber aber in C++ ginge das soooooo.. Herr Lehrer, im 
Keller brennt das Licht, aber ich hab's schon ausgemacht.

Nichtmal Arch-User kommen da mit. Gut, bei denen liegt's auch bloß 
daran, daß sie die meiste Zeit damit beschäftigt sind, ihr System ans 
Laufen zu kriegen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Nop schrieb:
> Weil man keinen Fetzen C diskutieren kann,

Es war nirgendwo gesagt, dass es C sein muss. Aber ich sehe schon, mit 
Argumenten kommt man hier nicht weiter.

von Bernd K. (prof7bit)


Lesenswert?

Vielleicht sollte erstmal genau geklärt werden was der Threadersteller 
eigentlich überhaupt erreichen will. Ich sehe nämlich nicht daß das an 
irgendeiner Stelle schon mal zur Sprache kam. Wahrscheinlich ist die 
Lösung vollkommen trivial.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Vielleicht sollte erstmal genau geklärt werden was der Threadersteller
> eigentlich überhaupt erreichen will.

Ich kann's mir denken. Er hat vermutlich ein Makro definiert, welches 
den ersten Pin eines externen Busses angibt, welcher aus mehreren Pins 
besteht:
1
#define BusStartPin GPIO_Pin_10
Jetzt möchte er mehrere Signale gleichzeitig ausgeben, also z.B. auf 
Pins 10-13:
1
uint8_t data = 0x5;
2
GPIO->BSRR = (data << 10) | ((~data & 0xF) << (10 + 16));
Nur wie kommt man hier an die 10 aus dem BusStartPin-Makro? GPIO_Pin_10 
ist ja (1 << 10). Mit log2 geht das:
1
GPIO->BSRR = (data << log2(BusStartPin)) | ((~data & 0xF) << (log2(BusStartPin) + 16));

Etwas logischer wäre es, BusStartPin direkt als 10 zu definieren. 
Allerdings erwarten diverse Funktionen der HAL es in der Form (1<<10), 
d.h. man soll GPIO_Pin_10 o.ä. übergeben. Man könnte es bei der Übergabe 
per "1<<BusStartPin" machen, aber das sieht auch komisch aus.

von Thomas E. (picalic)


Lesenswert?

Niklas G. schrieb:
> Es war nirgendwo gesagt, dass es C sein muss.

Hi Niklas,

wollte die o.a. Lösung gerade mal eben testen - so einfach geht es aber 
leider nicht (zumindest offenbar nicht ohne gößeren Portierungsaufwand).
Habe diese Zeilen:
1
template <typename T>
2
constexpr std::size_t log2 (const T& val, T bit = (std::numeric_limits<T>::digits-1)) {
3
  return val & (T { 1 } << bit) ? bit : log2 (val, bit-1);
4
}
einfach mal an den Anfang eines C++ Sourcefiles in einem vorhandenen 
Projekt eingefügt. Ergebnis:
1
compiling logwriter.cpp...
2
logwriter.cpp(64): error:  #20: identifier "constexpr" is undefined
3
  constexpr std::size_t log2 (const T& val, T bit = (std::numeric_limits<T>::digits-1)) {
4
logwriter.cpp(64): error:  #147: declaration is incompatible with type "std::size_t"  (declared at line 60 of "C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdlib.h")
5
  constexpr std::size_t log2 (const T& val, T bit = (std::numeric_limits<T>::digits-1)) {
6
logwriter.cpp(64): error:  #65: expected a ";"
7
  constexpr std::size_t log2 (const T& val, T bit = (std::numeric_limits<T>::digits-1)) {
8
logwriter.cpp(70): warning:  #12-D: parsing restarts here after previous syntax error
9
...
Ohne jetzt lange weiter geforscht zu haben, dürfte das Problem wohl mit 
der Compiler-Version zusammenhängen. ARM Compiler 6 statt 5 scheint 
"constexpr" zu kennen, aber für den Umzug des Projekts auf diesen 
Compiler wären zahlreiche Änderungen in vielen Dateien notwendig.
Fazit: die "unelegante" C-Lösung funktioniert wenigstens, sowohl für C, 
als auch für C++ Sourcen, und mit jeder Compiler-Version.

: Bearbeitet durch User
von Johannes S. (Gast)


Lesenswert?

ist der C++ Standard auch auf C++11 oder grösser eingestellt?
und ein '#include <limits>' ist nötig.

von Thomas E. (picalic)


Lesenswert?

Johannes S. schrieb:
> ist der C++ Standard auch auf C++11 oder grösser eingestellt?

Das dürfte das Problem sein: es wäre wohl C++14 notwendig, aber der 
Compiler 5 unterstützt nur C++11.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Thomas E. schrieb:
> Ohne jetzt lange weiter geforscht zu haben, dürfte das Problem wohl mit
> der Compiler-Version zusammenhängen

Ja, der Compiler muss C++11 kennen. Du könntest das "constexpr" 
weglassen und schauen ob er in der Lage ist, es zu optimieren (bei 
constexpr wäre das garantiert).

Thomas E. schrieb:
> aber für den Umzug des Projekts auf diesen
> Compiler wären zahlreiche Änderungen in vielen Dateien notwendig.
Wenn der Code derart unportabel ist, wäre eine Umstrukturierung nicht 
unangebracht... Auf genau eine Version eines bestimmten Compilers 
festgenagelt zu sein ist nicht so besonders zukunftssicher.

Thomas E. schrieb:
> Fazit: die "unelegante" C-Lösung funktioniert wenigstens, sowohl für C,
> als auch für C++ Sourcen, und mit jeder Compiler-Version.
Tja, manchmal kann man auch auf aktuelle Software wechseln... 
"constexpr" gibt es schon seit 2011, das sollte mittlerweile 
"angekommen" sein.

Thomas E. schrieb:
> Fazit: die "unelegante" C-Lösung funktioniert wenigstens, sowohl für C,
> als auch für C++ Sourcen, und mit jeder Compiler-Version.
Es sei den man wechselt die Plattform auf eine, auf welcher ein unsigned 
int nicht 32bit groß ist (z.B. AVR). Und für erst zur Laufzeit bekannte 
Variablen wird diese auch nicht zu "CLZ" optimiert.

von Thomas E. (picalic)


Lesenswert?

Niklas G. schrieb:
> Ja, der Compiler muss C++11 kennen.

Lt. Doku sollte der Compiler 5 C++11 kennen.

Ohne "constexpr" gibt es nur noch 2 Fehler:
1
logwriter.cpp(68): error:  #18: expected a ")"
2
    return val & (T { 1 } << bit) ? bit : log2 (val, bit-1);
3
logwriter.cpp(68): error:  #29: expected an expression
4
    return val & (T { 1 } << bit) ? bit : log2 (val, bit-1);

Niklas G. schrieb:
> Es sei den man wechselt die Plattform auf eine, auf welcher ein unsigned
> int nicht 32bit groß ist (z.B. AVR)

Soweit ich sehen kann, steckt im BITNR-Makro überhaupt kein Datentyp 
drin.
Beim Wechsel auf eine andere Plattform wird man bei der Verwendung von 
Bitbanding auch noch ganz andere Portierungs-Probleme haben, als die 
Wortbreite der Maschine. Daß das Projekt mal auf einen AVR o.ä. portiert 
wird, kann ich definitiv ausschließen.
Für Laufzeitberechnung ist das eh nicht gedacht.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Thomas E. schrieb:
> Ohne "constexpr" gibt es nur noch 2 Fehler:

Achja, die Initialisierung ist auch C++11. Mit "((T) 1)" statt "T{1}" 
sollte es gehen.

Thomas E. schrieb:
> Soweit ich sehen kann, steckt im BITNR-Makro überhaupt kein Datentyp
> drin.

Aber es nimmt implizit 32bits an, denn es fragt die Bits 0-31 ab.

von Thomas E. (picalic)


Lesenswert?

Niklas G. schrieb:
> Achja, die Initialisierung ist auch C++11. Mit "((T) 1)" statt "T{1}"
> sollte es gehen.

Ok, danke, das geht, aber der Compiler erzeugt nun tatsächlich eine 
log2()-runtime Funktion.

Niklas G. schrieb:
> Aber es nimmt implizit 32bits an, denn es fragt die Bits 0-31 ab.

Diese Einschränkung wird aber erst bei Datenbreiten über 32 Bit 
relevant. Bis wir mal STM64Fxxxx-MPUs einsetzen, bin ich garantiert in 
Rente!

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Thomas E. schrieb:
> Diese Einschränkung wird aber erst bei Datenbreiten über 32 Bit
> relevant.

Naja, wenn int 16bit ist gibt das bestimmt lustige Compiler-Fehler... 
Wenn man sich auf eine einzelne Plattform beschränkt ist es aber ok.

Thomas E. schrieb:
> Ok, danke, das geht, aber der Compiler erzeugt nun tatsächlich eine
> log2()-runtime Funktion.
Auch bei maximaler Optimierungsstufe? Der ARMCC soll ja so toll 
optimieren können...

: Bearbeitet durch User
von Thomas E. (picalic)


Lesenswert?

Niklas G. schrieb:
> Naja, wenn int 16bit ist gibt das bestimmt lustige Compiler-Fehler...

Ich wüsste nicht, wie ich die provozieren sollte.
Die Funktion
1
uint16_t testfunc (uint16_t val)
2
{
3
  return BITNR(val);
4
}
führt jedenfalls nicht zu Fehlern.

Niklas G. schrieb:
> Auch bei maximaler Optimierungsstufe? Der ARMCC soll ja so toll
> optimieren können...

Aktuell verwende ich -O2. Mit -O3 (AFAIK die höchste Stufe) habe ich 
nicht getestet. Aber selbst mit -O0 compiliert
1
uint16_t testfunc (uint16_t val)
2
{
3
  return BITNR(0x80);
4
}
zu
1
 MOVS          r0,#0x07
2
 BX            lr

von Niklas Gürtler (Gast)


Lesenswert?

Thomas E. schrieb:
> Ich wüsste nicht, wie ich die provozieren sollte.

Indem du es für AVR kompilierst... Wie gesagt, das Problem tritt nur 
beim Portieren auf.

Thomas E. schrieb:
> Aktuell verwende ich -O2.

Dann kann der wohl die Rekursion nicht auflösen...

von Thomas E. (picalic)


Lesenswert?

Niklas Gürtler schrieb:
> Indem du es für AVR kompilierst... Wie gesagt, das Problem tritt nur
> beim Portieren auf.

kapiere ich nicht - wieso sollte sich der Compiler da überhaupt für 
Datentypen interessieren? Dann müsste ja eine Erweiterung des Makros auf 
>32 Bit (bei int = 32 Bit) hier auch zu Fehlern führen - tut es aber 
nicht!

Ich behaupte mal, daß das BITNR-Makro unverändert auch bei C-Compilern 
füt AVR und sogar PIC12 funktioniert.

: Bearbeitet durch User
von Niklas Gürtler (Gast)


Lesenswert?

Thomas E. schrieb:
> Ich behaupte mal, daß das BITNR-Makro unverändert auch bei C-Compilern
> füt AVR und sogar PIC12 funktioniert.

Ok, kann sein dass das aufgrund der Integer Promotion Regeln doch geht, 
die kann ich mir nicht merken. Gibt es keine Warnung wenn man einen 16 
Bit Int mit 0x10000 vergleicht (außerhalb des Bereichs)?
Sollte int aber 64Bit sein, fehlen jedenfalls 32 Fälle...

von W.S. (Gast)


Lesenswert?

Thomas E. schrieb:
> Ich warte
> aber immer noch (wie wohl auch der Verfasser des original-Beitrags, der
> den elendigen Bandwurm 2012 gepostet hat) auf die kurze und elegante
> Lösung für dieses Problem. Für einen, der die hohe Kunst des
> Programmierens wirklich beherrscht, sollte das ja ein Klacks sein.

Ist es auch. Zu allererst wird die ganze ST-Lib einfach rausgehalten. 
Dann wird sich davon verabschiedet:

Jan K. schrieb:
> ich habe einen Pin welcher definiert wurde z.B. GPIO_Pin_10.

Dann schreibt man sich seinen Lowlevel-Treiber direkt und benutzt die 
bei diesem Chip vorhandenen Möglichkeiten mit BSRR für Ausgaben in IDR 
für Abfragen. Das ergibt dann nen Einzeiler, so wie ich das bereits 
geschrieben habe.

Voraussetzung: Man übernimmt nicht ohne jegliches Nachzudenken sowas wie 
GPIO_Pin_10 und rennt damit voll vor die Wand, sondern denkt VORHER 
drüber nach, wie man sinnvoll zum Ziele kommt. Von wegen "ich habe einen 
Pin welcher definiert wurde.." - das ist SichInDieHosenMach weil die 
Kraft fehlt, das Problem zu überblicken und es an der richtigen Stelle 
geradezurücken. Und DIESES Problem hier ist ja nun wirklich geradezu 
winzig.

Ich sag's nochmal: Ich erwarte von den Leuten, daß sie wissen, was sie 
tun, BEVOR sie es tun.

W.S.

von Thomas E. (picalic)


Lesenswert?

Niklas Gürtler schrieb:
> Ok, kann sein dass das aufgrund der Integer Promotion Regeln doch geht,
> die kann ich mir nicht merken. Gibt es keine Warnung wenn man einen 16
> Bit Int mit 0x10000 vergleicht (außerhalb des Bereichs)?

Bei einer Operation mit zwei unterschiedlich großen Integer-Datentypen 
wird ja immer der kleinere Operand vor der Operation erstmal in den 
größeren Datentypen umgewandelt. Für eine "nackte" Zahl wird zunächst 
der Datentyp benutzt, in den die Zahl 'reinpasst. Wenn man also einen 
uint8_t-Operanden mit 0x10000 verknüpft, wird er intern auf 32 Bit 
gecastet, weil 0x10000 ja auch nicht mehr in 16 Bit passt und somit 32 
Bits erforderlich sind. Dabei gibt der (zumindest der ARM-) Compiler 
auch keine Warnung aus. Eine Warnung gibt er u.U. zu den Auswirkungen 
aus, z.B. bei
if(var8bit & 0x10000)
  return;
(mehr code)
kommt eine Warnung "statement is unreachable" für die Zeils (mehr code).

W.S. schrieb:
> Zu allererst wird die ganze ST-Lib einfach rausgehalten.

Mach das halt, wenn Du es für richtig hältst. Viel Spass bei der 
Erstellung Deiner eigenen Header-Dateien mit den tausenden 
Bit-Definitionen der ganzen Pertipherie-Module. Aber schreib hier bitte 
nicht, daß "man" es so macht!

W.S. schrieb:
> Dann schreibt man sich seinen Lowlevel-Treiber direkt und benutzt die
> bei diesem Chip vorhandenen Möglichkeiten mit BSRR für Ausgaben in IDR
> für Abfragen. Das ergibt dann nen Einzeiler, so wie ich das bereits
> geschrieben habe.

Das simple Setzen und Rücksetzen von Portbits per BSRR-Register geht 
auch - oh Wunder! - mit dem Zeug von ST als Einzeiler, so daß es auch 
jeder andere STM32-Programmierer ohne Deinen selbstgestrickten und 
inkompatiblen Spezial-Treiber benutzen kann. Offensichtlich hast Du 
nicht verstanden, worum es geht - der TO hat sicher auch kein Problem 
damit, sein gewünschtes Portbit auf High zu setzen.

von W.S. (Gast)


Lesenswert?

Thomas E. schrieb:
> Offensichtlich hast Du
> nicht verstanden, worum es geht - der TO hat sicher auch kein Problem
> damit, sein gewünschtes Portbit auf High zu setzen.

Offensichtlich hast du weder Manieren noch eine Ahnung, worum es hier 
geht. Also laß lieber deine unangemessenen Behauptungen und versuche, 
die bisherigen Beiträge verstehend zu lesen.

W.S.

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.