kürzere Schreibweise für #define-Deklaration / C und MEGA16

OP #2306095
Lesenswert?

Hallo,

ich möchte im Programm ein Signal für die Modellbahn schalten.
Controller ist der ATMEGA16.

Momentan sieht mein Programm folgendermaßen aus:
1
#define vorsignal_rot_0 (PORTD &= ~(1 << 4))
2
#define vorsignal_rot_1 (PORTD |=  (1 << 4))
3
#define hauptsignal_rot_0 (PORTD &= ~(1 << 3))
4
#define hauptsignal_rot_1 (PORTD |=  (1 << 3))
5
#define vorsignal_gruen_0 (PORTD &= ~(1 << 5))
6
#define vorsignal_gruen_1 (PORTD |=  (1 << 5))
7
#define hauptsignal_gruen_0 (PORTD &= ~(1 << 2))
8
#define hauptsignal_gruen_1 (PORTD |=  (1 << 2))

Im Programm werden die Signale dann folgendermaßen auf z.B. rot 
geschaltet:
1
vorsignal_rot_1;
2
hauptsignal_rot_1;
3
vorsignal_gruen_0;
4
hauptsignal_gruen_0;

Gibt es eine Möglichkeit, das ganze noch etwas zu vereinfachen. So dass 
z.B. mit einer Deklaration die Rote Lampe ein und die grüne gleichzeitig 
ausgeschaltet wird? Momentan benötige ich hierfür 2 Deklarationen, die 
ich ständig beide aufrufen muss.
OP #2306479
Lesenswert?

MitLeser schrieb:
> einfach für EIN Makro alle Bitmanipulationen durchführen
> die gewünscht sind

zunächst mal Danke für den Hinweis.
Ich kenne mich mit C noch nicht so gut aus. Wenn ich es richtig 
verstanden habe einfach 2 Bitmanipulationen hinter einem #define 
geschrieben und durch ein & verknüpft. Ist das so richtig?
Das AVR Studio bringt mal keinen Fehler und funktionieren tut es auch.
1
#define vorsignal_rot (PORTD |=  (1 << 4)) & (PORTD &= ~(1 << 5))
2
#define hauptsignal_rot (PORTD |=  (1 << 3)) & (PORTD &= ~(1 << 2))
3
#define vorsignal_gruen (PORTD |=  (1 << 5)) & (PORTD &= ~(1 << 4))
4
#define hauptsignal_gruen (PORTD |=  (1 << 2)) & (PORTD &= ~(1 << 3))
Gast #2306490
Lesenswert?

R. F. schrieb:
> Das AVR Studio bringt mal keinen Fehler und funktionieren tut es auch.
Unsinn ist es trotzdem.

Dann lieber so:
1
#define vorsignal_rot_0 (PORTD &= ~(1 << 4))
2
#define vorsignal_rot_1 (PORTD |=  (1 << 4))
3
#define hauptsignal_rot_0 (PORTD &= ~(1 << 3))
4
#define hauptsignal_rot_1 (PORTD |=  (1 << 3))
5
#define vorsignal_gruen_0 (PORTD &= ~(1 << 5))
6
#define vorsignal_gruen_1 (PORTD |=  (1 << 5))
7
#define hauptsignal_gruen_0 (PORTD &= ~(1 << 2))
8
#define hauptsignal_gruen_1 (PORTD |=  (1 << 2))
9

10
vorsignal_rot_0 , hauptsignal_gruen_1; // <-- Da ist ein Komma
11
vorsignal_gruen_0;
12
...
Gast #2306493
Lesenswert?

Also ich habe ja Zweifel an der Grundidee, aber sowas muss jeder mal am 
Anfang durchmachen und ich will das jetzt nicht kritisieren.

Aber in:
>Wenn ich es richtig verstanden habe einfach 2 Bitmanipulationen hinter einem 
#define geschrieben und durch ein & verknüpft.

liegt jedenfalls ein Denkfehler. Warum sollten denn hier zwei 
Zusweisungen Und-verknüfpft werden? Da das Resultat nicht verwendet 
werden soll macht das keinen Sinn.

Ich vermute mal, das Und war die Lösung für das Problem: Wie führe ich 
zwei Zuweisungen in einem Define aus.

Das geht auch anders und in einer Form die der Sprache gerecht wird.

Ein Vorschlag ist:
1
#define vorsignal_rot { PORTD |=  (1 << 4); PORTD &= ~(1 << 5); }
Gast #2306515
Lesenswert?

