Forum: Compiler & IDEs Hilfe bei #Define


von Tobias Tezlaff (Gast)


Lesenswert?

Hallo,

ich habe ein Programm, das auf einer Hardware mit einer 
unterschiedlichen
Anzahl von LEDs laufen soll.

Ich habe eine Hardware mit 16, 32, 48, 56 und 64 LEDs, seriell 
angesteuert.
(tut ja nichts zur Sache!)
Nun möchte ich nicht 5 Programme schreiben, bzw. anpassen.

Wer kann mir das mit dem #Define mal erklären?

Ich würde gerne im Header des Programms vorgeben, wieviele LEDs ich 
grade habe, und dementsprechend den dafür vorgesehenen Programmteil 
nutzen.

z.B.
"wenn 16 LEDs, dann 2 Byte Lauflicht"
"wenn 32 LEDs, dann 4 Byte Lauflicht"
...
...

Danke im voraus....

Gruß Toby

von GAst (Gast)


Lesenswert?

Vieleicht so:

Im Header:

#define LED16
oder
#define LED32
oder
#define LED64
...

Im C-File:

#ifdef LED16
  blalalalalla
#endif

#ifdef LED32
  lululilale
#endif

...


von GAst (Gast)


Lesenswert?

Oder so:

Im Header:

#define LED     16   (oder 32, oder 64 ...)


Im C-File:

if (LED == 16)
{
  dumdidum
}

if (LED == 32)
{
  dadidum
}

...


Der Unterschied ist, dass im ersten Beispiel eine Konstante LED16 
deklariert wird, ohne ihr einen Wert zuzuordnen. Das muss auch nicht. Im 
Programm wird nun abgefragt, ob diese Konstante vorhanden ist, und 
entsprechend verfahren.

Im zweiten Beispiel wird die Konstante LED deklariert und ihr der Wert 
16 zugeordnet. Im Programm wird nun einfach abgefragt, welcher Wert 
dieser Konstante zugeordnet ist, und entsprechend verfahren. Du kannst 
mit dieser Konstanten auch rechnen:

VariableX = 3 * LED;     // Dann ist VariableX  3 * 16 = 48

Konstanten werden in der Regel im Header definiert und mit 
Großbuchstaben geschrieben. Dadurch lassen sie sich von normalen 
Variablen unterscheiden.

von Karl H. (kbuchegg)


Lesenswert?

Zunächst mal musst du dir im klaren sein, dass
#define
nichts magisches macht.
#define
maht einfach nur eine Textersetzung.

Wenn du also vereinbarst

#define ABC 123

dann wird der Präprozessor im Quelltext deines Programmes
alle Erwähnungen von ABC durch den Text 123 ersetzen.

Aus

int main()
{
  ABC = 5;
}

wird daher

int main()
{
  123 = 5;
}

und erst dieser Quelltext wird durch den eigentlichen Compiler
gejagt.

Nun gibt es auch Möglichkeiten mittels solcher Präprozessor-
Anweisungen einen Teil des Quelletxtes überhaupt ruaszuschmeissen
bzw. zu inkludieren, wenn ein bestimmte Bedingungen gelten.
Eine Bedingung könnte zb. sein, ob es ein bestimmes #define
überhaupt gibt:

#define KALLE 1

int main()
{

#ifdef KALLE
  printf( "Dies ist ein Test\n" );
#else
  printf( "Das erste Programm\n" );
#endif
}

Wieder: Alle Zeilen die mit # beginnen sind an den Präprozessor
addressiert, der sich den Quelltext vornimmt und eventuell ver-
ändert bevor ihn der eigentliche Compiler zu Gesicht kriegt.
(Der Präprozessor ist nichts anderes als ein Texteditor, der
genialerweise seine Anweisungen aus dem Text entnimmt, den
er bearbeitet)

Im gegenständlichen Fall geht der Präprozessor durch den
Text

#define KALLE 1

er merkt sich, dass er den Text KALLE durch den Text 1 ersetzen
muss. Er merkt sich aber auch, dass es ein Makro namens KALLE
gibt.

int main()

Da macht der Präprozessor gar nichts

#ifdef KALLE

Aha. Eine Anweisung an den Präprozessor. Welche? ifdef oder
ausgesprochen: "Wenn es ein #define gibt, welches KALLE hies"

Nun so ein #define gibt es. Es war ein paar Zeilen weiter oben
und der Präprozessor hat sich gemerkt das es das gab.
Also macht der Präprozessor weiter und inkludiert den
weiteren Quelltext in die Ausgabe:

  printf( "Dies ist ein Test\n" );

Danach stösst der Präprozessor auf das

#else

Damit ist der #ifdef Teil erledigt und da der #ifdef genommen
wurde, wird der #else Teil nicht in die Ausgabe übernommen.
Der weitere Text bis zum

#endif

entfällt also.


Damit baut der Präprozessor für den Compiler folgenden
Quelltext zusammen:

int main()
{
  printf( "Dies ist ein Test\n" );
}

und der übersetzt ihn dann.

Würde ich das
#ifdef KALLE 1
auskommentieren

/* #define KALLE 1 */

