Forum: Compiler & IDEs ARM-GCC: "pointers are not permitted as case values"


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,
und wieder einmal haben sich Lücken in meinem C-Wissen aufgetan, wo ich 
sie nicht vermutet habe. Obwohl mittlerweile auch der K&R in meinem 
Besitz und benutzt ist.

Ich will ein switch-case-Konstrukt mit einer Zeigervariable machen (ich 
nutze den ARM-GCC):
1
void init(GPIO_TypeDef * gpio) 
2
{
3
4
  switch(gpio) {
5
  case GPIOA:
6
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
7
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
8
    break;
9
  case GPIOB:
10
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
11
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
12
    break;
13
  case GPIOC:
14
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
15
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
16
    break;
17
  case GPIOD:
18
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
19
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
20
    break;
21
  case GPIOE:
22
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
23
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
24
  }
25
}
was wegen obiger Fehlermeldung nicht geht.

Aber wie geht man hier sauber vor? In ein uint32_t casten? In eine 
if/elseif-Kette umwandeln? Oder etwas ganz anderes?

Viele Grüße
W.T.

von Oliver S. (oliverso)


Lesenswert?

Der cast würde funktionieren, ist aber eher in die Kategorie "Hack" 
einzustufen. If/Else geht.

Oliver

von Rolf Magnus (Gast)


Lesenswert?

Warum übergibst du denn überhaupt einen Pointer, wenn du ihn nur zur 
Fall-Unterscheidung nutzen willst?

von Walter T. (nicolas)


Lesenswert?

Rolf Magnus schrieb:
> Warum übergibst du denn überhaupt einen Pointer, wenn du ihn nur zur
> Fall-Unterscheidung nutzen willst?

Kennst Du eine bessere Möglichkeit, GPIOs zu unterscheiden?

von Rene H. (Gast)


Lesenswert?

1
   switch(*gpio) ....

sollte das Problem lösen.

Grüsse,
R.

von Walter T. (nicolas)


Lesenswert?

Rene H. schrieb:
> switch(*gpio) ....
>
> sollte das Problem lösen.

Hmmmm...GPIO_TypeDef ist wie folgt definiert:
1
typedef struct
2
{
3
  __IO uint32_t CRL;
4
  __IO uint32_t CRH;
5
  __IO uint32_t IDR;
6
  __IO uint32_t ODR;
7
  __IO uint32_t BSRR;
8
  __IO uint32_t BRR;
9
  __IO uint32_t LCKR;
10
} GPIO_TypeDef;
Was bewirkt der Dereferenzierungsoperator bei einem struct?

Wenn ich darüber nachdenke, wundere ich mich gerade, warum der 
"=="-Operator hier funktioniert...naja, viel Padding wird bei uint32_t 
ja nicht gemacht.

von W.S. (Gast)


Lesenswert?

Rene H. schrieb:
> sollte das Problem lösen.

Das löst garnichts. Bei dem Controllertyp, den Walter gerade benutzt 
und dem Headerfile, was er dafür gerade in Händen hält, sind die 
Ports als struct's deklariert. Einen kompletten Struct kann man aber 
nicht als case-Value benutzen, das ist auch logischer Unsinn.

Abgesehen davon halte ich Walters Herangehensweise inzwischen auch für 
GRUNDFALSCH. Er versucht nämlich, die Ports per Namen zu 
vereinheitlichen, um Namensgleichheit zwischen ARM und AVR herzustellen. 
Wahrscheinlich will er gleiche Initialisierungs-Quelltexte für so 
unterschiedliche Architekturen schreiben. Sowas kann nicht funktionieren 
und es ist auch logischer Mumpitz. Ungleiche Hardwaren (Hardwares ?) 
kann man auf der alleruntersten Ebene NICHT über einen Kamm scheren, 
dazu schreibt man sich  lowest-level-Routinen, die höhere Funktionalität 
(also Lampe_ein, Motor_aus und so) auf die darunterliegende Hardware 
umsetzen.

