Forum: Compiler & IDEs Mehrfachbedingungen in defines?


von André Wippich (Gast)


Lesenswert?

Hallo.

Ich schreibe gerade ein Programm mit WinAVR, wo ich mehrere Ausgänge
größtenteils nacheinander in Schleifen schalten muss. Leider sind die
Ausgänge aber über mehrere Ports verteilt.

Um alles etwas abzukürzen hab ich die Port/Pin-Kombinationen als
defines hinterlegt:
1
#define Turn_On_y0()  (PORTB |= (1<<PB0))
2
#define Turn_On_y1()  (PORTB |= (1<<PB1))
3
#define Turn_On_y2()  (PORTB |= (1<<PB2))
4
#define Turn_On_y3()  (PORTB |= (1<<PB3))
5
#define Turn_On_y4()  (PORTA |= (1<<PA0))
6
#define Turn_On_y5()  (PORTA |= (1<<PA1))
7
#define Turn_On_y6()  (PORTA |= (1<<PA2))
8
#define Turn_On_y7()  (PORTA |= (1<<PA3))
9
#define Turn_On_y8()  (PORTA |= (1<<PA4))
10
#define Turn_On_y9()  (PORTA |= (1<<PA5))
11
#define Turn_On_y10()  (PORTA |= (1<<PA6))

Wenn ich jetzt Ausgänge einschalten möchte muss ich das bisher so
machen:
1
for (y = 0 ; y <= 10 ; y++)
2
{
3
   if (Ref[y] == 1) { Turn_On_y1(); }
4
   else if (Ref[y] == 1) { Turn_On_y2(); }
5
   ...
6
   else if (Ref[y] == 1) { Turn_On_y10(); }
7
}

Ich würde es aber lieber so haben, dass ich auch den Port/Pin übergeben
kann und dann nur noch
1
for (y = 0 ; y <= 10 ; y++)
2
{
3
   if (Ref[y] == 1) { Turn_On_y(y); }
4
}

schreiben muss. Aber dafür müsste ich es irgendwie mit den defines
hinbekommen, dass sie abhängig vom y den entsprechenden Port&Pin
entsprechen. Geht das irgendwie?

Danke schonmal!

____________________
http://www.dark-sun.de

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ja, mit geschachtelten Makros und nicht so ganz einfach zu
verstehenden Ersetzungen mit # (oder ##?, den Unterschied muss
ich immer ausprobieren).

von Rolf Magnus (Gast)


Lesenswert?

> Wenn ich jetzt Ausgänge einschalten möchte muss ich das bisher so
> machen:
1
for (y = 0 ; y <= 10 ; y++)
2
{
3
   if (Ref[y] == 1) { Turn_On_y1(); }
4
   else if (Ref[y] == 1) { Turn_On_y2(); }
5
   ...
6
   else if (Ref[y] == 1) { Turn_On_y10(); }
7
}

Den Code verstehe ich nicht. Die Bedingung ist doch immer gleich.

1
for (y = 0 ; y <= 10 ; y++)
2
{
3
   if (Ref[y] == 1) { Turn_On_y(y); }
4
}


> Aber dafür müsste ich es irgendwie mit den defines hinbekommen,
> dass sie abhängig vom y den entsprechenden Port&Pin entsprechen.
> Geht das irgendwie?

Du könntest doch einfach deine ifs (oder besser ein switch/case) in das
Makro mit aufnehmen.

von André Wippich (Gast)


Lesenswert?

Zur ersten Anmerkung:
Die Bedingung ist nicht immer gleich. Im Array Ref steht drin welcher
Pin eingeschaltet werden soll und im ersten Durchlauf ist y=0 (erster
Ausgang), im zweiten y=1 (zweiter Ausgang) ... , wodurch die einzelnen
Ausgangs-Sollwerte nacheinander abgearbeitet werden. Ich möchte in der
Schleife so wenig Zeit wie möglich verschwenden.

Zur zweiten Anmerkung:
Ich hab leider noch nie mit Makros gearbeitet und auch nirgends ein
Tutorial gefunden, dass sich damit eingehend beschäftigt...

