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
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_SET
Makro 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.
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_TABLE
und GPIO_OUT_REG_OFFSET
sind 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.
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.
Das Photon
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önneny
.Shantanu Mhapankar
Das Photon