Forum: Mikrocontroller und Digitale Elektronik Seltsames verhalten des GCC Compilers beim Arduino


von Christian J. (Gast)


Lesenswert?

Hallo,

kann einer das erklären ?

Die Funktion IR_Start() wird NICHT ausgeführt, das sehe ich am 
Empfänger. Egal ob ich sie kapsel, einzeln schreibe etc. Sie ist einfach 
nicht da, dem Datensatz fehlt das Startsignal.

NUR wenn ich IR_Start() direkt vor den Aufruf von SendPaket im 
hauptprogramm schreibe (was echt doof aussieht und nicht dahin gehört), 
wird sie ausgeführt und ein Start Token gesendet zum Empfänger.

Häh? Das kann doch nicht sein, oder? Allerdings benutzen alle Routinen 
IR_Carrier...

Hab ich da was übersehen?

Gruss,
Christian

Hier der Aufruf der Infrarot Sende Routine:
1
  // IR Datensatz senden
2
  #ifdef USE_INFRARED_SENDER
3
    IR_Start();
4
    IR_SendPaket(&GlobalVars_,sizeof(GlobalVars_));
5
  #endif

Hier die Routine für das Datenpaket:
1
void IR_SendPaket(void* data, uint8_t len)
2
{
3
  #define GAP_TIME   600  
4
  #define START_TOK  2400
5
  
6
  uint8_t chksum = 0;
7
  
8
  //Pin Init...
9
  pinMode(IRED_PIN,OUTPUT);
10
  IR_Start();
11
  
12
  // Anzahl Bytes ab jetzt im Satz
13
  IR_SendByte(len);      
14
  uint8_t* ptr = (uint8_t*) data;    // Ptr auf erstes Byte
15
16
  // Datensatz raus....
17
  for (int i=0;i<len;i++) {
18
    IR_SendByte(*ptr);
19
    chksum = chksum ^ (*ptr);
20
    ptr++;
21
  }
22
  
23
  IR_SendByte(chksum);    // Checksumme hinter Datensatz
24
}

Hier der Rest:
1
// Erzeuge Starttoken
2
void IR_Start()
3
{
4
  // Start Token senden, damit Slave sicher einrastet
5
  IR_Carrier(START_TOK);
6
  delayMicroseconds(GAP_TIME);
7
}  
8
9
// -------------------------------------------------------------
10
11
// Sendet 1 Byte über die IRED ab
12
void IR_SendByte(uint8_t data)
13
{
14
  #define ONE_TIME  1200
15
  #define ZERO_TIME  600
16
17
  uint16_t dur;    
18
19
  // Byte einzeln raus, 1 oder 0 erzeugen, Start mit LSB
20
  for (int i=0;i<8;i++) {
21
    // Wenn bit = 1 delay = 1200, sonst delay = 600
22
    dur = (data & 1) ? ONE_TIME : ZERO_TIME;
23
    data >>= 1;      // Nächstes Bit vorschieben
24
    IR_Carrier(dur);  // Carrier erzeugen
25
    delayMicroseconds(GAP_TIME);  // Gap immer 0,6ms
26
  } // end for..
27
    
28
} // end IR_Sendbyte...  
29
30
// -------------------------------------------------------------
31
32
// Erzeugt den 40 Khz Träger
33
void IR_Carrier(uint16_t dur)      //Dauer in Mikrosekunden
34
{
35
  #define KHZ40    25        // Periodendauer
36
  #define CYCLE    29         // 40khz gemessen    
37
38
  /* CYCLE muss solange angepasst werden, bis exakt 40khz
39
    am Oszilloskop am IRED Pin angezeigt werden */
40
41
  // Anzahl Durchläufe je nach 1 oder 0
42
  uint16_t rounds = dur / (uint16_t)KHZ40;  
43
  for (int i;i<rounds;i++) {
44
    digitalWrite(IRED_PIN,HIGH);
45
    delayloop16(CYCLE);
46
    digitalWrite(IRED_PIN,LOW);
47
    delayloop16(CYCLE);
48
  } // end for...  
49
  
50
  
51
} // end IR_Carrier...  
52
53
// ----------------------------------------------------------------

von Christian J. (Gast)


Lesenswert?

Hier noch das letzte Bisschen der Volständigkeit halber:
1
// Tickgenaue Warteschleife in Assembler
2
static inline void __attribute__((always_inline))
3
delayloop16 (unsigned int count)
4
{
5
    /* Die Schleife dauert  4 * count + 3  Ticks */
6
    asm volatile ("1:"           "\n\t"
7
                  "sbiw %0,1"    "\n\t"
8
                  "brcc 1b"
9
                  : "+w" (count));
10
}

