1 | #include <avr/io.h>
|
2 | #include <avr/interrupt.h>
|
3 | #include <inttypes.h>
|
4 |
|
5 | //PB2: ADC1 ist der Eingang für die Fotodiode.
|
6 | //PB0: ist der Ausgang für den Motor.
|
7 | //PB4: PCINT4 ist der Steuerausgang für die Fotodioden-Beleuchtung
|
8 |
|
9 | // Wenn der Motor dreht, ziehen abwechselnd schwarze und weiße Streifen an
|
10 | // der Fotodiode vorbei. Bei sieben gezählten Streifen soll er wieder anhalten.
|
11 | #define ANZAHL_STREIFEN 7
|
12 | char schwarze_streifen; //Zähler für die schwarzen Streifen
|
13 | char wirdsHeller; //wird es heller oder dunkler?
|
14 |
|
15 |
|
16 | // Wenn der Motor steht, soll das Teil 5 Minuten Pause machen bis er wieder anfängt
|
17 | // zu drehen.
|
18 | #define PAUSENLAENGE 600 // halbe Sekunden
|
19 | unsigned int pausenzeit; //zähler für die halben Sekunden
|
20 |
|
21 |
|
22 | // generelle Statemachine im Hauptprogramm
|
23 | #define WARTEN_AUF_MESSUNG 0
|
24 | #define MESSEN_STARTEN 2
|
25 | #define MESSEN_FERTIG 3
|
26 |
|
27 | #define PAUSE_EINLAEUTEN 6
|
28 | #define PAUSE 4
|
29 | #define WEITERDREHEN 5
|
30 |
|
31 | char cSREG;
|
32 |
|
33 |
|
34 | //Filtervariablen
|
35 | uint32_t mess_hilf, mess_0, mess_1, mess_2, mess_3, mess_4;
|
36 | uint32_t ergebnis_4, ergebnis_3, ergebnis_2, ergebnis_1, ergebnis_0;
|
37 | uint32_t A, B; // Filter zwischenergebnisse
|
38 |
|
39 | char communication; //Statemachine Steuervariable
|
40 |
|
41 |
|
42 | ISR(TIM1_COMPA_vect) // Timer 1 meldet einen OCR1A Vergleich
|
43 | {
|
44 | cSREG = SREG;
|
45 | // Interrupt Code
|
46 | if (communication == PAUSE)
|
47 | {
|
48 | pausenzeit = pausenzeit + 1;
|
49 |
|
50 | if (pausenzeit >= PAUSENLAENGE)
|
51 | {
|
52 | communication = WEITERDREHEN;
|
53 | }
|
54 | }
|
55 | else
|
56 | {
|
57 | communication = MESSEN_STARTEN; //Messintervall vorüber
|
58 | }
|
59 |
|
60 | SREG = cSREG;
|
61 | //TIFR = 0x7E;
|
62 | sei();
|
63 | }
|
64 |
|
65 | //Der ADC meldet sich hier, wenn die Messung fertig ist.
|
66 | ISR (ADC_vect)
|
67 | {
|
68 | communication = MESSEN_FERTIG;
|
69 | sei();
|
70 | }
|
71 |
|
72 |
|
73 | void adc_einstellen(void);
|
74 | void messwert_filtern(void);
|
75 |
|
76 | void timer1_einstellen(unsigned char); // umstellen von Pause zur Messen und andersherum
|
77 |
|
78 | void motor_ein(void); // ein
|
79 | void motor_aus(void); // aus
|
80 |
|
81 | int main(void)
|
82 | {
|
83 | // Initialisierungen: globale Variablen
|
84 |
|
85 | //Vor-Initialisierung, damit der Filter schneller auf Stand kommt
|
86 | ergebnis_4 = 32768;
|
87 | ergebnis_3 = 32768;
|
88 | ergebnis_2 = 32768;
|
89 | ergebnis_1 = 32768;
|
90 | ergebnis_0 = 32768;
|
91 |
|
92 | //Statemachine initialisieren; mit "drehen" starten.
|
93 | communication = WEITERDREHEN;
|
94 |
|
95 | wirdsHeller = 2; //wird es heller oder dunkler?
|
96 | schwarze_streifen = 0; //wie viele Streifen sind am Sensor vorbeigezogen?
|
97 | pausenzeit = 0; //Aktuell verstrichene Pausenzeit
|
98 |
|
99 | // Initialisierungen: Clock, Ports, Timer, ADC, Interrupts
|
100 | // Clock
|
101 | // Der interne Takt ist eingestellt auf 8 MHz
|
102 |
|
103 |
|
104 | // Watchdog deaktivieren
|
105 | // WDTCR = 0x00;
|
106 |
|
107 |
|
108 | // Portpin
|
109 | DIDR0 = 0x00;
|
110 | MCUCR = 0x00; // Pull Up Widerstände aktivieren
|
111 | DDRB = 0x3F; // Data Direction Register, vorerst komplett als Ausgang
|
112 | PORTB = 0x00; // Alle Pins auf Low
|
113 | //PINB // Eingangsregister -> will ich nicht -> weggelassen
|
114 |
|
115 | // nötige Peripherals einstellen
|
116 | timer1_einstellen(WEITERDREHEN);
|
117 | adc_einstellen();
|
118 | pwm_einstellen();
|
119 |
|
120 | // LED einstellen
|
121 | DDRB |= (1 << PB4); // Pin für LED auf "Ausgang" schalten
|
122 |
|
123 |
|
124 |
|
125 | // Timer-Interrupts aktivieren
|
126 | TIMSK = 0x40; // Timer 1 OCR1A Compare Match Interrupt enabled
|
127 | SREG |= 0x80;
|
128 |
|
129 |
|
130 | /*********************************************************************************/
|
131 |
|
132 |
|
133 | while(1) {
|
134 | // Statemachine
|
135 | switch (communication)
|
136 | {
|
137 | /**************************************************/
|
138 | case PAUSE: {
|
139 | // Warte auf nächsten Zyklusstart;
|
140 | // State wird im Interrupt verändert
|
141 |
|
142 | break;
|
143 | }
|
144 | /**************************************************/
|
145 | case WEITERDREHEN: { // Start eines neuen Zyklus
|
146 | // Sensorbeleuchtung starten
|
147 | PORTB |= (1 << PB4); // LED für den Sensor einschalten
|
148 | // Timer 1 umstellen für Sensor Interrupts
|
149 | timer1_einstellen(WEITERDREHEN);
|
150 | // Motor starten
|
151 | motor_ein();
|
152 | // Messung starten
|
153 | communication = MESSEN_STARTEN;
|
154 | break;
|
155 | }
|
156 | /**************************************************/
|
157 | case MESSEN_STARTEN: { //Messung starten
|
158 |
|
159 | ADCSRA |= (1<<ADSC); // ADSC auf 1 setzen startet den ADC
|
160 | communication = WARTEN_AUF_MESSUNG;
|
161 | break;
|
162 |
|
163 | }
|
164 | /**************************************************/
|
165 | case WARTEN_AUF_MESSUNG: {
|
166 | // Wartet auf nächstes Messwert-Timer-Intervall oder auf
|
167 | // die Fertigstellung der Messung. Beide States werden
|
168 | // in den Interrupt Handlern gesetzt
|
169 |
|
170 | break;
|
171 | }
|
172 | /**************************************************/
|
173 | case MESSEN_FERTIG: //Messwert fertig gemessen
|
174 | {
|
175 |
|
176 | mess_0 = ADCL;
|
177 | mess_hilf = ADCH;
|
178 | mess_hilf = mess_hilf * 256;
|
179 | mess_0 = mess_0 + mess_hilf;
|
180 | messwert_filtern();
|
181 | if (ergebnis_0 > ergebnis_4)
|
182 | {
|
183 | wirdsHeller = wirdsHeller + 1;
|
184 | if (wirdsHeller == 4) //einige Male in Folge heller geworden?
|
185 | { //-> Ein schwarzer Streifen ist vorbeigezogen. Zählen!
|
186 | schwarze_streifen = schwarze_streifen + 1;
|
187 | }
|
188 | if (wirdsHeller > 5)
|
189 | {
|
190 | wirdsHeller = 5;
|
191 | }
|
192 |
|
193 | }
|
194 | if (ergebnis_0 < ergebnis_4)
|
195 | { // Es wird dunkler
|
196 | wirdsHeller = 0;
|
197 | }
|
198 | if (schwarze_streifen >= ANZAHL_STREIFEN)
|
199 | {
|
200 | communication = PAUSE_EINLAEUTEN;
|
201 | }
|
202 | else communication = WARTEN_AUF_MESSUNG;
|
203 |
|
204 | break;
|
205 | }
|
206 | /**************************************************/
|
207 | case PAUSE_EINLAEUTEN:
|
208 | {
|
209 | // Motor stoppen
|
210 | motor_aus();
|
211 | // LED ausschalten
|
212 | PORTB &= ~(1 << PB4); // LED für den Sensor ausschalten
|
213 | // Pause machen bis zum nächsten Mal
|
214 | communication = PAUSE;
|
215 | // Timer 1 umstellen auf Pausenmodus
|
216 | timer1_einstellen(PAUSE);
|
217 | communication = PAUSE;
|
218 | break;
|
219 | }
|
220 | /**************************************************/
|
221 | default : {
|
222 | communication = PAUSE_EINLAEUTEN;
|
223 | // Im Fehlerfall lieber den Motor stoppen und in sichere States gehen
|
224 | break;
|
225 | }
|
226 | }
|
227 | }
|
228 | /***********************************************************************/
|
229 | return 0;
|
230 | }
|
231 |
|
232 | /*************************************************************
|
233 | Ausgelagerte ADC-Einstellen-Funktion für die Übersichtlichkeit
|
234 | *************************************************************/
|
235 | void adc_einstellen(void)
|
236 | {
|
237 | /*
|
238 | #define ADMUX _SFR_IO8(0x07)
|
239 | #define REFS1 7
|
240 | #define REFS0 6
|
241 | #define ADLAR 5
|
242 | #define REFS2 4
|
243 | #define MUX3 3
|
244 | #define MUX2 2
|
245 | #define MUX1 1
|
246 | #define MUX0 0
|
247 | */
|
248 | ADMUX = 0xE1; //Interne 1.1V Referenz, ADLAR = 1 für linksbündige Ergebnisse, Pin PB2 (ADC1D) als ADC-Eingang
|
249 |
|
250 | /*
|
251 | ADCSRB
|
252 | Bit 7: Bei 1 wird bipolar gemessen. 0.
|
253 | Bit 5: Bei 1 wird der Eingang negativ gemessen. 0.
|
254 | Bits 2:0 : Auto Trigger Source. Brauche ich nicht, weil ADATE = 0 ist.
|
255 | */
|
256 | ADCSRB = 0x00;
|
257 | // ADC1 Pin als Eingang schalten
|
258 | DIDR0 |= (1<<ADC1D); // Digital Input Disable Register; Bit 5 auf 1, damit der AD-Pin nicht digital verwendet wird.
|
259 | DDRB &= ~(1<<ADC1D); //Pin ADC1D als Eingang
|
260 | /*
|
261 | ADCSRA
|
262 | Bit 7, ADEN: ADC Enable. Auf 1.
|
263 | Bit 6, ADSC: ADC Start Converstion. Auf 1 zum Initialisieren vom AD
|
264 | Bit 5, ADATE: ADC Auto trigger enable: Bei bestimmten Ereignissen automatisch starten. 0 für Aus.
|
265 | Bit 4, ADIF: ADC Interrupt Flag, wird automatisch beschrieben wenn der ADC fertig ist
|
266 | Bit 3, ADIE: ADC Interrupt Enable, auf 1 wenn ein Interrupt nach der Wandlung ausgeführt werden soll. 1.
|
267 | Bit 2:0, ADPS2:0, ADC Prescaler Bits für System Clock. 110 für 8MHz geteilt durch 64 = 125kHz
|
268 | */
|
269 | ADCSRA = 0xCE; //führt auch gleich die erste Konversion für die Initialisierung mit durch
|
270 | }
|
271 |
|
272 | /*************************************************************
|
273 | Ausgelagerte Motor-Steuern-Funktion für die Übersichtlichkeit
|
274 | Benötigt OCR0A als PWM Ausgang vor-eingestellt
|
275 | *************************************************************/
|
276 |
|
277 | void motor_ein(void)
|
278 | {
|
279 | PORTB |= (1 << PB0); //Eins auf den Motorpin
|
280 | }
|
281 | void motor_aus(void)
|
282 | {
|
283 | PORTB &= ~(1 << PB0);//Null auf den Motorpin
|
284 | }
|
285 |
|
286 | /*************************************************************
|
287 | Ausgelagerte Pausentimer-Einstellen-Funktion für die Übersichtlichkeit
|
288 | Der Pausentimer soll messen, wann die Pause vorbei ist.
|
289 | Er soll außerdem den ADC Takt vorgeben wenn keine Pause ist.
|
290 | Die Funktion soll nur aufgerufen werden um den Timer-Modus umzustellen!
|
291 | *************************************************************/
|
292 | void timer1_einstellen(unsigned char state)
|
293 | {
|
294 | if (state == PAUSE)
|
295 | {
|
296 | GTCCR = 0x80; // Stoppen vom Timer via setzen von 1 im TSM Bit
|
297 | TCCR1 = 0x8F; // Clear on compare match mit OCR1C; Sysclock/16384 ist die Timer Clock (488 Hz)
|
298 | OCR1A = 0x0A; // 0x0A = 10; an dieser Zählmarke wird der OCR1A-Interrupt aktiviert.
|
299 | OCR1C = 244; // Nach 244 Zählern wird der Timer rückgesetzt -> 2 Interrupts / sec
|
300 | GTCCR = 0x00; // kein PWM B, kein Output force, kein löschen von TCCR1, 0 ins TSM Bit -> restart
|
301 | }
|
302 | else
|
303 | {
|
304 | GTCCR = 0x80; // Stoppen vom Timer via setzen von 1 im TSM Bit
|
305 | TCCR1 = 0x8A; // Clear on compare match mit OCR1C; Sysclock/512 ist die Timer Clock (15625 Hz)
|
306 | OCR1A = 0x0A; // 0x0A = 10; an dieser Zählmarke wird der OCR1A-Interrupt aktiviert.
|
307 | OCR1C = 31; // Nach 31 Zählern wird der Timer rückgesetzt -> 504 Interrupts / sec
|
308 | GTCCR = 0x00; // kein PWM B, kein Output force, kein löschen von TCCR1, 0 ins TSM Bit -> restart
|
309 | }
|
310 | }
|
311 |
|
312 |
|
313 | /****************************************************************************
|
314 | Messwerte filtern
|
315 |
|
316 | Benötigt:
|
317 | - 5 Ergebnisvariablen mit uint32_t
|
318 | - 5 Messwertvariablen mit uint32_t
|
319 | - Messwerte 16 Bit breit linksbündig (sorry)
|
320 | - Abtastfrequenz 500Hz
|
321 | - Filtervariablen 32 Bit breit
|
322 |
|
323 | Die Filterkoeffizienten wurden mit dem Excel aus dem Projektordner bestimmt.
|
324 | Es handelt sich um ein IIR Filter mit einer Grenzfrequenz von 12,5Hz
|
325 | und 4 Filterstufen nach Butterworth
|
326 |
|
327 | *****************************************************************************/
|
328 | void messwert_filtern(void)
|
329 | {
|
330 |
|
331 | A = 12 * mess_0; //TAP 0
|
332 | A = A + 50 * mess_1; //TAP 1
|
333 | A = A + 75 * mess_2; //TAP 2
|
334 | A = A + 50 * mess_3; //TAP 3
|
335 | A = A + 12 * mess_4; //TAP 4
|
336 |
|
337 | A = A + 50; // Faktor entfernen I: die Hälfte vom Faktor drauflegen gegen Rundungsfehler
|
338 | A = (int)(A/100); //Faktor entfernen II: Teilen und abrunden
|
339 |
|
340 | B = 14359 * ergebnis_1; //TAP 1
|
341 | B = B - 19405 * ergebnis_2; //TAP 2
|
342 | B = B + 11696 * ergebnis_3; //TAP 3
|
343 | B = B - 2652 * ergebnis_4; //TAP 4
|
344 |
|
345 | ergebnis_0 = A + B + 2000;// Faktor entfernen I: die Hälfte vom Faktor drauflegen gegen Rundungsfehler
|
346 | ergebnis_0 = (int)(ergebnis_0 / 4000); //Faktor entfernen II: Teilen und abrunden
|
347 |
|
348 | //Messwerte durch die TAPs schieben
|
349 | mess_4 = mess_3;
|
350 | mess_3 = mess_2;
|
351 | mess_2 = mess_1;
|
352 | mess_1 = mess_0; //mess0 wird vom ADC gefüllt
|
353 |
|
354 | //Ergebnisse durch die TAPs schieben
|
355 | ergebnis_4 = ergebnis_3;
|
356 | ergebnis_3 = ergebnis_2;
|
357 | ergebnis_2 = ergebnis_1;
|
358 | ergebnis_1 = ergebnis_0; //ergebnis_0 wird weiter oben berechnet.
|
359 | }
|