Forum: Compiler & IDEs Arrays, Pointer und Schleifen (avr-gcc)


von M*** L. (b2b)


Lesenswert?

Hallo liebe Mikrocontroller-Gemeinde!

Ich bin gerade dabei mich mit Mikrocontrollern und C zu beschäftigen.
Also seid nicht zu brutal zu mir!

Ich habe ein Array mit 10 Werten. Es soll der erste Wert des Arrays mit 
einem Pointer ausgelesen werden. Dieser Wert soll 10mal am Port A 
ausgegeben werden.
Nach dem zehnten Durchlauf soll der zweite Wert im Array genommen werden 
und auch wieder 10mal ausgegeben werden.
Das geht so weiter bis zum letzten Wert.
Nachdem nun die while-Schleife durchgelaufen ist, soll das Array wieder 
von vorne ausgelesen und die Werte wieder mittels Pointer ausgegeben 
werden.
Also quasi eine Endlosschleife.

Im AVR-Studio mit avr-gcc wird der Quellcode ohne Fehler compiliert, 
aber ich bin mir nicht sicher, ob sich meine Funktionsbeschreibung mit 
meinem Code deckt.
Mir ist auch noch aufgefallen, dass am Ende der while-Schleife nicht 
wieder in die while-Schleife zurückgesprungen wird und dass das Programm 
somit hängen bleibt.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
#include <stdlib.h>
5
#include <avr/pgmspace.h>
6
#define CLK 3686400
7
8
unsigned char i;
9
unsigned char Array[10] =
10
{ 0,1,2,3,4,5,6,7,8,9 } ;
11
12
unsigned char *ptr;
13
14
int main(void)
15
{
16
  for(;;)
17
  {
18
    while (ptr < (Array+(sizeof(Array)/sizeof(int))))
19
    {
20
    for(i=0; i<10; i++)
21
      {
22
      PORTA = *ptr++;
23
      }
24
    }
25
  }
26
}

Es wäre wohl auch gut einen Timer einzubauen, aber im Moment möchte ich 
mich erst einmal mit Arrays und Pointern auseinander setzen!

Vielleicht kann mir jemand bei meinem Problem behilflich sein?!
Es wäre jedenfalls schön, wenn mein Code genau das macht, was meine 
Funktionsbeschreibung vorgibt.

Vielen Dank!

von Stefan E. (sternst)


Lesenswert?

In deinem Code wird ptr nicht initialisiert.
Das muss am Anfang der for(;;)-Schleife passieren.

von Karl H. (kbuchegg)


Lesenswert?

Marc L. wrote:
>
> unsigned char *ptr;
>
> int main(void)
> {
>   for(;;)
>   {

ptr hat zu diesem Zeitpunkt noch keinen Wert, zeigt also
irgendwo hin

>     while (ptr < (Array+(sizeof(Array)/sizeof(int))))

wieso sizeof int?
Array ist ein unsigned char Array!
Ich zeig dir weiter unten wie du das besser schreiben kannst,
so dass hier kein Datentyp auftaucht.


>     {
>     for(i=0; i<10; i++)
>       {
>       PORTA = *ptr++;

PORTA ist nie auf Ausgang geschaltet worden!

Hier wird bei jedem Durchlauf durch die for Schleife der Pointer
erhöht. Das deckt sich nicht mit deiner Beschreibung dessen was
du erreichen willst.


1
....
2
3
unsigned char *ptr;
4
5
#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(*x))
6
7
int main(void)
8
{
9
  DDRA = 0xFF;
10
11
  for(;;)
12
  {
13
    ptr = Array;    // ptr zeigt auf den Anfang des Arrays
14
15
    while( ptr < Array + ARRAY_SIZE( Array ) )
16
    {
17
      // den Wert, auf den ptr zeigt, jetzt 10 mal ausgeben
18
      for(i = 0; i < 10; i++ )
19
      {
20
        PORTA = *ptr;
21
      }
22
      // und ptr auf das nächste Array Element vorschieben
23
      ptr++;
24
    }
25
  }
26
}

Alternativ könnte man die while Schleife auch so formulieren.
Dadurch hat man die Pointer Manipulation(en) an einer Stelle
beisammen ...
1
  for(;;)
2
  {
3
    for( ptr = Array; ptr < Array + ARRAY_SIZE( Array ); ptr++ )
4
    {
5
      for(i = 0; i < 10; i++ )
6
      {
7
        PORTA = *ptr;
8
      }
9
    }
10
  }
11
}

... und es wird die Analogie zu einer Index-basierten Schleife
über ein Array deutlicher
1
  for(;;)
2
  {
3
    for( j = 0; j < ARRAY_SIZE( Array ); j++ )
4
    {
5
      for(i = 0; i < 10; i++ )
6
      {
7
        PORTA = Array[j];
8
      }
9
    }
10
  }
11
}

von M*** L. (b2b)


Lesenswert?

Oh man, ihr seid ja schneller als die Feuerwehr!

Danke für den kurzen Hinweis, Stefan!

Und einen besonderen Dank an Karl heinz Buchegger für die ausführliche 
Beschreibung!

>>while (ptr < (Array+(sizeof(Array)/sizeof(int))))
>>wieso sizeof int?
Da hast du doch glatt einen Fehler von mir entdeckt!
Ich hatte das Array vom Typ INT auf CHAR geändert, da ich vorerst nur 
8bit brauche. Dabei vergaß ich das sizeof(int) zu ändern!

Deine Beschreibung zum Array mit Pointern habe ich mir ausgedruckt, fett 
rot eingerahmt und auf meinen Schreibtisch griffbereit hingelegt!

Ich werde mir deine Code-Snippets genau angucken und versuchen zu 
verstehen.
Es ist aber im Grunde ganz logisch, wie jede digitale Schaltung ;)
So nebenbei bemerkt, auf ptr++ hätte ich auch drauf kommen können!

Jedenfalls kann ich mich jetzt in das Thema gut einarbeiten und die 
Funktion "umschreiben".
Denn am Ende soll ein ADC-Wert eingelesen und an einer bestimmten 
Position im Array gespeichert werden (mit Pointer).
Nach der Speicherung des ADC-Wertes soll der gesamte Inhalt des Arrays 
10mal hintereinander byteweise am PORTA ausgegeben werden.
Danach erfolgt eine neue Messung am ADC und das Spiel geht weiter, bis 
das Array voll ist und wieder von vorne begonnen werden muss.

Ich muss zugeben, dass die finale Funktion anders ist als meine zur Zeit 
aufgestellte Funktion, aber irgendwie musste ich in das Thema 
einsteigen.
Um Arrays und Pointer verstehen zu können, wollte ich nicht gleich mit 
einer komplexen Aufgabe anfangen, sondern mich Stück für Stück an das 
Problen rantasten!

von Karl H. (kbuchegg)


Lesenswert?

Marc L. wrote:

> Nach der Speicherung des ADC-Wertes soll der gesamte Inhalt des Arrays
> 10mal hintereinander byteweise am PORTA ausgegeben werden.

NB: Du solltest du nochmal nachhaken. Denn so wie das jetzt
geschrieben ist, kannst du hardwaremässig am Port nicht feststellen,
dass die Ausgabe 10 mal gemacht wurde. Der Port nimmt beim ersten
mal die Pegel an und die restlichen 9 Ausgaben verändern diese
nicht. Im Grunde ist das also äquivalent zu:
   einmal ausgeben
   eine kurze Wartezeit einlegen.

Ob das das ist, was du haben möchtest, musst du entscheiden.
Sinn macht diese innere Schleife nicht wirklich.

von M*** L. (b2b)


Lesenswert?

Hallo Karl heinz Buchegger.

> Denn so wie das jetzt
> geschrieben ist, kannst du hardwaremässig am Port nicht feststellen,
> dass die Ausgabe 10 mal gemacht wurde. Der Port nimmt beim ersten
> mal die Pegel an und die restlichen 9 Ausgaben verändern diese
> nicht.
So soll es sein!
Man könnte es doch eventuell mit einem Oszilloskop testen, wenn ich neun 
Einsen und eine Null in das Array schreibe. Somit müsste doch am PA0 
9mal ein High und 1mal ein Low zu erkennen sein?!
Es ist egal, was am ADC anliegt, wenn das Array ausgegeben wird, solange 
der Takt hoch genug ist.

> Sinn macht diese innere Schleife nicht wirklich.
Ja, das stimmt schon, aber ich mache noch einen Bogen um die Timer.
Natürlich könnte man anstatt der Schleife eine Zeit einbringen, aber es 
sollen alle Werte im Array 10mal hintereinander ausgegeben werden.
Der Timer muss sich auf das gesamte Array auswirken, nicht nur auf die 
einzelne Ausgabe.
Darum macht wohl eine Schleife wieder Sinn?!

Ich muss mir jetzt wohl auch Gedanken machen, mit welcher Frequenz ich 
den ADC takte.
So wie es scheint, komme ich dann nicht um die Timer herum...

von Michael G. (linuxgeek) Benutzerseite


Lesenswert?

Marc L. wrote:
> Hallo liebe Mikrocontroller-Gemeinde!
>
> Ich bin gerade dabei mich mit Mikrocontrollern und C zu beschäftigen.
> Also seid nicht zu brutal zu mir!

Ich? Waer ich doch niemals ;)

> Ich habe ein Array mit 10 Werten. Es soll der erste Wert des Arrays mit
> einem Pointer ausgelesen werden. Dieser Wert soll 10mal am Port A
> ausgegeben werden.
> Nach dem zehnten Durchlauf soll der zweite Wert im Array genommen werden
> und auch wieder 10mal ausgegeben werden.

Was macht das fuer einen Sinn? Wenn Du den selben Wert 10 mal in Folge 
ausgibst bewirkt das keine Veraenderung ausser dass Du ueberfluessige 
Operationen durchfuehrst.

Was willst Du denn machen, vielleicht faengst besser mal so an.

Lg,
Michael

von Karl H. (kbuchegg)


Lesenswert?

Marc L. wrote:
> Hallo Karl heinz Buchegger.
>
>> Denn so wie das jetzt
>> geschrieben ist, kannst du hardwaremässig am Port nicht feststellen,
>> dass die Ausgabe 10 mal gemacht wurde. Der Port nimmt beim ersten
>> mal die Pegel an und die restlichen 9 Ausgaben verändern diese
>> nicht.
> So soll es sein!
> Man könnte es doch eventuell mit einem Oszilloskop testen, wenn ich neun
> Einsen und eine Null in das Array schreibe. Somit müsste doch am PA0
> 9mal ein High und 1mal ein Low zu erkennen sein?!
> Es ist egal, was am ADC anliegt, wenn das Array ausgegeben wird, solange
> der Takt hoch genug ist.

Du verstehst mich nicht, oder ich drücke mich schlecht aus.
Ich rede von der inneren Schleife. Dieser hier
1
....
2
      for(i = 0; i < 10; i++ )
3
      {
4
        PORTA = *ptr;
5
      }
6
....

Die macht nicht wirklich Sinn. Das könnte man auch so schreiben
1
#include <util/delay.h>
2
3
...
4
      PORTA = *ptr;
5
      _delay_ms( 1 );
6
...

(wenn man beispielsweise haben möchte, daß der Werte 1ms am
Port ansteht, bis dann der nächste Wert kommt. Hüte dich
vor selbst geschriebenen Schleifen um irgendwelche Verzögerungen
zu erreichen. In dem Fall hat der Optimizer keine Chance (weil
PORTA eine volatile Variable ist), aber in anderen Fällen
optimiert dir der Compiler solche Schleifen mit Leidenschaft
aus dem Code heraus.

> So wie es scheint, komme ich dann nicht um die Timer herum...

Die meisten Programme haben einen Timer der einen Basistakt
vorgibt. Da wirst du wahrschienlich auch nicht umhin kommen,
dir das mal zu Gemüte zu führen.

von M*** L. (b2b)


Lesenswert?

Hallo Michael G.

ich wollte eigentlich mein Vorhaben nicht preisgeben, denn es wird 
sowieso nicht klappen, wie ich es mir vorstelle...
Trotzdem bin ich guter Dinge und möchte es schwarz auf weiß sehen, ob 
mein Vorhaben klappt oder nicht.

Also, ich habe ein analoges Oszilloskop, das mir bei langsamen Signalen 
kein ausreichend gutes Bild ausgibt (Punkt statt Strich).
Stellt man die Zeit am Oszi runter, sieht man zwar einen Strich, doch 
diese Frequenz ist zu groß, um zum Beispiel eine Ladekurve eines 
Kondensators sichtbar zu machen.
Eine Lösung lautet: ein DSO (digitales Speicher-Oszilloskop) kaufen.
Nur ich möchte mir kein weiteres Oszi kaufen, darum habe ich mir 
gedacht, ich könnte doch mit dem AVR ein Signal mit dem ADC messen und 
es geloopt mittels R2R-Netzwerk wieder ausgeben.
Somit würde ich doch eine langsame Eingangskurve bei hoher Oszi-Frequenz 
sichtbar machen?!

-------

Hallo Karl heinz Buchegger,

stimmt, ob ich nun in einer Schleife hintereinander das Selbe ausgebe 
oder einfach nur "warte", das ist von der Funktion her das Gleiche.
Und danke für den Hinweis mit dem Umgang selbstgeschriebener Schleifen, 
die eine Verzögerung erzeugen sollen!

Also Update der ToDo-Liste: Mit Timern beschäftigen!

von Michael G. (linuxgeek) Benutzerseite


Lesenswert?

Marc L. wrote:
> Hallo Michael G.
>
> ich wollte eigentlich mein Vorhaben nicht preisgeben, denn es wird
> sowieso nicht klappen, wie ich es mir vorstelle...
> Trotzdem bin ich guter Dinge und möchte es schwarz auf weiß sehen, ob
> mein Vorhaben klappt oder nicht.

Naja das halte ich fuer keine brauchbare Einstellung. Auf der Grundlage 
bin ich auch nicht bereit zu helfen, wenn Du Informationen 
vorenthaeltst. Auch wenn Du so pessimistisch an die Sache herangehst hat 
das wenig Aussicht auf Erfolg.

Trotzdem viel Spass noch.

Michael

von M*** L. (b2b)


Lesenswert?

Hallo Michael G.,

also, wenn ich keinen Ehrgeiz hätte, würde ich von Anfang an mir ein DSO 
zulegen und den AVR zur Seite legen.

Aber ich will mein Vorhaben absolut umsetzen!
Sorry, dass ich so pessimistisch rüberkam, aber ich bekomme halt viel zu 
hören, dass meine Vorhaben nicht zu realisieren sind.
Und wenn es doch klappt, bin ich der Einzige, der sich über das Resultat 
freut und die "Das geht sowieso nicht"-Sager sehen nicht ein, dass die 
sich getäuscht haben.

Die Sache an sich habe ich doch nicht mehr vorenthalten.
Ich möchte mir ein DSO-Vorschaltgerät für ein analoges Oszi 
zusammenbauen.
Und um meine Vorstellung umsetzen zu können, muss ich mich mit Pointern 
und Arrays beschäftigen.
Das Projekt muss nicht beim ersten Anschalten sofort funktionieren.
Der Reiz liegt dann beim Optimieren!

Ich wollte halt nicht das Thema komplett vorstellen, weil ich erst mit 
dem AVR anfange und dieses Thema somit nicht innerhalb eines Tages 
fertig zu stellen wäre!

Ich möchte die Zwischenschritte verstehen und nicht einen vorgefertigten 
Code bekommen. Das bringt mich auch nicht weiter!
Über kleinere Code-Snippets bin ich sehr dankbar, denn damit kann ich 
weiterarbeiten und es hilft mir "Routinen" zu verstehen.
Aber wie schon geschrieben, einen komplett fertigen Code möchte ich 
nicht haben.

Also, sorry nochmal!

von die ??? (Gast)


Lesenswert?

Wirklich eine super Idee! Wieso soll das nicht klappen? Lass' dir bloß 
nichts einschwatzen. Weitermachen!

Wie bereits gesagt: Finger weg von selbstgebauten delay-Schleifen. Dafür 
gibts _delay_us und _delay_ms (Optimierung einschalten, am besten -Os) 
[1]. Timer sind gut - aber für Verzögerungen etwas overdosed.


[1] http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Zur Grundidee:

Wenn andere einen AVR benutzen, um Uhrzeiten auf einem Oszi 
auszugeben(http://dutchtronix.com/ScopeClock.htm und 
http://www.robotop.biz/atmel_avr.php) oder ein Oszi als Terminal 
benutzen (http://dutchtronix.com/ScopeTerm.htm) oder auf dem Oszi 
Tetris (http://pontoppidan.info/lars/index.php?proj=scopetris) oder 
Pong (http://web.mit.edu/edmond/www/projects/pong.html) spielen...

...warum dann nicht

> darum habe ich mir
> gedacht, ich könnte doch mit dem AVR ein Signal mit dem ADC messen und
> es geloopt mittels R2R-Netzwerk wieder ausgeben.
> Somit würde ich doch eine langsame Eingangskurve bei hoher
> Oszi-Frequenz sichtbar machen?!

von M*** L. (b2b)


Lesenswert?

Hallo,

vielen Dank für die moralische Unterstützung!
Ich glaube, ich bin der Einzige in meinem Umkreis von 50km, die sich 
intensiv mit der Elektronik beschäftigen wollen oder ich kenne nur die 
falschen Leute! ;(

// ...warum dann nicht
Ja, warum dann eigentlich nicht? grins

Ich habe mich ein wenig in das Datenblatt des ATmega32 eingelesen 
(dieser Mikrocontroller wird damit am Ende beschäftigt) und versuchte 
meinen Programm-Code zu erweitern:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define nop() asm volatile("nop")
5
#define CLK 3686400
6
7
unsigned char adc_wert;
8
unsigned char i = 0;
9
unsigned char Array[5] =
10
{ 0,1,2,3,4 } ;
11
unsigned char *ptr;
12
13
#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(*x))
14
#define ARRAY_LENGTH(x) (sizeof(x))
15
16
int main(void)
17
{
18
  DDRA = 0xFF;
19
  for(;;)
20
  {
21
  ADMUX = 0b01100000; // PA0 -> ADC0, ADLAR=1 (8bit)
22
  ADCSRA = 0b11000100; // ADC-Vorteiler = 16
23
  while(ADCSRA & (1<<ADSC)) nop(); // noppen, solange noch nicht fertig
24
  adc_wert = ADCH; // Ergebnis einlesen (8bit)
25
26
    ptr = Array; // ptr zeigt auf den Anfang des Arrays
27
    while( ptr < Array + ARRAY_SIZE(Array) )
28
    {
29
    // Hier kommt wohl die ADC-Routine zum Schreiben des Arrays rein?
30
    for(i = 0; i < ARRAY_LENGTH(Array); i++ ) // gesamtes Array ausgeben
31
      {
32
        PORTA = *ptr;
33
        delay_ms(1);
34
        ptr++; // ptr auf das nächste Array Element vorschieben
35
      } // for(i = 0; i < ARRAY_LENGTH(Array); i++ )
36
      ptr++; // ptr auf das nächste Array Element vorschieben
37
    } // while( ptr < Array + ARRAY_SIZE(Array) )
38
  } // for(;;)
39
} // main(void)

Ich muss die Abarbeitung eines Codes verstehen, aber ich denke, das 
kommt mit der Zeit.
Wer also Fehler findet, der kann es mir ruhig sagen!
Ich möchte schließlich etwas lernen!
Hhm, und wie ich einen ADC-Wert dem Pointer übergebe und dann an der 
bestimmten Stelle im Array speichern soll, weiß ich auch noch nicht so 
ganz.
Bestimmt mit
1
*ptr = adc_wert;
???
Jedenfalls ist das noch nicht im Programm implementiert.

Zur Erinnerung, das Programm soll folgendes machen:
Es soll ein ADC-Wert eingelesen und an einer bestimmten
Position im Array gespeichert werden (mit Pointer).
Nach der Speicherung des ADC-Wertes soll der gesamte Inhalt des Arrays
mehrmals hintereinander (entspricht das: verzögert?) byteweise am 
PORTA ausgegeben werden.
Danach erfolgt eine neue Messung am ADC und das Spiel geht weiter, bis
das Array voll ist und wieder von vorne begonnen werden muss.

Bin ich schon auf einem guten Weg oder sinkt mein Boot?

von Karl H. (kbuchegg)


Lesenswert?

Marc L. wrote:
>
>     ptr = Array; // ptr zeigt auf den Anfang des Arrays
>     while( ptr < Array + ARRAY_SIZE(Array) )
>     {
>     // Hier kommt wohl die ADC-Routine zum Schreiben des Arrays rein?
>     for(i = 0; i < ARRAY_LENGTH(Array); i++ ) // gesamtes Array ausgeben
>       {
>         PORTA = *ptr;
>         delay_ms(1);
>         ptr++; // ptr auf das nächste Array Element vorschieben
>       } // for(i = 0; i < ARRAY_LENGTH(Array); i++ )
>       ptr++; // ptr auf das nächste Array Element vorschieben
>     } // while( ptr < Array + ARRAY_SIZE(Array) )

Das kann nicht stimmen. ptr wird da viel zu oft erhöht.
Warum willst du das eigentlich unbedingt mit Pointern machen.
Ist doch Schwachsinn (verzeih dieses böse Wort).
Wenn du durch ein Array durchwillst, dann nimm doch Array Indizes,
also Zugriffe der Art  Array[i], und überlass das Optimieren dem
Compiler.

> Hhm, und wie ich einen ADC-Wert dem Pointer übergebe

einem Pointer kann man nichts übergeben.
Ein Pointer ist einfach nur eine Variable, die eine Adresse im
Speicher beinhaltet. So wie eine int Variable eine Variable ist
die eine ganze Zahl speichert.


>
> Zur Erinnerung, das Programm soll folgendes machen:
> Es soll ein ADC-Wert eingelesen und an einer bestimmten
> Position im Array gespeichert werden (mit Pointer).


void foo( int* Ziel )
{
  ....
  *Ziel = adc_wert;
}

int main()
{
  int Werte[10];
  int i;

  for( i = 0; i < ARRAY_SIZE(Werte); ++i )
    foo( &Werte[i] );

>
> Bin ich schon auf einem guten Weg oder sinkt mein Boot?

Ich denke, du machst dir das unnötig kompliziert. Pointer
benutzt man, wenn man es muss und nicht weil draussen so ein
schöner Tag ist.

von M*** L. (b2b)


Lesenswert?

Hallo Karl heinz Buchegger.

> Warum willst du das eigentlich unbedingt mit Pointern machen.
> Ist doch Schwachsinn (verzeih dieses böse Wort).
> Wenn du durch ein Array durchwillst, dann nimm doch Array Indizes,
> also Zugriffe der Art  Array[i], und überlass das Optimieren dem
> Compiler.
Von wollen kann keine Rede sein.
Ich habe mir absolut keine Vorgaben gesetzt, dachte nur, dass Pointer zu 
Arrays einfach dazugehören.

>> Bin ich schon auf einem guten Weg oder sinkt mein Boot?
> Ich denke, du machst dir das unnötig kompliziert. Pointer
> benutzt man, wenn man es muss und nicht weil draussen so ein
> schöner Tag ist.
Cooler Spruch, den muss ich mir merken. ;)
Okay, in meinem Fall könnte man also auf Pointer verzichten und auf 
Array Indizes zurückgreifen.

Ich mache es mir wirklich kompliziert, da hast du schon Recht.
Du würdest "mein Problem" in 10min lösen können, weil du weiß, was 
möglich ist und wie man am effizientesten programmiert.
Davon bin ich noch ein wenig entfernt, leider...

von Karl H. (kbuchegg)


Lesenswert?

Marc L. wrote:

> Ich mache es mir wirklich kompliziert, da hast du schon Recht.
> Du würdest "mein Problem" in 10min lösen können, weil du weiß, was
> möglich ist und wie man am effizientesten programmiert.
> Davon bin ich noch ein wenig entfernt, leider...

Der springende Punkt ist:
Du hast mit Array Indizes ein absolut taugliches Mittel,
dieses ganze Schleifen-Gedöngs in den Griff zu kriegen.
Pointer machen das keinen Deut einfacher oder übersichtlicher.

von daniel (Gast)


Lesenswert?

ein Array wie int feld[20]; ist linear im Speicher
und die Grösse zur compile Zeit bekannt, da braucht man
keine Zeiger um drüber zu iterieren.

genauso übertragbar in mehrdimensionalen Fall
int feld[10][20];
auch hier braucht man (im Normalfall) keine Zeiger

hier ein Programm von mir zum Üben ;)

#include <iostream>
#include <cstdlib>

using std::cout;
using std::endl;

int main() {
    int feld[3][10];
    for(int i=0; i<3; i++)
        for(int j=0; j<10; j++)
            feld[i][j] = i*10 + j;

    int (*x[3])[10] = {&feld[0], &feld[1], &feld[2]};
    for(int i=0; i<3; i++) {
        for(int j=0; j<10; j++)
            cout << (*x)[i][j] << ' ';
        cout << endl;
    }

    int (*y)[3][10] = &feld;
    for(int i=0; i<3; i++) {
        for(int j=0; j<10; j++)
            cout << (*y)[i][j] << ' ';
        cout << endl;
    }

    int * z[3][10];
    for(int i=0; i<3; i++)
        for(int j=0; j<10; j++)
            //z[i][j] = &feld[i][j];    // oder
            z[i][j] = (&feld[0][0] + i*10 + j);
    for(int i=0; i<3; i++) {
        for(int j=0; j<10; j++)
            cout << *(z[i][j]) << ' ';
        cout << endl;
    }

    int *(*q[3])[10] = {&z[0], &z[1], &z[1]};
    for(int i=0; i<3; i++) {
        for(int j=0; j<10; j++)
            //cout << *((*q)[i][j]) << ' ';     // oder
            cout << *(q[0][i][j]) << ' ';
        cout << endl;
    }

    return EXIT_SUCCESS;
}

von M*** L. (b2b)


Lesenswert?

Hallo daniel,

vielen Dank für dein kleines Übungsprogramm.
Das muss ich erst einmal auseinander nehmen und Schritt für Schritt für 
mich kommentieren.
Am besten ist es, wenn ich mir jetzt wirklich ein gutes C-Buch mit den 
Grundlagen zulege!

Und auch vielen Dank an Karl heinz Buchegger.
Nicht jeder hat soviel Verständnis mit einem Anfänger wie ich es nun mal 
noch immer bin!

Ich bin gerade nicht zu Hause (hab demnach kein AVR-Studio zum 
Compilieren zur Hand) und trotzdem lässt mich das Thema nicht in Ruhe!
Ich habe mein Programm umgeschrieben, aber so langsam wird es echt 
peinlich, wenn ich noch immer Fehler im Code habe.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define nop() asm volatile("nop")
5
#define CLK 3686400
6
#define ARRAY_LENGTH(x) (sizeof(x))
7
8
unsigned int i = 0;
9
unsigned int j = 0;
10
unsigned char adc_wert;
11
unsigned char Array[5] =
12
{ 0,1,2,3,4 } ;
13
14
int main(void)
15
{
16
  DDRA = 0xFF;
17
  for(;;)
18
  {
19
  ADMUX = 0b01100000; // PA0 -> ADC0, ADLAR=1 (8bit)
20
  ADCSRA = 0b11000100; // ADC-Vorteiler = 16
21
 
22
   for(j=0; j<ARRAY_LENGTH(Array); j++)
23
   {
24
    while(ADCSRA & (1<<ADSC)) nop(); // noppen, bis die Umwandlung fertig ist
25
    adc_wert = ADCH; // Ergebnis einlesen (8bit)
26
    Array[j] = adc_wert;
27
28
    for(i = 0; i < ARRAY_LENGTH(Array); i++ ) // gesamtes Array ausgeben
29
      {
30
        PORTA = Array[i];
31
        delay_ms(1);
32
      } // for(i = 0; i < ARRAY_LENGTH(Array); i++ )
33
    } // for(j=0; j<ARRAY_LENGTH(Array); j++)
34
  } // for(;;)
35
} // main(void)

von Karl H. (kbuchegg)


Lesenswert?

Im Tutorial gibt es eine schöne ADC Funktion. Benutze bitte
die von dort.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Aktivieren_des_ADC

Du kannst ja das Ergebnis davon immer noch durch 4 dividieren, damit
es in einen unsigned char passt.

von M*** L. (b2b)


Lesenswert?

So in etwa, ohne Verwendung der "Unterfunktion"?
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define nop() asm volatile("nop")
5
#define CLK 3686400
6
#define ARRAY_LENGTH(x) (sizeof(x))
7
8
unsigned int i = 0;
9
unsigned int j = 0;
10
unsigned char adc_wert;
11
unsigned char Array[5] =
12
{ 0,1,2,3,4 } ;
13
14
int main(void)
15
{
16
  ADMUX |= (1<<REFS0) | (1<<ADLAR) // AVCC w ext. cap at AREF pin, ADC0, ADLAR=1 (8bit)
17
  ADCSRA = (1<<ADEN) | (1<<ADPS2) // ADC-Vorteiler = 16
18
  DDRA = 0xFF;
19
  for(;;)
20
  {
21
   for(j=0; j<ARRAY_LENGTH(Array); j++)
22
   {
23
    ADCSRA |= (1<<ADSC); // eine ADC-Wandlung durchführen
24
    while(ADCSRA & (1<<ADSC)) nop(); // noppen, bis die Umwandlung fertig ist
25
    adc_wert = ADCH; // Ergebnis einlesen (8bit)
26
    ADCSRA &= ~(1<<ADEN); // ADC deaktivieren
27
    Array[j] = adc_wert;
28
29
    for(i = 0; i < ARRAY_LENGTH(Array); i++ ) // gesamtes Array ausgeben
30
      {
31
        PORTA = Array[i];
32
        delay_ms(1);
33
      } // for(i = 0; i < ARRAY_LENGTH(Array); i++ )
34
    } // for(j=0; j<ARRAY_LENGTH(Array); j++)
35
  } // for(;;)
36
} // main(void)

Wenn es so nicht geht, muss ich mir das ADC-Tutorial zu Hause genauer 
anschauen!

von daniel (Gast)


Lesenswert?

#define ARRAY_LENGTH(x) (sizeof(x))
Marc ich glaube dich verwirrt dieses Makro

je nach maschine kann sizeof unterschiedliche Werte
für dasselbe Datentyp ausgeben, meistens gilt aber auf einer 32bit 
maschine
sizeof(char) = 8
sizeof(short) = 16
sizeof(int) = 32
sizeof(long) = 32
sizeof(long long) = 64

wenn jetzt dein Array aus 5 Elementen besteht, dann
char x[5]; // 5 * sizeof(char) = 5
genau das gibt dir auch sizeof(x) = 5

wie dir hier auffällt kann sizeof sowohl variable
als auch datentyp bekommen.

Aus der Grösse eines Arrays kann man nicht unbedingt auf
die Anzahl der Elemente folgern!
int x[5]; // sizeof(x) => 32*4=128
dh du kannst nicht über 128 Elemente iterieren

for(int i=0; i<sizeof(x); i++) /**/ ;
das schreit nach segmentation fault
also nach speicherzugriffsverletzung

um von der Grösse eines Arrays auf die Anzahl der Elemente
zu kommen, musst du Gesamtgrösse durch Grösse_eines_Elements teilen
sizeof(x)/sizeof(x[0])
x[0] ist das erste Element des Arrays
oder in Makroform
#define SIZE(x) sizeof(x)/sizeof(x[0])

grüsse, daniel

von M*** L. (b2b)


Lesenswert?

Hallo!
Nach längerer Pause (war und bin eigentlich noch immer beruflich sehr 
eingespannt) habe ich mich heute mal wieder kurz an mein Projekt setzen 
können und "spielte" wieder ein wenig mit dem AVR-Studio und meinem Code 
rum.

daniel wrote:
> #define ARRAY_LENGTH(x) (sizeof(x))
> Marc ich glaube dich verwirrt dieses Makro

Vielen Dank für die Info!
Du hast dir echt sehr viel Mühe gegeben mir die "Größenberechnung" von 
Arrays näher zu bringen. Und du hast Recht!
Ich habe nicht daran gedacht, dass Elemente im Array ein Byte oder gar 
ein Word lang sein können.

Hier nun meine weitere Version des Codes.
Ich bin mir nicht sicher, ob der funktioniert...
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define nop() asm volatile("nop")
5
#define CLK 3686400
6
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
7
8
unsigned int i;
9
unsigned int j;
10
unsigned char adc_wert;
11
unsigned char Array[5] = { 0,0,0,0,0 } ;
12
13
int main(void)
14
{
15
  ADMUX |= (1<<REFS0) | (1<<ADLAR); // AVCC w ext. cap at AREF pin, ADC0, ADLAR=1 (8bit)
16
  ADCSRA = (1<<ADEN) | (1<<ADPS2); // ADC-Vorteiler = 16
17
  DDRA = 0xFF;
18
  for(;;)
19
  {
20
   for(j=0; j<ARRAY_LENGTH(Array); j++)
21
   {
22
    ADMUX = 0 | (1<<ADLAR);
23
    ADCSRA |= (1<<ADSC); // eine ADC-Wandlung durchführen
24
    while(ADCSRA & (1<<ADSC)) nop(); // noppen, bis die Umwandlung fertig ist
25
    adc_wert = ADCH; // Ergebnis einlesen (8bit)
26
    Array[j] = adc_wert;
27
28
    for(i = 0; i < ARRAY_LENGTH(Array); i++ ) // gesamtes Array ausgeben
29
      {
30
        PORTA = Array[i];
31
        _delay_ms(1);
32
      } // for(i = 0; i < ARRAY_LENGTH(Array); i++ )
33
    } // for(j=0; j<ARRAY_LENGTH(Array); j++)
34
  } // for(;;)
35
} // main(void)

Ich bekomme beim Compilieren die Warnung #warning "Compiler 
optimizations disabled; functions from <util/delay.h> won't work as 
designed".
Wieso bekomme ich denn diese Warnung? Der Pfad zur delay.h stimmt!

Bitte werft ein Auge auf den Code. Könnte der funktionieren?
Jedenfalls das AVR-Studio compiliert mir den Code ohne einen Fehler!

von Johannes M. (johnny-m)


Lesenswert?

Marc L. wrote:
> Ich bekomme beim Compilieren die Warnung #warning "Compiler
> optimizations disabled; functions from <util/delay.h> won't work as
> designed".
> Wieso bekomme ich denn diese Warnung? Der Pfad zur delay.h stimmt!
Warum die Warnug kommt, steht doch in der Meldung: Du hast die 
Compiler-Optimierung nicht aktiviert. Die ist aber für die korrekte 
Funktionalität der delay-Funktionen unbedingt erforderlich. Ohne 
Optimierung kann erstens das Programm extrem aufgebläht werden, und 
zweitens machen die delays nicht das, was Du willst.

von daniel (Gast)


Lesenswert?

hi,

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Taktfrequenz

speziell

Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen 
Taktfrequenz übereinstimmt. F_CPU muss dazu jedoch nicht unbedingt im 
makefile definiert werden. Es reicht aus, wird aber bei mehrfacher 
Anwendung unübersichtlich, vor #include <util/delay.h> (veraltet: 
#include <avr/delay.h>) ein #define F_CPU [hier Takt in Hz]UL 
einzufügen.

es ist keine besonders schöne lösung den makros
aus delay.h die information über vorherzudefinierende
makros mit einem wert zu übergeben .. aber so scheint
avr-gcc es zu handeln^^

grüsse,daniel

von Johannes M. (johnny-m)


Lesenswert?

Marc L. wrote:
> Bitte werft ein Auge auf den Code. Könnte der funktionieren?
> Jedenfalls das AVR-Studio compiliert mir den Code ohne einen Fehler!
Fehler sehe ich jetzt keine. Das nop in der Warteschleife des ADC 
kannste aber im Prinzip weglassen. Aber es stört in diesem Fall auch 
nicht...

von Johannes M. (johnny-m)


Lesenswert?

daniel wrote:
> es ist keine besonders schöne lösung den makros
> aus delay.h die information über vorherzudefinierende
> makros mit einem wert zu übergeben .. aber so scheint
> avr-gcc es zu handeln^^
Wie willst Du das sonst machen? Aber an sich ist es richtig, was Du 
sagst: F_CPU muss entweder im Makefile oder im Code (und dann vor dem 
#include<util/delay.h>) angegeben werden, sonst funktioniert es nicht.

von daniel (Gast)


Lesenswert?

@Johannes
keine Ahnung, viele Möglichkeiten bieten sich ja nicht an
-D option an Compiler übersieht man eher als
#define F_CPU vor dem include.
Ich denke der Weg über eine globale nichtstatische Variable,
die man irgendwo im Code einfügt ist noch schlimmer,
weil diese eventuell mehrmals mit unterschiedlichen Werten
vorkommen könnten und der Linker irgendeine nehmen würde.
Auf jeden Fall etwas nicht gutes. und zum anderen muss es
vielleicht compile-time constante sein, weil delay es so braucht.
(ich kenne die implementierung von delay nicht)

da fällt mir gerade was ein .. was ist wenn ich in a.c
und b.c delay include, aber jeweils andere F_CPU werte eingebe?
ich shcätze ich muss doch die implementierung oder dokumentation
anschauen :)

es war kein Vorwurf von mir an avr-gcc, tut mir leid wenn
es so rüberkam ;)

grüsse, daniel

von M*** L. (b2b)


Lesenswert?

Guten Tag!

Johannes M. wrote:
>> Marc L. wrote:
>> Bitte werft ein Auge auf den Code. Könnte der funktionieren?
> Fehler sehe ich jetzt keine. Das nop in der Warteschleife des ADC
> kannste aber im Prinzip weglassen. Aber es stört in diesem Fall auch
> nicht...
Vielen Dank! Das nop könnte ich wirklich weglassen, stört mich aber 
auch nicht! :)

Johannes M. wrote:
>> Marc L. wrote:
>> Ich bekomme beim Compilieren die Warnung #warning "Compiler
>> optimizations disabled; functions from <util/delay.h> won't work as
>> designed".
>> Wieso bekomme ich denn diese Warnung? Der Pfad zur delay.h stimmt!
> Warum die Warnug kommt, steht doch in der Meldung: Du hast die
> Compiler-Optimierung nicht aktiviert.
Ach, okay, danke! Ich hatte zwar den Takt angegeben gehabt und bekam 
beim compilieren folgende Meldung:
avr-gcc.exe  -mmcu=atmega32 -Wall -gdwarf-2 -DF_CPU=3686400UL -O0 
-fsigned-char -MD -MP -MT DKO.o -MF dep/DKO.o.d  -c  ../DKO.c
Ich wusste nicht, dass die Delay-Funktion eine Optimierung braucht.
Zum Testen wollte ich den Code eigentlich nicht optimieren, aber wenn es 
nicht anders geht...
Stelle ich nun die Optimierung auf -Os, so bekomme ich keine Warnung 
mehr.
Danke für die Info!
Mir ist aber aufgefallen, dass bei meinem Takt von 3.6864MHz der µC 
keine 1ms wartet, sondern nur 0,92ms.

daniel wrote:
> F_CPU muss dazu jedoch nicht unbedingt im
> makefile definiert werden. Es reicht aus, wird aber bei mehrfacher
> Anwendung unübersichtlich, vor #include <util/delay.h> (veraltet:
> #include <avr/delay.h>) ein #define F_CPU [hier Takt in Hz]UL
> einzufügen.
Im Makefile habe ich den Takt zwar angegeben, aber aus Vorsicht könnte 
man ja auch folgendes in den Code schreiben:
1
#ifndef F_CPU
2
#define F_CPU 3686400UL
3
#endif
4
5
#include <util/delay.h>
6
#include <avr/io.h>
7
8
...

So, und nun wieder zum eigentlichen Thema...
Ich habe mein Array folgendermaßen angegeben:
unsigned char Array[5] = { 0,0,0,0,0 } ;
Wenn ich jetzt nur unsigned char Array[5]; schreibe, sind dann die 
einzelnen Elemente auch Null oder muss ich explizit angeben, dass alle 
Elemente am Anfang Null sein sollen?
Das Array wird ja nirgends gespeichert und es sollten die Elemente beim 
Einschalten des µC's wieder auf Null gesetzt werden?!
Oder brauche ich auf jeden Fall eine Angabe wie { 0,0,0,0,0 } ?

von Johannes M. (johnny-m)


Lesenswert?

Marc L. wrote:
> unsigned char Array[5] = { 0,0,0,0,0 } ;
> Wenn ich jetzt nur unsigned char Array[5]; schreibe, sind dann die
> einzelnen Elemente auch Null oder muss ich explizit angeben, dass alle
> Elemente am Anfang Null sein sollen?
> Das Array wird ja nirgends gespeichert und es sollten die Elemente beim
> Einschalten des µC's wieder auf Null gesetzt werden?!
> Oder brauche ich auf jeden Fall eine Angabe wie { 0,0,0,0,0 } ?
Globale Variablen werden automatisch zu 0 initialisiert. Solange Dein 
Array global ist, kannst Du Dir die manuelle Initialisierung sparen. Bei 
lokalen Variablen hingegen wäre sie erforderlich, wenn gewährleistet 
sein soll, dass bei jedem Aufruf der betreffenden Funktion alle Elemente 
0 sind.

von daniel (Gast)


Lesenswert?

soweit ich mich rictig erinnern kann
werden globale und static (in der funktion) variablen
mit ihrem default, also 0 initialisiert.
funktionslokale werden nicht initialisiert,
intel c compiler icc tat aber auch dies.
aber c standard deckt es nicht ab.

es reicht aber in deinem fall einfach
char feld[10]={0};
um alle auf 0 zu setzen. wenn ich nicht ganz falsch
liege reicht auch {}; aber die erste geht sicher.

grüsse, daniel

von die ??? (Gast)


Lesenswert?

daniel wrote:
> char feld[10]={0}; um alle auf 0 zu setzen

Da halten sich afair nicht alle Compiler dran...

von M*** L. (b2b)


Lesenswert?

Hallo,

man macht es am besten so, wie es Johannes M. geschrieben hat: 
Beitrag "Re: Arrays, Pointer und Schleifen (avr-gcc)"

Denn auf einer WWW-Seite fand ich folgende Info:
Nutzung des SRAM durch avr-gcc:
.bss = Statische und (modul-)globale, initialisierte Daten, die zu 0 
initialisiert sind bzw. *keinen Initializer haben (und also auch zu 0 
initialisiert werden)*.

Das heiß doch, wenn ich Johannes M. und die Info von der Seite richtige 
verstanden habe, dass ein *unsigned char Array[5];* außerhalb der 
Funktionen und nach den Includes bzw. Defines ausreichen sollte, um alle 
Elemente eine 0 zuzuweisen.

von Rolf Magnus (Gast)


Lesenswert?

GCC hält sich daran. Ein anderes Verhalten wäre auch nicht ISO-konform.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

daniel wrote:

> je nach maschine kann sizeof unterschiedliche Werte
> für dasselbe Datentyp ausgeben, meistens gilt aber auf einer 32bit
> maschine
> sizeof(char) = 8
> sizeof(short) = 16
> sizeof(int) = 32
> sizeof(long) = 32
> sizeof(long long) = 64

Nein.  sizeof(char) ist per definitionem in C immer gleich 1 -- selbst
dann, falls der Datentyp char 16 oder 32 bits umfasst.  (Gibt's wohl
auf einigen DSPs, sonst eher nicht.)

Die anderen Werte wären dann für eine gängige 32-bit-Maschine 2, 4, 4, 
8.

von daniel (Gast)


Lesenswert?

@Marc L
ja, bei globalen muss man nicht explizit initialisieren.
ich mach meistens
int glob; // = 0
als hinweis für mich oder anderen leser, dass es
nicht unabsichtlich vergessen wurde.

int glob = 0;
sollte aber auch nach .bss gehen, und nicht .data

@Jörg
erwischt ;) .. ich habe in bits gedacht

grüsse,daniel

von M*** L. (b2b)


Lesenswert?

Jörg Wunsch wrote:
> sizeof(char) ist per definitionem in C immer gleich 1 -- selbst
> dann, falls der Datentyp char 16 oder 32 bits umfasst.  (Gibt's wohl
> auf einigen DSPs, sonst eher nicht.)


1
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
Wenn ich Array[5] als unsigned char definiere, ist dann sizeof(Array) => 
5 * 1 Byte = 5 Byte.
sizeof(Array[0]) wären dann demnach 1Byte.
Bytes statt Bits zu verwenden ergibt folglich Sinn, denn die Schleife 
for(j=0; j<ARRAY_LENGTH(Array); j++) soll ja nur 5mal durchlaufen werden 
und nicht 40mal! g
Ich hatte vorher auch nicht darauf geachtet.
Danke!

von M*** L. (b2b)


Lesenswert?

daniel wrote:
> @Marc L
> ja, bei globalen muss man nicht explizit initialisieren.
> ich mach meistens
> int glob; // = 0
> als hinweis für mich oder anderen leser, dass es
> nicht unabsichtlich vergessen wurde.

Vielen Dank daniel!
Quellcode zu kommentieren macht ja bekanntlich immer Sinn und sollte auf 
keinen Fall fehlen, auch wenn es im ersten Moment länger dauert den Code 
fertig zu stellen, aber nach ein paar Wochen weiß man selber nicht mehr, 
was man gemacht hat und weiß die Kommentare zu schätzen! ;)
Auch für "fremde" Leser sind Kommentare unverzichtbar, das stimmt!

von Rolf Magnus (Gast)


Lesenswert?

> Quellcode zu kommentieren macht ja bekanntlich immer Sinn

Mitnichten.

von Matthias L. (Gast)


Lesenswert?

>Ich glaube, ich bin der Einzige in meinem Umkreis von 50km, die sich
>intensiv mit der Elektronik beschäftigen wollen oder ich kenne nur die
>falschen Leute! ;(

Aus welcher Gegend bist du denn? PLZ?

von M*** L. (b2b)


Lesenswert?

Rolf Magnus wrote:
>> Quellcode zu kommentieren macht ja bekanntlich immer Sinn
>
> Mitnichten.

Hä? ;)
Synonyme für mitnichten:
keinesfalls, keineswegs, nicht, ausgeschlossen, längst nicht, nein, 
undenkbar, auf keinem Fall, unter keinen Umständen, unter keiner 
Bedingung



Matthias Lipinsky wrote:
>>Ich glaube, ich bin der Einzige in meinem Umkreis von 50km, die sich
>>intensiv mit der Elektronik beschäftigen wollen oder ich kenne nur die
>>falschen Leute! ;(
>
> Aus welcher Gegend bist du denn? PLZ?

Ich habe eigentlich nur mein Gefühl genannt.
Es kommt mir halt so vor, dass ich mich immer mit Sachen beschäftige, 
die Leute in meinem Umkreis kaum interessieren.
Darum habe ich auch keinen "persönlichen" Kontakt zu Leuten, mit denen 
ich gemeinsam µC-Schaltungen aufbauen, programmieren und optimieren 
kann.
Ich komme aus der Gegend von Salzgitter/Braunschweig (Niedersachsen).

von Bernhard R. (barnyhh)


Lesenswert?

@ Marc L.

Und was ist mit TU Braunschzweig / FH Wolfenbüttel... ? Da sitzen doch 
einige (Edel-)Bastler.

Bernhard

von M*** L. (b2b)


Lesenswert?

Bernhard R. wrote:
> Und was ist mit TU Braunschzweig / FH Wolfenbüttel... ? Da sitzen doch
> einige (Edel-)Bastler.
Wäre eine Möglichkeit, aber da kenne ich keinen.
Kann ja nicht mal so eben dort aufschlagen und mich durchfragen bzw. 
nach einer Arbeitsgruppe suchen, oder doch? ;)
Ach, da fällt mir ein, vielleicht sollte ich mal einen Lehrer von mir 
fragen, der kennt bestimmt "E-Techniker", die auf diesem Gebiet fit 
sind!

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.