von Karl H. (kbuchegg)


Lesenswert?

Christian J. schrieb:

> Hier der Aufruf der Infrarot Sende Routine:
>
>
1
>   // IR Datensatz senden
2
>   #ifdef USE_INFRARED_SENDER
3
>     IR_Start();
4
>     IR_SendPaket(&GlobalVars_,sizeof(GlobalVars_));
5
>   #endif
6
>

und wo soll dieser Aufruf sein?
Der muss ja in einer Funktion enthalten sein. (zb innerhalb der Funktion 
setup(), die vom Arduino System beim Programmstart aufgerufen wird)

Die sehe ich aber nicht.

von Christian J. (Gast)


Lesenswert?

Ich meinte den Aufruf von IR_Start. Der steht im obigen Beispiel in 
beiden Funktionen, einmal Sendpaket und einmal im Hauptaufruf. Der in 
Sendpaket wird nicht ausgeführt, das ist echt seltsam. Der Sende Header 
gehört ja normalerweise in die Routine wo auch die Daten gesendet 
werden.

von Christian J. (Gast)


Lesenswert?

@Karl-Heinz: Ich habe mein Passwort vergessen und das Forum meint, dass 
es meine email nicht kennt. Kannst Du das vielleicht richten? Passiert 
leider, wenn man das Passwort jahrelang im Browser gespeichert hat und 
nach einer Neuinstallation es vergessen hat.

von Thomas E. (thomase)


Lesenswert?

Aus deinen Codefetzen geht hervor, daß eine bestimmte Funktion 
aufgerufen wird, die dann natürlich auch ausgeführt wird. Was soll da 
auch sonst mit passieren?

Daß es nicht funktioniert, ist bestenfalls durch die Glaskugel 
erkennbar. Vielleicht gibt es aber eine esoterische Lösung für das 
Problem.

Also zeig deinen ganzen Code. Oder zumindest einen abgespeckten 
funktionsfähigen Teil, der den Fehler reproduzierbar enthält. Bei 
Funktionen, die aufgerufen und (scheinbar) nicht ausgeführt werden, 
liegt der Fehler immer da, wo er garantiert nicht liegt. Und seltsames 
Compiler-Verhalten sitzt meist vor dem PC.

mfg.

von Christian J. (Gast)


Lesenswert?

Hi Thomas,

es gibt hin und wieder seltsame Dinge und da ist alles gepostet was dazu 
gehört, das ganze Projekt ist viel zu gross, über 3000 Zeilen. Die 
Compileroptimierung habe ich gern im Verdacht, weiss nicht wie ich die 
abschalten kann beim Arduino, da ich nicht weiss wo der sein make File 
versteckt hält, zumindest das was er benutzt, es gibt viele davon.

for (i=0;i<10;i++); verschwindet auch im Nichts, wenn man es so stehen 
lässt, dachte erst damit könnte ich Verzögerung erzeugen...

Gruss,
Christian

von Karl H. (kbuchegg)


Lesenswert?

Christian J. schrieb:
> Hi Thomas,
>
> es gibt hin und wieder seltsame Dinge und da ist alles gepostet was dazu
> gehört, das ganze Projekt ist viel zu gross, über 3000 Zeilen.

Dann speck es ab.

Du hast ein Problem im Code, nicht wir

von Karl H. (kbuchegg)


Lesenswert?

Christian J. schrieb:
> @Karl-Heinz: Ich habe mein Passwort vergessen und das Forum meint, dass
> es meine email nicht kennt. Kannst Du das vielleicht richten?

Nein, leider.
An derartige Dinge kommen wir Moderatoren nicht ran

von Thomas E. (thomase)


Lesenswert?

Christian J. schrieb:
> Hi Thomas,
>
> es gibt hin und wieder seltsame Dinge und da ist alles gepostet was dazu
> gehört,

Vielleicht holt ja einer seine Wünschelrute aus dem Keller.

> das ganze Projekt ist viel zu gross, über 3000 Zeilen. Die
> Compileroptimierung habe ich gern im Verdacht, weiss nicht wie ich die
> abschalten kann beim Arduino, da ich nicht weiss wo der sein make File
> versteckt hält, zumindest das was er benutzt, es gibt viele davon.
>
> for (i=0;i<10;i++); verschwindet auch im Nichts, wenn man es so stehen
> lässt, dachte erst damit könnte ich Verzögerung erzeugen...
>
> Gruss,
> Christian

