ARM-GCC: "pointers are not permitted as case values"

#3628225
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.
#3628326
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.
Gast #3628342
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.
Gast #3628351
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.
#3628358
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.
Gast #3628494
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...
Gast #3628686
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.
#3629597
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))
Gast #3631343
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.
#3631448
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!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren