1 /*
2 Badlüftersteuerung mit 2 Geschwindigkeiten
3 via Frequenzdrittelung am Triac
4
5 Hardware: ATtiny13
6 Pin Port Funk.
7 1 PB5 !RESET (Triac für Trafo Spiegelheizung)
8 2 PB3 Lichtschalter, 50 Hz low-aktiv
9 3 PB4 Zwangseinschaltung, 50 Hz low-aktiv
10 4 GND
11 5 PB0 OC0A Triac-Gate, low-aktiv
12 6 PB1 INT0 Netzfrequenz und -Phase
13 7 PB2 ADC1 Temperatursensor an Warmwasserleitung
14 8 Ucc
15
16 Funktion:
17 Vorbild: Übliche Lüftersteuerung.
18
19 Einsatzerfahrung:
20 */
21
22 #include <avr/io.h>
23 #include <avr/interrupt.h>
24 #include <avr/sleep.h>
25 #include <avr/fuse.h>
26 #include <avr/signature.h> // gepatcht: "const" weg!
27
28 #define GATELEN 8 // Länge Gateimpuls in 125 µs: 8 = 1 ms
29 #define ONDELAY 50 // Einschaltverzögerung in 20 ms: 50 = 1 Sekunde
30 #define OFFDELAY 50 // Ausschaltverzögerung in 20 ms: 50 = 1 Sekunde
31 #define BLOWIGN 12
32 #define BLOWMIN 12 // Ausblaszeit nach Licht in 5-s-Schritten
33 #define BLOWMAX 60
34 #define TEMPON 128 // aktiviert temperaturbedingte volle Drehzahl
35 #define TEMPOFF 126 // mit Hysterese
36
37 FUSES={
38 0x7B, // Stromspar-Oszillator 128 kHz ohne Taktteiler
39 0xF9, // Brown-Out unter 4,3 V
40 };
41
42 typedef unsigned char byte;
43 #define NOINIT __attribute__((section(".noinit")))
44
45 register byte t0h asm("r2"); // High-Teil des Zählers
46 register byte t180 asm("r3"); // Zeit für 180° Phasenwinkel
47 register byte light asm("r4"); // Zeitzähler für Licht-Ein in 5 s
48 register byte dlyct asm("r16"); // Verzögerung in 5 s, max. 22 Minuten
49 register byte flags asm("r17"); // diverse Bits:
50 // Bit 1:0: Phasenzykluszähler 0..2
51 // Bit 2: Eingeschaltet auf 1/3 Drehzahl (Lichtschalter)
52 // Bit 3: Eingeschaltet auf voller Drehzahl (Licht aus)
53 // Bit 4: Eingeschaltet auf voller Drehzahl (Zwangsschalter)
54 // Bit 5: Eingeschaltet auf voller Drehzahl (Temperatur von Dusche)
55 // Bit 6: Vorheriger Zustand des Lichtschalters
56
57 // Der Zähler darf nie überlaufen, sonst fehlen Nulldurchgänge
58 ISR(TIM0_OVF_vect,ISR_NAKED) {
59 flags =0; // alles aus
60 TIMSK0=0;
61 dlyct =0;
62 light =0;
63 reti();
64 }
65
66 static void trig(byte p) {
67 byte n=flags&0x07;
68 if (flags&0x38 || n==p) {
69 TCCR0A=0x80; // OC0A bei Compare-Match löschen
70 TCCR0B=0x82; // Jetzt löschen (Triac bekommt Gatestrom)
71 TCCR0A=0xC0; // OC0A bei Compare-Match setzen lassen
72 }
73 n=~flags&0x60;
74 if (!n) {
75 PORTB&=~0x20;
76 TIFR0 =0x04;
77 TIMSK0=0x0E;
78 }
79 }
80
81 // Phase wird negativ (etwas nach dem Nulldurchgang wegen Spannungsteiler)
82 // Aufruf mit 50 Hz (INT0 High-Low-Flanke)
83 ISR(INT0_vect,ISR_NAKED) {
84 byte t=TCNT0; // Zählerwert abholen
85 if (TIMSK0&0x02) { // Erster Nulldurchgang entdeckt? (Länge gültig?)
86 if (t>=128) { // muss zwischen 128 und 255 liegen, sonst Störimpuls
87 TCNT0=0; // Zähler starten
88 t180=t>>1; // Spiegelbild: Phase positiv
89 OCR0B=t180>>1; // 90°
90 TIFR0 =0x0A; // Interrupts quittieren
91 TIMSK0=0x0A; // T0-Vergleich-B-Interrupt aktivieren
92 flags++;
93 t=~flags&3;
94 if (!t) flags&=0xFC;
95 trig(0x04);
96 OCR0A=GATELEN; // später
97 if (dlyct && !--dlyct) {
98 if (flags&0x40) flags|=0x04; // 1/3 ein
99 else{
100 if (flags&0x08) {
101 light =0; // Zähler tilgen
102 }else{
103 if (light>=BLOWIGN) {
104 flags|=0x08;
105 if (light<BLOWMIN) light=BLOWMIN;
106 }else{
107 flags&=~0x04; // aus
108 light =0; // nicht akkumulieren (wäre verwirrend)
109 }
110 }
111 }
112 }
113 if (!++t0h) { // Überlauf alle ≈5 Sekunden
114 ADCSRA=0xD8; // A/D-Wandler starten (mit Interrupt)
115 if (flags&0x40 && light<BLOWMAX) ++light; // Einschaltzeit messen
116 if (flags&0x08 && !--light) flags&=~0x0C; // Lüfter aus
117 }
118 }
119 }else{
120 TCNT0=0; // Zähler starten
121 TIFR0 =0x02; // Vorherige T0-Überläufe quittieren
122 TIMSK0=0x02; // T0-Überlauf-Interrupt aktivieren
123 }
124 GIFR=0x40; // Schwarm-Interrupts tilgen
125 reti();
126 }
127
128 // Software-Ausschaltflanke für Spiegelheizungs-Triac (an !RESET)
129 // Aufruf mit 100 Hz
130 ISR(TIM0_COMPA_vect,ISR_NAKED) {
131 PORTB|=0x20;
132 TIMSK0=0x0A;
133 reti();
134 }
135
136 // 1. Abtastzeitpunkt für Tasten bei 90°
137 // 2. Phase wird (= ist jetzt sicher) positiv bei 180°
138 // Aufruf mit 2×50 Hz
139 ISR(TIM0_COMPB_vect,ISR_NAKED) {
140 if (OCR0B<64) { // im Bereich 32..63
141 DIDR0=0x27; // Digitaleingänge aktivieren
142 OCR0B=t180;
143 asm("rjmp ."); // 2 Takte warten
144 if (PINB&0x08) { // Licht aus?
145 if (flags&0x40) {
146 flags&=~0x40;
147 if (flags&0x04) {
148 dlyct=OFFDELAY; // zum Ausschalten
149 }else{
150 dlyct=0; // Zeitgeber anhalten
151 }
152 }
153 }else{ // Licht an?
154 if (!(flags&0x40)) {
155 flags|= 0x40;
156 flags&=~0x08; // Auf 1/3 zurück
157 dlyct=ONDELAY;
158 }
159 }
160 if (PINB&0x10) { // Zwang aus?
161 flags&=~0x10;
162 }else{ // Zwang ein
163 flags|= 0x10;
164 }
165 }else{ // im Bereich 64..127
166 // DIDR0=0x3D;
167 // asm("rjmp ."); // 2 Takte warten
168 // if (PINB&0x02) { // muss high sein, sonst Problem (induktive Last — oder der Trenntrafo?)
169 trig(0x05);
170 OCR0A=OCR0B+GATELEN; // später
171 // }else{
172 // flags =0; // alles aus
173 // TIMSK0=0;
174 // dlyct =0;
175 // light =0;
176 // }
177 }
178 DIDR0=0x3F; // Eingänge wieder deaktivieren
179 reti();
180 }
181
182 // Aufruf alle 5 Sekunden
183 ISR(ADC_vect,ISR_NAKED) {
184 byte adc=ADCH; // Analogwert abholen
185 ADCSRA=0; // A/D-Wandler stoppen
186 if (flags&0x20) {
187 if (adc<TEMPOFF) flags&=~0x20;
188 }else{
189 if (adc>=TEMPON) flags|=0x20; // Heißleiter zieht Eingang hoch
190 }
191 reti();
192 }
193
194 int main() {
195 CLKPR =0x80;
196 CLKPR =0x01; // 128 kHz / 2 = 64 kHz (genau richtig)
197 PORTB =0x19; // Pullups für Eingänge
198 TCCR0A=0xC0; // OC0A bei Compare-Match setzen
199 TCCR0B=0x82; // loszählen mit Vorteiler 8, Periode = 31 Hz, Compare-Match auslösen
200 DDRB =0x21; // Triac-Gate = Ausgang
201 MCUCR =0x22; // Sleep aktivieren, INT0-Interrupt bei fallender Flanke
202 GIMSK =0x40; // INT0-Interrupt aktivieren
203 DIDR0 =0x3F; // Alle Eingänge aus
204 ACSR =0x80; // Analogvergleicher aus
205 ADMUX =0x21; // 8 Bit, ADC1 = PB2, ratiometrisch bzgl. Ucc = 5 V
206 asm(
207 " clr r4 \n"
208 " clr r16 \n"
209 " clr r17 \n"
210 );
211 sei(); // zunächst ist INT0 die einzige Interruptquelle
212 for(;;) {
213 sleep_cpu(); // der Rest ist ISR-gesteuert da wenig rechenaufwändig
214 }
215 }
216
Detected encoding: UTF-8 | 0
|