Forum: Mikrocontroller und Digitale Elektronik PID Regelung mit Widerständen myAVR in C


von Gabriel 85 (Gast)


Lesenswert?

Hallo!

Ich hatte zwischenzeitlich den PID Regler aufgebaut, und es hatte 
geklappt. Jetzt wollte ich einen Widerstandswert als Regelgröße einlesen 
und das klappt nicht...vielleicht weiss jemand rat.

Das Programm funktioniert wie folgt:
Nach den LCD_init funktionen beginnt das main Programm.

Mit der Funktion ReadChannel(..) rufe ich die einzelnen ADC Kanäle 
(0,1,2) auf. Kanal 0 liest meine eine Spannung ein, Kanal 1 die 
Sollwertspannung (über Poti) und Kanal 2 liest eine Spannung ein die 
linear zum Strom meines Sensors ist.

Aus 0 und 2 wird durch Division ein Widerstandswert (Istwert) ermittelt.
Aus 1 und 2 analog auch ein Widerstandswert (Sollwert).

Nun werden diese Werte in PID Berechnung zwecks Regelung eingegeben, und 
man erhält y , das in main einfach über PWM (B1) ausgegeben wird.

Die Berechnung der Regelabweichung stimmt auch soweit, da bei meinem 
Aktor der Widerstand mit zunehmenden Stellweg kleiner wird.

Hardwaremäßig sind B1 mit C2 verbunden, da der Regler einen Stromwert 
für einen Aktor vorgibt.

Mein Aktor hat einen eingebauten Sensor, der mit steigendem Strom seinen 
Widerstand ändert.


Der Regler an sich, die Display_inits und die Read_Channels 
funktionieren alle einwandfrei. Ich denke es ist irgendwo ein Bug im 
Wirkprinzip oder in main.

