Forum: Mikrocontroller und Digitale Elektronik AVR C; Variable wird nicht übnernommen


von __Son´s B. (bersison)


Lesenswert?

Hallo,
drehe mich bei folgendenm Problem im Kreis. Variable "P" wird nicht 
übernommen;

void setPort (char P, char bit) {
P |= (1<<bit); }

int main(void) {
// mach was
setPort(PORTB, 0);
// mach was
}

Sobald ich in der oberen Funktion " PORTB |= (1<<bit); " einsetze, 
funktioniert die Zuweisung!

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

@bersison bitte unterlasse es in Zukunft den Threadtitel mit einem 
Unterstrich zu beginnen.

von troll (Gast)


Lesenswert?

Schlag mal dein C-Buch im Kapitel "call by value <-> call by reference" 
auf und guck nach wie PORTB definiert ist.

von Daniel (Gast)


Lesenswert?

zusätzlich zum Vorredner - achte auch darauf, wie schwer dein
Compiler flucht. Dazu solltest du ihm das Fluchen erlauben (-Wall)

von __Son´s B. (bersison)


Lesenswert?

troll schrieb:
> Schlag mal dein C-Buch im Kapitel "call by value <-> call by reference"
> auf und guck nach wie PORTB definiert ist.

Leider habe ich diesen Hinweis nicht verstanden. Geht´s auch etwas 
konkreter? Aus einem der Fachbücher habe ich den Tip.

PORTB 0-7 ist als Ausgang definiert!
Es kommen keine Warnmeldungen vom Compiler.

von ich (Gast)


Lesenswert?

Hinweis 1 >>>  "call by value <-> call by reference" <<<
Hinweis 2 >>>  guck nach wie PORTB definiert ist <<<

Now it's your turn.

von Karol B. (johnpatcher)


Lesenswert?

Darf man denn fragen was du eigentlich vorhast?

Anders gefragt: Erfordert dein Vorhaben wirklich eine solche 
Funktionalität zur Laufzeit? Das schaut für mich nämlich (ohne den 
genauen Kontext zu kennen) eher nach einer Angelegenheit für ein Makro 
aus. Das würde dann nämlich während des Kompilierens expandiert werden 
und würde dir neben dem o.g. Problemen auch "Rechenzeit" einsparen.

von Peter D. (peda)


Lesenswert?

__Son´s Bersi__ schrieb:
> Leider habe ich diesen Hinweis nicht verstanden.

Die Argumente einer Funktion sind immer eine Kopie.
Wenn Du nun die Kopie änderst, ist das dem Original völlig wurscht.

Es gibt nun 3 Lösungen:
1.
Man weißt den Returnwert wieder dem Original zu.

2.
Man übergibt einen Pointer auf das Original.

3.
Man nimmt keine Funktion, sondern ein Macro.

von Kaj (Gast)


Lesenswert?

Wenn du was an eine Funktion übergeben willst, dann soltest du dir auch 
gedanken darüber machen was du da wirklich übergibts!

Also: was glaubst du, was  "PORTB"  ist?

... und nein, die Antwort ist nicht "42"!

von __Son´s B. (bersison)


Lesenswert?

Kaj schrieb:
> Wenn du was an eine Funktion übergeben willst, dann soltest du dir auch
> gedanken darüber machen was du da wirklich übergibts!
>
> Also: was glaubst du, was  "PORTB"  ist?
>
> ... und nein, die Antwort ist nicht "42"!

nicht "42"? komisch...

Die Originalidee habe ich aus einem Fachbuch, welches als Makro 
geschrieben war. Mir geht es jetzt aber um eine Funktion mit gleichem 
Inhalt!

Konkrete Antworten auf folgende Fragen habe ich mir HIER erhofft;
Was ist "PORTB" und vor allem, wie kann es an eine Variable übergeben 
werden?

von Stefan E. (sternst)


Lesenswert?

__Son´s Bersi__ schrieb:
> Was ist "PORTB" und vor allem, wie kann es an eine Variable übergeben
> werden?

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

von Fabian O. (xfr)


Lesenswert?

__Son´s Bersi__ schrieb:
> Was ist "PORTB" und vor allem, wie kann es an eine Variable übergeben
> werden?

Für Dich ist nicht relevant, was PORTB genau ist. Du kannst es wie eine 
globale Variable nutzen. Du hast schon einen grundsätzlichen Denkfehler 
an anderer Stelle:
1
void meine_funktion(int x)
2
{
3
  x = 5;
4
}
5
6
int main(void)
7
{
8
  int a = 2;
9
  meine_funktion(a);
10
  printf("%i", a);
11
}

Was kommt da raus? 2.
Warum? Weil an die Funktion der Inhalt von a übergeben wird. Also der 
Wert 2. Dass der vorher in der Variable a gestanden hat, weiß die 
Funktion nicht. Sie bekommt eine Kopie dieses Werts in der Variable x. 
Das ist eine neue Variable, die nur innerhalb von meine_funktion gültig 
ist.

Diese neue Variable setzt sie auf 5 und kehrt zurück. Beim Zurückkehren 
der Funktion verliert x auch schon wieder seine Gültigkeit. Damit macht 
meine_funktion im Prinzip überhaupt nichts. Der Compiler wird das 
komplett wegoptimieren.

von __Son´s B. (bersison)


Lesenswert?

Fabian O. schrieb:

> Für Dich ist nicht relevant, was PORTB genau ist. Du kannst es wie eine
> globale Variable nutzen. Du hast schon einen grundsätzlichen Denkfehler

Danke für die Info, aber bei meinem Bsp wird eine 0 oder 1 zurück 
gegeben.

void setPort (char P, char bit) {
P |= (1<<bit); }
int main(void) { // mach was
if (setPort(PORTB, 0) { // mach was } }

Ich bin mir nicht sicher, ob der Fehler darin liegt, dass es sich bei 
der "PORTB"-Übergabe, um einen reinen String handelt, ich ihn aber als 
char-Wert deklariert habe?

Ich werde morgen mal folgende Variante ausprobieren;
void setPort (volatile char P, char bit)

von Rolf M. (rmagnus)


Lesenswert?

__Son´s Bersi__ schrieb:
> Die Originalidee habe ich aus einem Fachbuch, welches als Makro
> geschrieben war. Mir geht es jetzt aber um eine Funktion mit gleichem
> Inhalt!

Tja, und jetzt weißt du, warum es ein Makro war. Mit einer Funktion geht 
das so nicht.

__Son´s Bersi__ schrieb:
>> Für Dich ist nicht relevant, was PORTB genau ist. Du kannst es wie eine
>> globale Variable nutzen. Du hast schon einen grundsätzlichen Denkfehler
>
> Danke für die Info, aber

Nichts "aber"! Fabians Erklärung paßt genau.

> bei meinem Bsp wird eine 0 oder 1 zurück gegeben.

Nein. Der Return-Typ von setPort ist void, also wird nichts 
zurückgegeben.

> void setPort (char P, char bit) {
> P |= (1<<bit); }
> int main(void) { // mach was
> if (setPort(PORTB, 0) { // mach was } }

Da müßtest du eigentlich eine Fehlermeldung vom Compiler bekommen, da 
setPort nicht den passenden Rückgabetyp hat. Ist das wirklich der Code, 
den du genau so durch den Compiler hast laufen lassen?

> Ich bin mir nicht sicher, ob der Fehler darin liegt, dass es sich bei
> der "PORTB"-Übergabe, um einen reinen String handelt, ich ihn aber als
> char-Wert deklariert habe?

Was für ein String? In deinem Programm kommt nichts vor, das auch nur im 
entferntesten was mit Strings zu tun hätte.

> Ich werde morgen mal folgende Variante ausprobieren;
> void setPort (volatile char P, char bit)

Das wird genau gar nichts bringen. Lies nochmal die Erklärung von Fabian 
und denke darüber nach.

von Karl H. (kbuchegg)


Lesenswert?

Fabian O. schrieb:
> __Son´s Bersi__ schrieb:
>> Was ist "PORTB" und vor allem, wie kann es an eine Variable übergeben
>> werden?
>
> Für Dich ist nicht relevant, was PORTB genau ist.


Ja und nein.

Ich kann seine Denkweise schon irgendwo auch verstehen.
Seine Denkweise ist: ich übergebe den Port an die Funktion und die 
Funktion soll mir das Bit setzen.

Und genau da kommt jetzt das ins Spiel, was Fabian angesprochen hat.

Hier
1
int main()
2
{
3
  setPort(PORTB, 0);
4
}

wird ja nicht der Port selber übergeben, sondern der momentane Wert der 
'Variablen' PORTB.

Die Lösung kommt jetzt daher, dass die ganzen Sache von den WinAVR bzw. 
Atmel Leuten so vorbereitet wurde, dass man auch von PORTB die Adresse 
nehmen kann. D.h. PORTB kann man sich tatsächlich, auch wenn es sich 
hier um Hardware handelt, wie eine Variable vorstellen (auch wenn es in 
Wirklichkeit keine ist).

Und ab da kommen dann alle C-Dinge ins Spiel, die bereits angesprochen 
wurden und die sich um Argumentübergabe drehen. Man will an dieser 
Stelle beim Aufrufer eben nicht den momentanten Wert der 'Variablen' 
PORTB übergeben, sondern man will die Funktion in die Lage versetzen, 
die 'Variable' selbst zu verändern. In völliger Analogie zu
1
void foo( unsigned char * a )
2
{
3
  *a = 5;
4
}
5
6
int main()
7
{
8
  unsigned char wert = 2;
9
10
  foo( &wert );
11
}

und PS: der für einen C-Programmierer relevante Datentyp für PORTB 
lautet "volatile unsigned char".


Und Bersi.
Ich glaub ich hab dir das schon mal gesagt: Dein µC-Fachbuch ist schön 
und gut (und auf jeden Fall besser als nichts). Aber du wirst da drinnen 
nur die Hälfte des für dich relevanten C-Sprachstandards finden. Ok, 
Argument Passing soltle eigentlich etwas ausführlicher beschrieben sein, 
weil es nun mal extrem wichtig ist, aber ich kenne auch derartige 
Sprach-Kurzeinführungen. Die sind oft nur Lückenbüsser, weil es sich auf 
dem Buchumschlag nun mal gut macht, wenn dort steht "Mit einer 
Einfführung in C". Aber die Sprache wird dort nicht angemessen 
beschrieben bzw. erklärt, weil man ja schnellstmöglich zum eigentlichen 
Thema des Buches übergehen will, der nun mal µC-Programmierung lautet. 
D.h. die Sprachbeschreibungen in solchen Büchern sind oft tatsächlich 
nur halbherzig und im Grunde sollte man so vergehen als ob sie nicht 
vorhanden wären und zum Studium eines derartigen Buches bereits 
Sprachkentnisse vorausgesetzt werden. Aus dem, was in diesen Büchern 
beschrieben ist, lernt man die Sprache meistens nicht ausreichend gut 
genug. Daraus folgt: Du wirst um ein dezidiertes C-Buch nicht 
herumkommen. Ein derartiges Buch hat auch nicht ohne Grund 200 Seiten. 
200 Seiten, die du in deinem µC-Fachbuch aus naheliegenden Gründen nicht 
finden wirst, obwohl die Hälfte davon eigentlich auf jeden Fall 
notwendig wäre.

von __Son´s B. (bersison)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Und Bersi.
>
> Ich glaub ich hab dir das schon mal gesagt: Dein µC-Fachbuch ist schön
>
> und gut (und auf jeden Fall besser als nichts). Aber du wirst da drinnen
>
> nur die Hälfte des für dich relevanten C-Sprachstandards finden.

Hallo Karl Heinz,
deine Ausführungen "treffen den Nagel auf den Kopf"!
Mir liegen 2 Fachbücher über AVR+C vor. Hier springe ich dauernd, weil 
viele Themen oberflächlich durchschritten werden, um zu konkreten 
Projekten(die mich nicht interessieren) zu kommen.
Meine nächste Anschaffung wird auf dein Anraten ein Fachbuch für C sein. 
Hier entweder;
(1) Die Programmiersprache C, ISBN 10 3540237852, 9,95€
(2) Programmieren in C, ISBN 3446154973, 29€
(3) C: Programmieren von Anfang an, ISBN 10 3499600749, 11,99€

Deine/Eure Tips werde ich zeitnah ausprobieren - DANKE!

von __Son´s B. (bersison)


Lesenswert?

so, es ist vollbracht und funktioniet einwandfrei;

void setPort(volatile unsigned char *P, char bit)
{
  *P |= ~(1<<bit);
}

int main(void)
{
setPort(&PORTB, 0);
// etc.
}

von Lötlackl *. (pappnase) Benutzerseite


Lesenswert?

Ich bin für
> (2) Programmieren in C, ISBN 3446154973, 29€

von __Son´s B. (bersison)


Lesenswert?

Hallo, hier kommt noch ein Folgeproblem.

Wie kann der "PORTB" als Variable über mehrere Funktionen weitergereicht 
werden?
BSP: Obwohl setPort() und clrPort() einwandfrei funktionieren, reicht 
inpWeit() die Variablen nicht weiter;
1
-----------------------------------------------
2
void setPort(volatile unint8_t *P, uint8_t bit)
3
{
4
  *P |= (1<<bit);
5
}
6
7
void clrPort(volatile unint8_t *P, uint8_t bit)
8
{
9
  *P &= ~(1<<bit);
10
}
11
12
uint16_t impWeit(volatile unint8_t *P, uint8_t bit, uint16_t high, uint16_t low) //Impulsweitensteuerung
13
{
14
  setPort(*P,bit);  //setzt Ausgang high
15
  warte_ms(high);   //Warteschleife
16
  setPort(*P,bit);  //setzt Ausgang low
17
  warte_ms(low);    //Warteschleife
18
}
19
20
int16_t main(void)
21
{
22
  while(1)
23
  {
24
    impWeit(&PORTB, 1, 10, 20);
25
  }
26
  return 0;
27
}
------------------------------------------------

von Karl H. (kbuchegg)


Lesenswert?

__Son´s Bersi__ schrieb:

> BSP: Obwohl setPort() und clrPort() einwandfrei funktionieren, reicht
> inpWeit() die Variablen nicht weiter;

Orientier dich an den Datentypen


>   setPort(*P,bit);  //setzt Ausgang high

Was will die Funktion haben?
Sie will

   void setPort(volatile unint8_t *P, uint8_t bit)

einen Pointer haben: volatile unint8_t *P

Was hast du? Was ist der Datentyp deines P in der Funktion hier?
  uint16_t impWeit(volatile unint8_t *P, uint8_t bit,
                   uint16_t high, uint16_t low)

P ist ein volatile unint8_t *P, ist also auch ein Pointer.

Was willst du weitergeben? Die Adresse, die im Pointer gespeichert ist, 
oder den Wert der sich unter dieser Adresse im Speicher verbirgt? (Also: 
die Adresse von PORTB, oder den momentanten Wert, der am PORTB anliegt?)

Du willst die Adresse weitergeben. Denn
1) bringt dir der Wert nichts
2) will die Funktion ja die Adresse haben.

( 2) ist eine direkte Folge von 1). 2) könnte natürlich auch durch einen 
falschen Funktionsaufbau zustande kommen. Daher muss man an dieser 
Stelle auch überlegen, ob es überhaupt richtig ist, dass die Funktion 
einen Pointer nimmt oder ob nicht dieses Detail falsch implementiert 
ist. Aber in deinem Fall folgt 2) direkt aus 1) und ist daher als 
'Forderung' korrekt.)


Daher die Frage: Wozu der * in

   setPort(*P,bit);

der * würde den Pointer dereferenzieren und dir den Wert an der Adresse 
von P liefern (also das was am PORTB anliegt). Das willst du aber gar 
nicht! Du willst ja die Adresse von PORTB weitergeben. Deshalb nimmt ja 
auch die Funktion eine Pointer und keinen uint8_t

Ergo

  setPort( P, bit );

denn P enthält ja bereits diese Adresse!


nicht einfach nur in Funktionen wahllos irgendwelche * und & einstreuen. 
BIssi nachdenken, wie die Dinge zusammenhängen. Es ist alles völlig 
logisch!
Bei Pointern muss man immer unterscheiden zwischen
* will ich mit dem Pointer selber, als mit der in ihm gespeicherten
  Adresse arbeiten
* oder meine ich den Wert, der von dieser Adresse referenziert wird.


Du kannst dir das ganze Pointer/Adressen Ding wie eine Bibliothek 
vorstellen.
Die Bücher in den Regalen, das sind deine Werte im Speicher.
Pointer sind einfach nur die Karteikarten, auf denen steht, wo 
(Gang/Regal/Platz) ein Buch zu finden ist. Worum dreht es sich bei einer 
Operation: Ist das eine Operation mit der Karteikarte (die Kartekarte an 
einen anderen Leser weitergeben) oder ist es eine Operation mit dem Buch 
selber (welches 'gefunden' wird, indem man die Ortsangabe auf der 
Karteikarte auswertet).
In deinem Beispiel gibt eine Funktion die Karteikarte selber an die 
nächste Funktion weiter. Und erst die letzte in der Kette schaut auf die 
Karte (macht den *) um das Buch ausfindig zu machen, in welchem sie 
etwas eintragen soll.

von __Son´s B. (bersison)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ergo
>   setPort( P, bit );
> denn P enthält ja bereits diese Adresse!

Funktioniert!

Karl Heinz Buchegger schrieb:
> Orientier dich an den Datentypen

Zeigt mir, dass ich mich sehr viel intensiver mit dem Thema 
Datentyp/Pointer auseinander setzen muss!

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.