PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : IO-Pins in Variablen festlegen [solved]



clupus
23.01.2006, 09:05
Hallo allerseits,

ich weiß, das ist eine Anfängerfrage, aber mich interessierts trotzdem:

Ich will, weil ich noch nicht weiß, wie das genaue Layout aussehen wird, mal die IO-Ports nich fest hardcoden, sondern in einer externen Datei festlegen. Dass ich also statt dem PORTA (z.B. hängt da Motor 1) das Register PORTM1 verwende, um später festlegen zu können, wo was hängt.

Was ich jetzt nicht weiß: Wenn ich ihm irgendwo eine Variable mit
uint_8 PORTM1; erzeuge und anschließend den Wert PORTA zuweise, wird dann nicht im Speicher irgendwo ein Speicherplatz reserviert, der "tot" ist? Wie kann ich das umgehen?

Wenn ich den Motor am 1 Pin (LSB) habe, kann ich ja (mit PINM1=0) den Motor mit
PORTM1 |= _BV(PINM1); einschalten. Wenn ich jetzt aber mit
PORTM1 |= _BV(PINM1) + _BV(PINM2); noch den 2. Motor mit dazuschalte, rechnet er mir doch jedes Mal im µC die beiden Zahlen zusammen (braucht eine gewisse Zeit und v.a. mehrere Speicherzeilen im Flash) und Oder-verknüpft das Ergebnis dann mit PORTM1, oder? Wie kann ich den Compiler dazu bringen, dass er das als feste Konstante dem AVR überginbt?

MfG
Christian

askazo
23.01.2006, 10:25
Die Lösung Deines Problems ist die Compileranweisung #define.
Mit
#define PORTM1 PORTA
sagst Du dem Compiler, dass er beim compilieren PORTM1 mir PORTA ersetzen soll. Das ganze belegt also keinen Speicherplatz im µC. PORTM1 nennt sich dann nicht Variable, sondern Makro.
PORTA ist übrigens auch nur ein Makro...

Gruß,
askazo

clupus
23.01.2006, 16:29
Kann ich dann auch folgendes sagen?:
#define M1_vorw _BV(PINM1A) + _BV(PINM1E)
Bemerkung: PINM1A und PINM1E sind zwei von drei Steuerleitungen (Enable und Halbbrücke)

Es geht halt drum, dass er die beiden Werte _BV(PINM1A) und _BV(PINM1E) erst addiret und dann in dem Makro ablegt.

MfG
Christian

askazo
23.01.2006, 16:33
Das müsste auch funktionierten.
Einfach mal ausprobieren....

askazo

SprinterSB
23.01.2006, 17:03
Die Addition stört nicht weiter, das Ergebnis ist ja zur Compilezeit bekannt. Oder willst du eine Bibliothek mit zulinkbaren Modulen machen, die nur dazugebunden werden sollen? Dann geht es natürlich nicht über Makros.

Bei deinem Beispiel müssen PINM1A und PINM1E auf dem gleichen Port liegen, voll flexibel bist du da immer noch nicht.

Ausserdem musst du nicht nur den Port kennen (PORTX-Register) sondern auch Direction-Register (DDRX) und evtl Port Input Register (PINX). Die liegen nicht bei allen AVRs nebeneinander im Adressraum.

Werte werden auch nicht in einem Makro "abgelegt". Ein Makro macht nichts weiter als reinen Textersatz zur Compilezeit.

Falls du Makros willst wie SET(PORTB_1), CLR(PORTB_1) oder MAKE_OUTPUT(PORTB_1) geht das zwar, aber die Makros werden recht unübersichtlich, weil im MAKE_OUTPUT() das DDR angefasst werden muss und im SET() das PORT-Register und mit CLR() noch eine Maskenoperation hinzukommt. Der Compiler macht zwar den optimalen Code, weil er nur Konstanten zu falten hat, aber einen Fehler aus so verschlungenen Makros rausfischen ist keine Freude.

clupus
05.02.2006, 20:58
Ich hab jetzt hier mal die aktuelle Version meines Codes (Nur die die Hardware ansteuernde Codes). Wie leicht ersichtlich, gibt es eine Unmenge kleine Prozeduren, die nur ein Bit setzen oder löschen. Gibt es eine schönere/schnellere/bessere/klarere Programmiermöglichkeit?

MfG
Christian

SprinterSB
06.02.2006, 10:05
Das kommt darauf an, was du haben willst. Und was "schön" ist, wird ja ganz unterschiedlich gesehen.

So wie du es organisiert hast, kannst du gut von der Hardware abstrahieren. Wenn du einen anderen µC nimmst, bräuchest du nur diesen Hardware-abhängigen Teil auszutauschen/neuschreiben. Funktionsaufrufe sind für so was natürlich langsamer als inline-Funktionen oder Makros (die ja direkt in den Code eingeflickt werden).

Klarer wären zumindest klarere Namen. Wenn du in 3 Monaten wieder auf die Quelle schaust, wird's schwer, das zu verstehen mit den kryptischen Bezeichnern.

clupus
06.02.2006, 17:25
Das mit den Bezeichnern passt schon. Die sind in einem Schaltplan und einer technischen Zeichnung im Unterverzeichnis sofort zuzuordnen.

Wie würde das noch schneller gehen? (Rein Interessehalber) Du sagst irgendwas mit inline? Wäre das dann:
uint8_t inline function UW_STOP(){ [...] }

Mfg
Christian

SprinterSB
06.02.2006, 17:39
Ich nehm hier mal das UW_R.

Das Original:
UW_R(){
OUT = OUT & 255 - _BV(UW_M) | _BV(UW_P);
UW_ACTION = UW_A_R;
}

Als inline-Funktion (In den Header schreiben und includieren):
Das Original:
static inline void UW_R()
{
OUT &= ~(_BV(UW_M) | _BV(UW_P));
UW_ACTION = UW_A_R;
}

Als Makro (ebenfalls in den Header, evtl. Seiteneffekte beachten!)

#define UW_R() \
do {
OUT &= ~(_BV(UW_M) | _BV(UW_P));
UW_ACTION = UW_A_R;
} while(0)

Das while (0) macht man, damit das ganze ein Block wird, und wie gewohnt benutzbar ist. Etwa:

if (...)
UW_R();
else
...

In beiden Fällen muss in dem Header auch das Zeug includet werden, das man sonst so braucht (<avr/io.h>, OUT, ...).

Kommt auch drauf an, wo die Priorität ist, ob bei Code-Größe oder Geschwindigkeit und was OFF ist, etc.

Inline ist übrigens nur eine Empfehlung an den Compiler, Code zu inlinen. Eine externe Funktion zu inlinen, kann z.B. nicht gehen. Die wird als ganz normale Funktion da stehen später. Und während ein Makro immer aufgelöst wird, wird nicht bei allen Optimierungsstufen geinlinet.