Gibt es eine Möglichkeit, einen generischen GPIO-Code für MSP430 zu schreiben?

Ich arbeite derzeit an einem MSP430-Board und versuche, Bibliotheken dafür zu erstellen, damit ich sie problemlos in meinem Projekt verwenden kann. Ich beginne mit grundlegenden Digital-I/O-Funktionen.

Angenommen, ich muss P1.0 auf EIN stellen. In diesem Fall mache ich normalerweise Folgendes:

P1SEL &= (~BIT0);           // Set P1.0 SEL for GPIO
P1DIR |= BIT0;              // Set P1.0 as Output
P1OUT |= BIT0;              // Set P1.0 HIGH

Aber ich muss eine Funktion schreiben, die nur Port Num, Pin und Value nimmt und die obigen Register setzt. Etwas wie das:

void setVal(int x, int y) {
    PxSEL &= (~BITy);
    PxDIR |= BITy;
    PxOUT |= BITy;
}

Wobei x Port und Y Pin ist. Gibt es eine Möglichkeit, eine solche Funktion zu implementieren? ODER wurde das schon mal gemacht? Wenn ja, teilen Sie bitte den Link dafür. Ich dachte daran, vielleicht eine Nachschlagetabelle zu verwenden und das Register über die Indizierung auszuwählen. Aber ich bin mir nicht sicher, ob das ein guter Ansatz ist. Jede Hilfe wäre willkommen.

Danke

Der Bitmaskenteil ist einfach: uint16_t BITy = 1 << y;. Um die Port-Register zu erhalten, müssen Sie sich wahrscheinlich die Rohadressen ansehen und herausfinden, wie Sie sie aus berechnen können y.
Danke @ThePhoton. Gibt es keinen besseren Weg, dies für den PORT-Nummernteil zu tun?
Es wäre portabler, einige große Fallanweisungen mit den Makros PxSEL/PxDIR/PxOUT zu machen. Hoffentlich ist Ihr Compiler schlau genug, um das zur Laufzeit auf etwas zu reduzieren, das einigermaßen schnell ist.

Antworten (4)

Dies kann mit einer Reihe von Makros erfolgen:

#define GPIO_MODE_GPIO(...)  GPIO_MODE_GPIO_SUB(__VA_ARGS__)
#define GPIO_MODE_GPIO_SUB(port, pin) (P##port##SEL &= ~(1<<pin)) // making an assumption about register layout here

#define GPIO_OUT_SET(...)  GPIO_OUT_SET_SUB(__VA_ARGS__)
#define GPIO_OUT_SET_SUB(port, pin) (P##port##OUT &= ~(1<<pin))

//etc...

Der ##Operator führt die Zeichenfolgenverkettung im Präprozessor durch. Die zweistufigen Makros werden verwendet, um eine gewisse Makroerweiterung zwischen dem ersten und dem zweiten Makro zu ermöglichen, wodurch Sie Dinge wie die folgenden tun können:

#define LED_IO  1,5
GIPO_OUT_SET(LED_IO);

Ohne diese zusätzliche Indirektion würde sich der Präprozessor darüber beschweren, dass er nicht genügend Argumente für das GPIO_OUT_SETMakro hat.

Dieser Systemstil hat sich ziemlich gut an jede MCU angepasst, die ich bisher verwendet habe, von AVRs bis ARMs, und da es sich um ein Präprozessor-Makro handelt, lässt es sich in den kleinstmöglichen Satz von Anweisungen kompilieren.

Dies funktioniert nur, wenn die Portnummer zur Kompilierzeit bekannt ist. Der Skelettcode von OP impliziert, dass er zur Laufzeit an eine Funktion übergeben wird (obwohl dies möglicherweise nicht das ist, was sie tatsächlich benötigen).
Das hatte ich angenommen, Photon
Fair genug, das war eine große Annahme von meiner Seite. Ich kann mir einige Situationen vorstellen, in denen Sie ein IO basierend auf einer Variablen festlegen möchten, aber ich bin selbst nie auf eine solche Anwendung gestoßen (abgesehen von Arduino und anderen stark abstrahierten HALs, bei denen das GPIO der Einfachheit halber zur Laufzeit abstrahiert wird). statt tatsächlicher Notwendigkeit oder Nutzen).
@ThePhoton Du meinst, das funktioniert nur dann nicht, wenn ich einen Port basierend auf einer Variablen festlegen möchte, die ich während der Laufzeit erhalten habe, oder? Aber wird funktionieren, wenn meine LED_IO und andere IOs konstant sind?

Gibt es eine Möglichkeit, eine solche Funktion zu implementieren?

Sicher, das kann man machen. Aber der Code wird nicht mehr portierbar sein.
Sie müssen das Verhalten validieren, wenn sich das Teil ändert.

Die GPIO-Peripherieregister werden auf dem Bus in den folgenden Bereichen abgebildet:

MODULE       BASE  SIZE
Port P1, P2  0200h 0020h  
Port P3, P4  0220h 0020h  
Port P5, P6  0240h 0020h

Dies ist eine etwas untraditionelle Implementierung von TI. Sie haben gerade und ungerade Ports zu einem Block zusammengeführt. Normalerweise sind alle Register eines GPIO-Blocks fortlaufend, was einen arithmetischen Ansatz praktikabler machen würde.
Das obige Layout ergibt jedoch die folgenden Register:

