Forum: Compiler & IDEs GCC Funktion wird manchmal nicht aufgerufen


von A. R. (redegle)


Lesenswert?

Hallo,

ich habe in GCC eine Funktion geschrieben, die ein Display ansteuert. 
Also der Funktion übergebe ich die zu adressierende Zeile und Spalte 
sowie das zu schreibende Symbol. Anschließend steuert die Funktion bei 
dem Display die Adresse an und übermittelt das Symbol. Das ganze 
funktioniert auch. Befindet sich ganz unten.

Diese Funktion wird in der main zu Beginn mehrmals aufgerufen und 
schreibt an 6 Ausgewählte stellen eine 0.
1
  Display_write(0b00110000,0,0);
2
  Display_write(0b00110000,0,1);
3
  Display_write(0b00110000,1,0);
4
  Display_write(0b00110000,2,0);
5
  Display_write(0b00110000,3,0);
6
  Display_write(0b00110000,3,1);

Nach dem Einschalten des Controllers gibt es aber immer wieder einzelne 
Stellen (unterschiedlich), die nicht beschrieben werden. Kann mir das 
jemand erklären? Beim deuggen mittels Atmel Studio 6.0 werden immer nur 
die ersten beiden Funktionen aufgerufen.


1
void Display_write(uint8_t Symbol, uint8_t Zeile,uint8_t Spalte) //Zeile und Spalte beginnen mit 0
2
{
3
  
4
  uint8_t Adresse = 0;
5
  PORTC &= ~((1 << PORTC1) | (1 << PORTC6)); //RS und R/W auf 0
6
  Adresse = 0b10000000;//DD RAM Address Set (Position zum Beschreiben)  
7
  switch(Zeile)//Zeile anwählen
8
  {    
9
    case 0:
10
    Adresse += 0x00;//Zeile 1
11
    break;
12
    case 1:
13
    Adresse += 0x40;//Zeile 2
14
    break;
15
    case 2:
16
    Adresse += 0x14;//Zeile 3
17
    break;
18
    case 3:
19
    Adresse += 0x54;//Zeile 4
20
    break;
21
    default:
22
    // Do nothing
23
    break;
24
  }
25
  
26
  Adresse +=  Spalte; //Spaltenadresse hinzufügen
27
  
28
  PORTD = Adresse;
29
  _delay_us(2);
30
  PORTC |= ((1 << PORTC7));  //Enable  1
31
  _delay_us(50);
32
  PORTC &= ~(1 << PORTC7);  //Enable  0
33
  _delay_us(2);
34
  
35
  
36
  PORTC |= ((1 << PORTC1)); //RS auf 1
37
  PORTD = Symbol;//Symbol schreiben
38
  _delay_us(2);
39
  PORTC |= ((1 << PORTC7));  //Enable  1
40
  _delay_us(50);
41
  PORTC &= ~(1 << PORTC7);  //Enable  0
42
  _delay_us(2);
43
}

von Peter II (Gast)


Lesenswert?

A. R. schrieb:
> Beim deuggen mittels Atmel Studio 6.0 werden immer nur
> die ersten beiden Funktionen aufgerufen.

und was passiert dann?

also der der GCC hier einen fehler hat, ist zu 999.9999% ausgeschlossen.

von Karl H. (kbuchegg)


Lesenswert?

A. R. schrieb:

> Nach dem Einschalten des Controllers gibt es aber immer wieder einzelne
> Stellen (unterschiedlich), die nicht beschrieben werden. Kann mir das
> jemand erklären?
Sporadische Fehler am LCD sind des öfteren ein Hinweis darauf, dass das 
Timing etwas zu knapp sein könnte.

von A. R. (redegle)


Lesenswert?

>und was passiert dann?

Ich setzte auf alle 6 Funktionen in der main einen Breakpoint sowie 
einen Breakpoint innerhalb der Funktion.

F5 --> Breakpoint auf der ersten Funktion in Main.
F5 --> Breakpoint in der Funktion.
F5 --> Breakpoint auf der zweiten Funktion in Main.
F5 --> Breakpoint in der Funktion
F5 --> Breakpoint auf der dritten Funktion in Main.
F5 --> Breakpoint auf der vierten Funktion in Main.
F5 --> Breakpoint auf der fünften Funktion in Main.
F5 --> Breakpoint auf der sechsten Funktion in Main.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> A. R. schrieb:
>
>> Nach dem Einschalten des Controllers gibt es aber immer wieder einzelne
>> Stellen (unterschiedlich), die nicht beschrieben werden. Kann mir das
>> jemand erklären?
> Sporadische Fehler am LCD sind des öfteren ein Hinweis darauf, dass das
> Timing etwas zu knapp sein könnte.

Insbesonders nach dem Setzen des Cursors
1
  PORTD = Adresse;
2
  _delay_us(2);
3
  PORTC |= ((1 << PORTC7));  //Enable  1
4
  _delay_us(50);
5
  PORTC &= ~(1 << PORTC7);  //Enable  0
6
  _delay_us(2);
solltest du dem LCD ein wenig Zeit geben um den Befehl auch auszuführen.

von Karl H. (kbuchegg)


Lesenswert?

Und tu dir selbst einen Gefallen und teile diese Funktion in 2 
Teilfunktionen auf: die eine setzt den Cursor an eine bestimmte Stelle, 
die andere gibt ein Zeichen aus
1
void Display_write(uint8_t Symbol, uint8_t Zeile,uint8_t Spalte) //Zeile und Spalte beginnen mit 0
2
{
3
  Display_set_cursor( Zeile, Spalte );
4
  Display_write_char( Symbol );
5
}

Beide Teilfunktionen werden dir in weiterer Folge noch gute Dienste 
leisten.

von A. R. (redegle)


Lesenswert?

>Sporadische Fehler am LCD sind des öfteren ein Hinweis darauf, dass das
>Timing etwas zu knapp sein könnte.

Danke für den Hinweis.
Da fällt mir ein, dass ich noch den Prozssortakt für die Funktion 
_delay_us() definieren muss. Sorry, habe bis jetzt noch recht wenig mit 
GCC gemacht.

Ich denke folgendes sollte ausreichen:
1
#define F_CPU 16000000UL

Was bedeutet das UL?

@ Karl Heinz Buchegger,

danke für den Tipp. Im Moment ging es mir ersteinmal darum, das Display 
überhaupt anzusteuern.

von Karl H. (kbuchegg)


Lesenswert?

A. R. schrieb:

> Ich denke folgendes sollte ausreichen:
>
>
1
#define F_CPU 16000000UL
>
> Was bedeutet das UL?

unsigned long

http://www.mikrocontroller.net/articles/FAQ#Welche_Datentypen_haben_Konstante.3F

von A. R. (redegle)


Lesenswert?

Ich habe nun die Zeit nachgemessen.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define F_CPU 16000000UL
5
6
7
int main(void)
8
{
9
  //Pullups und Ausgangssignale für die Ein- und Ausgänge setzten.
10
  PORTA = 0b11111111;//Pullups aktivieren
11
  PORTB = 0b00000000;
12
  PORTC = 0b00000000;
13
  PORTD = 0b00000000;
14
  
15
  //Port zu Eingang bzw. Ausgang definierten
16
  DDRA = 0b00000000;  //Schalter
17
  DDRB = 0b10111010;  //Sensoren + SD-Karte (SPI)
18
  DDRC = 0b11000011;  //Display Ansteuerung + JTAG
19
  DDRD = 0b11111111;  //Display Datenleitung
20
  
21
  while(1)
22
  {
23
    _delay_us(25);
24
    PORTD = 255;
25
    _delay_us(25);
26
    PORTD = 0;
27
  }   
28
}

Statt einer Onzeit von 25µs ist das Rechteck nur 1,624µs on. Das ist 
etwas Faktor 15,39 zu kurz. Kann mir jemand erklären, woran das liegt?

Optimization steht auf -O1.
Der Systemtakt beträgt 16 MHz. Es ist auch das Fuse mit Vorteiler 8 
ausgeschaltet.

von A. R. (redegle)


Lesenswert?

Ich habe nun die Daten delay.h etwas untersucht und dort die Funktion 
"__builtin_avr_delay_cycles()" gefunden. Wenn ich diese in der Main mit 
dem Wert 400 aufrufe erzeugt dies mein 25µs delay.

Der Fehler muss also innerhalb der Bibliothek delay.h liegen.

Scheinbar werden 60 und nicht 400 Takte gewartet.

von (prx) A. K. (prx)


Lesenswert?

Schon mal dran gedacht, dass delay.h die Taktfrequenz wissen muss, um 
die Umrechnung korrekt durchführen zu können? Wenn du die aber erst 
danach bekannt gibst, dann kommt eben etwas Falsches raus. Aber such 
ruhig weiter den Fehler bei Anderen.

von A. R. (redegle)


Lesenswert?

>Schon mal dran gedacht, dass delay.h die Taktfrequenz wissen muss, um
>die Umrechnung korrekt durchführen zu können?

JA!
Deswegen steht da auch
1
#define F_CPU 16000000UL
.

>Wenn du die aber erst danach bekannt gibst, dann kommt eben etwas falsches raus.

Vielen Dank für den Hinweis!

>Aber such ruhig weiter den Fehler bei Anderen.

Wie kommst du darauf, dass ich jemand anderen für meine Fehler 
verantwortlich mache? Ich habe lediglich versucht den Fehler 
einzugrenzen.

von Walter S. (avatar)


Lesenswert?

A. R. schrieb:
> Wie kommst du darauf, dass ich jemand anderen für meine Fehler
> verantwortlich mache?

A. R. schrieb:
> Der Fehler muss also innerhalb der Bibliothek delay.h liegen

von A. R. (redegle)


Lesenswert?

Dadurch das die Funktion "__builtin_avr_delay_cycles()" einwandfrei 
funktioniert ließ sich ein Hardwarefehler ausschließen.

Mit dem Satz "Der Fehler muss also innerhalb der Bibliothek delay.h 
liegen" meinte ich nicht, dass die Bibliothek fehlerhaft programmiert 
ist!

von A. R. (redegle)


Lesenswert?

So erneut ein Problem.

Diesmal macht mir die Optimierung von Atmel Studio 6.0 zu schaffen.

Das Programm ist wie folgt aufgebaut:

Defines
Includes
Globale Variablen (Benötige Zugriff im Interrupt und in der main)
Unterfunktionen
Interrupts

Main
   Initiallisierungen vom Display, Interrupts aktivieren etc.
   Dauerschleife (agiert basierend auf den Interrupts)

Die globalen Variablen habe ich "einfach" vor der main Platziert. Das 
sieht dann so aus:
1
uint16_t Millisekunden = 0;  //Wird jede Millisekunde vom Interrupt um 1 erhöht. Zählt von 0 bis 999
2
uint64_t Zeit = 0;      //Wird ggf. später implementiert
3
uint8_t Sekunden = 0;
4
uint8_t Minuten = 0;
5
uint8_t Stunden = 0;
6
7
uint8_t Trigger_Sekunde = 0; //Wird jede Sekunde vom Interrupt auf 1 gesetzt

Die Variablen brauche ich um Informationen von den Interrupts in der 
main aufzurufen. Wenn das Falsch ist oder es bessere Methoden gibt bitte 
ich um eure Vorschläge. In Assembler könnte ich feste Register oder 
Adressen verwenden.

Timer 1 wurde von mir so programmiert, dass er jede ms ein Interrupt 
auslöst. Erreicht ein Zähler die 1000 wird der Zähler auf 0 gesetzt und 
die Variable "Trigger_Sekunde" wird auf 1 gesetzt.

In der main wird auf die Variable "Trigger_Sekunde" gepollt. Ist 
Trigger_Sekunde == 1, so wird der Temperatursensor aktiviert und ein 
externes Interrupt eingeschaltet. Über das Interrupt werden nun die 
seriellen Daten des Sensors erfasst. Wurden alle Bits empfangen wird der 
Sensor vom Interrupt deaktiviert.

Das ganze funktioniert einwandfrei. Nur habe ich das Problem, dass der 
Compiler mir seit kurzem die komplette Dauerschleife wegoptimiert hat 
bzw. diese überhaupt nicht mehr aufgerufen wird.
1
while(1)
2
{        
3
  //Display_write_char(0b00110000);
4
    
5
  if(Trigger_Sekunde)//Wird wegoptimiert
6
  {
7
  // Tu etwas
8
  }
9
}

Wenn ich "//Display_write_char(0b00110000);" einfüge wird die 
if-Schleife aufgerufen. Wenn ich die Optimierung von -O1 auf -O0 stelle 
wird die if-Schleife auch aufgerufen.

Hat jemand eine Idee, warum der Compiler die if-Schleife oder die 
while-Schleife für überflüssig erachtet?

Die if-Schleife ist mittlerweile sehr lang geworden deswegen habe ich 
diese nich gepostet. Es wird aber mindestens ein Port gesetzt.
Kann es sein, dass der Compiler denkt, dass die Bedingung (globale 
Variabel) immer 0 ist?

Vielen Dank für eure Hilfe.

von Lötlackl *. (pappnase) Benutzerseite


Lesenswert?

A. R. schrieb:
> Nur habe ich das Problem, dass der
>
> Compiler mir seit kurzem die komplette Dauerschleife wegoptimiert hat

gähn... http://www.mikrocontroller.net/articles/Volatile

von A. R. (redegle)


Lesenswert?

Danke!

von A. R. (redegle)


Lesenswert?

Hat schonmal jemand mit dieser Bibliothek bezüglich SD-Karten 
gearbeitet?

Bibliothek:
http://www.mikrocontroller.net/articles/AVR_FAT32#Der_Status
Thread:
Beitrag "MMC SD library FAT16 FAT32 read write"


Ich habe nun die Header wie folgt eingefügt:
1
//Includes für SD-Karte
2
#define __AVR_ATmega644__
3
#include <stdlib.h>
4
5
// In mmc_config.h werden alle nötigen Konfigurationen vorgenommen
6
#include "mmc_config.h"
7
#include "file.h"
8
#include "fat.h"
9
10
// Hardwareabhängig
11
#include "mmc.h"

Hierzu habe ich bei Toolchain/AVR/GNU C Compiler/Directories den 
entsprechenden "Include Path" eingesetzt. Die Headerdateien werden 
gefunden.

Bei Toolchain/AVR/GNU C Compiler/Directories habe ich den Pfad mit den 
Headern und den *.c Dateien auch mal vorsichtshalber eingefügt.

Wenn ich nun folgendes Programmiere:
1
uint8_t Initiallisierung = mmc_init();

bekomme ich die Fehlermeldung: "undefined reference to 'mmc_init'.

Scheinbar fehlen die *.c Dateien?
Weiß jemand, wo ich diese einbinden kann?

Ich hatte nun einfach mal folgendes versucht:
1
#include "file.c"
2
#include "fat.c"
3
#include "mmc.c"

Daraufhin kam mehrmals folgende Fehlermeldung:

"undefined reference to 'TimingDelay'
Das ist eine Variable die in mmc.c verwendet und in mcc.h deklariert 
wird.

In mcc.h steht folgendes. Also normal sollte den der mcc.c die Variable 
bekannt sein.
1
// timer variable ( 10 ms intervall)
2
extern volatile uint8_t TimingDelay;

von A. R. (redegle)


Lesenswert?

Ein paar Bilder, damit man sieht, dass eure Hilfe nicht um sonst ist.

Beitrag "Re: Datenlogger Temperatur"

von troll (Gast)


Lesenswert?

A. R. schrieb:
>
1
> #include "file.c"
2
> #include "fat.c"
3
> #include "mmc.c"
4
>
Großer Unfug. Du musst die C-Dateien im AVR-Studio zum Projekt 
hinzufügen.

von A. R. (redegle)


Angehängte Dateien:

Lesenswert?

>Großer Unfug.

Ja!

>Du musst die C-Dateien im AVR-Studio zum Projekt hinzufügen.

Eben genau wie das geht wusste ich nicht!

Ich habe mittlerweile den Projektmappen-Expolorer entdeckt. Dort habe 
ich nun alle Header und *.c Dateien eingefügt. Siehe Bild im Anhang.

Nur bekomme ich die Fehlermeldungen, welche sich auch im Anhang 
befinden, in der Datei mmc.c obwohl die notwendigen defines in der Datei 
mmc.h gemacht werden. Hat jemand eine Erklärung dafür?

Wenn ich die defines direkt in der Datei mmc.c ergänze kommen die 
Fehlmeldungen nicht.

von trollalala (Gast)


Lesenswert?

A. R. schrieb:
> Ich habe mittlerweile den Projektmappen-Expolorer entdeckt. Dort habe
> ich nun alle Header und *.c Dateien eingefügt. Siehe Bild im Anhang.
Neeein! Nur die c-Dateien einfügen, die Header werden wo nötig vom 
Präprozessor eingefügt.

Korrigier das erstmal, dann sehen wir weiter. Hilfreich ist bei solchen 
Sachen auch eine Minimalbeispiel z.B. 2 C-Dateien und 1 Header um die 
Grundlagen zu "erforschen".

von trollalala (Gast)


Lesenswert?

trollalala schrieb:
> die Header werden wo nötig vom
> Präprozessor eingefügt.
mittels #include "header.h"
Zu <> oder "" siehe Beitrag "Re: C-Header wird nicht gefunden"

von A. R. (redegle)


Lesenswert?

Was ist daran falsch die Headerdateien mit ins Projekt aufzunehmen?
Wenn ich dort Änderungen vornehmen möchte ist es doch Sinnvoll, diese 
direkt im Projekt zu haben.

Das Einbinden mit "" sollte auch richtig sein, wenn die Header im 
Projekt sind. Es gab auch keine Fehlermeldung, dass die Header nicht 
gefunden werden.

Ich habe auch nochmal das angesprochene Minimalbespiel gemacht um zu 
zeigen das alles Funktionieren sollte.

CFil1.c:
1
#include "IncFile1.h"
2
3
void funktion(void)
4
{
5
  Hallo = Hallo+1;
6
  Hallo = Test5;
7
}

IncFile1.h:
1
#ifndef INCFILE1_H_
2
#define INCFILE1_H_
3
4
#include <avr/io.h>
5
6
uint8_t Hallo = 0;
7
#define Test5 5;
8
9
#endif /* INCFILE1_H_ */

Durch das Einfügen des Headers wird die Variable "Hallo" und das define 
Test5 gefunden.


Ich sehe jetzt keinen Grund, warum das im richtigen Programm nicht 
funktioniert.

von trollalala (Gast)


Lesenswert?

A. R. schrieb:
> Was ist daran falsch die Headerdateien mit ins Projekt aufzunehmen?
> Wenn ich dort Änderungen vornehmen möchte ist es doch Sinnvoll, diese
> direkt im Projekt zu haben.
Es ist schlichtweg Unsinn. Headerdateien erzeugen keinen Code, warum 
sollte man sie also ins Projekt aufnehmen wo sie "compiliert" werden 
würden?

> uint8_t Hallo = 0;
Aua! Globale Variable und dann noch im Header deklariert (oder 
definiert, das verwechsel ich ständig). Ganz böse.

von A. R. (redegle)


Lesenswert?

Scheinbar hatte der Compiler damit ein Problem, dass der globalen 
Variable
1
extern volatile uint8_t TimingDelay;

aus dem hinzugefügten Programmcode kein Wert zugewiesen worden ist.


>Es ist schlichtweg Unsinn. Headerdateien erzeugen keinen Code, warum
>sollte man sie also ins Projekt aufnehmen wo sie "compiliert" werden
>würden?

Also soll ich die Headerdateien im Notepad bearbeiten und nicht in der 
Entwicklungsumgebung?


