Forum: Compiler & IDEs ADC Einlesen Auswerten Ausgeben


von Mark P. (pisy)


Lesenswert?

Hallo zusammen,
ich bin neu im Gebiet der uC. Ich habe schon einige Tutorials 
durchgearbeitet und "spiele" selber noch mit dem MyAVR-Board mit einem 
Mega8 ein bischen herum.

Meine Frage.
Ich will z.B. ein 5V Spannungssignal in ein PWM umwandeln, das Signal 
über ein RC Glied Filtern und wieder einlesen. Es soll darauf 
hinauslaufen, dass ich noch einen Poti als Störgröße hinter dem RC Glied 
einbaue. Um eine PID Regelung zu verwirklichen.

Das PWM Signal bekomme ich hin und es wird auch genau ausgegeben über 
UART.
Ich benutze dazu fast 1:1 das Tutorial. Jetzt will ich aber das analoge 
Signal (also hinter RC) wieder einlesen auf einem Pin von Port C.
Wie teile ich dem Controller mit das er z.B. den Wert an Port C1 
einlesen soll.
Was mich da wundert, ich habe den Port C nicht initialisiert. Und der 
Controller liest automatisch das anliegende Signal ein und wandelt es 
abgreifbar in Port B um. Jetzt habe ich aber zwei Signale auf C anliegen 
die 5 Volt zum PWM und meinen Rückgabewert. Ich brauche also denke ich 
eine Zuweisung.
Wie verwirkliche ich das?

Danke im Voraus.

von Gast (Gast)


Lesenswert?

Es gibt da einen Multiplexer den du umschalten kannst und der dir dann 
den ensprechenden Eingang auf den internen AD-Wandler legt. Steht aber 
alles im Datenblatt oder ADC.

von Mark P. (pisy)


Lesenswert?

Hi danke erst einmal für die Antwort. Ich habe es schon hinbekommen mit 
dem Multiplexer. Er gibt die Signale wertegerecht an.
Jetzt habe ich das Problem mit dem PID Regler. Ich habe folgende 
Rechnung über die such Funktion gefunden

[[Beitrag "PID- Regler"]]

Um über eine Ausgabe eine Regelung festzustellen habe ich die P I D 
Konstanten auf 0.01 in einer float abgespeichert. Also eine relativ 
langsame Regelung.

Ohne Regelung kann ich jetzt also Werte ausgeben und einlesen und diese 
stimmen.
Sobald ich die Regelung einbaue komme ich immer in einen Überlauf. Meine 
Werte regeln sich auf 1024 und wollen weiter ansteigen, so dass ich 
einen Filter über "if" eingebaut habe.

char buffer[20];
  uint16_t pidsetpoint=250;
  float pidoutput=0;
  float err_0=0;
  float err_1=0;
  float err_2=0;
  float P=0.1;
  float I=0.1;
  float D=0;

  while(1)
  {
  pidsense=einlesen(1);

  err_2=err_1;
  err_1=err_0;
  err_0=pidsetpoint-pidsense;

  pidoutput= pidoutput+P*(err_0-err_1);
  pidoutput=pidoutput+I*(err_1);
  pidoutput=pidoutput+D*(err_0-2*err_1+err_2);

  if (pidoutput>1023)
    {
      pidoutput=1023;
    }
  else if (pidoutput<1)
    {
      pidoutput=1;
    }
  else
    {
      pidoutput=pidoutput;
    }

    OCR1A=pidoutput;

     sprintf(buffer, "\n%d", pidsense);
    //sprintf(buffer, "\n%d", pidsense);
    print(buffer);


Ich bin davon ausgegangen, dass ich jetzt die Rechnungen hätte 
nachvollziehen können, aber bei mir läuft alles immer auf einen Überlauf 
hin. Ist echt frustrierend ich bin schon seit heute morgen dran den 
Fehler zu suchen.

von Mark P. (pisy)


Lesenswert?

ggf ein Vorzeichenfehler, mein Regler tastet sich zum Setpoint langsam 
hoch, sobald er drüber ist, schmiert er ab Richtung 1024...

von Oliver (Gast)


Lesenswert?

Beantworten kann das keiner, da dein Code unvollständig ist. Aber ich 
rate mal, daß das kein Vorzeichenfehler ist, da err_0 gar kein 
Vorzeichen hat.
1
uint16_t pidsetpoint=250;
2
...
3
err_0=pidsetpoint-pidsense;

Denk mal über diese Zeilen nach...

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Neben der unsigned Sache.

Leg auch mal den I-Anteil still.
Am Anfang genügt es, nur den P Anteil in Betrieb zu nehmen.
Und: Beim P-Anteil geht es nur um die momentane Regelabweichung.

   pidoutput = pidoutput + P * err_0;

von Oliver (Gast)


Lesenswert?

>Beim P-Anteil geht es nur um die momentane Regelabweichung.

>   pidoutput = pidoutput + P * err_0;

e nä...
1
pidoutput= pidoutput+P*(err_0-err_1);

passt schon. Alternativ wäre es
1
pidoutput= P * err_0;

Oliver

von Mark P. (pisy)


Lesenswert?

Danke für eure Hilfe,
ja mit dem unsigned muss ich morgen mal versuchen, hab den Board jetzt 
nicht vor Ort, ich mache aber sofort eine Rückmeldung.
Danke vielmals.

von Mark P. (pisy)


Lesenswert?

Es klappt war die unsigned Sache. Schlimm, schlimm man sieht das u vor 
lauter Kommatas nicht;)

