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 | }
|