>> uint8_t Hallo = 0;
>Aua! Globale Variable und dann noch im Header deklariert (oder
>definiert, das verwechsel ich ständig). Ganz böse.

Das war ein Testprogramm mit paar Zeilen um zu zeigen dass das 
includieren funktioniert.
Außerdem wäre diese Variable nur der Datei CFil1.c gültig.

von trollalala (Gast)


Lesenswert?

A. R. schrieb:
> Also soll ich die Headerdateien im Notepad bearbeiten und nicht in der
> Entwicklungsumgebung?
Bearbeiten kannst du sie in der IDE, aber dort nicht zum Projekt 
hinzufügen.

> Außerdem wäre diese Variable nur der Datei CFil1.c gültig.
Nein, in jeder Datei die IncFile1.h includiert. Das dürfte dann bei 
mehreren Dateien beim Linken Chaos erzeugen.

von Karl H. (kbuchegg)


Lesenswert?

trollalala schrieb:
> A. R. schrieb:
>> Was ist daran falsch die Headerdateien mit ins Projekt aufzunehmen?
>> Wenn ich dort Änderungen vornehmen möchte ist es doch Sinnvoll, diese
>> direkt im Projekt zu haben.
> Es ist schlichtweg Unsinn. Headerdateien erzeugen keinen Code, warum
> sollte man sie also ins Projekt aufnehmen wo sie "compiliert" werden
> würden?

Na, ja.
Zumindest im 4-er AVR-Studio gab es im Projektbaum eine Unterabteilung 
"Header-Files" (oder so ähnlich).
Damit hat man zumindest im Projekt die Information, welche Header Files 
zum Projekt dazugehören und kann sie bei Editierbedarf einfach 
annavigieren.

Aber bitte in die Rubrik "Header Files" verschieben!


> Aua! Globale Variable und dann noch im Header deklariert (oder
> definiert, das verwechsel ich ständig). Ganz böse.

An dieser Stelle meinst du "definieren".
Deklaration ist alles nach dem Muster "Lieber Compiler, es gibt ..."
Definition ist: "Hier musst du auch Speicher dafür reservieren."

Denk einfach an Mathe: Eine Definition ist etwas, mit dem du einen 
Begriff in allen seinen Details in das Problem mit hineinziehst.

von trollalala (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> An dieser Stelle meinst du "definieren".
Danke Karl Heinz, auf dich ist Verlass. :-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

trollalala schrieb:
> A. R. schrieb:
>> Was ist daran falsch die Headerdateien mit ins Projekt aufzunehmen?
>> Wenn ich dort Änderungen vornehmen möchte ist es doch Sinnvoll, diese
>> direkt im Projekt zu haben.

> Es ist schlichtweg Unsinn. Headerdateien erzeugen keinen Code, warum
> sollte man sie also ins Projekt aufnehmen wo sie "compiliert" werden
> würden?

Einen Header in ein Projekt aufzunehmen zu können finde ich sinnvoll.
Wenn die IDE damit dann Unsinn anstellt, weil sie ein seltsames Konzept 
von "Projekt" hat, spricht das nicht für die IDE.

Sinnvolle Aktionen mit einem header sind zum Beispiel:

• Nicht compilieren, sondern nur die Abhängigkeiten (auch rekursiv im
  Build-Prozess beachten

• Compilieren, wodurch gcc per default ein .h.gch erzeugt (Precompiled
  Header)

• Andere, vom Benutzer zuortenbare Rolle.  Beispiel: Behandlung wie
  eine .c-Datei,  was u.U sinnvoll sein kann.

• Weitere Aktion wäre statische Analyse (MISRA, lint, ...)

• Eigenschaften einstellen, zB ob die Datei mit archiviert/versioniert
  wird, in Dokumentationserzeugung eingeht (doxygen), Coding-Style,
  ob es eine Binärdatei ist oder Text-Datei, welche Line Endings, ...

Es gilt also viele Sachen, die man mit einem Header machen kann ausser 
mit dem Compiler-Hammer drauf zu hauen ;-)

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.