von Mark P. (pisy)


Angehängte Dateien:

Lesenswert?

Hallo,
ich wollte nicht nochmal einen Thread aufmachen, ich hoffe der hier wird 
noch gelesen.
Folgendes:
Mein PWM benutzt den Timer 1. Jetzt will ich aber meine 
PID-Regelschleife nur eine gewisse Zeit laufen lassen um sich dann einen 
neuen Sollwert zu holen. Der Sollwert lässt sich über den Poti 
einstellen.
Programm im Anhang.

Meine Idee war.
Einen Timer z.B. 1000ms laufen zu lassen, wenn die 1000ms um sind soll 
ein Interrupt gesetzt werden. Dieser setzt mir in meiner do{...}while 
Schleife die Bedinung für den Abbruch. Nach dem Abbruch soll der Timer 
zurückgesetzt werden und mit dem neuen Sollwert von vorne geregelt 
werden.

Jetzt habe ich aber Timer1 vom ATMega8 schon fürs PWM missbraucht und 
bei Timer zwei sagt er mir bei egal welchen Wert ich eingebe, dass meine 
Hardware dafür nicht ausgelegt sei und Timer 0 hat nicht die Optionen 
die ich benötige.
Muss ich jetzt eine Art Shaking Hands mit dem Timer hinbekommen, oder 
kann ich mich irgendwie geschickt über einen anderen Timer aus der 
Affaire ziehen?

PS: Die Schleife if(pidsetpoint=pidsende)
Soll mir nur sagen, wann der Sollwert erreicht wurde, hab dies benötigt 
um die Regelparamenter zu bestimmen

von Mark P. (pisy)


Lesenswert?

Okay also ich habe jetzt nochmal einen Timer programmiert:

void init()
{
  //--- Timer 2 initialisieren ---
  TCCR2=0x06;    // Teiler 1/256
  TIMSK|=0x04;   // Interrupt bei Überlauf
  //--- Interrupts erlauben ---
  sei();
}
//--------------------------------------------------------------------
// TIMER1_COMPA_vect - Timer1 Interrupt bei Vergleichswert
// aktuelle Einstellung:  10 Hz  100 ms
//--------------------------------------------------------------------
ISR(TIMER2_OVF_vect)
{
  abbruch=0;
  print("überlauf");
}

---->Hier werden Werte eingelesen über getChar sie txt<-----

int main (void)
.
.
.
init();

while(1)
{

//Meine Rechnung

do
{
}while (abbruch==0);
abbruch=1;

}
return(0);
}


Meine Ausgabe über UART ist nun eine Unendlichabfrage meiner 
einzugebenden Parameter.
Ich hab schon den Aufruf des Timers quer durchs Programm verschoben. Ich 
kanns es mir auch nicht erklären.

von Karl H. (kbuchegg)


Lesenswert?

Mark Pisani schrieb:

> Mein PWM benutzt den Timer 1. Jetzt will ich aber meine
> PID-Regelschleife nur eine gewisse Zeit laufen lassen um sich dann einen
> neuen Sollwert zu holen. Der Sollwert lässt sich über den Poti
> einstellen.
> Programm im Anhang.

Warum so kompliziert.
Wenn dein Regler sauber regelt, muss er auch damit klarkommen, dass du 
den Sollwert veränderst, während er noch auf einen ganz anderen Sollwert 
hingearbeitet hat.

Hol den ADC Wert in jedem Schleifendurchlauf und gut ists.
Wenn du das geschickt machst, dann machst du das so, dass du den ADC 
Wert abholst und während der ADC den nächsten Wert sampelt, rechnest du 
einen Durchgang durch die Regelschleife.

   ADC starten
   warte auf ADC

   while( 1 ) {

     Sollwert = ADC-Wert
     ADC erneut starten

     PID berechnen           // --+
                             //   |  in der Zeit sampelt der ADC
     Stellwert ausgeben      // --+
  }

Ein Schleifendurchlauf durch die Regelschleife wird sicher länger 
dauern, als der ADC zum sampeln benötigt. Der neue ADC Wert steht daher 
sofort zur Verfügung, wenn du ihn brauchst. Deine Regelschleife 
verlängert sich zeitmässig daher nur um das Abholen des ADC Werts und 
das Anstossen des nächsten Sample-Vorgangs. Und wenn deine Schleife 
tatsächlich mal schneller sein sollte, als der ADC, dann ist das auch 
nicht schlimm. Läuft halt ein Regeldurchgang mit einem alten ADC Wert.

   ADC starten
   warte auf ADC

   while( 1 ) {

     if( ADC fertig ) {
       Sollwert = ADC-Wert
       ADC erneut starten
     }

     PID berechnen           // --+
                             //   |  in der Zeit sampelt der ADC
     Stellwert ausgeben      // --+
  }

von Karl H. (kbuchegg)


Lesenswert?

Mark Pisani schrieb:

> Ich hab schon den Aufruf des Timers quer durchs Programm verschoben. Ich
> kanns es mir auch nicht erklären.

Ich auch nicht. Vor allem deswegen nicht, weil du keine Frage 
formulierst. Aber ich rate mal ins Blaue und sage: volatile

von Mark P. (pisy)


Lesenswert?

Ja tut mir ja auch irgendwie leid;)
Also wie auch immer ich habs hinbekommen, es lag daran dass der Wizard 
im Timer 2 bei Takt/8 und "sei" bei OVF das falsche Bit setzt. Hab es 
dann richtig gesetzt und es rennt.
Trotzdem danke

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.