Source file: /~heha/mb-iwp/FlyStick/Firmware.zip/Funk-Joystick/fs-c.c

/* Programm für VR-FlyStick (Bestückungsvariante C: mit Funktransceiver 433 MHz)
 * Batteriebetrieb! Hardware: RFM12 (Pollin) sowie:
 * Das Gerät benutzt 2 Knöpfe und 2 LEDs.
 * grün = Senden OK, rot = Senden ging schief (kein Empfänger, Empfänger zu weit o.ä,)
 * Kanalauswahl-Modus: beide Tasten 4 s gedrückt halten: LED = rot permanent
 *  zuerst Blink-Ausgabe der aktuellen Kanal-Nummer in Gelb zum Mitzählen
 *  obere Taste schaltet Kanal um 1 weiter, LED gelb pro Tastendruck
 *  längeres LED gelb wenn Kanal 1 erreicht
 *  untere Taste loslassen:
 *   obere nicht gedrückt = Ende, Speichern und Aussenden Frequenz-Hopping-Paket
 *   obere gedrückt = nicht speichern (Abbruchmöglichkeit)
 * Gemessener Ruhestromverbrauch: 0,71 µA (120330)
 *
 * tabsize = 8, encoding = utf-8?, lineends = LF
 */

#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <util/delay.h>

#include "../rf12/rf12.h"

/************
 * Hardware *
 ************/
/*
Verwendung der Ports:
PA0	(13)	-	Taster T1 (oben)
PA1	(12)	-	Taster T2 (unten)
PA2	(11)	-	LED grün
PA3	(10)	-	LED rot
PA4	(9)	(SCK)	RFM12: SCK	Programmieranschluss
PA5	(8)	(MISO)	RFM12: SDO	Programmieranschluss
PA6	(7)	(MOSI)	RFM12: SDI	Programmieranschluss
PA7	(6)	-	RFM12: !SEL und PullUp 47 kΩ

PB0	(2)	-	RFM12: FFIT (ungenutzt)
PB1	(3)	-	RFM12: !IRQ (ungenutzt)
PB2	(5)	-	RFM12: VDI (ungenutzt)
PB3	(4)	/RESET	RFM12: !RES	Programmieranschluss

*/

BYTE keystate;	// Tastenstatus, Bit 7 = Toggle-Bit bei Wiederholungen

ISR(PCINT0_vect) {	// Pegelwechsel an PINA (Tasten)
}

