Source file: /~heha/basteln/Haus/Lüfter/luft3.zip/main.cpp

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