Hübsches Beispiel.
Das macht ja auch nichts, ausser ein paar Takte sinnlos auf der Stelle 
treten. Da schlägt die Optimierung gnadenlos zu.
1
volatile char i;
2
...
3
for(i = 0; i < 10; i++);

Jetzt darf sie das nicht.

mfg.

von Christian J. (Gast)


Lesenswert?

Hi Thomas,

das Problem ist gelöst.... es ist gelöst aber ich weiss nicht warum.
Ich habe mal gelernt, so in den 80igern (UCSD Pascal) dass man in jeder 
Funktion am Anfang die Variablen deklariert und initialisiert.

Bei C schludert man etwas und schreibt sowas

for (int i=0;i<10;i++) ....

Ich habe aus allen Funktionen jetzt das ausgemerzt und die Variablen am 
Funktionsanfang hingeschrieben.

und siehe da: Das "S" für den Start Token wird gesendet und empfangen. 
Habe 2 Arduinos gleichzeitig am Rechner, einen Mega und einen Nano. Der 
eine sendet Infrarotbytes mit dem TSOP, der andere empfängt sie und 
stellt sie erstmal als 0 und 1 Ketten da.

Tja....

Das sind se, die Binärketten des Struct der übertragen wird, das S steht 
schön wie es soll am Anfang bevor die Bits rappeln....

Arduino Nano online!
Free RAM:1578 Bytes
TSOP Module detected!
RTC detected!

S00101000010111000100100111100101100000101011001100110011001101011000001
S00101000011010010110010111100101100000101011001100110011001101011000001
S00101000001101001110110111100101100000100101100110011001101101011000001
S00101000011101010010001111100101100000101011001100110011001101011000001
.....

von Maxx (Gast)


Lesenswert?