Vielleicht gibt es ja auch andere, einfachere Lösungswege für mein
Problem...
- Ich habe ein Array in dem drin steht welche Ausgänge an bzw. aus sein
sollen
- Die Ausgänge steuern eine LED-Spalte an, die LEDs hab ich mit y0 bis
y10 durchnummeriert
- Die Ausgänge selbst sind auf mehrere Ports verteilt
- Ich brauche einen schnellen Weg um die Daten aus dem Array zu den
Ausgängen zu bekommen.
- Optimal wäre etwas wie:

for (y = 0 ; y <= 10 ; y++)
{
   Ausgang(y) = Ref[y];
}

wobei Ausgang(0)   PB0
      Ausgang(1)   PB1
...   Ausgang(10)  PA6
wären.

Für einen fetzen Programmcode an dem ich mich orientieren könnte wäre
ich wirklich sehr dankbar!

PS: Wie lautet bitte der Befehl um den Code vom üblichen Text
hervorzuheben?

von A.K. (Gast)


Lesenswert?

Geht mit normalen Präprozessor-Makros eher nicht. Makros werden
aufgelöst bevor der Compiler den Code sieht, der Wert von y ist jedoch
erst zur Laufzeit bekannt. Mir ist kein Weg bekannt, in C-Makros
programmierte Schleifen zu basteln.

Mit dem M4-Präprozessor wird das zwar funktionieren, aber das ist eine
andere Baustelle. Der expandiert deine Schleife dann in 10 einzelne
Zeilen - was normalerweise entsprechend Platz kostet, in diesem Fall
aber vertretbar ist.

Portadressen und Pin-Masken können natürlich in Tabellen hinterlegt
werden. In diesem Fall sinnvoll. Nachteilig ist, dass dann keine
CBI/SBI-Befehle rauskommen und das damit nicht mehr Interrupt-fest ist.

von André Wippich (Gast)


Lesenswert?

@A.K.:
Es muss nicht interrupt-fest sein, weil es entweder selbst im einzigen
Interrupt stehen wird oder keine Interrupts geben wird. Muss mal sehen
ob das zeitmäßig hinhaut. Die LED-Spalte soll möglichst in festen
Zeitabständen aktualisiert werden. Daher dachte ich an einen
Timerinterrupt der die Aktualisierung der LED-Spalte vornimmt.

von Simon K. (simon) Benutzerseite


Lesenswert?

>>Zur ersten Anmerkung:
Die Bedingung ist nicht immer gleich. Im Array Ref steht drin welcher
Pin eingeschaltet werden soll und im ersten Durchlauf ist y=0 (erster
Ausgang), im zweiten y=1 (zweiter Ausgang) ... , wodurch die einzelnen
Ausgangs-Sollwerte nacheinander abgearbeitet werden. Ich möchte in der
Schleife so wenig Zeit wie möglich verschwenden.
<<

Ja, das schon. Allerdings wird das portbit, sofern es einmal gesetzt
wurde, nie mehr gelöscht. also ist das dumm.

von André Wippich (Gast)


Lesenswert?

Du liebe Güte, das ist doch nur ein Beispiel... Das Ausschalten erfolgt
an anderer Stelle und hat mit der eigentlichen Frage auch nix zu tun.

Mir geht es ja nur um einen "automatisierten" Weg um die Ausgänge
schnell in einer Schleife zu beschalten. Wenn ich erstmal weiß wie's
geht kann ich das entsprechend ausweiten und für das tatsächliche
Programm adaptieren.

Prinzipiell hast Du aber natürlich recht. Damit das Programmbeispiel
mehr Sinn macht:
1
// Funktion, die zunächst alle y-Ausgänge ausschaltet
2
Turn_Off_All_y();
3
4
// Schleife zum Beschalten der entsprechenden y-Ausgänge
5
for (y = 0 ; y <= 10 ; y++)
6
{
7
   if (Ref[y] == 1) { Turn_On_y1(); }
8
   else if (Ref[y] == 1) { Turn_On_y2(); }
9
   ...
10
   else if (Ref[y] == 1) { Turn_On_y10(); }
11
}
(Erneut die Bitte mir zu sagen wie man Codeabschnitte hervorhebt)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Mir geht es ja nur um einen "automatisierten" Weg um die Ausgänge
> schnell in einer Schleife zu beschalten.