int main()
{

#ifdef KALLE
  printf( "Dies ist ein Test\n" );
#else
  printf( "Das erste Programm\n" );
#endif
}

dann würde der Präprozessor beim #ifdef KALLE kein Makro namens
KALLE gefunden haben und er würde den Text aus dem #else Teil
einbauen.

int main()
{
  printf( "Das erste Programm\n" );
}

Das ganze geht aber auch noch weiter. #ifdef hat nur abgefragt
ob ein bestimmtes Makro existiert. Für #ifdef macht es keinen
Unterschied ob das nun so

#define KALLE 1

oder so

#define KALLE irgendwas

oder gar so

#define KALLE

definiert wurde. #ifdef kümmert sich nur darum, ob das Makro
existiert.
Mittels #if hingegen, kann man gezielt auch nach Werten abfragen

int main()
{
#if KALLE == 1
  printf( "Das war eine 1\n" );
#elif KALLE == 2
  printf( "Das war eine 2\n" );
#else
  printf( "Keine Ahnung\n" );
#endif
}

stellt du dem ein
#define KALLE 1
voran, dann baut der Präprozessor diesen Code daraus

int main()
{
  printf( "Das war eine 1\n" );
}

lautet das Makro aber

#define KALLE 2

so erzeugt der Präprozessor

int main()
{
  printf( "Das war eine 2\n" );
}

und in allen anderen Fällen macht er

int main()
{
  printf( "Keine Ahnung\n" );
}

daraus.






von Tobias Tezlaff (Gast)


Lesenswert?

Hallo,

danke dafür.

Aber ehe ich es Falsch verstehe,
wenn ich jetzt z.B. 16 LEDs definiere,

   #define LED16

dann wird nur für 16 LEDs Code erzeugt,
(also nur der Code genutzt, der in

   #ifdef LED16
     blalalalalla
   #endif

steht)
nicht für die anderen, ja?

Gruß Toby

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo Karl Heinz,

da warst Du schneller als ich schreiben konnte,

danke für deine sehr ausführliche Erklärung!

Gruß Toby

von Karl H. (kbuchegg)


Lesenswert?

Tobias Tezlaff wrote:
> Hallo,
>
> danke dafür.
>
> Aber ehe ich es Falsch verstehe,
> wenn ich jetzt z.B. 16 LEDs definiere,
>
>    #define LED16
>
> dann wird nur für 16 LEDs Code erzeugt,
> (also nur der Code genutzt, der in
>
>    #ifdef LED16
>      blalalalalla
>    #endif
>
> steht)

ganz genau.
Der WItz in C ist es, dass du den Quelltext vor dem Compilieren
automatisch bearbeiten lassen kannst. Bearbeiten heist bei
#ifdef, dass Quelltextteile auch rausfliegen können bevor
sie der eigentliche Compiler sieht.

> nicht für die anderen, ja?
>

Das hängt davon ob, wie deren Bedingungen lauten.
Im obigen wird der Quelltext
   blablablabla
genau dann mitcompiliert, wenn LED16 definiert ist.
Über anderen Code sagt das nichts aus.


von johnny.m (Gast)


Lesenswert?

@GAst:
> ...dass im ersten Beispiel eine Konstante LED16 deklariert wird,...
#define deklariert keine Konstante und auch nichts anderes. Mit #define 
erzeugt man ein Präprozessor-Makro, und das ist nichts anderes als ein 
Platzhalter für das, was hinter dem Bezeichner steht. Es handelt sich um 
eine reine Textersetzung, die lediglich dazu dient, den Schreibaufwand 
zu reduzieren und die Übersichtlichkeit des Codes zu erhöhen. Der 
Präprozessor ersetzt beim Durchlaufen alle definierten Makros mit dem 
dazugehörigen Text. Wie in C Konstanten deklariert werden steht auf 
einem anderen Blatt und hat mit der Fragestellung hier nichts zu tun.

Das nur der Vollständigkeit halber... Ansonsten hat Karl Heinz ja schon 
alles gesagt.

von GAst (Gast)


Lesenswert?

Nur der Vollständigkeit halber...

Hast Du wohl recht und ich bin der Blödmann.

von Oliver (Gast)


Lesenswert?

Das ist zwar alles richtig, was bisher gesagt wurde, aber trotzdem 
unklar.

#ifdef LED16
  blalalalalla
#endif

#ifdef LED32
  lululilale
#endif

führt schnell dazu, daß du fünf Programme in einem C-file schreibst, 
dazu aber noch ineinander verschachtelt. Das gibt nur Durcheinander, und 
führt zu Fehlern. Das must du am Ende fünfmal kompilieren, fünfmal 
debuggen, und das ergibt garantiert fünf mal fünf Ärger.

Sinnvoll ist so etwas:
1
#define ANZAHL_LEDS 16 // hier trägst du die Anzhal der LEDs ein
2
3
#define LAUF_BITS ((ANZAHL_LEDS)/4)) // nur so als Beispiel
4
5
...
6
7
for (int i=0;i<ANZAHL_LEDS;i++)
8
...

EIN Programm, mit parametrierbaren Konstanten. Und davon möglichst 
wenig.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Oliver wrote:
> EIN Programm, mit parametrierbaren Konstanten. Und davon möglichst
> wenig.