P1OUT = 0200h + 02h  
P2OUT = 0200h + 03h  
P3OUT = 0220h + 02h  
P4OUT = 0220h + 03h  
P5OUT = 0240h + 02h  
P6OUT = 0240h + 03h  

Mit diesen können Sie den folgenden Code erstellen, indem Sie die obige Tabelle verwenden:

void setVal(int x, int y) {
    const unsigned char *PxOUT = {0x202, 0x203, 0x222, 0x223, 0x242, 0x243};
    if(y)
        *PxOUT[x] |= (1<<y);
    else
        *PxOUT[x] &= ~(1<<y);
}

Eine Tabelle ist die schnellste verfügbare Methode. Auf Kosten einiger ROMs.
Dies kann geändert werden, um auch den Zugriff auf andere Register zu ermöglichen.
Beispielsweise hat PxOUT[] -2 auf das PxIN-Register zugegriffen, und +2 ist das PxDIR-Register.

Ich habe meine Infos aus dem Datenblatt des msp430fr2153 entnommen .
Tabelle 6-33 und Tabelle 6-41. Dies stimmt möglicherweise nicht mit Ihrem Teil überein, bitte überprüfen Sie .

Mein Ansatz bei MCUs, die >8kB Speicher bieten, besteht darin, GPIOs in Strukturen zu packen:

//
// GPIO Single Pin Descriptor 
//
struct GPIO_SPD      {      const char *    Description;
                            uint8_t         Port;
                            uint16_t        Pin;
                            bool            Initialzied;
};

Dann können Sie einen GPIO in Ihrem Modul definieren:

struct GPIO_SPD GpioBlinkingLed= {"Simply blinking LED", GPIO_PORT_P1, GPIO_PIN0, false};

Und verwenden Sie diese Struktur in einfachen gpio-Funktionen, die Ihrer ähnlich sind. Diese Struktur kann beispielsweise wie folgt direkt in der TI- Treiberbibliothek verwendet werden:

GPIO_setAsOutputPin(GpioBlinkingLed.Port, GpioBlinkingLed.Pin);
GPIO_setOutputHighOnPin(GpioBlinkingLed.Port, GpioBlinkingLed.Pin);
GPIO_toggleOutputOnPin(GpioBlinkingLed.Port, GpioBlinkingLed.Pin);
// etc.

Diese Funktionen befinden sich im sogenannten gpio.c-Treiber und bauen natürlich auf einer Nachschlagetabelle auf:

void GPIO_setOutputHighOnPin (uint8_t port, uint16_t pin) 
{
    uint16_t portAddress = GPIO_PORT_ADDRESS_TABLE[port];

    (*((volatile uint16_t *)(portAddress + GPIO_OUT_REG_OFFSET))) |= pin;
}

Ich habe es speziell vereinfacht, aber Sie werden die Idee bekommen. GPIO_PORT_ADDRESS_TABLEund GPIO_OUT_REG_OFFSETsind vordefiniert und die Werte stammen aus dem MCU-Datenblatt. Glücklicherweise stellt TI MSP430WARE zur Verfügung , das diese letzten Bits für Sie erledigt.

Hallo @GAttuso, danke dafür! Ich kann jedoch GPIO_PORT_ADDRESS_TABLE und GPIO_OUT_REG_OFFSET für mein Board MSP430G2553 in MSP430WARE nicht finden?
@Shantanu Mhapankar Ich habe die Treiberbibliothek überprüft und keine Header-Dateien für die MSP430G2x-Serie gefunden. Wahrscheinlich müssen Sie also Ihre eigene Implementierung vornehmen.

Es wäre schön, wenn der Mikrocontroller-Code Substitution verwenden könnte, wie es Ihre typischen Computer-Skriptsprachen könnten, aber das würde eine zusätzliche Komplexitätsebene erfordern, die nur auf dem Mikrocontroller durchgeführt werden könnte, wodurch Speicher, Codeplatz und Verarbeitung verbraucht würden.

Sie könnten dies mit if- oder case-Anweisungen tun, und Ihr Compiler sollte in der Lage sein, dies nach Bedarf zu optimieren. Sie könnten den Registerwert nehmen, den PxSel oder was auch immer tatsächlich bedeutet, und diesen stattdessen verwenden. Es würde immer noch if- oder case- oder Vergleichsanweisungen erfordern. Wie Photon in den Kommentaren erwähnt, würden Sie im Grunde eine Nachschlagetabelle erstellen. Zum Glück sind diese ziemlich begrenzt.

Allerdings wurden diese Funktionen zu Tode gemacht, das Rad wurde von unzähligen Entwicklern mehrmals umgeschrieben. Eine Google-Suche nach gpio-Bibliothek würde zu einem Paar führen. TI bietet einige ihrer Codebeispiele an. Und eine ziemlich generische Kopie findet sich auf Energia , dem msp430 (und anderen TI-Mikrocontrollern) Port des Arduino-Code-Frameworks. Energia kann direkt verwendet werden, nur für die gpio Pin-Bibliothek, oder Sie können es kopieren, indem Sie der Lizenz dafür folgen.