Forum: Mikrocontroller und Digitale Elektronik fehler mit funktionen in c


von pille1990 (Gast)


Lesenswert?

ich beschäftige mich im moment mit funktionen in c. nun wollte ich das 
auch mal ausprobieren und wenn ich dieses programm dann kompiliere, 
kommt immer folgender fehler:

C:\Users\Benedikt\Documents\default/../test2.c:15: undefined reference 
to `funktion'
make: *** [test2.elf] Error 1
Build failed with 1 errors and 0 warnings...

zeile 15 ist da wo die funktion mit dem namen "funktion" aufgerufen 
wird.


1
#include <avr/io.h>
2
3
void funktion();
4
5
int main(void)
6
{
7
8
while(1)
9
  {
10
11
  DDRD = 0xff;
12
13
  PORTD = 0xff;
14
  
15
  funktion();
16
17
  PORTD = 0x00;
18
  }
19
  
20
  void funktion()
21
  {
22
  PORTD &= ~(1 << PA0);
23
  }
24
  
25
}

was mache ich da falsch?
wo liegt hier der fehler?

von Sven P. (Gast)


Lesenswert?

Unter Umständen wärs sinnvoll, die 'funktion' auch im richtigen 
Namensraum zu definieren...
1
#include <avr/io.h>
2
3
  void funktion()
4
  {
5
  PORTD &= ~(1 << PA0);
6
  }
7
8
int main(void)
9
{
10
11
while(1)
12
  {
13
14
  DDRD = 0xff;
15
16
  PORTD = 0xff;
17
  
18
  funktion();
19
20
  PORTD = 0x00;
21
  }
22
 
23
  
24
}

von Roland Praml (Gast)


Lesenswert?

1
#include <avr/io.h>
2
3
void funktion1(); // dies ist ein Prototyp, welcher später definiert wird
4
void funktion2()  // diese Fkt wird hier sofort definiert
5
{
6
  PORTD &= ~(1 << PA0);
7
}
8
9
10
int main(void)  // dies ist die Main-Fkt. In dieser können AFAIK keine weiteren definiert werden
11
{
12
while(1)
13
  {
14
  DDRD = 0xff;
15
  PORTD = 0xff;
16
  
17
  funktion1();
18
  funktion2();
19
20
  PORTD = 0x00;
21
  }
22
}
23
24
void funktion1() // hier wird die Funktion im zuvor angelegten Prototyp definiert
25
{
26
  PORTD &= ~(1 << PA0);
27
}
soweit klar?
Gruß
Roland

von pille1990 (Gast)


Lesenswert?

danke für die hilfe...nach 2 stunden verzweifeltem probiern und im 
internet lesen habt ihr mir das sehr gut erklärt...danke!!!

von Georg W. (gewe)


Lesenswert?

Hallo pille1990,

wenn Du Prototyping machst, muss die entsprechende Funktion im gleichen 
Gültigkeitsbereich liegen, d.h. außerhalb von main().

Wenn Du funktion() vor main() einfügst, brauchts Du nicht einmal das 
Prototyping.

Außerdem würde ich void funktion(void) schreiben. Ahnliches gilt für int 
main(void). Hier fehlt der Rückgabewert oder Du schreibst void main 
(void).

Mit diesem Hinweisen kannst Du das Programm fehlerfrei übersetzen.

Wobei ich aber den Sinn des Programms nicht sehen kann.

PS: Falls Du den Port D immer als Ausgang nutzen möchtest, genügt es die 
Datenrichtungsbits einmal vor der Hauptschleife zu beschreiben.

PPS: Wie lange bleibt Port D 0xff bzw. 0x00?

cu
Georg

von pille1990 (Gast)


Lesenswert?

das programm war einfach nur zum testen wie das mit den funktionen 
funktioniert....
jetzt habe ich aber ein sinnvolleres programm geschrieben...bei dem ich 
aber schon wieder ein problem habe.

ich will einen zähler realisieren, der die tastendrücke eines tasters 
zählt und diese dann auf einem port ausgibt.
dazu frage ich einfach den taster ab
1
#include <avr/io.h>
2
3
#define TASTER PC0
4
#define EINGANG PINC
5
#define AUSGANG PORTD
6
7
void press()
8
  {
9
  
10
  while(!EINGANG & (1 << TASTER));      //solange in der while schleife laufen, bis der taster gedrückt ist
11
  i++;
12
  AUSGANG = i;
13
  }
14
15
void release()
16
  {
17
  while(EINGANG & (1 << TASTER));    //solange in der while schleife laufen, bis der taster wieder losgelassen wird
18
  }
19
20
void main(void)
21
{
22
  DDRC = 0x00;
23
  DDRD = 0xff;
24
25
  
26
  
27
  while(1)
28
  {
29
30
  int i;
31
  i = 0;
32
33
  press();
34
35
  release();
36
  }
37
  
38
}

nun zum problem:
da ich ja die variable "i" in der main funktion deklariert habe, ist sie 
in der press funktion nicht verfügbar.
ich will aber nun, dass die variable auch in der press funktion 
verfügbar ist ohne dort die folgenden programmzeilen zu schreiben:
1
int i;
2
i = 0;

da gibt es doch für die funktionen diese parameter...aber irgendwie 
verstehe ich nicht wie man diese hier einsetzt.

noch eine frage:
warum bekomme ich vom compiler einen fehler wenn ich die funktion 
"press" und "release" nach der main funktion deklariere?

von Matthias N. (vbchaos)


Lesenswert?

pille1990 wrote:
> nun zum problem:
> da ich ja die variable "i" in der main funktion deklariert habe, ist sie
> in der press funktion nicht verfügbar.
> ich will aber nun, dass die variable auch in der press funktion
> verfügbar ist ohne dort die folgenden programmzeilen zu schreiben:

Stichwort: Globale Variable. Dazu schreibst du das int i = 0; einfach 
außerhalb der Funktionen, sinnvollerweise über die erste Funktion und 
unter die includes. Dort findet man globale definitionen am einfachsten 
wieder. Irgendwo im AVR Tutorial werden globale Variablen sicherlich 
erklärt.
Edit (Der Ordnung halber): Mit Übergabewerten (Parametern) an Funktionen 
hat das nichts zu tun.

> noch eine frage:
> warum bekomme ich vom compiler einen fehler wenn ich die funktion
> "press" und "release" nach der main funktion deklariere?

Weil: Dein compiler geht das von oben nach unten durch. Wenn er in einer 
funktion, bei dir die main, einen aufruf sieht, den er noch nicht kennt 
(weil eben weiter unten definiert), macht er n warning, weil er den 
aufruf nicht kennt. Ausführen lässt sich das ganze trotzdem, weil der 
Compiler wahrscheinlich mehrere durchgänge macht (zwecks optimierung), 
daher auch nur eine Warning und kein Error. Er möchte dich quasi sanft 
darauf hinweisen, dass dein Programmierstil da noch Nachbesserung 
vertragen könnte :) Daher also: Wenn du aufrufe machst auf funktionen, 
die später erst definiert werden, macht man oben (unter includes, über 
die erste funktion) einen Prototyp der noch kommenden funktionen. In 
deinem Fall: void press (void);

von Georg W. (gewe)


Lesenswert?

Hallo pille1990,

übersetzen lässt sich:
1
#include <avr/io.h>
2
3
..
4
5
void press(int i)
6
  {
7
  
8
  while(!EINGANG & (1 << TASTER));      //solange in der while schleife laufen, bis der taster gedrückt ist
9
  i++;
10
  AUSGANG = i;
11
  }
12
13
...
14
15
void main(void)
16
{
17
  DDRC = 0x00;
18
  DDRD = 0xff;
19
  
20
  while(1)
21
  {
22
23
  int i;
24
  i = 0;
25
26
  press(i);
27
  release();
28
  }
29
  
30
}

Funktioniern wird das aber auch nicht, da bei jedem Durchlauf der 
while-Schleife i auf 0 gesetze wird. D.h. für i gilt das selbe wie für 
DDRD,
1
#include <avr/io.h>
2
3
...
4
5
void main(void)
6
{
7
  int i;
8
  i = 0;
9
10
  DDRC = 0x00;
11
  DDRD = 0xff;
12
  
13
  while(1)
14
  {
15
16
  press(i);
17
  release();
18
  }
19
  
20
}

Du wirst aber Probleme bekommen weil der Ausgabeport 8 Bit "breit" ist, 
du aber einen 16 Bit Integer zuweist.

Um solche Knackpunkte zu vermeiden werden in der stdint.h die Datentypen 
mit der Bitbreite definiert:



typedef signed char  int8_t
typedef unsigned char  uint8_t
typedef signed int  int16_t
typedef unsigned int  uint16_t
typedef signed long int  int32_t
typedef unsigned long int  uint32_t
typedef signed long long int  int64_t
typedef unsigned long long int  uint64_t

Wenn Du i als uint8_t deklarierst, läufst Du hier nicht in ein 
Überlaufproblem hinein.

cu
Georg

von Sven P. (Gast)


Lesenswert?

Achso, nochwas: 'void main()' ist in C99 nicht erlaubt.

C99 final draft:
1
5.1.2.2.1 Program startup 
2
The function called at program startup is named main. The implementation declares no
3
prototype for this function. It shall be defined with a return type of int and with no
4
parameters:
5
int main(void) { /* ... */ } ...

von Georg W. (gewe)


Lesenswert?

@Sven,

stimmt. Achse auf mein Haupt.

Das kommt davon, wenn man Tips nicht ausprobiert.

Beim Compilieren kommt ja auch eine entsprechende Warnung.

Somit sollte es dann heißen int main (void) und am Ende nach der while 
Schleife noch ein return (0) z.B.

cu
Georg

von Johannes M. (johnny-m)


Lesenswert?

Georg Werner wrote:
> @Sven,
>
> stimmt. Achse auf mein Haupt.
Aber ne schwere Achse, damit es richtig weh tut ;-)

> Somit sollte es dann heißen int main (void) und am Ende nach der while
> Schleife noch ein return (0) z.B.
Erstens ist das return am Ende überflüssig, da main, wenn es auf einem 
µC ohne OS läuft, nirgendwohin etwas zurückgeben kann. Zweitens steht 
das return hinter einer Endlosschleife und wird sowieso nie erreicht 
(weshalb manch ein Compiler dabei sogar eine Warnung ausspuckt, dass da 
Code steht, der nie erreicht wird). Drittens ist return keine 
Funktion, was ein Grund dafür ist, dass sein Argument nicht in 
Klammern eingeschlossen wird. OK, es funktioniert auch mit Klammern, 
aber die korrekte Schreibweise ist einfach "return ausdruck;"

von Karl H. (kbuchegg)


Lesenswert?

Johannes M. wrote:

> Erstens ist das return am Ende überflüssig, da main, wenn es auf einem
> µC ohne OS läuft, nirgendwohin etwas zurückgeben kann. Zweitens steht
> das return hinter einer Endlosschleife und wird sowieso nie erreicht
> (weshalb manch ein Compiler dabei sogar eine Warnung ausspuckt, dass da
> Code steht, der nie erreicht wird). Drittens ist return keine
> Funktion, was ein Grund dafür ist, dass sein Argument nicht in
> Klammern eingeschlossen wird. OK, es funktioniert auch mit Klammern,
> aber die korrekte Schreibweise ist einfach "return ausdruck;"

und viertens ist main die grosse Ausnahme.
Auch wenn main einen Returntyp von int hat, ist es zulässig das return 
am Ende wegzulassen. Der Compiler muss sich selbst ein
 return 0;
dazudenken

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.