1 | /************************************************************
|
2 | *
|
3 | * Einfacher Reaktivlicht-Blinker mit LDR
|
4 | * Stromverbrauch:
|
5 | * ca. 4,1µA @ 3V im Tag- und im Nachtmodus
|
6 | *
|
7 | ************************************************************/
|
8 |
|
9 | /*
|
10 | ATtiny13
|
11 |
|
12 | SELFPRGEN=0
|
13 | DWEN=0
|
14 | BODLEVEL=Brown-out detection disabled
|
15 | RSTDISBL=0
|
16 | SPIEN=1
|
17 | EESAVE=0
|
18 | WDTON=0
|
19 | CHKDIV8=1
|
20 | SUT_CKSEL=Int. RC Osc. 9.6 MHz; Start-up time: 14 CK + 64 ms
|
21 |
|
22 | Low/High = 0x6A,0xFF
|
23 |
|
24 | */
|
25 |
|
26 | /*
|
27 | Vers. 1.01 vom 24.06.2009
|
28 | - Urversion
|
29 |
|
30 | Vers. 1.02 vom 15.07.2009
|
31 | - Verbesserte Helligkeitswerte; Timing bei Tag- und Nachtbetrieb unterschiedlich
|
32 |
|
33 | Vers. 1.03 vom 19.07.2009
|
34 | - Testmodus, Logikanalysator-Ausgabe, Dauerblinken
|
35 |
|
36 | Vers. 1.04 vom 25.02.2010
|
37 | - Timing leicht geändert; Umschaltung zwischen Reaktivlicht-Typen nach LED-Farbe
|
38 |
|
39 | Vers. 1.05 vom 26.05.2010
|
40 | - Fehlerkorrektur in WDT_Prescaler_Change
|
41 | - andere Helligkeitsbewertung (heller = höherer Wert)
|
42 | - diverse Verbesserungen, Vereinfachungen und Bereinigungen
|
43 |
|
44 | */
|
45 |
|
46 |
|
47 | #define F_CPU 600000UL // Taktfrequenz 600 kHz
|
48 |
|
49 | #include <avr/io.h>
|
50 | #include <avr/interrupt.h>
|
51 | #include <avr/sleep.h>
|
52 | #include <avr/eeprom.h>
|
53 | #include <avr/wdt.h>
|
54 | //#include <avr/pgmspace.h>
|
55 | #include <util/delay.h>
|
56 | //#include <stdint.h>
|
57 | //#include <string.h>
|
58 |
|
59 | // Systemkonstanten
|
60 |
|
61 | // System Clock Prescaler [CLKPR]
|
62 |
|
63 | #define CLKDIV1 0
|
64 | #define CLKDIV2 1
|
65 | #define CLKDIV4 2
|
66 | #define CLKDIV8 3
|
67 | #define CLKDIV16 4
|
68 | #define CLKDIV32 5
|
69 | #define CLKDIV64 6
|
70 | #define CLKDIV128 7
|
71 | #define CLKDIV256 8
|
72 |
|
73 | // ADC-Referenz [REFS0]
|
74 |
|
75 | #define RefVcc 0x0 // Vcc als Referenz
|
76 | #define Ref11 0x1 // interne Referenz 1,1V
|
77 |
|
78 | // ADC-Messmodus [MUX0]
|
79 |
|
80 | #define ADC_PB5 0
|
81 | #define ADC_PB2 1
|
82 | #define ADC_PB4 2
|
83 | #define ADC_PB3 3
|
84 |
|
85 | // ADC-Clock-Vorteiler (für ADC-Clock zw. 50kHz und 200kHz) [ADPS0]
|
86 |
|
87 | #define ADC_Clk2 1
|
88 | #define ADC_Clk4 2
|
89 | #define ADC_Clk8 3
|
90 | #define ADC_Clk16 4
|
91 | #define ADC_Clk32 5
|
92 | #define ADC_Clk64 6
|
93 | #define ADC_Clk128 7
|
94 |
|
95 | // ADC Autotrigger Source [ADTS0]
|
96 |
|
97 | #define ADC_FreeRunning 0 // freilaufend
|
98 | #define ADC_AnalogComp 1 // Analog Comparator
|
99 | #define ADC_ExtIRQ0 2 // External Interrupt Request 0
|
100 | #define ADC_CompareA 3 // Timer/Counter Compare Match A
|
101 | #define ADC_TimerOvr 4 // Timer/Counter Overflow
|
102 | #define ADC_CompareB 5 // Timer/Counter Compare Match B
|
103 | #define ADC_PinChange 6 // Pin Change Interrupt Request
|
104 |
|
105 | // Timer Clock Divider [CS00]
|
106 |
|
107 | #define Tim0CLKoff 0
|
108 | #define Tim0CLK1 1
|
109 | #define Tim0CLK8 2
|
110 | #define Tim0CLK64 3
|
111 | #define Tim0CLK256 4
|
112 | #define Tim0CLK1024 5
|
113 | #define Tim0CLKExtHL 6
|
114 | #define Tim0CLKExtLH 7
|
115 |
|
116 | // Timer Clock Divider [CS10]
|
117 |
|
118 | #define Tim1CLKoff 0
|
119 | #define Tim1CLK1 1
|
120 | #define Tim1CLK2 2
|
121 | #define Tim1CLK4 3
|
122 | #define Tim1CLK8 4
|
123 | #define Tim1CLK16 5
|
124 | #define Tim1CLK32 6
|
125 | #define Tim1CLK64 7
|
126 | #define Tim1CLK128 8
|
127 | #define Tim1CLK256 9
|
128 | #define Tim1CLK512 10
|
129 | #define Tim1CLK1024 11
|
130 | #define Tim1CLK2048 12
|
131 | #define Tim1CLK4096 13
|
132 | #define Tim1CLK8192 14
|
133 | #define Tim1CLK16384 15
|
134 |
|
135 |
|
136 | #define WPD_16ms 0b00000000 // 16 ms
|
137 | #define WPD_32ms 0b00000001 // 32 ms
|
138 | #define WPD_64ms 0b00000010 // 64 ms
|
139 | #define WPD_125ms 0b00000011 // 0.125 s
|
140 | #define WPD_250ms 0b00000100 // 0.25 s
|
141 | #define WPD_500ms 0b00000101 // 0.5 s
|
142 | #define WPD_1s 0b00000110 // 1.0 s
|
143 | #define WPD_2s 0b00000111 // 2.0 s
|
144 | #define WPD_4s 0b00100000 // 4.0 s
|
145 | #define WPD_8s 0b00100001 // 8.0 s
|
146 |
|
147 | // Definitionen
|
148 |
|
149 | #define LED_TEST 0
|
150 | #define LED_ROT 1
|
151 | #define LED_GRUEN 2
|
152 | #define LED_WEISS 3
|
153 |
|
154 | // Hier LED-Typ (nach Farbe) wählen !!!
|
155 |
|
156 | //#define LEDTYP LED_TEST
|
157 | #define LEDTYP LED_ROT
|
158 | //#define LEDTYP LED_GRUEN
|
159 |
|
160 | //#define DataOut // serielle Datenausgabe zum Logikanalysator
|
161 | //#define Dauerblinken // ständiges Blinken
|
162 |
|
163 | #define Blinkwartezeit 300 // Wartezeit (ms) bevor das Blinken beginnt
|
164 | #define Blinkdauer 60 // Dauer (ms) eines Blinkimpulses
|
165 | #define Blinkpause (500-Blinkdauer) // Pause (ms) zwischen zwei Blinkimpulsen
|
166 |
|
167 | #if LEDTYP == LED_TEST
|
168 | #define Blinkzahl 1 // Anzahl der Blinkimpulse
|
169 | #elif LEDTYP == LED_ROT
|
170 | #define Blinkzahl 5 // Anzahl der Blinkimpulse
|
171 | #elif LEDTYP == LED_GRUEN
|
172 | #define Blinkzahl 6 // Anzahl der Blinkimpulse
|
173 | #endif
|
174 |
|
175 | // Grenze zwischen Tag- und Nachtumschaltung:
|
176 | // 1 Lux (R = 1,4 MOhm), ergibt einen ADC-Wert von ca. 154
|
177 |
|
178 | #define HellDiff 10 // Auslöseschwelle
|
179 | #define TagSchwelle 154 // Grenze für Umschaltung zwischen Tag und Nacht
|
180 |
|
181 | #define NachtRate WPD_250ms // Messrate im Nachtmodus
|
182 | #define NachtRateZt 250 // in ms
|
183 |
|
184 | #define TagLimit (5UL * 60000UL / NachtRateZt) // 5 Minuten hell = Tag
|
185 | #define NachtLimit 8 // 1 Minute dunkel = Nacht (8 * 8s)
|
186 |
|
187 | // Portleitungen
|
188 |
|
189 | #define LED PB3
|
190 | #define LDR PB4 // ADC2
|
191 | #define LDRSpg PB0
|
192 | #define Licht PB1
|
193 |
|
194 | #define CLKHI PORTB |= (1<<0)
|
195 | #define CLKLO PORTB &= ~(1<<0)
|
196 | #define DATAHI PORTB |= (1<<1)
|
197 | #define DATALO PORTB &= ~(1<<1)
|
198 | #define SYNC_ON PORTB |= (1<<2)
|
199 | #define SYNC_OFF PORTB &= ~(1<<2)
|
200 |
|
201 | #define LED_ON PORTB |= (1<<LED)
|
202 | #define LED_OFF PORTB &= ~(1<<LED)
|
203 |
|
204 | #define LDR_ON PORTB |= (1<<LDRSpg)
|
205 | #define LDR_OFF PORTB &= ~(1<<LDRSpg)
|
206 |
|
207 | #define LichtON PORTB |= (1<<Licht)
|
208 | #define LichtOFF PORTB &= ~(1<<Licht)
|
209 |
|
210 | // globale Variablen
|
211 |
|
212 | unsigned int Helligkeit;
|
213 | unsigned int Hintergrund;
|
214 | unsigned char Tagmodus;
|
215 |
|
216 | /******************************************
|
217 | * *
|
218 | * Auslösung erkannt, Aktion durchführen *
|
219 | * *
|
220 | ******************************************/
|
221 |
|
222 | void ExecuteAction (const unsigned char Anzahl)
|
223 | {
|
224 | unsigned char i;
|
225 |
|
226 | _delay_ms(Blinkwartezeit);
|
227 |
|
228 | for (i=0; i<Anzahl; i++)
|
229 | {
|
230 | LED_ON;
|
231 | _delay_ms(Blinkdauer);
|
232 | LED_OFF;
|
233 | _delay_ms(Blinkpause);
|
234 | }
|
235 | }
|
236 |
|
237 | /******************************
|
238 | * *
|
239 | * Ein Word seriell ausgeben *
|
240 | * (für Logikanalysator) *
|
241 | * *
|
242 | ******************************/
|
243 |
|
244 | void SerOut (unsigned int Wert)
|
245 | {
|
246 | unsigned char i;
|
247 |
|
248 | for (i=0; i<16; i++)
|
249 | {
|
250 | if (Wert & 0x8000)
|
251 | DATAHI;
|
252 | else
|
253 | DATALO;
|
254 |
|
255 | CLKHI;
|
256 | Wert = Wert << 1;
|
257 | CLKLO;
|
258 | }
|
259 | }
|
260 |
|
261 | /*****************************
|
262 | * *
|
263 | * Testwerte seriell an *
|
264 | * Logikanalysator ausgeben *
|
265 | * *
|
266 | *****************************/
|
267 |
|
268 | void WertOut (void)
|
269 | {
|
270 | CLKLO;
|
271 | SerOut(Helligkeit);
|
272 | SerOut(Hintergrund);
|
273 | SerOut(Tagmodus);
|
274 | DATALO;
|
275 | }
|
276 |
|
277 | /*************************
|
278 | * *
|
279 | * Watchdog-Zeit ändern *
|
280 | * *
|
281 | *************************/
|
282 |
|
283 | void WDT_Prescaler_Change (unsigned char Value)
|
284 | {
|
285 | cli();
|
286 | wdt_reset();
|
287 | WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
|
288 | WDTCR = (0<<WDE) | (1<<WDTIE) | (Value<<WDP0); // Set new prescaler(time-out) value
|
289 | sei();
|
290 | }
|
291 |
|
292 | /******************
|
293 | * *
|
294 | * Hauptprogramm *
|
295 | * *
|
296 | ******************/
|
297 |
|
298 | int main(void)
|
299 | {
|
300 | unsigned int TagCtr;
|
301 | unsigned int AuslGrw;
|
302 | #if LEDTYP == LED_TEST
|
303 | unsigned char Ausloes;
|
304 | #endif
|
305 |
|
306 | // Clock einstellen
|
307 |
|
308 | CLKPR = 0x80; // CLKPCE setzen, um Zugriff auf Clock Prescaler zu erreichen
|
309 | CLKPR = CLKDIV16; // Clock Prescaler auf /16 stellen (9,6 MHz -> 600 kHz)
|
310 |
|
311 | // Watchdog einstellen
|
312 |
|
313 | WDTCR = (1<<WDCE) | (1<<WDE); // auf Einstellung vorbereiten
|
314 | WDTCR = (0<<WDCE) | (0<<WDE) | (1<<WDTIE) | (WPD_8s << WDP0); // Interrupt mode, Zeit = 8s
|
315 | WDTCR |= (1<<WDTIF); // Interruptflag löschen
|
316 |
|
317 | // Ports einstellen
|
318 |
|
319 | PORTB = 0b100000;
|
320 | DDRB = 0b001111;
|
321 | // |||||+- 0[5]:MOSI/AIN0/OC0A/PCINT0/TXD LDR-Versorgung (DataOut-Clock)
|
322 | // ||||+-- 1[6]:MISO/AIN1/OC0B/INT0/PCINT1/RXD Licht-Anzeige (DataOut-Data)
|
323 | // |||+--- 2[7]:SCK/ADC1/T0/PCINT2 (DataOut-Sync)
|
324 | // ||+---- 3[2]:ADC3/CLKI/PCINT3 LED (über 100 Ohm gegen GND)
|
325 | // |+----- 4[3]:ADC2/PCINT4 LDR (gegen GND, 2,2 MOhm an Vcc)
|
326 | // +------ 5[1]:RESET/dW/ADC0/PCINT5
|
327 |
|
328 | DIDR0 = (1<<ADC2D);
|
329 |
|
330 | // Timer 0 einstellen
|
331 |
|
332 | // ADC einstellen
|
333 |
|
334 | ADMUX = (RefVcc<<REFS0) | (1<<ADLAR) | (ADC_PB4<<MUX0); // Referenz = Vcc
|
335 |
|
336 | // Variablen einstellen
|
337 |
|
338 | TagCtr = 0;
|
339 | Helligkeit = 255;
|
340 | Hintergrund = 255;
|
341 | #if LEDTYP == LED_TEST
|
342 | Ausloes = 0;
|
343 | #endif
|
344 | Tagmodus = 1; // im Tagmodus starten
|
345 |
|
346 | // Hauptprogramm starten
|
347 |
|
348 | sei(); // Interrupts ermöglichen
|
349 |
|
350 | ExecuteAction(Blinkzahl); // beim Start zum Test
|
351 |
|
352 | while(1)
|
353 | {
|
354 | #ifdef Dauerblinken
|
355 | ExecuteAction(Blinkzahl); // dauernd Aktion durchführen
|
356 | #else
|
357 | // ADC abfragen (Tags alle 8s, Nachts alle <NachtRateZt> ms)
|
358 |
|
359 | set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
360 | sleep_mode(); // durch Watchdog-Timer aufwecken lassen
|
361 |
|
362 | LDR_ON;
|
363 |
|
364 | #ifdef DataOut
|
365 | SYNC_ON; // Synchronsignal
|
366 | #endif
|
367 |
|
368 | ADCSRA = (1<<ADEN) | (1<<ADIE) | (ADC_Clk2<<ADPS0); // ADC einschalten, ADC-Interrupt
|
369 | ADCSRA |= (1<<ADSC); // ADC starten
|
370 |
|
371 | set_sleep_mode(SLEEP_MODE_ADC);
|
372 | sleep_mode(); // durch ADC aufwecken lassen
|
373 |
|
374 | LDR_OFF;
|
375 |
|
376 | Helligkeit = 255 - ADCH; // 0 = dunkel, 255 = hell
|
377 | ADCSRA = 0; // ADC ausschalten
|
378 |
|
379 | // gemessenen Helligkeitswert auswerten
|
380 |
|
381 | if (Tagmodus) // aktuell im Tagmodus
|
382 | {
|
383 | if (Helligkeit < TagSchwelle) // prüfen ob es Nacht ist
|
384 | {
|
385 | #ifndef DataOut
|
386 | LichtOFF;
|
387 | #endif
|
388 |
|
389 | TagCtr++;
|
390 |
|
391 | if (TagCtr >= NachtLimit)
|
392 | {
|
393 | Tagmodus = 0; // auf Nacht umschalten
|
394 | TagCtr = 0;
|
395 |
|
396 | WDT_Prescaler_Change(NachtRate);
|
397 | }
|
398 | }
|
399 | else
|
400 | {
|
401 | #ifndef DataOut
|
402 | LichtON;
|
403 | #endif
|
404 |
|
405 | TagCtr = 0;
|
406 | }
|
407 | }
|
408 | else // aktuell im Nachtmodus
|
409 | {
|
410 | AuslGrw = Hintergrund + HellDiff; // zu erreichende Schwelle
|
411 |
|
412 | if (Helligkeit > AuslGrw) // Auslösung durch Helligkeitssprung?
|
413 | {
|
414 | #if LEDTYP == LED_TEST
|
415 | ExecuteAction(++Ausloes); // ja, Aktion durchführen
|
416 | #else
|
417 | ExecuteAction(Blinkzahl); // ja, Aktion durchführen
|
418 | #endif
|
419 | Helligkeit = 255;
|
420 | }
|
421 | else
|
422 | {
|
423 | if (Helligkeit > TagSchwelle) // prüfen ob es Tag ist
|
424 | {
|
425 | #ifndef DataOut
|
426 | LichtON;
|
427 | #endif
|
428 |
|
429 | TagCtr++;
|
430 |
|
431 | if (TagCtr >= TagLimit)
|
432 | {
|
433 | Tagmodus = 1; // auf Tag umschalten
|
434 | TagCtr = 0;
|
435 |
|
436 | WDT_Prescaler_Change(WPD_8s);
|
437 | }
|
438 | }
|
439 | else
|
440 | {
|
441 | #ifndef DataOut
|
442 | LichtOFF;
|
443 | #endif
|
444 |
|
445 | TagCtr = 0;
|
446 | }
|
447 | }
|
448 | }
|
449 |
|
450 | #ifdef DataOut
|
451 | SYNC_OFF;
|
452 | WertOut();
|
453 | #endif
|
454 |
|
455 | Hintergrund = Helligkeit; // letzten Helligkeitswert als Hintergrundhelligkeit
|
456 | #endif
|
457 | }
|
458 | }
|
459 |
|
460 | /*****************************
|
461 | * *
|
462 | * Watchdog Timer Interrupt *
|
463 | * (nur zum Aufwecken) *
|
464 | * *
|
465 | *****************************/
|
466 |
|
467 | EMPTY_INTERRUPT(WDT_vect);
|
468 |
|
469 | /****************************
|
470 | * *
|
471 | * ADC conversion complete *
|
472 | * (nur zum Aufwecken) *
|
473 | * *
|
474 | ****************************/
|
475 |
|
476 | EMPTY_INTERRUPT(ADC_vect);
|