Da hast du grundsätzlich schon recht.
Nur so einfach geht das meist nicht.

Denn: Beim Übergang von bsp. 8 Led auf 16 Led muss
beispielsweise ein 2-ter Port initialisiert werden.

Das kriegst du anders als mit #ifdef nicht hin.

#define LEDS 16

int main()
{
   // wenn nur 8 Leds benutzt werden, spielt sich alles am
   // Port B ab, von 8 bis 16 Leds an den Ports B und D
   // 24 Leds mach ich an den Ports B, D und C

   DDRB =0xFF;      // für 8 Led
#if LEDS > 8
   DDRD = 0xFF;
#endif
#if LEDS > 16
   DDRC = 0xFF;
#endif

   ....

Auch für die Aufteilung der Lauflichtbits wird man um ein
paar #if nicht herumkommen.

In der Praxis wird es wohl auf eine Kombination aus beiden
Techniken hinauslaufen:
Nach Möglichkeit alles in Formeln packen, die mittels ein
paar Makros parametriert werden. Wo das nicht geht, muss
man auf #if ausweichen.

von Karl H. (kbuchegg)


Lesenswert?

> #define LEDS 16
>
> int main()
> {
>    // wenn nur 8 Leds benutzt werden, spielt sich alles am
>    // Port B ab, von 8 bis 16 Leds an den Ports B und D
>    // 24 Leds mach ich an den Ports B, D und A
>
>    DDRB =0xFF;      // für 8 Led
> #if LEDS > 8
>    DDRD = 0xFF;
> #endif
> #if LEDS > 16
>    DDRA = 0xFF;
> #endif
>
>    ....
>
> Auch für die Aufteilung der Lauflichtbits wird man um ein
> paar #if nicht herumkommen.

Zur Verdeutlichung:
Warum nicht einfach so:

   DDRB = 0xFF;
   if( LEDS > 8 )
     DDRD = 0xFF;
   if( LEDS > 16 )
     DDRA = 0xFF;

Auf einem Mega8 gibt es keinen PORT A. Damit ist aber
    DDRA = 0xFF;
ein schöner Syntaxfehler :-)

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo,

ich habe nun meinen ersten Test hinter mir.
Im Main sende ich per UARt eine "Kennun" der Hardware.

// Definition der LED Anzahl
#define ANZAHL_LEDS 48

im Main:

#if ANZAHL_LEDS == 16
         TxDatenSenden('N');
         TxDatenSenden('V');
         TxDatenSenden('1');
         TxDatenSenden('6');
         TxDatenSenden('S');
         TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#elif ANZAHL_LEDS == 32
  TxDatenSenden('N');
  TxDatenSenden('V');
  TxDatenSenden('3');
  TxDatenSenden('2');
  TxDatenSenden('S');
  TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#elif ANZAHL_LEDS == 40
  TxDatenSenden('N');
  TxDatenSenden('V');
  TxDatenSenden('4');
  TxDatenSenden('0');
  TxDatenSenden('S');
  TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#elif ANZAHL_LEDS == 48
  TxDatenSenden('N');
  TxDatenSenden('V');
  TxDatenSenden('4');
  TxDatenSenden('8');
  TxDatenSenden('S');
  TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#elif ANZAHL_LEDS == 56
         TxDatenSenden('N');
  TxDatenSenden('V');
  TxDatenSenden('5');
  TxDatenSenden('6');
  TxDatenSenden('S');
  TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#elif ANZAHL_LEDS == 64
  TxDatenSenden('N');
  TxDatenSenden('V');
  TxDatenSenden('6');
  TxDatenSenden('4');
  TxDatenSenden('S');
  TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#else
  TxDatenSenden('N');
  TxDatenSenden('V');
  TxDatenSenden('0');
  TxDatenSenden('0');
  TxDatenSenden('S');
  TxDatenSenden('/');
  TxDatenSenden('W');
  TxDatenSenden(10);  //Vorschub
  TxDatenSenden(13);  //Rücklauf
#endif

Geht so weit wirklich gut!

Nun habe ich allerdings 6 einzelne Programme in einem Großen.
Da gebe ich Oliver Recht, das es schnell unübersichtlich wird.
Vorallem, da es nicht nur ein Lauflicht ist.
Ich nutze einen externen EEProm, in dem Biler gespeichert werden.
Die LED Reihe dreht sich, wie ein Propeller Uhr.

Nun muß ich ja alle Routinen, wie z.B. das EEProm lesen/schreiben
sechs mal schreiben, und zwar für jede LED Anzahl eine eigene,
diese mit dem #elif von einander getrennt.
(Dumm Ausgedrückt ! Ich hoffe ihr versteht mich)

So eine EEProm Routine sieht so aus:

void Write_SPI_EEProm (void){

BYTE HighAdress = 0;
BYTE LowAdress = 0;
BYTE Daten1 = 0;
BYTE Daten2 = 0;
BYTE Daten3 = 0;
BYTE Daten4 = 0;
BYTE Daten5 = 0;
BYTE Daten6 = 0;
BYTE Daten7 = 0;
BYTE Daten8 = 0;
//BYTE status = 0;

//LED Rot an
sbi(PORTD, 7);

while(RxBufferStatus()==0);
myReceivedByte = RxBufferLesen();
if(myReceivedByte==':'){

  while(RxBufferStatus()==0);
    HighAdress = RxBufferLesen();
  while(RxBufferStatus()==0);
    LowAdress = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten1 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten2 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten3 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten4 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten5 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten6 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten7 = RxBufferLesen();
  while(RxBufferStatus()==0);
    Daten8 = RxBufferLesen();

  //status = Read_EEProm_Status();
  //if ( status == 0b00000000 ){
  //while(Read_EEProm_Status()!=0);
  cbi(PORTB,0);
  spiTransferByte(0b00000110);//write enable WREN
  sbi(PORTB,0);
  cbi(PORTB,0);
  spiTransferByte(0b00000010);//write command
  spiTransferByte(HighAdress);//high adresse
  spiTransferByte(LowAdress);  //low adresse
  spiTransferByte(Daten1);  //data
  spiTransferByte(Daten2);  //data
  spiTransferByte(Daten3);  //data
  spiTransferByte(Daten4);  //data
  spiTransferByte(Daten5);  //data
  spiTransferByte(Daten6);  //data
  spiTransferByte(Daten7);  //data
  spiTransferByte(Daten8);  //data
  sbi(PORTB,0);

while(RxBufferStatus()==0);
myReceivedByte = RxBufferLesen();
if(myReceivedByte==';'){

  //Bestätigung schicken
  TxDatenSenden('O');

    }
  }

//LED Rot aus
cbi(PORTD, 7);

}

Hier bschreibe ich den EEProm mit den per UART Empfangenen Bytes.
Wenn ich das alles 6 mal schreiben muß, wird es doch ganz schön 
unübersichtlich, nicht?
Das sollte doch besser in eine Funktion gehen, in der Man die LED Azahl 
durch 8 teilt.
ann habe ich zumindest schon mal die byte Anzahl, die ich im EEProm 
speichern muß/will.

Gruß Toby

von Karl H. (kbuchegg)


Lesenswert?

Tobias Tezlaff wrote:
> Hallo,
>
> ich habe nun meinen ersten Test hinter mir.
> Im Main sende ich per UARt eine "Kennun" der Hardware.
>
> // Definition der LED Anzahl
> #define ANZAHL_LEDS 48
>
> im Main:
>
> #if ANZAHL_LEDS == 16
>          TxDatenSenden('N');

[Snip]

>   TxDatenSenden(10);  //Vorschub
>   TxDatenSenden(13);  //Rücklauf
> #endif
>
> Geht so weit wirklich gut!
>
> Nun habe ich allerdings 6 einzelne Programme in einem Großen.
> Da gebe ich Oliver Recht, das es schnell unübersichtlich wird.

Kein Wunder.
Mann. Du musst dir Hilfsfunktionen bauen:

void TxStringSenden( char * String )
{
  while( *String )
    TxDatenSenden( *String++ );
}

und in main() dann

#if ANZAHL_LEDS == 16
   TxStringSenden( "NV16S/W\r\n" );
#elif ANZAHL_LEDS == 32
   TxStringSenden( "NV32S/W\r\n" );
#elif ANZAHL_LEDS == 40
   TxStringSenden( "NV40S/W\r\n" );
#elif ANZAHL_LEDS == 48
   TxStringSenden( "NV48S/W\r\n" );
#elif ANZAHL_LEDS == 56
   TxStringSenden( "NV56S/W\r\n" );
#elif ANZAHL_LEDS == 64
   TxStringSenden( "NV64S/W\r\n" );
#else
   TxStringSenden( "NV00S/W\r\n" );
#endif

Schon besser. Aber wenn ich mir die Strings so anschaue,
dann sind die alle nach dem gleichen Muster aufgebaut:
Zuerst kommt ein "NV", dann die Anzahl der Leds, gefolgt
von dem String "S/W\r\n"

D.h. wenn ich die Anzahl der LED als Zahl habe, dann mach
ich flugs daraus einen String, setzte "NV" davor und
"S/W\r\n" hinten dran und habe den String fertig zur Ausgabe:

* Wenn sprintf kein Problem ist, dann zb so:

     sprintf( Buffer, "NV%02dS/W\r\n", ANZAHL_LEDS );
     TxStringSenden( Buffer );

* Falls sprintf keine gute Idee ist:

     TxStringSenden( "NV" );
     itoa( Buffer, ANZAHL_LEDS,10 );
     TxStringSenden( Buffer );
     TxStringSenden( "S/W\r\n" );

* Falls es kein Problem ist dass der #else Fall nicht
  richtig behandelt wird:

#define Stringize(x)  #x

     TxSTringSenden( "NV" Stringize(ANZAHL_LEDS) "S/W\r\n" );

Wodurch dein ganzer 50-Zeiler in nur einer Zeile unterkommt.


> Wenn ich das alles 6 mal schreiben muß, wird es doch ganz schön
> unübersichtlich, nicht?

Du musst dir gemeinsame Teile suchen und in eigene Funktionen
auslagern. Oder gemeinsame Teile in Formeln beschreiben, in die
deine Kennzahlen eingehen.
Ohne Nachzudenken einfach ein paar #if und #define
einzubauen ist genau der Weg, den Oliver (zu Recht)
bekrittelt hat.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger wrote:
>
> #define Stringize(x)  #x
>
>      TxSTringSenden( "NV" Stringize(ANZAHL_LEDS) "S/W\r\n" );
>

Falls du dich frägst wie das funktioniert:
Das benutzt den sog. 'Stringize' Operator # in einem Makro.
Der Präprozessor bau beim Stringizen (also für das #x) einfach
nur Gänsefüschen rund um das Argument.

Aus
     "NV" Stringize(ANZAHL_LEDS) "S/W\r\n"

wird durch die erste Makro Substitution
(ANZAHL_LEDS ersetzeN)

     "NV" Stringize(32) "S/W\r\n"

Dann wird das Stringize Makro evaluiert, dass durch den #
erzwingt, dass das ganze umgeformt wird zu

     "NV" "32" "S/W\r\n"

und zu guter letzt ist der Compiler noch verpflichtet, String
Konstanten die direkt aufeinanderfoglen zu einer einzigen
zusammenzufassen:

     "NV32S/W\r\n"

e voila.

Du hättest das ganze auch so machen können:

   TxStringSenden(
#if ANZAHL_LEDS == 16
                  "NV16S/W\r\n"
#elif ANZAHL_LEDS == 32
                  "NV32S/W\r\n"
#elif ANZAHL_LEDS == 40
                  "NV40S/W\r\n"
#elif ANZAHL_LEDS == 48
                  "NV48S/W\r\n"
#elif ANZAHL_LEDS == 56
                  "NV56S/W\r\n"
#elif ANZAHL_LEDS == 64
                  "NV64S/W\r\n"
#else
                  "NV00S/W\r\n"
#endif
                 );

Denk immer daran: Der Präprozesser ist eine reine Textverarbeitungs-
maschinerie. Den kümmern C Anweisungen oder deren Grenzen oder
Klammern oder ; herzlich wenig. Das Einzige ist: Aus Strings
hält er sich raus:

#define ABC 123

int main()
{
  printf( "ABC" );
}

das druckt wirklich ABC, und nicht 123.

>
>> Wenn ich das alles 6 mal schreiben muß, wird es doch ganz schön
>> unübersichtlich, nicht?
>
> Du musst dir gemeinsame Teile suchen und in eigene Funktionen
> auslagern. Ohne Nachzudenken einfach ein paar #if und #define
> einzubauen ist genau der Weg, den Oliver (zu Recht) bekrittelt hat.

Du willst faul sein. Du willst nicht alles 6 mal schreiben
muessen. Also musst du nach Wegen suchen, wie du dieses Ziel
erreichen kannst. Die Lösung dafür heist aber so gut wie nie:
Cut&Paste

von Karl H. (kbuchegg)


Lesenswert?

> Nun muß ich ja alle Routinen, wie z.B. das EEProm lesen/schreiben
> sechs mal schreiben, und zwar für jede LED Anzahl eine eigene,
> diese mit dem #elif von einander getrennt.
> (Dumm Ausgedrückt ! Ich hoffe ihr versteht mich)

Warum musst du da 6 eigene Funktionen schreiben.
Der eigentliche Lesevorgang ist doch immer derselbe, nur
die Anzahl der zu lesenden Bytes verändert sich.

> void Write_SPI_EEProm (void){
>
> BYTE HighAdress = 0;
> BYTE LowAdress = 0;
> BYTE Daten1 = 0;
> BYTE Daten2 = 0;
> BYTE Daten3 = 0;
> BYTE Daten4 = 0;
> BYTE Daten5 = 0;
> BYTE Daten6 = 0;
> BYTE Daten7 = 0;
> BYTE Daten8 = 0;

Aus diesen Datenx Variablen machst du gleich mal ein Array ...

  BYTE Daten[ANZAHL_LEDS] = 0;

> //BYTE status = 0;
>
> //LED Rot an
> sbi(PORTD, 7);
>
> while(RxBufferStatus()==0);
< myReceivedByte = RxBufferLesen();
> if(myReceivedByte==':'){
>
>  while(RxBufferStatus()==0);
>    HighAdress = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    LowAdress = RxBufferLesen();

> while(RxBufferStatus()==0);
>    Daten1 = RxBufferLesen();
> while(RxBufferStatus()==0);
>    Daten2 = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    Daten3 = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    Daten4 = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    Daten5 = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    Daten6 = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    Daten7 = RxBufferLesen();
>  while(RxBufferStatus()==0);
>    Daten8 = RxBufferLesen();

... denn dann kannst du das Datenlesen sofort
in eine Schleife packen.

  for( i = 0; i < ANZAHL_LEDS; ++i ) {
    while(RxBufferStatus()==0);
      Daten[i] = RxBufferLesen();
  }

>
>  //status = Read_EEProm_Status();
>  //if ( status == 0b00000000 ){
>  //while(Read_EEProm_Status()!=0);
>  cbi(PORTB,0);
>  spiTransferByte(0b00000110);//write enable WREN
>  sbi(PORTB,0);
>  cbi(PORTB,0);
>  spiTransferByte(0b00000010);//write command
>  spiTransferByte(HighAdress);//high adresse
>  spiTransferByte(LowAdress);  //low adresse

...

>  spiTransferByte(Daten1);  //data
>  spiTransferByte(Daten2);  //data
>  spiTransferByte(Daten3);  //data
>  spiTransferByte(Daten4);  //data
>  spiTransferByte(Daten5);  //data
>  spiTransferByte(Daten6);  //data
>  spiTransferByte(Daten7);  //data
>  spiTransferByte(Daten8);  //data

... und auch hier wieder eine Schleife benutzen
  for( i = 0; i < ANZAHL_LEDS; ++i )
    spiTransferByte( Daten[i] );

>  sbi(PORTB,0);
>
> while(RxBufferStatus()==0);
> myReceivedByte = RxBufferLesen();
> if(myReceivedByte==';'){
>
>   //Bestätigung schicken
>   TxDatenSenden('O');
>
>  }

...
und schon brauchst du wieder nur eine Funktion.
Diese Funktion wird über ANZAHL_LEDS parametrisiert.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger wrote:
>> #define Stringize(x)  #x
>>
>>      TxSTringSenden( "NV" Stringize(ANZAHL_LEDS) "S/W\r\n" );
>>

Ah. reingefallen. (Passiert mir immer wieder :-)

Das geht so nicht.
Aber so

#define STR(x)   #x
#define Stringize(x)  STR(x)

  TxSTringSenden( "NV" Stringize(ANZAHL_LEDS) "S/W\r\n" );

Man muss den Umweg über ein 'Zwischenmakro' gehen.
Die erste Lösung würde
    NVANZAHL_LEDSS/w\r\n

erzeugen, also zuerst ANZAHL_LEDS in "" verpacken und erst
dann ANZAHL_LEDS durch die Zahl ersetzen (was aber nicht passiert,
da sich der Präprozessor aus Strings raushält).

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo Karl Heinz,

ich habe nun diese Hilfsfunktion eingebaut:

void TxStringSenden( char * String )
{
  while( *String )
    TxDatenSenden( *String++ );
}

und in main() dann

#if ANZAHL_LEDS == 16
   TxStringSenden( "NV16S/W\r\n" );
#elif ANZAHL_LEDS == 32
   TxStringSenden( "NV32S/W\r\n" );
#elif ANZAHL_LEDS == 40
   TxStringSenden( "NV40S/W\r\n" );
#elif ANZAHL_LEDS == 48
   TxStringSenden( "NV48S/W\r\n" );
#elif ANZAHL_LEDS == 56
   TxStringSenden( "NV56S/W\r\n" );
#elif ANZAHL_LEDS == 64
   TxStringSenden( "NV64S/W\r\n" );
#else
   TxStringSenden( "NV00S/W\r\n" );
#endif


Sag mir bitte, warum soll sprintf ein Problem sein?
Verstehe es nicht so ganz, was Du damit meinst, oder was die Funktion 
bewirken kann.

Werde nachher mal die Funktionen für das EEProm schreiben und lesen 
verkürzen!

Gruß Toby

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo nochmal,

muß ich nicht hier:

Aus diesen Datenx Variablen machst du gleich mal ein Array ...

  BYTE Daten[ANZAHL_LEDS] = 0;

die ANZAHL_LEDS durch 8 teilen, da die LEDs ja Bits sind, und die Daten 
Bytes sind?

Bei z.B. 64 LEDs habe ich ja nur 8 Bytes, also nur 8 mal die i Schleife 
durchgehen, und nicht 64!

Gruß Toby

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


Lesenswert?

> Sag mir bitte, warum soll sprintf ein Problem sein?

Weil es die eierlegende Wollmilchsau der Bibliotheksfunktionen
schlechthin ist.  Entsprechend schleppt sie einiges an Code mit
sich herum.  Wenn das für dich kein Problem ist, dann ist auch
sprintf() kein Problem. ;-)

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo,

danke für deine Antwort.
Leider ist mein Mega8 schon fast voll.
Es passt nichtmal mehr der Bootloader rein! ;-(

Nunja, dann werde ich die sprintf() Funktion wohl eher mal raus lassen.

Gruß Toby

von Karl H. (kbuchegg)


Lesenswert?

Tobias Tezlaff wrote:
> Hallo nochmal,
>
> muß ich nicht hier:
>
> Aus diesen Datenx Variablen machst du gleich mal ein Array ...
>
>   BYTE Daten[ANZAHL_LEDS] = 0;
>
> die ANZAHL_LEDS durch 8 teilen, da die LEDs ja Bits sind, und die Daten
> Bytes sind?

Wenn das bei dir so ist, dann musst du das wohl durch 8
teilen :-)

>
> Bei z.B. 64 LEDs habe ich ja nur 8 Bytes, also nur 8 mal die i Schleife
> durchgehen, und nicht 64!

Klingt gut.

Mach das aber nicht so direkt im Code, sondern definiere
dir wieder ein Makro

#define ANZAHL_LEDS 16
#define ANZAHL_BYTES  ( ANZAHL_LEDS / 8 )

und das benutzt du dann:

  BYTE Daten[ANZAHL_BYTES] = 0;

  ...

  for( i = 0; i < ANZAHL_BYTES; ++i )
    ...

von Karl H. (kbuchegg)


Lesenswert?

Tobias Tezlaff wrote:
> Hallo,
>
> danke für deine Antwort.
> Leider ist mein Mega8 schon fast voll.

Für ein simples Lauflicht ?!

Hast du den Optimizer vom Compiler aktiviert?

von Tobias Tezlaff (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Karl Heinz,

danke zuerst.

Natürlich ist es kein simples Lauflicht!

Es ist, wie oben kurz mal erwähnt, eine Art Propeller Uhr,
nur halt mit bis zu 2mal 64 LEDs.
2mal 64 LEDs, ja, das kommt daher, da ich das Ganze in einem 
Modellhubschrauber unterbringen will.
Der hat 2 Rotorblätter, in diese kommen je bis zu 64 LEDs,
einmal nach oben abstrahlend, einmal nach unten.

Nun kommt der ganze Kram der Spaltenberechnung, der 
Umdrehungsberechnung,
und die 16x12 Schrift im Programmspeicher hinzu.

Gruß Toby

von Tobias Tezlaff (Gast)


Lesenswert?

Hmmm,

so'n Mist.

Ich habe nun folgendes:

// Definition der Led und Byte Anzahl
#define ANZAHL_LEDS 48
#define ANZAHL_BYTES  ( ANZAHL_LEDS / 8 )

...

void Read_SPI_EEProm (void){

BYTE HighAdress = 0;
BYTE LowAdress = 0;
BYTE Daten[ANZAHL_BYTES] = 0;
BYTE i = 0;

  //LED Rot an
  sbi(PORTD, 7);

  while(RxBufferStatus()==0);
   myReceivedByte = RxBufferLesen();
   if(myReceivedByte==':'){
    while(RxBufferStatus()==0);
     HighAdress = RxBufferLesen();
    while(RxBufferStatus()==0);
     LowAdress = RxBufferLesen();
     cbi(PORTB,0);
     spiTransferByte(0b00000011);  //read command
     spiTransferByte(HighAdress);  //high adresse
     spiTransferByte(LowAdress);  //low adresse
     for( i = 0; i < ANZAHL_BYTES; ++i ){
      while(RxBufferStatus()==0);
      Daten[i] = RxBufferLesen();
      }
     sbi(PORTB,0);

  while(RxBufferStatus()==0);
   myReceivedByte = RxBufferLesen();
  if(myReceivedByte==';'){
  //Daten mit "D" ankündigen, PC wartet, bis "D" kommt
  TxDatenSenden('D');
   for( i = 0; i < ANZAHL_LEDS; ++i )
   spiTransferByte( Daten[i] );
   }
    }
 //LED Rot aus
 cbi(PORTD, 7);
}

Nun bekomme ich aber den Fehler:

error: invalid initializerbei dieser Zeile:
BYTE Daten[ANZAHL_BYTES] = 0;

Dann passt doch was nicht mit dem Array.

Gruß Toby

von Karl H. (kbuchegg)


Lesenswert?

Tobias Tezlaff wrote:
>
> Es ist, wie oben kurz mal erwähnt, eine Art Propeller Uhr,

Hab ich irgendwie überlesen :-)

> nur halt mit bis zu 2mal 64 LEDs.
> 2mal 64 LEDs, ja, das kommt daher, da ich das Ganze in einem
> Modellhubschrauber unterbringen will.
> Der hat 2 Rotorblätter, in diese kommen je bis zu 64 LEDs,
> einmal nach oben abstrahlend, einmal nach unten.

Geil.
Da ich selbst Heli fliege, interessiert mich das brennend.
Wenn du fertig bist: Gib mir bitte Nachricht!

von Karl H. (kbuchegg)


Lesenswert?

Tobias Tezlaff wrote:

> error: invalid initializerbei dieser Zeile:
> BYTE Daten[ANZAHL_BYTES] = 0;
>
> Dann passt doch was nicht mit dem Array.

Der Compiler beschwert sich, dass die Initialisierung
nicht stimmt.

* lass das  = 0  weg.
  da die Werte sowieso ausgelesen werden, wäre eine Initialisierung
  auf 0 nur Zeitverschwendung.

* Da das ein Array ist, muss die Initialisierung so lauten
  BYTE Daten[ANZAHL_BYTES] = { 0 };


von Rolf Magnus (Gast)


Lesenswert?

Wie ist es denn mechanisch geplant? Also daß die LEDs nicht die 
Luftströmung auf dem Blatt stören, daß die Blätter stabil bleiben und 
daß sie sauber ausgetrimmt sind? Wie wird das Signal an den Rotor 
übergeben?

von Tobias Tezlaff (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Rolf,

ich habe LED Module mit je 8 LEDs, die weren von einer "zentralen 
Steuerplatine" angesteuert.

Leider sind die Platinen etwas teuer, und aus kronischem Geldmangel 
gehts nur langsam voran.

Außerdem würde ich gerne jedem Blatt einen eigenen Prozessor mit LP 
spendieren.

Gruß Toby

von Tobias Tezlaff (Gast)


Angehängte Dateien:

Lesenswert?

Hier noch ein Bild der Steuerplatine.

Aus Kostengründen habe ich 240 LED Module fertigen lassen, und erstmal 
25 Steerplatinen.
Im Nutzen waren die im Endeffekt günstiger, als Prototypen.

Die LED Module werden ins Blatt mit einlaminiert, die Steuerplatine soll 
eine Art Rotorbremse werden.

Zum Signa habe ich 2 Möglichkeiten.
1. Ein Hallsensor im Bereich der Taumelscheibe.
2. Ein Hallsensor am Hauptzahnrad, welcher einem weiterem AVR im festen 
Teil des Helis einen INT auslöst.
Dieser sendet dann ein 36KHz Signal zum IR Empfangsmodul der drehenden 
Steuerplatine.

Bei der 2. Version kann ich durch auswertung eines Empfängersignals und 
dadurch veränderlichen Pulslängen des IR Signals sogar noch zwischen 3 
Bildern mit einem 3 Stufenschalter am FS-Sender umschalten.

Leider macht das IR Empfangsmodul noch ein paar Probleme, da anscheinend 
mehr 36KHz Signale rummschwirren, wie ich mir nie hätte träumem lassen.
So bekomme ich ständig unerwartete Interups, die mich wirklich stören.

Gruß Toby

von Tobias Tezlaff (Gast)


Lesenswert?

Ach, noch was,

ich habe 2 Versionen geplant.

Eine 16 LED Text Version
eine 32 bis 64 LED Grafik Version.

Beide sind auf den Bildern zu sehen.
Die Holzblätter dienen nur zum Vergleich, es sind 600er Raptor 50 
Blätter.

Zur Stromversorgung kann ich nur noch sagen, es gibt ein Schaltnetzteil 
auf der Steuerplatine.
Versorgt wird die Hardware aus 2x 2 350er Lipos.

Alles zum Projekt ist hier :

http://www.rc-heli.de/board/index.php?topic=57220.0

Gruß Toby

von Oliver (Gast)


Lesenswert?

>Leider macht das IR Empfangsmodul noch ein paar Probleme, da anscheinend
>mehr 36KHz Signale rummschwirren, wie ich mir nie hätte träumem lassen.
>So bekomme ich ständig unerwartete Interups, die mich wirklich stören.

Genau deshalb macht man die Auswertung ja auch nicht per Interrupt, Und 
36kHz-IR-Signale kommen eigentlich nur von Fernbedienungen. Solange da 
keine anderen in der Nähe betätigt werden, gibt es auch keine Signale.

guggst du hier:
Beitrag "Fernbedien RC5 Empfänger"

Deine Rotor-LED-Bilder sind allerdings wirklich Klasse.

Oliver

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo Oliver,

ich war bei meinem Vater, der auch mit AVRs etwas macht.
Er hat mal dasEmpfangsodul an seinen Oszi gehängt.
Das Signal, die Masse und VCC waren absolut sauber.
Trotzdem gab es dort ständig Interrupts.
Bis wir die Neonröhre mal ausgeschaltet haben, (so eine Lampe, mit 2 
Röhren, etwas verspiegelt).
Dann gab es keinen Interupt mehr.

Also liegt es nicht nur an IR Fernbedienungen, sondern auch an allen 
anderen Lichtquellen, die sich grade so spiegeln und reflektieren, 
daszufällig 36 KHz dabei raus kommen.

Des weiteren geht es bei meiner Anwendung nicht mit einem 
Übertragungsprotokoll wie z.B. RC5 Code.
Habe ich schon ausprobiert, nur dauert die Empfangsroutine immer eine 
gewisse Zeit.
Wenn sich mein Rotor aber schneller dreht, verschiebt sich das Bild, 
wenn die Übertragung immer gleich lange dauert,
da ich mit der IR Übertragung auch den überaus wichtigen Referenzpunkt 
(Nullpunkt) bei jeder Umdrehung nutze.

Gruß Toby

von Tobias Tezlaff (Gast)


Lesenswert?

Hallo nochmal,

evtl. habe ich mich etwas schlecht ausgedrückt!

Ich habe mal den IR Empfänger an di UART angeschlossen, und versucht,
per IR Signal ein Byte 1,2 oder 3 damit zu übertragen.
Hat ja soweit gut geklappt.
Nur dauert bei 2400 Baud die Übertragung eines Bytes etwa 4ms.
Nun habe ich im Rotor gewartet, bis per UART eine 1,2 oder 3 angekommen 
ist.
Das war dann das Zeichen dafür, das der Rotor nun bei Spalte 0 ist, also 
der Referenzpunkt.
Bei einer Drehzahl von 1500 U/min waren die 4 ms etwa (ich weiß es nicht 
mehr genau) 10 Grad versetzt zur eigentlichen Referenz.
Nun kann derRotor aber auch mit 2000 U/min drehen, und dann würde sich 
das Bild ja wieder um einige Grad verdrehen.
Daher habe ich auf diese Weise nicht weiter gemacht.

Ich hoffe, ihr könnt meinen Gedankengängen folgen! ;-)

Gruß Toby

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.