C-Programmiersyntax für NXP LPC1768

Meine Frage mag seltsam erscheinen, aber ich bin sehr neu in nxp-Mikrocontrollern. Es scheint mir, dass die Leute bei der C-Programmierung unterschiedliche Syntax verwenden. Um beispielsweise einen Pin als Ausgang zu setzen, haben wir:

LPC_GPIO1->FIODIR |= (1<<29);

während es eine andere Syntax gibt, die dasselbe tut und so aussieht:

FIO1DIR |= (1<<29);

Und beide haben ähnliche Bibliotheken. Da ich den zweiten Typ lerne, wollte ich wissen, ob es eine Referenz für diese Art der Codierung gibt?

Öffnen Sie die Header-Dateien und verfolgen Sie zurück, bis Sie herausfinden, wo diese definiert sind. Wie Sie sehen können, werden sie unterschiedlich geschrieben. Wenn Sie sie gefunden haben, werden Sie sehen, was der Unterschied ist.

Antworten (2)

es wird alles von den verwendeten Header-Dateien gesteuert. Der erste Ansatz verwendete eine Struktur (Zeiger) auf die gpio-Basisadresse, um alle Register zu adressieren, die sich auf diesen Port beziehen. Der zweite Ansatz verwendete einen Zeiger auf genau dieselbe Adresse.

Letztendlich ist es das gleiche, andere Verpackung.

Bearbeiten: Hier ist das Wesentliche.

typedef struct {
    uint32_t PIN;           //data register
    uint32_t SET;
    uint32_t DIR;
    uint32_t CLR;
} GPIO_TypeDef;             //gpio structure

#define GPIO                ((GPIO_TypeDef *)&IOPIN)

int main(void) {
    while (1) {
        GPIO->DIR ^=0xffff; //flip the lowest 16 bits
        IODIR     ^=0xffff; //doing the same
    }
}

Dieser spezielle Chip hat vier Register, die seinem GPIO, IOPIN, IOSET, IODIR und IOCLR zugeordnet sind; Eine Möglichkeit, es anzugehen, in diesem Fall IODIR, besteht darin, das in der Header-Datei definierte Makro zu verwenden, wie in der Hauptschleife gezeigt.

Alternativ können Sie eine Struktur GPIO_TypeDef definieren, die die vier Register in der richtigen Reihenfolge enthält (gemäß dem Compiler). Sie "reparieren" dann den GPIO als Zeiger auf diese Struktur, aber verankert an derselben Adresse, beginnend mit IOPIN.

Die beiden Ansätze, die niedrigsten 16 Bits in den IODIR-Registern umzudrehen, erreichen also dasselbe Ziel.

Dieser Ansatz erfordert einige Dinge: 1. Die Portstruktur ist von Port zu Port, von Chip zu Chip gleich; 2. Die Endianness ist bekannt.

Die meisten neueren Chips (mit Ausnahme von msp430 und pic) folgen dem. Sie werden also den heutzutage weit verbreiteten Struct-Ansatz verwenden.

Danke, ich muss lernen, wie sie funktionieren. Wo kann ich eine Referenz haben?
@user2326844 open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf ist die Referenz.
Der Vorteil der Verwendung von Strukturen besteht darin, dass es möglicherweise einfacher ist, Ihren Code zu warten, wenn Sie eine Funktion von einem Port zu einem anderen verschieben. Der mögliche Nachteil ist, dass es etwas größeren Code erzeugen kann, aber in der Praxis werden die meisten modernen optimierenden Compiler erkennen, dass dies nur zwei verschiedene Arten sind, mit demselben Register zu sprechen, so dass dies herausoptimiert wird und keinen Unterschied in der endgültigen Codegröße machen sollte.

Dies ist elementare C-Programmierung, die nichts mit Ihrem Mikrocontroller zu tun hat.

Sie können eine Variable haben:

unsigned int jim;

und Sie können Strukturen haben

typedef struct
{
    unsigned int bob;
    unsigned int ted;
    unsigned int carol;
    unsigned int alice;
}  MYSTRUCT;
MYSTRUCT one;
MYSTRUCT *two;

In einem Fall haben wir den Compiler gebeten, den Speicher für diese Struktur zuzuweisen, es ist im Grunde keine Malloc-Sache. Für die beiden Fälle haben wir also nur einen Zeiger zugewiesen, die Adresse der Struktur, aber der Speicher für die Struktur ist noch nicht zugewiesen. In dem Fall, den Sie sehen, verwenden sie den letzteren Fall und verwenden dann C-Tricks, um die Adresse von zwei zu einer bestimmten Adresse zu machen. Dann können wir wissen / annehmen, dass aus diesem bob, ted, ... bekannte Offsets sind. Wenn Sie also beispielsweise eine Bank von UART-Registern haben und es 10 davon gibt und sie 32 Bit breit sind, könnten Sie theoretisch eine 10-Elemente-Struktur von 32-Bit-Variablen definieren und dem Compiler mitteilen, dass Sie diese Struktur auf den Anfang dieses Registers abzielen möchten Bank und dann durch Zugriff auf Elemente innerhalb der Struktur greifen Sie auf diese Register zu. Oder Sie könnten einfach einzelne Variablen wie die obige jim-Variable erstellen und diese ebenfalls auf eine Stelle verweisen (während der Deklaration, damit zur Kompilierzeit die Adresse erzwungen wird). Es gibt mehr C-Syntax, die ich nicht zeige, um die Adresse zu erzwingen, aber die Verwendung der oben definierten Variable für diese Variationen lautet wie folgt:

jim=5;
one.bob=5;
two->bob=5;

Bei einer Variablen verwenden Sie nur den Namen, wenn es sich um eine Variable innerhalb einer Struktur handelt, dann hängt es davon ab, ob der Basisname der Struktur ein Zeiger ist oder nicht. Wenn kein Zeiger verwendet wird, verwenden Sie einen Punkt zwischen dem Basisnamen der Struktur und der Variablen darin one.bob, und wenn es ein Zeiger ist, dann verwenden Sie einen Pfeil zwei->bob. Nehmen Sie an, dass eins und zwei nicht auf dieselbe Adresse zeigen, also sind dies wirklich zwei separate Bob-Variablen.

Was Sie sehen, sind zwei verschiedene Möglichkeiten, das Problem der Erstellung von Variablen anzugehen, die an der Adresse eines Hardwareregisters verwendet werden können, und durch die Verwendung verschiedener Deklarationstechniken erhalten Sie die beiden Stile, auf die Sie in Ihrer Frage hingewiesen haben.

Entschuldigung, ich kann im Moment nicht abstimmen. Also, vielen Dank.