Source file: /~heha/basteln/Haus/Licht/vf_rel/fb.zip/fbe/main.cpp

/*
Licht-Funk-Verlängerung für Struppen und Dresden: Empfänger
Stromversorgung per Kondensatornetzteil: Schaltung netzverbunden!

Hardware: ATtiny13
Pin	Port	Funktion
1	PB5	!RESET	-
2	PB3		Debug
3	PB4		Lerntaste (Jumper), low-aktiv, ungenutzt
4	GND
5	PB0		LED, low-aktiv
6	PB1	PCINT1	Funkempfänger	
7	PB2		Relais via Depletion-MOSFET: H = aus, L = ein
8	Ucc

Funktion:
Vorbild: Pollin-Funk-Schaltsteckdose PFS-3, mit dem Zusatz eines Timeouts.
Angelernt werden die ersten 20 Binärbit des Funktelegramms,
die letzten vier Binärbit sind fest in Funksteckdosen-Belegung:
0001 für „on“, 0100 für „off“.
2 Minuten nach dem letzten Einschaltbefehl geht das Relais aus;
der Sender sollte alle 40 Sekunden den Ein-Befehl wiederholen.
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/fuse.h>
#include <avr/signature.h>	// gepatcht: "const" 'raus


FUSES={
 0x7B,	// Stromspar-Oszillator 128 kHz ohne Taktteiler
 0xFB,	// Brown-Out unter 2,7 V
};

typedef unsigned char byte;
#define NOINIT __attribute__((section(".noinit")))

register byte t0h asm("r2");	// Zählt halbe Sekunden; Nachleuchtdauer LED
register byte t0x asm("r3");	// Timeout-Zähler: Zählumfang 131 s
register byte opinb asm("r4");	// Vorhergehendes PINB (nur Bit 1 wird benutzt)
register byte codel asm("r5");	// Empfangene Bits, 0..24
register byte code0 asm("r6");	// Speicher für empfangene Bits
register byte code1 asm("r7");
register byte code2 asm("r8");	// Jüngstes Bit = Bit 7
register byte sync  asm("r9");

// Mit jedem Zählerüberlauf alle 2 ms werden Timeout-Zähler weitergezählt
ISR(TIM0_OVF_vect,ISR_NAKED) {
 if (!++t0h) {
  PORTB|=0x01;		// LED aus (Anzeige gültiger Kode)
  if (!++t0x) PORTB|=0x04;	// Relais aus
 }
 reti();
}
#if 0
static void eelog(byte v) {
 while (EECR&2);
 ++EEAR;
 EEDR=v;
 EECR|=0x04;
 EECR|=0x02;
}
#endif
// Ein gültiger RC-Kode aus 24 Bits wurde empfangen
static void check_code() {
 PORTB&=~0x01;		// LED ein
 t0h=0x80;			// 1/4 s (auch wenn das den Timeoutzähler etwas stört)
// eelog(code0);
// eelog(code1);
// eelog(code2);
 if ( code0==0b00100000		// "00f0"
  &&  code1==0b10101000) {	// "0fff" (Pollin-Tastwippe "D")
  switch (code2) {
   case 0b10001000: PORTB&=~0x04; t0x=0; break;	// "0f0f" Relais ein
   case 0b00101000: PORTB|= 0x04; 	  break;	// "0ff0" Relais aus
  }
 }
// TODO: Lernfunktion und EEPROM nutzen
// Aber eigentlich genügt das hier für die gestellte Aufgabe!!
}

// Erreicht der Bit-Zeitschlitz die 2-ms-Marke ohne Flanke,
// kann das das erfolgreiche Einlesen des Sync-Bits bedeuten,
// wenn der Funkempfänger LOW liefert UND 24 Bits eingelesen wurde.
// Sonst ist's ein Fehler.
ISR(TIM0_COMPB_vect,ISR_NAKED) {
 if (!(opinb&0x02) && !sync && codel>=24) check_code();
 TIMSK0=0x02;	// Keine Zeitschlitzmessung (nur Überlauf)
 codel=0;	// Empfangenen Kode löschen
 reti();
}

ISR(PCINT0_vect,ISR_NAKED) {
 byte cpinb=PINB^opinb;	// Pegelwechsel-Bits
 opinb^=cpinb;		// opinb = jetziger Zustand
 if (cpinb&0x02) {	// Pegelwechsel an PB1?
  byte t=TCNT0-OCR0B;	// Zeit seit letzter steigender Flanke
  if (opinb&0x02) {	// Steigende Flanke = Start einer Bitzelle
   OCR0B=TCNT0;		// Zeitmessung starten
   if (TIMSK0&0x08) {	// Nicht das erste Bit?
    if (t<byte(F_CPU*0.001024*0.8)) goto fail;	// ±20 % Abweichung zulassen
    if (t>byte(F_CPU*0.001024*1.2)) goto fail;
    PINB|=0x08;		// Debug-Ausgang toggeln: Anfang nicht-erster Zeitschlitz
   }else TIMSK0=0x0A;	// Zeitschlitzmessung
  }else{		// Fallende Flanke = Bit
   asm(
"	ror	r9\n"	// letztes (mglw. SYNC-)Bit
"	ror	r8\n"
"	ror	r7\n"
"	ror	r6\n"
"	inc	r5\n");
   sync=t>=byte(F_CPU*0.001024*0.5);
   if (sync) t-=byte(F_CPU*0.001024*0.5);
   if (t<byte(F_CPU*0.001024*0.25*0.7)) goto fail;	// ±30 % Abweichung zulassen
   if (t>byte(F_CPU*0.001024*0.25*1.3)) goto fail;
  }
 }
 reti();
fail:
 TIMSK0=0x02;	// Keine Zeitschlitzmessung (nur Überlauf)
 codel=0;	// Empfangenen Kode löschen
 reti();
}

int main() {
 PORTB =0x3D;	// Offene Eingänge mit Pullups, Ausgang auf H
 TIMSK0=0x02;	// Überlauf-ISR aktivieren
 TCCR0B=0x01;	// loszählen mit Vorteiler 1
 DDRB  =0x0D;	// LED, Relais = Ausgang; PB3 = Debug-Ausgang
 MCUCR =0x20;	// Sleep aktivieren
 PCMSK =0x02;	// Da wackelt's
 GIMSK =0x20;	// Pegelwechsel-Interrupt aktivieren
 ACSR  =0x80;	// Analogvergleicher aus
 EECR  =0;
 asm(
"	in	r4,%0	\n"	//opinb
"	clr	r5	\n"
::"I"(_SFR_IO_ADDR(PINB)));	//Zustand von PINB einlesen
 sei();		// Pegelwechsel und Timer-Überlauf = Interruptquellen
 for(;;) {
  sleep_cpu();	// der Rest ist ISR-gesteuert da wenig rechenaufwändig
 }
}
Detected encoding: UTF-80