Könnte es sein dass ein Wert (y) der vom µC ausgegeben wird, nicht 
direkt in den Eingang C2 gespeist werden kann, da er zum selben 
augenblick verwendet wird? (Hat mir jemand erzählt)
Hier der Quellcode:
1
//---------------------------------------------------------------------------
2
// Title      : Regler mit LCD
3
// Date       : 27.10.2008
4
// Version    : 1.0
5
// Autor      : 
6
//---------------------------------------------------------------------------
7
#define    CLOCK  3686400
8
#include  <io.h>
9
#include  <avr/delay.h>
10
/////////////////////////////////////////////////////////////////////////////
11
// allgemeine-Funktionen
12
//---------------------------------------------------------------------------
13
//  wait_ms(..) - Wartet einige Millisekunden
14
//  Die Dauer ist nicht kalibriert.
15
//  PE:  miliSec=Anzahl der zu wartenden Millisekunden
16
//---------------------------------------------------------------------------
17
void wait_ms(int miliSec)
18
{
19
  _delay_loop_2( 1*(CLOCK/(1000/4)) * miliSec);  // 4 Zyklen warteschleife
20
}
21
//////////////////////////////////////////////////////////////////////////////
22
//  LCD-Funktionen für myAVR-Board + myAVR-LCD
23
//  4-BitModus an PortD Bit 4-7
24
//  PortD Bit 2 = RS, high=Daten, low=Kommando
25
//  PortD Bit 3 = E, high-Impuls für gültige Daten
26
//---------------------------------------------------------------------------
27
//  lcd_send(..) - sendet ein Byte an LCD im 4-Bit-Modus
28
//  RS muss vorher richtig gesetzt sein
29
//  PE:  data=zu sendendes Byte
30
//---------------------------------------------------------------------------
31
void lcd_send(char data)
32
{
33
  // aktuelles RS ermitteln
34
  char rs=PORTD;
35
  rs&=4;
36
  // High-Teil senden
37
  char tmp=data;
38
  tmp&=0xf0;
39
  tmp|=rs;
40
  PORTD=tmp;
41
  // Schreibsignal
42
  sbi(PORTD,3);
43
  cbi(PORTD,3);
44
  // Low-Teil senden
45
  tmp=data;
46
  tmp&=0x0f;
47
  tmp*=16;
48
  tmp|=rs;
49
  PORTD=tmp;
50
  // Schreibsignal
51
  sbi(PORTD,3);
52
  cbi(PORTD,3);
53
  // verarbeiten lassen
54
  wait_ms(1);
55
}
56
//---------------------------------------------------------------------------
57
//  lcd_cmd(..) - sendet ein Kommando an LCD
58
//  PE:  cmd=Kommando-Byte
59
//---------------------------------------------------------------------------
60
void lcd_cmd(char cmd)
61
{
62
  cbi(PORTD,2);    // RS löschen = Kommando
63
  lcd_send(cmd);    // senden
64
}
65
//---------------------------------------------------------------------------
66
//  lcd_write(..) - sendet ein Zeichen (Daten) an LCD
67
//  PE:  text=Zeichen
68
//---------------------------------------------------------------------------
69
void lcd_write(char text)
70
{
71
  sbi(PORTD,2);    // RS setzen = Daten
72
  lcd_send(text);    // senden
73
}
74
//---------------------------------------------------------------------------
75
//  lcd_write(..) - sendet eine Zeichenkette an LCD
76
//  Die Zeichenkette muss mit 0x00 abgeschlossen sein
77
//  PE:  pText=Zeiger auf Zeichenkette
78
//---------------------------------------------------------------------------
79
void lcd_write(char* pText)
80
{
81
  while(pText[0]!=0)
82
  {
83
    lcd_write(pText[0]);
84
    pText++;
85
  }
86
}
87
//---------------------------------------------------------------------------
88
//  lcd_write(..) - sendet eine Zeichenkette an LCD
89
//  PE:  pText=Zeiger auf Zeichenkette
90
//    count=Anzahl der zu sendenden Zeichen
91
//---------------------------------------------------------------------------
92
void lcd_write(char* pText, int count)
93
{
94
  while(count!=0)
95
  {
96
    lcd_write(pText[0]);
97
    pText++;
98
    count--;
99
  }
100
}
101
//---------------------------------------------------------------------------
102
//  lcd_home(..) - Cursor auf Position 1,1
103
//---------------------------------------------------------------------------
104
void lcd_home()
105
{
106
  lcd_cmd(0x02);
107
  wait_ms(2);      // warten
108
}
109
//---------------------------------------------------------------------------
110
//  lcd_clear(..) - löscht die Anzeige im LCD
111
//---------------------------------------------------------------------------
112
void lcd_clear()
113
{
114
  lcd_cmd(0x01);
115
  wait_ms(2);      // warten
116
}
117
//---------------------------------------------------------------------------
118
//  lcd_on(..) - schaltet das LCD an
119
//---------------------------------------------------------------------------
120
void lcd_on()
121
{
122
  lcd_cmd(0x0E);
123
}
124
//---------------------------------------------------------------------------
125
//  lcd_off(..) - schaltet das LCD aus
126
//---------------------------------------------------------------------------
127
void lcd_off()
128
{
129
  lcd_cmd(0x08);
130
}
131
//---------------------------------------------------------------------------
132
//  lcd_goto(..) - setzt die Cursorposition
133
//   PE:  row = Zeile 1..2
134
//    col = Spalte 1..16
135
//---------------------------------------------------------------------------
136
void lcd_goto(int row, int col)
137
{
138
  row--;        // Null-basierend
139
  row&=0x01;      // sicherheitshalber
140
  row*=0x40;      // Zeile nach Bit 6 bringen
141
  col--;        // Null-basierend
142
  col&=0x0f;      // sicherheitshalber
143
  char tmp=row|col;
144
  tmp|=0x80;      // Cursor setzen
145
  lcd_cmd(tmp);    // senden
146
}
147
//---------------------------------------------------------------------------
148
//  lcd_init(..) - Schaltet die Ports und Initialisiert das LCD
149
//---------------------------------------------------------------------------
150
void lcd_init()
151
{
152
  // Port D = Ausgang
153
  DDRD=0xff;
154
  PORTD=0;
155
  // warten bist LCD-Controller gebootet
156
  wait_ms(200);    
157
  // 4-BitModus einschalten
158
  PORTD=0x20;      
159
  // Schreibsignal
160
  sbi(PORTD,3);
161
  cbi(PORTD,3);
162
  wait_ms(5);      // Zeit zum Umschalten lassen
163
  // ab hier im 4-Bit-Modus
164
  lcd_cmd(0x28);    // Funktions-Set: 2 Zeilen, 5x7 Matrix, 4 Bit
165
  //lcd_off();
166
  lcd_cmd(0x06);    // Entry Mode
167
  lcd_on();
168
  lcd_clear();
169
170
171
}
172
173
//---------------------------------------------------------------------------
174
// Main-Funktion
175
//---------------------------------------------------------------------------
176
int main (void)
177
{
178
    uint16_t ReadChannel(uint8_t mux_adc_channel);         // Auslese-Funktion deklarieren
179
  unsigned int PID_Berechnung (signed int x, signed int w);  // REGLER deklarieren
180
  TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1);       // 10Bit-PWM-Modus (Auflösung=1024!)
181
                                    //Nicht Invertierend
182
  TCCR1B = (1<<CS10);                     // Aktivieren mit Prescaler 1, also keine Frequenz-'Teilung'
183
   DDRB |= (1<<DDB1);                       // Nicht vergessen, da es sich hier um einen Ausgang handelt, 
184
                                 //müssen wir das dem Mikrocontroller auch mitteilen
185
  wait_ms(2);    
186
  lcd_init();
187
  while (true)           // Mainloop
