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.
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.
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.
ggf ein Vorzeichenfehler, mein Regler tastet sich zum Setpoint langsam hoch, sobald er drüber ist, schmiert er ab Richtung 1024...
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
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;
>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
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.
Es klappt war die unsigned Sache. Schlimm, schlimm man sieht das u vor lauter Kommatas nicht;)
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
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.
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 // --+ }
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.