Forum: Mikrocontroller und Digitale Elektronik Globale Variablen im ISR


von Thomas P. (tommy2002)


Angehängte Dateien:

Lesenswert?

Hallo,

langsam werden meine Codes länger und unübersichtlich, deswegen wollte 
ich jetzt mein Projekt sinnvoll in externe Dateien auslagern. Bei dem 
ganzen machen mir irgendwie globale Variablen in der ISR Probleme.

Mein Programm soll folgendes machen:

Wenn sich was an der Schnittstelle tut, wird ein Interrupt ausgelöst. 
Somit bearbeitet mein µC den Krempel, der in der  ISR(USART_RXC_vect) 
{...} steht.

Jetzt habe ich aber 2 Variablen mit denen ich in der main Fkt. arbeiten 
möchte.

1. uart_string <- Hat in meinem Fall 3 Felder. 2 Bytes + \0
Die Variable wird zum testen an PORTC ausgegeben um 8 LED's zu treiben.

2. uart_str_complete <- gibt einfach eine boolsche Aussage, ob nun alles 
empfangen wurde. Dieser muss nach der verwendung des Codes wieder auf 0 
zurückgesetzt werden.

Mein Problem ist nun: Nach der Auslagerung bekomme ich es ums verrecken 
nicht mit den globalen Variablen gebacken.

z.B. das steht in uart.c
1
volatile uint8_t uart_str_count = 0;
2
volatile unsigned char nextChar;
3
volatile char uart_string[UART_MAXSTRLEN+1] = "";
4
volatile uint8_t uart_str_complete = 0;

Normal sind diese Variablen doch global oder nicht?
Selbst wenn ich es direkt vor allen includes in meine main.h setze 
klappt es nicht. Ich kann die Fkt. uart_string nicht in meiner Main 
verwenden so wie es in meinem Code ist. Es kommt immer die 
Fehlermeldung: 'uart_string' undeclared (first use in this function).


Hoffentlich habe ich mein Problem einigermaßen verständlich formuliert.

Weiß jemand eine Lösung für dieses Problem?

Als Anhang füge ich noch meinen Code hinzu.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

main.h:
1
extern int main (void);

Die Idee ist ja schon nicht schlecht, für main braucht man das 
allerdings nicht.

Wenn du da jetzt deine globalen Variablen mit extern davor hinschreibts, 
wird alles gut.

Oliver

von Oliver S. (oliverso)


Lesenswert?

main.h:
1
extern int main (void);

Die Idee ist ja schon nicht schlecht, für main braucht man das 
allerdings nicht.

Wenn du da jetzt deine globalen Variablen mit extern davor hinschreibst, 
wird alles gut.

Oliver

von Thomas P. (tommy2002)


Lesenswert?

Problem ist nur: Der header main.h wird öfter gecallt.

Nun wird die Fehlermeldung: multiple definition für die Var. ausgegeben.

Ich habe es auch schon mit einem global_var.h header versucht und dann 
in meiner main.h geschrieben:
(Da gibt es hier ja eine schöne Anleitung)


main.h
1
...
2
...
3
#define EXTERN
4
#include "global_var.h"
5
6
...
7
...

global_var.h
1
#ifndef EXTERN
2
#define EXTERN extern
3
#endif
4
5
EXTERN volatile char uart_string[3] = "";


Hat leider auch nichts gebracht...
Wenn ich es mit einer anderen Variable versuche und diese dann in die 
global einfüge klappt es überall. Mir scheint es irgendwie, als hätte 
der ISR ein Problem damit.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

main.c:
volatile char uart_string[3] = "";

main.h
extern volatile char uart_string[3];

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:

> Hat leider auch nichts gebracht...

SChmeiss das EXTERN Makro raus und mach es so, wie es die meisten 
machen.
Vor allen Dingen dann, wenn du nicht weisst das eine Initialisierung 
automatisch aus einer Deklaration eine Definition macht.

Nicht künsteln! Dann funktioniert das auch


main.h
1
....
2
#include "global_vars.h"
3
...

global_vars.h
1
....
2
extern volatile char uart_string[3];
3
....

uart.c
1
...
2
#include "global_vars.h"
3
4
volatile char uart_string[3] = "";
5
6
Funktionen ...

von Thomas P. (tommy2002)


Lesenswert?

Daniel A. schrieb:
> main.c:
> volatile char uart_string[3] = "";
>
> main.h
> extern volatile char uart_string[3];

Klappt leider auch nicht... :-(

Letztendlich macht das selbe ja der Header global_var.h

Irgendwie stehe ich mit dem extern noch ein wenig auf dem Schlauch.


Ich habe zumindest gelernt: Eine Variable welche innerhalb einer 
Funktion deklariert wird, lebt nur so lange, wie diese auch ausgeführt 
wird. Danach wird Sie aus dem Speicher gelöscht.

Alles andere was vor der main{} steht und nicht in einer Funktion ist, 
ist doch grundsätzlich als global zu sehen, oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Daniel A. schrieb:
>> main.c:
>> volatile char uart_string[3] = "";
>>
>> main.h
>> extern volatile char uart_string[3];
>
> Klappt leider auch nicht... :-(

Doch das klappt.
Dann hast du noch einen Hund eingebaut.

> Letztendlich macht das selbe ja der Header global_var.h

Nein, das tust du eben NICHT!

Sobald du eine Initialisierung angibst, ist das 'extern' nur noch 
Makulatur!
Die Initialisierung MUSS im Header File weg!

> Alles andere was vor der main{} steht und nicht in einer Funktion ist,
> ist doch grundsätzlich als global zu sehen, oder nicht?

Darum gehts überhaupt nicht.
Es geht um die Unterscheidung zwischen Definition und Deklaration. Eine 
Definition ist etwas, bei dem vom Compiler tatsächlich Speicherplatz 
reserviert wird. Das sind Definitionen
1
int i;
2
double j;
3
4
void foo()
5
{
6
  j = 1;
7
}

Eine Deklaration ist etwas, das den Compiler darüber informiert, dass 
etwas existiert und welche Eigenschaften es hat. Das sind Deklarationen
1
extern int i;
2
extern double j;
3
4
void foo( void );

Das hier
1
extern int i = 5;
ist KEINE Deklaration, sondern eine Definition. Die Intialisierung 
erzwingt dies.

Du kannst in einem kompletten Programm beliebig viele Deklarationen von 
ein und demselben Teil haben, solange die nur alle übereinstimmen. Du 
kannst aber nur EINE Defintion haben.

von Karl H. (kbuchegg)


Lesenswert?

So baust du das auf

uart.h
1
#ifndef UART_H
2
#define UART_H
3
4
#include <stdint.h>
5
6
#define UART_MAXSTRLEN 2
7
8
extern volatile uint8_t uart_str_count;
9
extern volatile unsigned char nextChar;
10
extern volatile char uart_string[UART_MAXSTRLEN+1];
11
extern volatile uint8_t uart_str_complete;
12
13
void uart_init(void);
14
#endif

uart.c
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "uart.h"
4
5
volatile uint8_t uart_str_count = 0;
6
volatile unsigned char nextChar;
7
volatile char uart_string[UART_MAXSTRLEN+1] = "";
8
volatile uint8_t uart_str_complete = 0;
9
10
void uart_init(void)
11
{
12
...
13
}
14
15
ISR(USART_RXC_vect)
16
{
17
...
18
}

main.c
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
5
#include "uart.h"
6
7
int main()
8
{
9
  uart_init();
10
11
  DDRC = 0xFF; // PORTC Ausgang
12
  sei();
13
14
  while (1)
15
  {    
16
    PORTC = uart_string[0];
17
    _delay_ms(5000);
18
    PORTC = uart_string[1];
19
    _delay_ms(5000);
20
    
21
    PORTC = 0xFF;
22
    _delay_ms(50);
23
    PORTC = 0x00;
24
    _delay_ms(50);
25
    PORTC = 0xFF;
26
    _delay_ms(5000);
27
  }
28
  return 0; // never reached
29
}


In jedem File wird das und nur das #includ-iert was tatsächlich in 
diesem File (sei es Header oder C-File) notwendig ist. Sieh dir jede 
Datei an. Jede ist in sich vollständig. Die Header Datei uart.h enthält 
alles, was der Source Code in uart.h von sich preisgeben will. Aber auch 
nicht mehr. Im Header File wird die Schnittstelle zu uart.c festgelegt. 
Da sind alle Informationen, die ein Verwender deines UART Moduls vom 
Modul wissen muss um es zu verwenden. Dazu gehören natürlich die nach 
aussen sichtbaren Funktionen. Dazu gehören aber auch die globalen 
Variablen, die vom Code in uart.c zur geschätzen (vorsichtigen) 
Verwendung bereit gestellt werden. Und dazu gehören auch auch Makros, 
sofern sie von allgemeinerem Interesse sind und nicht nur irgendwelche 
Details im Code beschreiben, die ausser dem Code in uart.c niemanden was 
angehen. In letzterem Fall kommen sie dann ins C-File. Aber der 
entscheidende Punkt ist: Im Header File sind letzten Endes dann nur 
Deklarationen (es gibt auch Ausnahmen), im C-File sind die zugehörigen 
Definitionen.

Solche Rundumschlag-Header Dateien, die erst mal 200 andere Header 
includieren, die meidest du besser. So viel Arbeit ist das nicht, bei 
den AVR typisch kleinen Programmen jedes File in sich konsistent zu 
machen.

F_CPU gibst du am besten in die Projekteinstellungen vom Atmel Studio

: Bearbeitet durch User
von Thomas P. (tommy2002)


Lesenswert?

So, es funktioniert tatsächlich!



Karl Heinz schrieb:
> Solche Rundumschlag-Header Dateien, die erst mal 200 andere Header
> includieren, die meidest du besser. So viel Arbeit ist das nicht, bei
> den AVR typisch kleinen Programmen jedes File in sich konsistent zu
> machen.

Stimmt auch wieder...


Ich werde mich noch mit den Variablen auseinandersetzen, da besteht noch 
ein wenig Nachholbedarf ;-). Oben hast du ja ein paar gute Beispiele 
gebracht.

Vielen Dank für die Mühe, welche du dir gemacht hast Karl Heinz.

EDIT:
Kleine Frage noch: Was für einen Unterschied macht es, wenn ich die 
F_CPU nicht in Atmel-Studio eintrage sondern als #define?


Gruß
Thomas

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Thomas P. schrieb:
> Kleine Frage noch: Was für einen Unterschied macht es, wenn ich die
> F_CPU nicht in Atmel-Studio eintrage sondern als #define?

In den Projekteinstellungen steht sie einmal drin und gilt für alle 
Dateien. Wenn du die Frequenz änderst, änderst du sie einmal und alle 
wissen Bescheid.
Schreibst du es als define in jede einzelne Datei, musst du bei einer 
Änderung jede Datei ändern. Vergisst du das oder benutzt eine Datei in 
verschiedenen Projekten, compilieren z.B. die Timer mit einer anderen 
Frequenz als der UART. Und diese andere Frequenz ist garantiert die 
falsche.


mfg.

von Falk B. (falk)


Lesenswert?

@ Thomas Eckmann (Firma: Thomas Eckmann Informationst.) (thomase)

>Schreibst du es als define in jede einzelne Datei, musst du bei einer
>Änderung jede Datei ändern.

Genau DAS macht man eben nicht. Man schreibt sie EINMAL in EINE 
Header-Datei, z.B. main.h, die wird dann bei Bedarf von allen anderen 
Dateien #include genutzt.

von Peter II (Gast)


Lesenswert?

Falk Brunner schrieb:
> Genau DAS macht man eben nicht. Man schreibt sie EINMAL in EINE
> Header-Datei, z.B. main.h, die wird dann bei Bedarf von allen anderen
> Dateien #include genutzt.

warum sollte aber Datei die überhaupt nichts von der main wissen soll 
(nur dann kann man sie auch in anderen Projekten nutzen) eine main.h 
einbinden?

Im makefile oder den Projekteinstellung ist F_CPU besser aufgehoben.

von Thomas E. (thomase)


Lesenswert?

Falk Brunner schrieb:
> Genau DAS macht man eben nicht. Man schreibt sie EINMAL in EINE
> Header-Datei, z.B. main.h, die wird dann bei Bedarf von allen anderen
> Dateien #include genutzt.

Das habe ich doch auch geschrieben, dass man das nicht macht. Aber in 
main.h, wenn man sowas überhaupt hat, hat das auch nichts zu suchen.

Das gehört in die Projetkteinstellungen.

mfg.

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.