188
  {
189
    uint16_t SollR;
190
    uint16_t SollR1;
191
    uint16_t Ieingang;
192
    uint16_t Ieingang1;
193
        uint16_t Ueingang;
194
        uint16_t Reingang;
195
      
196
        uint16_t adc;
197
    uint16_t adc2; 
198
    uint16_t Y1;
199
    
200
           
201
        Ueingang = ReadChannel(0);
202
        SollR = ReadChannel(1);
203
        Ieingang = ReadChannel(2);
204
        Ieingang1= Ieingang/2; // Umrechnung U --> I Toellner Laborgerät
205
        
206
        Reingang = Ueingang/Ieingang1;
207
        
208
        SollR1 = SollR/Ieingang1;
209
        
210
        adc2 = Reingang;
211
        adc2 = adc2/64;
212
    lcd_goto(1,1);
213
    //lcd_write("Sollposition (°)");
214
    int j;
215
    j=0;
216
    for (j=0;j<adc2;j++)  lcd_write(0xFF);
217
    for (j=j;j<16;j++)  lcd_write(' ');
218
    wait_ms(2);
219
    
220
    adc = SollR1;
221
    adc=adc/64;    //10 bit auf 4 bit reduzieren
222
    
223
    int i;
224
    i=0;
225
    lcd_goto(2,1);
226
    
227
    
228
    for (i=0;i<adc;i++)  lcd_write(0xFF);
229
    for (i=i;i<16;i++)  lcd_write(' ');
230
    wait_ms(2);
231
232
                    
233
    Y1 =PID_Berechnung(Reingang, SollR1);
234
    OCR1A = Y1; 
235
   
236
237
    
238
  }
239
return 0;
240
}
241
//---------------------------------------------------------------------------------------------------------
242
uint16_t ReadChannel(uint8_t mux_adc_channel) //Unsere Funktion zum ADC-Channel aus lesen
243
{
244
  uint8_t i;
245
  uint16_t result = 0;         //Initialisieren wichtig, da lokale Variablen
246
                               //nicht automatisch initialisiert werden und
247
                               //zufällige Werte haben. Sonst kann Quatsch rauskommen
248
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);    // Frequenzvorteiler auf 32 setzen und ADC aktivieren 
249
 
250
  ADMUX = mux_adc_channel;                      // übergebenen Kanal waehlen
251
  ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen 
252
 
253
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
254
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
255
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung (Der ADC setzt dieses Bit ja wieder auf 0 nach dem 
256
                    // Wandeln)
257
  while ( ADCSRA & (1<<ADSC) ) {
258
     ;     // auf Abschluss der Wandlung warten 
259
  }
260
 
261
  // Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen
262
  for(i=0;i<4;i++)
263
  {
264
    ADCSRA |= (1<<ADSC);            // eine Wandlung
265
    while ( ADCSRA & (1<<ADSC) ) {
266
      ;     // auf Abschluss der Wandlung warten 
267
    }
268
    result += ADCW;            // Wandlungsergebnisse aufaddieren
269
  }
270
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren ("Enable-Bit" auf LOW setzen)
271
 
272
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
273
 
274
  return result;
275
}
276
//-----------------------------------------------------------------------------------------------------------
277
278
//-----------------------------------------------------------------------------------------------------------
279
// REGLER
280
// W, X, Y in INT  - Eingänge mit Vorzeichen
281
// interne Variablen sollten dennoch in double gehalten werden (Gleitkommadarstellung)
282
283
unsigned int PID_Berechnung (signed int x, signed int w)
284
{
285
286
double Kp;
287
double I;
288
double Ta;
289
double D;
290
unsigned int prop;
291
unsigned int integ;
292
unsigned int diff;
293
signed int ealt;
294
int16_t esum;
295
int16_t e;
296
297
uint16_t y; 
298
299
300
301
302
Kp = 0.5;
303
I = 0.1;
304
D= 0.05;
305
Ta = 0.1;
306
307
308
309
310
311
if (x > w)
312
{   
313
e = x - w;            // aktuelle Regelabweichung bestimmen
314
}
315
316
if (x < w)
317
{
318
  e = 0;
319
}
320
321
if ((integ <= 1023)&&(integ > 0))
322
 {
323
   esum+=e;        // Summe der Regelabweichung aktualisieren  
324
 }
325
326
prop = (Kp*e);   // Proportional Faktor
327
integ = (I*Ta*esum); // Integraler Anteil
328
diff = (D*((e-ealt))/Ta);   // Differenzieller Anteil
329
ealt = e;                   // Regelabweichung für nächste Abtastung merken
330
y = prop + integ + diff;    // Ausgangsstellgröße
331
332
333
//if (y >= 1023)               // Stellgröße auf 0..1023 begrenzen(10 bitPWM)
334
//{
335
  // y = 1023;
336
 //}
337
 if (y < 1)
338
  {
339
    y = 0;
340
  }
341
 
342
343
return (y+10);              // unsigned int weil y nur zwischen 0 und 1023
344
 }

von Gabriel 85 (Gast)


Angehängte Dateien:

Lesenswert?

Im Anhang hab ich noch ein Bild was das ganze eigentlich soll...die 
Berechnung des Widerstandes im Microcontroller aus y und Ua ist 
notwendig

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.