ist zwar zu spät aber so wie Kali schreibt hatte ich es auch gemeint.
ich würde coding style-mässig jedoch Makros gross schreiben, gibt es 
hier im Forum auch Beiträge zu, Suchwörter "coding style", 
"programmierregeln" etc.
Bsp:
#define LED_AUS()   (PORTD |=  (1 << 4))
OP #2306541
Lesenswert?

Hallo zusammen,

ich habe es jetzt so gemacht:
1
#define vorsignal_rot { PORTD |=  (1 << 4); PORTD &= ~(1 << 5); }
2
#define hauptsignal_rot { PORTD |=  (1 << 3); PORTD &= ~(1 << 2); }
3
#define vorsignal_gruen { PORTD |=  (1 << 5); PORTD &= ~(1 << 4); }
4
#define hauptsignal_gruen { PORTD |=  (1 << 2); PORTD &= ~(1 << 3); }

Funktioniert bestens. Das mit der Großschreibung werde ich noch ändern. 
Hier werde ich noch nach den empfolenen Beiträgen suchen und mal in 
aller Ruhe durchlesen. Vielleicht kann ich noch mehr Punkte verbessern.
#2306549
Lesenswert?

R. F. schrieb:
> Gibt es eine Möglichkeit, das ganze noch etwas zu vereinfachen. So dass
> z.B. mit einer Deklaration die Rote Lampe ein und die grüne gleichzeitig
> ausgeschaltet wird? Momentan benötige ich hierfür 2 Deklarationen, die
1
#define vorsignal_rot_0 (PORTD &= ~(1 << 4))
2
#define vorsignal_rot_1 (PORTD |=  (1 << 4))
3
#define vorsignal_gruen_0 (PORTD &= ~(1 << 5))
4
#define vorsignal_gruen_1 (PORTD |=  (1 << 5))
5

6
/* ... */
7

8
#define vor_stopp vorsignal_rot_1; vorsignal_gruen_0;
9
#define vor_start vorsignal_rot_0; vorsignal_gruen_1;

So ist das wenigstens in sich stimmig.
#2306582
Lesenswert?

Was spricht eigentlich dagegen, dafür das in C vorgesehene Sprachmittel 
zu benutzen: Funktionen?
1
void vor_stopp( void )
2
{
3
  vorsignal_rot_1;
4
  vorsignal_gruen_0;
5
}
6

7
void vor_start( void )
8
{
9
  vorsignal_rot_0;
10
  vorsignal_gruen_1;
11
}
12

13

14
int main()
15
{
16
   ....
17

18

19
  vor_stopp();
20
  ...

Ich kann auf lange Sicht nur davor warnen, zuviel 'Funktions-Kram' in 
Makros zu quetschen. Zumnal hier zwischen den Zeilen lesbar ist, dass 
der Fragesteller wieder mal eine falsche Vorstellung davon hat, was 
Makros eigentlich machen.
Gast #2306596
Lesenswert?

>#define vor_stopp vorsignal_rot_1; vorsignal_gruen_0;
>#define vor_start vorsignal_rot_0; vorsignal_gruen_1;
>
>So ist das wenigstens in sich stimmig.

Mit sowas fällt man ganz schnell auf die Fresse.


 if(blabla) vor_stopp;

Was macht der Präprozessor daraus:

 if(blabla) vorsignal_rot_1; vorsignal_gruen_0;;

Mal sortieren:

 if(blabla) vorsignal_rot_1;
 vorsignal_gruen_0;;

Tut nicht unbedingt was es soll;)
Gast #2306647
Lesenswert?

Oder auch so:
1
#define SET_SIGNAL(x)    PORTD = (PORTD & 0x3c) | x
2
#define HG               (1 << 2)
3
#define HR               (1 << 3)
4
#define VR               (1 << 4)
5
#define VG               (1 << 5)
6

7
#define VOR_ROT          SET_SIGNAL(VR)
8
#define VOR_GRUEN        SET_SIGNAL(VG)
9
#define HAUPT_ROT        SET_SIGNAL(HR)
10
#define HAUPT_GRUEN      SET_SIGNAL(HG)
11

12
#define VH_AUS           SET_SIGNAL(0)
13
#define VH_EIN           SET_SIGNAL(VR | VG | HR | HG)
#2306718
Lesenswert?

Karl Heinz Buchegger schrieb:
> Was spricht eigentlich dagegen, dafür das in C vorgesehene Sprachmittel
> zu benutzen: Funktionen?

Genau. Und dann auch gleich die Regeln mit eingebaut.
Es gibt drei mögliche Signalkombinationen:

Hp0 + Vr0, Hp1 + Vr1, Hp1 + Vr0