Am schnellsten ist natürlich ein table lookup.  Für 8 Eingangs-
variablen (also 8 Bit) ist das durchaus noch realistisch, da
nur 256 Elemente nötig sind.  Wenn deine LEDs auf zwei
verschiedenen Ports angeordnet sind, enthält jedes Element dann
zwei Byte (für jeden der beiden Ports eins), d. h. die Tabelle
wäre 512 Bytes groß.  Das müsste selbst auf einem ATmega8 noch
tolerierbar sein (du solltest sie natürlich in den ROM legen).

von André Wippich (Gast)


Lesenswert?

Das mit den Lookup-Tables hab ich zwar nicht ganz verstanden aber es hat
mich auf ne Idee gebracht.
1
volatile unsigned char* x_Port[] = { &PORTC, &PORTC, &PORTC, &PORTC,
2
&PORTC, &PORTC, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD };
3
unsigned char x_Pin[] = {PC7, PC6, PC5, PC4, PC3, PC2, PD7, PD6, PD5,
4
PD4, PD3};
5
6
int main(void)
7
{
8
  DDRA = 0x7F;
9
  DDRB = 0x0F;
10
  DDRC = 0xFC;
11
  DDRD = 0xF8;
12
  
13
  PORTA = 0x7F;
14
  PORTB = 0x0F;
15
  PORTC = 0x00;
16
  PORTD = 0x00;
17
        
18
        int wert;
19
        while(1)
20
        {
21
           for (wert = 0; wert <= 10; wert++)
22
           {
23
        *x_Port[wert] |=  (1<<x_Pin[wert]);
24
25
              wait(5000); // Ein, zwei Sekunden Pause
26
27
              *x_Port[wert] &=  ~(1<<x_Pin[wert]);
28
           }
29
        }
30
}

Ich hab einfach die Adressen und entsprechenden Pinnummern in zwei
Arrays abgelegt und greife dann so in der Schleife über das Array auf
sie zu.

Aber es klappt nicht so ganz :-( Es werden immer nur die Ausgänge 1 bis
4 eingeschaltet. An den Ausgängen 0 und 5 bis 10 tut sich nix. Es
müssten ja nacheinander die Ausgänge 0 bis 10 durchgeschaltet werden.
Aber das passiert nur bei 1 bis 4, beim Rest tut sich nix aber die
Wartepause besteht - die Schleife wird also weiterhin durchlaufen, nur
scheint das Programm mit den Werten die es in den Arrays findet nix
anfangen zu können...

Ich verstehe nicht warum...

von Rolf Magnus (Gast)


Lesenswert?

Warum es mit teilweise nicht klappt, weiß ich nicht. Aber ich hätte noch
eine Optimierung. Speichere im Array gleich die bitverschobenen Werte
ab, also:
1
unsigned char x_Pin[] =
2
{
3
    _BV(PC7), _BV(PC6), _BV(PC5), _BV(PC4), _BV(PC3),
4
    _BV(PC2), _BV(PD7), _BV(PD6), _BV(PD5), _BV(PD4), _BV(PD3)
5
};

und nachher sowas wie:
1
        *x_Port[wert] |=  x_Pin[wert];

Damit sparst du Code und Zeit, denn das Verschieben um eine erst zur
Laufzeit bekannte Anzahl von Bits ist verhältnismäßig aufwendig und in
diesem Fall eigentlich unnötig.

von André Wippich (Gast)


Lesenswert?

@Rolf:
Danke für den Tip. Das ist wirklich besser und schneller. Hätte ich
auch selber drauf kommen können.

Den Fehler habe ich jetzt auch gefunden. Der Programmcode funzt, aber
ich der Speicher war "überladen". Hatte ein paar "Bilder" als
11x11-Array gespeichert und gar nicht dran gedacht, dass jedes dieser
Arrays dann 121 Byte Platz schluckt :-) Da war die
Speicherplatzauslastung dann Ruck Zuck auf 120%... Dann wundert es auch
nicht, warum ich meine Spalten und Zeilen Arrays nicht mehr vernünftig
anlegen konnte bzw. das Programm nix mehr damit anfangen konnte.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.