W.S.

von Rene H. (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Was bewirkt der Dereferenzierungsoperator bei einem struct?

Ah, ok, das wusste ich nicht, dass das ein typedef struct ist, ich 
dachte an einen enum. Dann geht das natürlich nicht.
In dem Fall würde ich eher auf if else gehen (nicht schön, braucht aber 
meines Wissens nicht mehr Taktzyklen).

Walter Tarpan schrieb:
> naja, viel Padding wird bei uint32_t
> ja nicht gemacht.


Da sollten bei einem ARM eigentlich kaum Padding Bytes eingesetzt 
werden.

Grüsse,
R.

von Walter T. (nicolas)


Lesenswert?

W.S. schrieb:
> Abgesehen davon halte ich Walters Herangehensweise inzwischen auch für
> GRUNDFALSCH.
> [...]
> Wahrscheinlich will er gleiche Initialisierungs-Quelltexte für so
> unterschiedliche Architekturen schreiben.

Auf einem gewissen Level hast Du sogar Recht: Ja, in der main() soll 
tatsächlich einfach
1
Pwmdat_t = pwm_init(GLCD_PWM_CH);
stehen. Für beide Architekturen.

W.S. schrieb:
> [...]
> Er versucht nämlich, die Ports per Namen zu
> vereinheitlichen, um Namensgleichheit zwischen ARM und AVR herzustellen.
> [...]

Nein, das will ich nicht. Nicht überall. Du mischst hier mehrere 
Forenanfragen zu Funktionen auf völlig unterschiedlicher Ebene. Es gibt 
eine Handvoll Bitbangig-Sachen, wo ich das tatsächlich gemacht habe. Und 
es gibt deutlich mehr Parallelimplementierungen, wo sich AVR und Cortex 
lediglich die Headerdateien teilen.

Und generell hat das bislang sehr gut geklappt. Ich habe mein Projekt 
jetzt zu 90% von AVR auf ARM migriert und die Source-Code-Qualität auf 
beiden Seiten hat deutlich zugenommen.

von holger (Gast)


Lesenswert?

>Ich habe mein Projekt
>jetzt zu 90% von AVR auf ARM migriert und die Source-Code-Qualität auf
>beiden Seiten hat deutlich zugenommen.

Und die Geschwindigkeit vermutlich deutlich abgenommen.

von Dr. Sommer (Gast)


Lesenswert?

Protip: Die CMSIS/Standard Peripheral Library hat extrem schlechtes 
Design. Alles was man an Informationen benötigt um einen Port zu 
identifizieren und nutzen ist eine 3 (oder so) bit-Zahl die die 
Port-Nummer (0-4 = A-E o.ä.) angibt. Die Adresse der zugehörigen 
Port-Register und die RCC-Register-Bitnummer ("RCC_APB2Periph_GPIOx") 
lässt sich direkt daraus berechnen (einfach mal in die Definition von 
RCC_APB2Periph_GPIOx, und ins Manual über die RCC-Register schauen). 
D.h. es reicht dann
1
enum Port { GPIOA=0, GPIOB, GPIOC, GPIOD };
2
void init (Port p) {
3
  RCC_APB2PeriphClockCmd(4 << p,ENABLE);
4
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
5
}
6
int main () {
7
  init (GPIOB);
8
}
Und schon hast du den ganzen Aufwand gespart...

von 11erin (Gast)


Lesenswert?

Walter Tarpan schrieb:
> In ein uint32_t casten?
Wenn schon casten, dann korrekterweise in ein intptr_t.

von Klaus W. (mfgkw)


Lesenswert?

oder noch korrekter (tm) uintptr_t

von Walter Tarpan (Gast)


Lesenswert?

holger schrieb:
> Und die Geschwindigkeit vermutlich deutlich abgenommen.

Nicht genug, als daß die Funktion des Geräts dadurch beeinträchtigt 
worden wäre.

Vermutlich dauert die Initialisierung jetzt 100mal länger als zuvor - 
dummerweise ist sie aber immer noch so schnell, daß sie fertig ist, 
bevor ich den Finger vom Netzschalter zu den Bedienelementen bewegt 
habe.

Dr. Sommer schrieb:
> Protip: Die CMSIS/Standard Peripheral Library hat extrem schlechtes
> Design. Alles was man an Informationen benötigt um einen Port zu
> identifizieren und nutzen ist eine 3 (oder so) bit-Zahl die die
> Port-Nummer (0-4 = A-E o.ä.) angibt. Die Adresse der zugehörigen
> Port-Register und die RCC-Register-Bitnummer ("RCC_APB2Periph_GPIOx")
> lässt sich direkt daraus berechnen (einfach mal in die Definition von
> RCC_APB2Periph_GPIOx, und ins Manual über die RCC-Register schauen).
> D.h. es reicht dannenum Port { GPIOA=0, GPIOB, GPIOC, GPIOD };
> void init (Port p) {
>   RCC_APB2PeriphClockCmd(4 << p,ENABLE);
>   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
> }
> int main () {
>   init (GPIOB);
> }Und schon hast du den ganzen Aufwand gespart...

Danke für den Tipp! Die GPIOx als enum neu zu definieren wiederstrebt 
mir zwar- aber das Ganze funktioniert ja auch mit den 
Original-Definitionen.

von Dr. Sommer (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Danke für den Tipp! Die GPIOx als enum neu zu definieren wiederstrebt
> mir zwar-
Immer noch viel besser als der "#define" Haufen aus der Std Peripheral 
Library... Von mir aus auch "static const uint8_t GPIOA = 0;".

von Walter T. (nicolas)


Lesenswert?

Klaus Wachtler schrieb:
> oder noch korrekter (tm) uintptr_t

So einfach ist es doch nicht:
1
  switch( (uintptr_t) Pwmcfg.GPIOx) {
2
  case (uintptr_t) (GPIOA):
3
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
4
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
5
  }
liefert mir immer noch die Fehlermeldung "warning: case label is not an 
integer constant expression [-pedantic]", obwohl definiert ist:
1
#define GPIOA ((GPIO_TypeDef *) ((((uint32_t)0x40000000) + 0x10000) + 0x0800))

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das ist kein Fehler, es ist eine Warnung.

von Walter T. (nicolas)


Lesenswert?

Johann L. schrieb:
> Das ist kein Fehler, es ist eine Warnung.

Stimmt. Den Tippfehler kann ich allerdings wegen des Hinweises auf den 
Tippfehler nicht mehr beheben :-)

von W.S. (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Und generell hat das bislang sehr gut geklappt.

..und hat dir bisher ca. 70000 Quellzeilen beschert, wie du in einem 
anderen Thread geschrieben hattest. Ich stell jetzt mal keine 
Vermutungen an, ob das bislang alles nur Quellzeilen waren, die aus dem 
Gleichmach-Wunsch zwischen AVR und ARM deinerseits herrühren. Ich sag 
nur eins: du stellst das Ganze verkehrt an, stolperst dabei über 
grundsätzliche Unmöglichkeiten, die dir nicht nur in C blühen, sondern 
dich auch in allen anderen Programmiersprachen angrinsen würden - und du 
bist erstaunlich beratungsresistent. Na dann mach mal.

W.S.

von Walter T. (nicolas)


Lesenswert?

W.S. schrieb:
> [...]
> und du
> bist erstaunlich beratungsresistent.

Du meinst weil ich
 a) in einem Menü einen Funktionszeiger caste, anstelle das Menü einfach 
komplett objektorientiert neu zu schreiben und
 b) einfache Bitbanging-Funktionen partout nicht für jede Plattform 
einzeln in eine Datei schreibe
bin ich beratungsresitent?

Du hast Recht!

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.