Die Kombination Hp0 + Vr1 (Halt + Fahrt erwarten) ist ausgeschlossen.


void Signal (char Hp, char Vr)
{
  PORTD &= 0xC3;               //Alle aus
  if (!Hp)                     //Hp0 Halt
  {
    //Halt, Halt erwarten
    PORTD |= (1<<3)|(1<<4);   //Hp0 + Vr0 = rot + gelb-gelb
  }
  else
  {                           //Hp1 Fahrt
    //Fahrt, Fahrt erwarten
    if (Vr) PORTD |= (1<<5)|(1<<2);    //Hp1 + Vr1 = grün + grün-grün
    //Fahrt, Halt erwarten (Nachdem der Zug das Vorsignal passiert hat)
    else PORTD |= (1<<4)|(1<<2);   //Hp1 + Vr0 = grün + gelb-gelb
  }
}

Die Aufrufe sind dann

#define Hp0 0
#define Hp1 1
#define Vr0 0
#define Vr1 1

Signal(Hp0, Vr0);       //rot, gelb-gelb
Signal(Hp0, Vr1);       //unzulässig, angezeigt wird Hp0, Vr0
Signal(Hp1, Vr1);       //grün, grün-grün
Signal(Hp1, Vr0);       //grün, gelb-gelb



Damit muß man sich nur einmal Gedanken über die richtigen 
Signalstellungen machen. Dazu ist ein Computer schließlich da.
Und C ist das auch.


mfg.
#2306731
Lesenswert?

Thomas Eckmann schrieb:


> Die Aufrufe sind dann
>
> #define Hp0 0
> #define Hp1 1
> #define Vr0 0
> #define Vr1 1
>
> Signal(Hp0, Vr0);       //rot, gelb-gelb
> Signal(Hp0, Vr1);       //unzulässig, angezeigt wird Hp0, Vr0
> Signal(Hp1, Vr1);       //grün, grün-grün
> Signal(Hp1, Vr0);       //grün, gelb-gelb

Das würde ich dann aber nicht so machen.

Wenn es da Zustände gibt, dann sollte man die auch benennen, zb mit 
#defines.

Der Signal-Funktion übergebe ich dann nur noch die Siganlbezeichnung und 
den Zustandsnamen. Wie sich dass dann auf die Lichtführung auswirkt, 
dass soll sich die Funktion selber ausklamüsern.

> Hp0 + Vr0, Hp1 + Vr1, Hp1 + Vr0

Ich versteh zu wenig von Eisenbahn um zu wissen, was das für Zusände 
sind.
Ich denke mal das wird irgendwie sowas sein wie
1
#define TRAIN_STOP      0
2
#define TRAIN_GO        1
3
#define TRAIN_ATTENTION 2

und die Aufrufe sind dann
1
  Signal( ENTER_SIGNAL_RIGHT, TRAIN_STOP );

und das hat ja dann schon eine ganz andere dokumentarische Qualität.
#2306739
Lesenswert?

Simon K. schrieb:
> @Thomas: Man darf Funktionsnamen, Variablennamen und defines auch ruhig
> länger als 3 Zeichen machen ;-)
> Schild hochhalt Für aussagekräftigere Bezeichnungen!

Vergiss' es. Aussagekräftigere Bezeichnungen dafür gibt es nicht.

Das sind die offiziellen Abkürzungen aus dem Signalbuch der Bahn.
Deutsche Bundesbahn, um genau zu sein. Ganz so neu ist das Buch auch 
nicht mehr.

mfg.
Gast #2306746
Lesenswert?

Ich verstehe nie so recht, warum sich die Leute so gegen Benutzung des 
_BV()-Makros sperren; m. M. n. macht das den Code lesbarer und beugt 
Flüchtigkeitsfehlern a la (1<3) vor...

Leider hat der TE nicht erwähnt, wie oft und in welchem Kontext er die 
Signal-Makros/-Funktionen aufruft; je nachdem ist natürlich die eine 
oder andere Variante besser. Für Anfänger denke ich jedoch auch, dass 
Thomas' Funktion die beste Lösung darstellt. Beim Einsatz von Makros 
muss man sich immer wieder bewusst machen, dass das lediglich eine 
Textersetzung darstellt und Probleme wie von holger beschrieben geradezu 
heraufbeschwört. Um sicher zu gehen, empfiehlt es sich, alle Parameter 
von Makros (sofern sie über Parameter verfügen) im Makro-Körper in 
Klammern zu setzen, und, sofern der Makro-Körper mehrere Befehle 
enthält, ihn in geschweifte Klammern zu setzen. Beispiel:
1
#define IAMAMACRO(x) { doSomeThing(1+(x)*(x)/27); doAnotherThing(); }
#2306771
Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich versteh zu wenig von Eisenbahn um zu wissen, was das für Zusände
> sind.

