2016-08-07 12 views
1

Ich habe gerade angefangen, C (aus Java kommend) zu schreiben. Ich versuche herauszufinden, wie sich die Sprache einer Bedingung anpasst, die auf dem Namen einer Definition basiert.Bedingte Anweisungen basierend auf dem Namen von # define

z.B. Ich habe eine riesige Header-Datei, die ich nicht mit vielen Definitionen bearbeiten kann (sollte).

#define GPIO_OTYPER_OT_0      ((uint32_t)0x00000001) 
#define GPIO_OTYPER_OT_1      ((uint32_t)0x00000002) 
#define GPIO_OTYPER_OT_2      ((uint32_t)0x00000004) 
#define GPIO_OTYPER_OT_3      ((uint32_t)0x00000008) 
#define GPIO_OTYPER_OT_4      ((uint32_t)0x00000010) 
#define GPIO_OTYPER_OT_5      ((uint32_t)0x00000020) 

Und so weiter;

Ich möchte eine Funktion/Deklaration (oder was auch immer die Lösung ist) machen, um auf den _ # Teil der Definition zu wirken.

(Pseudo-Code)

void initialize(int X) { 
    GPIOA->MODER |= GPIO_MODER_MODER%X_5; 
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT_%X; 
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR%X; 
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5; 
    GPIOA->ODR |= GPIO_ODR_ODR_%X; 
} 

Wo% X int X

Alles, was ich denken kann, ist eine switch-Anweisung für jeden X aber X verfügt über einen großen Bereich, so dass die Quelldatei sehr groß sein würde.

bearbeiten: https://github.com/espruino/Espruino/blob/master/targetlibs/stm32f4/lib/stm32f411xe.h ist die Header-Datei.

+2

Ich würde eine zweite Kopfzeile generieren, die den ursprünglichen (unveränderbar) Header nachahmt, aber dies ist sinnvoll. Zum Beispiel: '#define GPIO_OTYPER_OT (x) ((uint32_t) 1 << (x))' und dann vielleicht '# GPIO_OTYPER_OT_0 GPIO_OTYPER (0)' usw. definieren und Code generieren, um die Ergebnisse zu überprüfen. Dann benutze den gesunden Header, nicht den wahnsinnigen. –

+1

Ich würde das Problem betrachten, das Sie versuchen zu lösen, um zu sehen, warum das, was Sie tun, der falsche Weg ist, es zu lösen. – stark

+1

Korrigieren Sie mich, wenn ich falsch liege, aber 'initialize()' riecht sehr viel wie das nächste, was Sie tun würden, ist es in einer Schleife zu nennen ", um jeden GPIO-Pin zu initialisieren"; Das wäre ein eindeutiger Fall von Denken auf der falschen Ebene. Wenn Sie initialisieren, tun Sie dies auf der _port_-Ebene, d. H. 'GPIOA-> OTYPER = GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_2 | GPIO_OTYPER_OT_5; '. Eine Flut von Lese-Modifizier-Schreib-Operationen auf demselben Register ist eine vollständige Verschwendung von Zyklen. Ich vermute, dass Sie _straight_ nicht in Bit-bangende bidirektionale Protokolle oder irgendetwas anderes springen, das die Neukonfiguration von beliebigen GPIOs zur Laufzeit erfordern könnte. – Notlikethat

Antwort

1

Verwenden von ST GPIO-Abstraktionsschicht, die here gefunden werden kann. Bemerkenswert ist, siehe GPIO_InitTypeDef, die Ihnen eine Struktur für das, was Sie oben tun, und GPIO_Init, die tatsächlich tun, was Sie wollen, gibt. Die Initialisierungsstruktur nimmt Pins als eine Bitmaske, so dass @artless Rauschen in einem Kommentar vorgeschlagen wird, können Sie einfach 1<<X Ihre Maske erstellen. Das gesamte MCU-spezifische Verhalten und die Registerzuordnung sind außerhalb Ihres Codes verborgen.

Wenn Sie versuchen, Ihre eigene Treiberschicht als eine Übung zu implementieren, oder weil Sie die ST-Bibliothek denken nicht sehr gut ist, dann würde ich noch einen Blick darauf werfen, wie sie GPIO_Init im C file umgesetzt. Sie verwenden Verschiebung, aber Sie werden feststellen, dass, wenn Sie mit den Registern umgehen, es ist nicht immer so einfach wie 1<<X (Beachten Sie jedoch, dass für ihre Konfigurationsstruktur immer so einfach ist). Einige Register haben mehrere Bits für jeden Pin (Modus: 2 Bits, Pull-Konfiguration: 2 Bits, Alternativfunktion: 4 Bits, aufgeteilt auf mehrere Register).