ISR(TIM0_OVF_vect) {	// für sinnvoll funktionierenden SLEEP-Befehl (16 ms)
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/
static void HardwareInit() {
 ACSR  = 0x80;		// Analogvergleicher abschalten: –70µA
 PORTA = 0x83;
 PORTB = 0;		// keine PullUps
 DDRA  = 0xDC;		// SEL, MOSI, SCK, LEDs als Ausgänge
 if (PINA&3==3) {	// keine Taste gedrückt: Tiefschlaf!
  rf12_powerdown();
  PRR   = 0x0F;		// alles AUS
  MCUCR = 0x30;		// Power-Down: alles aus außer Pin-Change (Tasten)
  GIMSK = 0x10;		// Pin-Change an PINA
  PCMSK0= 0x03;		// beide Tasten
  GIFR  = 0x10;		// anhängigen Interrupt löschen
  sei();
  PORTA|= 0x80;		// !SEL = High wegen PullUp
  sleep_cpu();		// warten auf Tastendruck
  cli();
  GIMSK = 0;
  rf12_powerup();	// Quarz aktivieren (ca. 2 mA)
  rf12_init();		// Baudrate u.v.a.m. einstellen
  rf12_xfer(0);		// Status auslesen (probeweise, IRQ löschen)
 }
 MCUCR = 0x20;		// Idle
 PRR   = 0x0B;		// Timer0 reaktivieren
 TIFR0 = 0x01;		// Überlaufbit löschen
 TIMSK0= 0x01;
 TCCR0B= 0x03;		// Vorteiler 64, Überlauf alle 16 ms (Tastenabfragetakt)
 sei();
}

int __attribute__((noreturn)) main(void) {
 BYTE keychange=0;
 BYTE resend_countdown=0;
// gedrückte Taste alle 1 s wiederholt aussenden, sonst denkt der Empfänger,
// der Sender sei tot, und nimmt den Tastendruck zurück
 BYTE retry_countdown=0;
// 3 Sendeversuche unternehmen (die mit einem erfolgreichen ACK enden müssen)
// sonst bleibt die rote LED an zur Fehleranzeige
 BYTE led_countdown=0;
 BYTE prog_countdown=0;
 BYTE b=eeprom_read_byte((void*)0xFFF0);
 if ((BYTE)(b-1)<12) rf12_channel=b;	// Arbeitskanal setzen
 for (;;) {	// Endlos-Hauptschleife
  if (keystate&3 || retry_countdown || led_countdown);
  else HardwareInit();
  sleep_cpu();	// CPU schläft bis zu 16 ms, keine weiteren Interrupts
  
  if (!keychange) {
   keychange=(~PINA^keystate)&0x03;
   if (keychange) prog_countdown = PINA&3 ? 0 : 255;
   else if (!--resend_countdown) keychange=0x80;
  }
  if (prog_countdown && !--prog_countdown) {
// modale Schleife, untere Taste muss die ganze Zeit gedrückt bleiben!
   PORTA |= 0x08;	// rote LED permanent ein
   rf12_powerdown();	// 
// Kanalnummer ausspucken (grüne LED blinken lassen)
   b=rf12_channel;
   do{
    BYTE k=10; do {sleep_cpu(); if (PINA&2) goto raus;} while(--k);
    PORTA |= 0x04;	// grüne LED ein
    k=10; do {sleep_cpu(); if (PINA&2) goto raus;} while(--k);
    PORTA &=~0x04;	// grüne LED aus
   }while(--b);
// Tastendrücke auswerten
   do{
    sleep_cpu();
    keychange=(~PINA^keystate)&0x03;
    if (~keystate&keychange&1) {	// obere Taste gedrückt
     rf12_channel = rf12_channel==12 ? 1 : rf12_channel+1;
     PORTA|=0x04;	// grüne LED ein
     led_countdown=10;
     if (rf12_channel==1) led_countdown=30;	// länger für Kanal 1
    }
    if (keystate&keychange&1) {		// obere Taste losgelassen
     led_countdown=1;
    }
    if (led_countdown && !--led_countdown) PORTA&=~0x04;	// grüne LED aus
    keystate^=keychange;
    keychange=0;
   }while (!(PINA&2));	// mit dem Lösen des unteren Knopfes speichern
   if (PINA&1) {	// oberer Knopf nicht gedrückt?
    eeprom_write_byte((void*)0xFFF0,rf12_channel);	// abspeichern
    rf12buf.type_len=0x81;	// 1 Byte Daten
    rf12buf.sendrecv=WIRELESS_ID<<4; // lokale ID = 1, Nachricht an Busmaster (0)
    rf12buf.data[0]=rf12_channel;
    rf12_txdata(rf12buf.all);	// für Nahbereich: Empfänger informieren
   }
raus:
   PORTA&=~0x0C;
  }
  if (keychange) {
   retry_countdown=4;
   resend_countdown=61; // Nach jeweils 1 Sekunde wiederholen (KeepAlive)
   keystate^=keychange;
   keychange=0;	
  }
  if (retry_countdown) {
   rf12buf.type_len=0x21;	// NeedAck, 1 Byte Daten
   rf12buf.sendrecv=WIRELESS_ID<<4; // lokale ID = 1, Nachricht an Busmaster (0)
   rf12buf.data[0]=keystate;
   rf12_txdata(rf12buf.all);
   rf12_xfer(0);		// ??
   BYTE tic=TCNT0;
   do if (PINA&0x20) {		// dann Empfang ohne TimeOut möglich
    if (!rf12_rxdata(rf12buf.all)	// CRC OK
// Die oberen Bits müssen gleich sein, RequestAck muss 0 sein,
// IsAck muss 1 sein, Länge muss (hier) 0 sein
    && rf12buf.type_len==0x10	// ACK-Bit und Länge 0
// Sender und Empfänger müssen vertauscht sein
    && rf12buf.sendrecv==WIRELESS_ID) {// Sender 0, Empfänger 1
     PORTA&=~0x08;	// rote LED aus
     PORTA|= 0x04;	// grüne LED ein
     retry_countdown=0;
     led_countdown=3;	// 50 ms
     goto ok;
    }
    break;
   }while ((BYTE)(TCNT0-tic)<150);
   PORTA|= 0x08;	// rote LED ein
   PORTA&=~0x04;	// grüne LED aus
   retry_countdown--;
   led_countdown=14;	// 0,2 s (erscheint länger wegen Wiederholungen)
ok:
   if (!retry_countdown) rf12_powerup();
// Empfänger AUS (spart 11 mA zwischendurch, nur noch LED oder Timer0)
  }
  if (led_countdown && !--led_countdown) PORTA&=~0x0C;
 }
}
Detected encoding: UTF-80