Das Vorsignal ist dazu da, dem Lokführer anzuzeigen, was ihn am 
Hauptsignal erwartet, da sein Bremsweg etwas länger ist, als der eines 
Autofahrers.

Deshalb ist auch Hp0 + Vr1, das heisst nun mal so, unmöglich. Das 
bedeutet nämlich am Vorsignal: "Du kannst voll Stoff geben". Und am 
Hauptsignal:
"Ätschibätsch, reingelegt".

Signal(Hp0, Vr0);
Signal(Hp0, Vr1);
Signal(Hp1, Vr1);
Signal(Hp1, Vr0);

Ich finde es sehr logisch, daß so zu machen. Bei diesem Signal handelt 
es sich um ein Blocksignal auf freier Strecke.

Das kennt eigentlich bei Lichtsignalen nur diese beiden Zustände:
Signal(Hp0, Vr0); = Halt, Block belegt
Signal(Hp1, Vr1); = Fahrt, Block frei

Ein Eisenbahner versteht das.
Selbst wenn er Programmierung für schwarze Magie hält.

Karl Heinz Buchegger schrieb:
> Signal( ENTER_SIGNAL_RIGHT, TRAIN_STOP );
das ist mir in diesem Zusammenhang vielzu abstrakt.

mfg.
Angehängte Dateien:
Gast #2307479
Lesenswert?

>> Signal( ENTER_SIGNAL_RIGHT, TRAIN_STOP );
>das ist mir in diesem Zusammenhang vielzu abstrakt.

Das versteht aber ein Programmierer, auch wenn er Eisenbahnabkürzungen 
für schwarze Magie hält ;)
#2307545
Lesenswert?

Thomas Eckmann schrieb:

>
> Signal(Hp0, Vr0);
> Signal(Hp0, Vr1);
> Signal(Hp1, Vr1);
> Signal(Hp1, Vr0);
>
> Ich finde es sehr logisch, daß so zu machen.

Find ich nicht.
Ich finde das ist eine Fehlerquelle, die nicht sein muss. Zwischen 
erstem und zweitem Argument besteht ein dubioser Zusammenhang, den mir 
zwar die Funktion garantieren kann, aber wenn ich beim Aufruf schon 
sicherstellen kann, dass die Funktionslogik da gar nicht eingreifen 
muss, dann ist das allemal besser.

> Bei diesem Signal handelt
> es sich um ein Blocksignal auf freier Strecke.
>
> Das kennt eigentlich bei Lichtsignalen nur diese beiden Zustände:
> Signal(Hp0, Vr0); = Halt, Block belegt
> Signal(Hp1, Vr1); = Fahrt, Block frei

Meine Sicht der Dinge ist es, die Sachen mögichst schnell auf tiefer 
Ebene zu abstrahieren. Was interessiert mich wieviele Lichter es wann 
und wo gibt.
Ich gebe den Block frei, und genau so will ich das auch programmieren

  Signal( Blocksignal1, BLOCK_FREI );

Da brauch ich nur noch ein paar Füllwörter einfügen und das Verb in die 
Mitte stellen und lese da ganz zwanglos "An dieser Stelle im Code wird 
die Blocksignal-Anlage 1 so gestellt, dass der Block freigegeben ist".
Was das jetzt für die Signale selbst bedeutet (und wieviele Lampen da im 
Spiel sind), interessiert mich doch nicht. Mein Ziel hab ich erreicht: 
Der Block ist zumindest signaltechnisch freigeschaltet. Auf einer nächst 
höheren Ebene gebe ich dann vielleicht noch den Block selber frei, der 
neben den Signalen dann auch noch Weichen schalten muss um die 
Fahrstrasse geschaltet zu kriegen.

Aber das ist meine Sicht der Dinge. Andere mögen das anders sehen.
#2307715
Lesenswert?

Karl Heinz Buchegger schrieb:
> Meine Sicht der Dinge ist es, die Sachen mögichst schnell auf tiefer
> Ebene zu abstrahieren. Was interessiert mich wieviele Lichter es wann
> und wo gibt.
> Ich gebe den Block frei, und genau so will ich das auch programmieren