Christian J. schrieb:
> for (int i;i<rounds;i++) {

Welchen Wert hat ist i denn am Anfang? Initialisieren!

von Karl H. (kbuchegg)


Lesenswert?

Christian Julius schrieb:

> Ich habe mal gelernt, so in den 80igern (UCSD Pascal) dass man in jeder
> Funktion am Anfang die Variablen deklariert und initialisiert.


Ernsthaft:
Kauf dir ein C-Buch

Das hat keinen Sinn, wenn du an C mit 30 Jahre alten Pascal Kentnissen 
rangehst. Da auf die Schnauze zu fallen, ist nur eine Frage der Zeit.

> Bei C schludert man etwas und schreibt sowas

Nein. Man schludert nicht.
Man lernt die Sprache und dann WEISS man, was wann warum passiert.

> for (int i=0;i<10;i++) ....

Das ändert nichts.
Wenn im Schleifenkörper nichts ist, was den Compiler daran hindern würde 
die Schleife rauszuwerfen, dann wird der Optimizer zuschlagen und sie 
rauswerfen.

Oder siehst du irgendeinen Grund, warum
1
  for( int i = 0; i < 10; i++ )
2
    j = 5;
tatsächlich abgearbeitet werden soll?
Wenn diese Schleife abgearbeitet wird, dann gibt es i danach nicht mehr 
und j hat den Wert 5.
Das kann auch der Compiler rausfinden, und dein ganzes Konstrukt durch
1
  j = 5;
ersetzen. Die Ergebnisse sind dieselben.

> und siehe da:

zufällig.
Du hast Dinge vor dem Optimizer so versteckt, dass er sie nicht mehr 
findet. Gratuliere: du hast gerade das ad absurdum geführt, wofür 
Compilerbauer jahrelang gearbeitet haben: Dem Compiler immer mehr und 
immer bessere Fähigkeiten beizubringen, wie er für dich deinen 
Programmcode optimieren kann und für dich Laufzeit aus dem Code 
rausholen kann. Basierend auf den Regeln der Sprache.
Nur leider beherrscht du diese Regeln nicht und daher hast du 
unwissentlich dem Optimizer Optimierungspotential geboten, welches der 
ausgenutzt hast.

Man kann natürlich die Taktik "ich verwirre meinen Compiler maximal" 
weiterführen. Man kann aber auch die Regeln der Sprache lernen und MIT 
dem COmpiler arbeiten anstatt GEGEN ihn.

von Fux (Gast)


Lesenswert?

Christian J. schrieb:
> das ganze Projekt ist viel zu gross, über 3000 Zeilen

Erster Schritt beim Debuggen: Problem reduzieren.

von Christian J. (Gast)


Lesenswert?

Karl-Heinz,

glaube mir, ich habe schon zehntausende C Zeilen geschrieben und es lief 
bisher immer. Optimierung ist zb im industriellen Umfeld (Programmierung 
von Sicherheitsgeräten) VERBOTEN, ebenso wie Rekursion, Benutzung von 
Zeigern, noch schlimmer Doppel-** also Zeiger auf Zeiger, Type Casting 
usw.

Warum? Weil man sich damit Fehler reinhaut, schon allein durch die 
Priorisierung in Ausdrücken, zigfache Verschachtelung, die man ohne das 
nicht hätte. Manche Codes erzeugen den Eindruck durch ihre völlige 
Unleserlichkeit, dass der Coder meint, er würde so als echter Profi 
gelten, wenn er möglichst unleserlich schreibt. zb durch Nutzung von ? 
statt IF THEN ELSE.

Der Compiler hat das zu tun was ich ihm sage und nicht eigenmächtig zu 
entscheiden was sinnvoll ist und was nicht.  Es ist schön, wenn er 
merkt, dass eine Funktion reentrant ist, also zb aus ISR und 
Hauptprogramm aufgerufen wird und da Vorkehrungen trifft aber ansonsten 
entscheide ICH.
Genauso wie der WORD Mist, etwas zu schreiben und Word ändert es ab, 
formatiert es, weil es meint ich wolle das so.

Ich werde mich aber mit dem GCC mal näher befassen, vor allem mit dem 
Präprozessor

von Maxx (Gast)


Lesenswert?

Christian Julius schrieb:
> glaube mir, ich habe schon zehntausende C Zeilen geschrieben und es lief
> bisher immer. Optimierung ist

Mit Glück.
Die fehlende Initialisierung ist ein Kardinalfehler. Was da raus kommt 
ist Zufall. Du gibst an es ist dir egal. Du erwartest aber gleichzeitig, 
dass dir der C Compiler da ne 0 rein setzt. (anders kann ich das ",aber 
ohne Optimierer" nicht interpretieren)

Das ist nicht der Optimierer, das ist dein Glücksspiel (deine Wette).

von Thomas E. (thomase)


Lesenswert?

Christian Julius schrieb:
> Der Compiler hat das zu tun was ich ihm sage und nicht eigenmächtig zu
> entscheiden was sinnvoll ist und was nicht.
Der Compiler macht genau, was du ihm sagst. Der Optimierer ist per 
default eingeschaltet. Also optimiert er. Schaltest du ihn ab, optimiert 
er nicht.
Nur bei eingeschalteter Optimierung kannst du nicht erwarten, daß der 
Compiler deine Gedanken liest. Da musst du ihm schon, z.B. mit volatile, 
auf die Sprünge helfen. Oder die Optimierung abschalten. Alles deine 
Entscheidung.

mfg

von Karl H. (kbuchegg)


Lesenswert?

> Der Compiler hat das zu tun was ich ihm sage und nicht
> eigenmächtig zu entscheiden was sinnvoll ist und was nicht.

Oh. Er entscheidet nicht eigenmächtig.
Es gibt einen normierten Standard, in dem genau drinnen steht, wie das 
Verhalten sein muss. Und in diesem Rahmen bewegt sich der Optimizer.

von Karl H. (kbuchegg)


Lesenswert?

> glaube mir, ich habe schon zehntausende C Zeilen geschrieben und
> es lief bisher immer. Optimierung ist zb im industriellen Umfeld
> (Programmierung von Sicherheitsgeräten) VERBOTEN, ebenso wie Rekursion,
> Benutzung von Zeigern, noch schlimmer Doppel-** also Zeiger auf
> Zeiger, Type Casting usw.

Wie hast du es geschafft 'zehntausende Zeilen C Code' ohne Zeiger 
und/oder Type Castings zu schreiben?

Sorry. Aber den Teil nehm ich nicht ernst. Zusammen mit so man anderen 
Aussagen.

Zurück zu deinem Problem.
Dein Eröffnungsposting ist recht nichtssagend. Wenn du genau wissen 
willst, was da passiert, dann mach ein komplettes Beispiel zurecht, 
stell sicher, dass es das Verhalten zeigt und poste es. Alles andere ist 
Kaffeesatzlesen.

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.