edit: Ich schlage nicht vor, weitere Bibliotheken hinzuzufügen, die Sie noch nicht haben. Die Bibliothek/Codebasis, auf die Sie die Header-Datei verwiesen haben, enthält bereits die Peripheriebibliothek von ST, so dass es für mich (sinnvoll) ist, sie zu verwenden.

+0

Danke, ich wollte es durch die CMSIS-Bibliothek implementieren, um besser zu verstehen, wie es funktioniert (ohne die "Abstraktion"). Ich werde auf jeden Fall überprüfen, wie die HAL implementiert wurde. Und wahrscheinlich anfangen zu verwenden, wenn ich mit meinem Verständnis der Dinge vertraut bin. – Enders

+0

Es ist eine gute Übung, um Ihre eigenen Treiber zu schreiben, obwohl es nicht das aufregendste ist. Ich würde normalerweise vorschlagen, ein Gefühl dafür zu bekommen, wie Low-Level-Treiber normalerweise strukturiert sind, bevor Sie Ihre eigenen ausprobieren, aber jeder lernt anders. Tun Sie, was Ihnen am meisten hilft. – rjp

+0

Ich würde auch vorschlagen, auf eine neuere Version des Headers von ST zu überprüfen. Ich glaube, dass NXP/Freescale damit begonnen hat, Makros zu unterstützen, die die Pin-Nummer als Parameter haben, also könnte es eine neuere Version von ST geben, die etwas ähnliches hat. – rjp

1

Es gibt keine Möglichkeit, eine beliebige ganze Zahl in einen Makronamen einzufügen. Ihre Ganzzahlen sind jedoch klein (kleiner als 32, da alle Ihre Konstanten einen 32-Bit-Typ haben). So können Sie einen Switch-Anweisung in einer einzeiligen Ausdruck konvertieren, etwa so:

x == 0 ? GPIO_OTYPER_OT_0 : \ 
x == 1 ? GPIO_OTYPER_OT_1 : \ 
x == 2 ? GPIO_OTYPER_OT_2 : \ 
... 
x == 31 ? GPIO_OTYPER_OT_31 : 0 

Hier können Sie auch eine „default“ Ausdruck machen, die einen Laufzeitfehler generieren - so etwas wie (abort(), (uint32_t)0).

Um dies zu allgemeineren, trennen Sie die GPIO_OTYPER_OT_ Teil in ein Makro-Argument, und verwenden Sie die "Paste Operator" ##:

#define MY_MACRO(name, x) \ 
x == 0 ? name ## 0 : \ 
x == 1 ? name ## 1 : \ 
x == 2 ? name ## 2 : \ 
... 
x == 31 ? name ## _31 : \ 
(abort(), name ## 0) 

Anwendungsbeispiel:

GPIOA->ODR |= MY_MACRO(GPIO_ODR_ODR_, x); 

Sie haben eine machen separate Makro für die Namen, die x in der Mitte haben:

#define MY_MACRO2(prefix, x, suffix) (\ 
(x) == 0 ? prefix ## 0 ## suffix : \ 
(x) == 1 ? prefix ## 1 ## suffix : \ 
... 
(x) == 31 ? prefix ## 31 ## suffix : \ 
(abort(), prefix ## 0 ## suffix)) 

Hier fügte ich auch die notwendigen Klammern hinzu (etwa x und um das gesamte Makro), wie es bei C-Makros üblich ist.


P.S. Wenn Ihre große Header-Datei keine Makros mit Zahlen bis 31 definiert, aber eine kleinere Grenze hat, können Sie das Makro, das all diese Namen erwähnt, nicht verwenden, da Sie einen Kompilierungsfehler erhalten würden. Fügen Sie in diesem Fall das Maximum in den Namen des Makros ein. Dann können Sie sie in einer „rekursiven“ Art und Weise definieren:

#define MY_MACRO_MAX1(prefix, x) \ 
x == 0 ? prefix ## 0 ## suffix : prefix ## 1 ## suffix 

#define MY_MACRO_MAX2(prefix, x) \ 
x == 2 ? prefix ## 2 ## suffix : MY_MACRO_MAX1(prefix, x) 

#define MY_MACRO_MAX3(prefix, x) \ 
x == 3 ? prefix ## 3 ## suffix : MY_MACRO_MAX2(prefix, x) 

#define MY_MACRO_MAX4(prefix, x) \ 
x == 4 ? prefix ## 4 ## suffix : MY_MACRO_MAX3(prefix, x) 

#define MY_MACRO_MAX5(prefix, x) \ 
x == 5 ? prefix ## 5 ## suffix : MY_MACRO_MAX4(prefix, x) 

... 
Verwandte Themen