Damit bist du aber schon eine Stufe höher.
Denn dahinter stecken immer noch die Signale mit ihren 6 Lämpchen. Und 
irgendwo müssen dann ja auch die Ports geschaltet werden. Wobei ich für 
mich die Funktion dann noch um Hp2 und Vr2 erweitern würde. Damit kann 
ich dann den ganzen Bereich abdecken. Ob es sich dann um ein 
Blocksignal, ein Ein- oder Ausfahrsignal oder sonstwas handelt ist auf 
dieser Ebene vollkommen egal. Mag sein, daß man sich an den 
Variablennamen stören kann. Aber mir sind diese Kürzel geläufig und 
machen somit für mich auch Sinn.

Mit der Funktion oben kann man natürlich nur ein bestimmtes Signal 
schalten. Was ja noch ein bisschen wenig ist. Aber bei der Frage oben 
ging es nur darum, den Aufruf für das Bitgefriggel zu vereinfachen. Was 
ein Blocksignal ist, muß doch diese Funktion gar nicht wissen.

Aber Dutzende Signale schalten kann man damit natürlich nicht.

Da kommt dann wieder deine Funktion ins Spiel.

Karl Heinz Buchegger schrieb:
> Signal( Blocksignal1, BLOCK_FREI );

Jetzt interessiert es mich auch nicht, welche Lämpchen geschaltet werden 
oder ob da einer mit 'ner Fahne winkt. Das will ich in der Funktion gar 
nicht drin haben.

Aber dahinter muß das Bitgefriggel ja wieder gelöst werden. Ob nun so, 
wie in meinem Beispiel oder aus einer Tabelle in der alle sinnvollen 
Bitmuster drin stehen, die man dann per SPI auf 595er schiebt. Oder wie 
auch immer, das ist dann wieder völlig egal.


mfg.
#2307726
Lesenswert?

Thomas Eckmann schrieb:

> Damit bist du aber schon eine Stufe höher.
> Denn dahinter stecken immer noch die Signale mit ihren 6 Lämpchen. Und
> irgendwo müssen dann ja auch die Ports geschaltet werden.

Dieses Wissen würde ich dann sowieso in Tabellen ablegen. Was alles muss 
geschaltet werden, wenn ich einen Block freigebe. Damit ist dann aber 
die Information über Vp und Hp sowieso schon aus dem Programmtext in 
eine veränderbare Konfiguration gewandert, die ich unter einem Namen 
verfügbar habe und bei der mir die Konfiguration mitteilt, welche 
Hardware jetzt ganz genau bei welcher 'Zustandsänderung' zu betätigen 
ist.


Aber wir 'streiten' um des Kaisers Bart. Letztendes muss der TO das für 
sich entscheiden, was er alles tun will und wie er es machen will.
Was mir wichtig ist: Diese Attitüde, alles in ein Makro zu quetschen - 
sie führt auf lange Sicht gesehen meistens zu schlechtem Code. Solange 
man nur 1 oder 2 Statements ersetzen will, mag es noch angehen. Aber 
spätestens wenn die Chance besteht, dass der Code auch mal ein wenig 
komplizierter ist, fährt man mit Funktionen immer besser.

Ich hab das eigentlich im Desktop Bereich so nie erlebt, dass gerade 
Anfänger alles sofort in Makros quetschen um sich dann hinterher mit 
untauglichen Debugmitteln einen Wolf zu suchen, wie da jetzt durch die 
Makroexpansion Unsinn rauskommt. Dazu kommt dann meistens auch noch eine 
verquerte Vorstellung davon, was Makros eigentlich darstellen. Erkennbar 
daran, dass dann plötzlich von 'Variablen' bzw. 'Funktionen' die Rede 
ist, im Code aber nur ein unschuldiges #define steht.
#2307737
Lesenswert?

Karl Heinz Buchegger schrieb:
> Dieses Wissen würde ich dann sowieso in Tabellen ablegen.

Das Bitgefriggel ist dann reine Fleißarbeit, die man hat, bevor man auch 
nur eine einzige Zeile Code geschrieben hat.

Karl Heinz Buchegger schrieb:
> Aber wir 'streiten' um des Kaisers Bart.

Stimmt. Irgendwo ist immer ein wenig Interpretationsspielraum.

Karl Heinz Buchegger schrieb:
> Was mir wichtig ist: Diese Attitüde, alles in ein Makro zu quetschen -
> sie führt auf lange Sicht gesehen meistens zu schlechtem Code.

Und vor allen Dingen aufgebläht.

Der Codefetzen "Makro" wird schließlich jedes Mal wieder eingebaut. Ob 
nun in C oder Assembler. Eine entsprechende Funktion oder ein 
Unterprogramm existiert nur einmal. Darüber muß man sich auch im Klaren 
sein.
Zwei Seiten Quelltext und der Speicher läuft über...

mfg.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren