www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATTiny85 Unterschied an welcher Stelle einer Schleife eine Rechenoperation steht??


Autor: hochsitzcola (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich kämpfe mit dem Problem, daß eine Variable in eine if- Abfrage 
aktualisiert wird und in der Hauptschleife weiterverarbeitet wird. 
Aufgabe ist eine Art P-Regelung. In der If-Abfrage wird der Zielwert 
festgelegt, in der Hauptschleife läuft der Stellwert dem Sollwert 
hinterher, zweck ist, daß sich der Stellwert nicht sprunghaft ändert, 
sondern schön sanft dem Sollwert folgt.

if ...
 {
    ziel=...;
 }
x=ziel*1000;
y += (ziel-y)/100;
Ausgabe=y/1000;

Wenn ich das so wie dargestellt programmiere läuft das Ganze. Wenn ich 
die zeile x=ziel*1000; mit in die If-Abfrage schreibe, eigentlich muß 
die Rechnung ja nur einmal ausgeführt werden, läuft nichts!

Kann mir das jemand erklären?
Gruß
Hochsitzcola

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: someone (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das wird wohl an dem if und der ganzen schleife liegen ... poste den 
ganzen Code.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Problem ist nicht ausschliesslich der gezeigte Codeausschnitt. Der 
sollte "laufen".

Du könntest sogar in dem Codeausschnitt die Zeile x=ziel*1000; weg 
lassen (was manche Compiler sogar beim Optimieren machen), weil x im 
sichtbaren Bereich des Ausschnitts nirgends weiter benutzt wird.

Du solltest auch überlegen, welchen Wert ziel bei der Berechnung von 
y hat, wenn der if-Anweisungsblock nicht ausgeführt wird.

Du solltest auch überlegen welchen Wert y beim Eingang in den 
gezeigten Codeabschnitt hat, weil in der Zeile y += (ziel-y)/100; auf 
y etwas aufaddiert wird, was selbst wieder von y abhängig ist.

Autor: Hochsitzcola (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hier der betreffende Code-Schnipsel:
     while(1) {
      ADCSRA |= (1<<ADSC);        // Wandlung starten
        while ( !(ADCSRA & (1<<ADIF)) );
        vact = ADCW;// AD-Wert auslesen 
          
    if (abs(vact-vacta)>15)
      {
      vacta=vact;
      if (vact>max){max=vact;}
        if (vact<min){min=vact;}
        for( i=0; i<7; i++ )
        {
         valt[i]=valt[i+1];
        }
      valt[7] = vact;

      if (flanke==0) //steigende Flanke suchen
      {
      z=0;
      for( i=0; i<7; i++ )
          {
        if (valt[i] < valt[i+1])
                      {
                      z++;
                      }
          }
        if (z==7)
                        {
            flanke =1;
                        }
      }
      if (flanke==1) //fallende Flanke suchen
        {
        z=0;
        for( i=0; i<7; i++ )
        {
        if (valt[i] > valt[i+1])
          {
          z++;
                 }
        }
        if (z==7)
                                {
                     flanke =0;
         delta=0;
        for( i=0; i<4; i++ )/
        {
        avedelta[i]=avedelta[i+1];
        }
        avedelta[4]=max-min;
        for( i=0; i<5; i++ )
        {
                                 delta+=avedelta[i];
        }
        delta/=20; 
        min=1024;
        max=0;
                              }
      }
    }

      ziel=delta*1000l;
      kruecke += (ziel-kruecke)/1000;
      aus = kruecke/1000;

      
      OCR0A = aus; //PWM aktualisieren

    }
    }

Hab den Code etwas eingekürzt. Es wird eine schwingende Spannung 
(kleiner 5 Hz) untersucht. Die Amplitude der Schwingung soll als PWM 
geglättet wieder ausgegeben werden. Die Amplitude wird über 5 
Schwingungen gemittelt.

Gruß
Hochsitzcola

Autor: Rubelus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh mein Gott, rück mal richtig deinen Code ein, da bekommt man ja 
Augenkrebs beim lesen :D

"Ziel" und "Aus" wird bestimmt "wegcompiliert"... Ist Ziel als long 
definiert? Als was ist "aus" definiert?

Das wäre dann Quasi so:

kruecke = krueke + ((delta*1000l)-kruecke)/1000);
OCR0A = kruecke/1000;

Dann lass uns mal mit einem Delta von 125 rechnen, und das Ziel liegt 
bei 512 Krueke ist bei 124:

Krueke = 124 + ((125*1000)-124)/1000); = 248,87 = int 249
OCR0A  = 125/1000;                     = 0,249  = int 0


Wohin soll denn dein OCR0A deinen Timer setzen wenn er im Grunde immer 
wieder auf 0 fällt?

Autor: Rubelus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falsch Krueke müsste ja bei 386 liegen also nochmal neu Rechnen:

Krueke = 386 + ((125*1000)-386)/1000); = 510,61 = int 511
OCR0A  = 125/1000;                     = 0,510  = int 1

Behebt das Problem aber immer noch nicht ;)

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast 15 { und 16 } im Codeausschnitt. Und keine Definitionen von 
Variablen aus denen ich sicher den Datentyp und die Initialwerte 
erkennen kann. Damit kann ich nur spekulieren:

1. Prüfe, ob du den Fall (abs(vact-vacta)<=15) ausreichend 
berücksichtigst

2. Prüfe deine Rechnungen auf Überläufe. Letztlich endet alles in einem 
uint8_t Wert (OCR0A = aus;)

3. Tipp für schlanke, schnelle Programme: Nutze Ganzzahlrechnungen und 
2er-Potenzen. Also z.B. 16 oder 32 statt 20, 1024 statt 1000 usw.

Autor: Hochsitzcola (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

die Variablen sind glaub ich ausreichend groß definiert. So wie 
dargestellt läuft das ja auch. Ich hatte schon einigen Ärger mit 
Überläufen, die sind aber jetzt behoben. Das mal nehmen und wieder 
teilen mache ich um Fließkommazahlen zu vermeiden.

@Stefan: das mit den Klammern ist mir beim zusammenkürzen passiert, der 
Code ist so sicher nicht lauffähig. Danke für den Tip mit den 
2er-Potenzen.

@rubelus: sorry für den Augenkrebs, beim reinkopieren ist leider die 
Formatierung verlorengegangen.

hier noch die Variablen-Deklaration:
        unsigned int i = 0;//Schleifenzähler
  unsigned int z = 0;
  unsigned int max = 0;  //Maximalwert Spannung
  unsigned int min = 1024;  //Minimalwert Spannung
  unsigned long vact = 0;
  unsigned long vacta = 0;//Aktuelle Spannung
  unsigned int valt[8];//alter Spannungswert
  unsigned int avedelta[5];//verlauf Delta
  unsigned int delta = 0;
  unsigned int aus = 0;
  unsigned int flanke = 0;
  long ziel = 0;
  long kruecke= 0;

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.