/*
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-8 | 0
|