Source file: /~heha/mb-iwp/Kleinkram/Sandkasten.zip/fe/fe-d.cpp

/* Name: fe.cpp (Sandkasten-Empfänger, eine Art AccessPoint)
 * Projekt: Drahtlose VR-Sandkasten
 * Tabweite: 8, Kodierung: UTF-8, Zeilenenden: LF
 */

#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 <string.h>
#include <avr/fuse.h>
#include <avr/signature.h>
extern "C"{
#include "usbdrv.c"
}
#include "../rf12/rf12.h"

FUSES={
  0b11000001,	// Interner High-Speed-Oszillator 16 MHz, hochfahren mit BOD, kein Taktteiler
  0b01010101,	// RESET als Portpin, BrownOut bei 2,7 V
  0b11111111,	// unverändert
};

/************
 * Hardware *
 ************/
/* Anschlüsse des Mikrocontrollers ATtiny45:
PB0	(5)	DI	RFM12 SDO
PB1	(6)	DO	RFM12 SDI, grüne LED nach 00
PB2	(7)	SCK	RFM12 SCK, rote LED nach 00
PB3	(2)	PCINT3	USB D-, 1,5 kΩ nach 3P3
PB4	(3)		USB D+
PB5	(1)		RFM12 !SEL
Ucc	(8)	3P3	3,3 V vom LP2950-33
┴	(4)	00	Masse
*/

/*******
 * USB *
 *******/
#define DW(x) (x)&0xFF,(x)>>8
#define DD(x) DW((x)&0xFFFF),DW((x)>>16)

// USB Report-Deskriptor
PROGMEM const uchar usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
  0x05,0x01,		// G Usage Page (Generic Desktop)
  0x09,0x04,		// L Usage (Joystick)
  0xA1,1,		// M Collection (Application)
  0x15,0,		// G  Logical Minimum (0)
  0x25,1,		// G  Logical Maximum (1)
  0x75,1,		// G  Report Size (1 bit)
  0x95,6,		// G  Report Count (6)
  0x05,0x09,		// G  Usage Page (Buttons)
  0x19,1,		// L  Usage Minimum (Button 1)
  0x29,6,		// L  Usage Maximum (Button 6)
  0x81,0x02,		// M  Input (Var)
  0x95,2,		// G  Report Count (2)
  0x81,0x01,		// M  Input (Const)

  0x05,0x06,		// G  Usage Page (Generic Device Controls)
  0x09,0x21,		// L  Usage (Wireless Channel)
  0x75,8,		// G  Report Size (8 bit)
  0x95,1,		// G  Report Count (1)
  0x15,1,		// G  Logical Minimum (1)
  0x25,12,		// G  Logical Maximum (12)
  0xA4,			// G  Push
  0x37,DD(4331225),	// G   Physical Minimum (433.1225 MHz)
  0x47,DD(4347175),	// G   Physical Maximum (434.7175 MHz)
  0x55,2,		// G   Unit Exponent (2 = 100 Hz)
  0x66,DW(0xF001),	// G   Unit (Hz)
  0xB1,0x02,		// M   Feature (Var)
  0xB4,			// G  Pop – entfernt physikalisches Maß
  0x09,0x22,		// L  Usage (Wireless ID)
  0x25,15,		// G  Logical Maximum (15)
  0xB1,0x02,		// M  Feature (Var)
  0xC0};		// M End Collection

static struct {
  byte k;		// Tastenstatus
  operator byte*() {return (byte*)this;}
}InputReport;

// Wireless_ID ist (nur) mit EEPROM-Programmiergerät änderbar,
// Kanalnummer per Hopping-Paket vom Funksender aus
EEMEM struct ee_t{
  byte ch,id;
  operator byte*() {return (byte*)this;}
}ee={(WIRELESS_ID-1)%12+1,WIRELESS_ID};

static ee_t FeatureReport={(WIRELESS_ID-1)%12+1,WIRELESS_ID};

/*******
 * LED *
 *******/
// grün: empfangsbereit
// grün verlöschend: Empfangssignal (zugeordnete Wireless_ID)
// rot: Fehler, Timeout (blockiert RFM12-Kommunikation)
const bool LED_BY_SPI=true;

static void green_off() {
  if (LED_BY_SPI) {	// Variante 1
    USIDR = 0;		// SPI-Senderegister-MSB mit 0-Bit laden
  }else{		// Variante 2
    PORTB&=~0x02;
    USICR = 0;		// Portpin dem SPI entreißen
  }
}

static void green_on() {
  if (LED_BY_SPI) {	// Variante 1
    USIDR = 0x80;	// SPI-Senderegister-MSB mit 0-Bit laden
  }else{		// Variante 2
    PORTB|= 0x02;
    USICR = 0;		// Portpin dem SPI entreißen
  }
}

static void red_on() {	// Rote LED einschalten, grün geht dabei aus
  rf12::ss_hi();
  USICR = 0;		// USI deaktivieren
  PORTB|= 0x04;
}

/*********************
 * OUT data received *
 *********************/
uchar usbFunctionWrite(uchar *data, uchar len) {
  if (len>=2) {
    byte b=data[0];
    if (byte(b-1)<12 && FeatureReport.ch != b) {
      rf12::channel = FeatureReport.ch = b;
      eeprom_write_byte(&ee.ch,b);
    }
    b=data[1];
    if (byte(b-1)<15 && FeatureReport.id != b) {
      FeatureReport.id = b;
      eeprom_write_byte(&ee.id,b);
    }
  }
  return 1;
}
 
/***********************
 * SETUP data received *
 ***********************/
uchar usbFunctionSetup(uchar data[8]) {
  usbRequest_t *rq = (usbRequest_t*)data;
/* class request type (x01x xxxx) */
  if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
    if (rq->bRequest == USBRQ_HID_GET_REPORT) { // wValueH: ReportType, wValueL: ReportID
      if (rq->wValue.bytes[1] == 1) {		// Input
        usbMsgPtr = InputReport;
        return sizeof InputReport;
      }else if (rq->wValue.bytes[1] == 3) {	// Feature
        usbMsgPtr = FeatureReport;
        return sizeof FeatureReport;
      }
    }else if (rq->bRequest == USBRQ_HID_SET_REPORT) { // wValueH: ReportType, wValueL: ReportID
      if (rq->wValue.bytes[1] == 3) {	 	// Feature
        return 0xFF;	// OUT entgegennehmen
      } 
    }
  }
  return 0;
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/
static void HardwareInit() {
  byte b = eeprom_read_byte(&ee.ch);
  if (byte(b-1)<12) rf12::channel = FeatureReport.ch = b;
  b = eeprom_read_byte(&ee.id);
  if (byte(b-1)<15) FeatureReport.id = b;
  DDRB  = 0x26;
  rf12::powerup();
  rf12::init();
  rf12::xfer(0);	// (unnötig)
  TCCR0B= 0x02;		// Zeitgeber 0 mit Vorteiler 8 (erforderlich für USB)
  TCCR1 = 0x0B;		// Zeitgeber 1 mit Vorteiler 1024
  rf12::rxmode();	// Lauscher auf Empfang
  rf12::xfer(0);	// Interrupt loseisen (unnötig)
  green_on();
}

static byte keychange;	// !=0 wenn InputReport zu senden ist
static byte led_countdown;	// für beide LEDs
static byte resend_countdown;	// hier: Wenn der Sender nicht wiederholt, Tasten lösen

static void check_ether() {
  if (rf12::data()) {	// Stehen Daten zum Abholen bereit??
    led_countdown = 30;	// Mit dem nächsten rxdata() ist die grüne LED unkontrolliert
    if (!rf12::rxdata(rf12::buf)) {	// Datenblock lesen, Prüfsumme OK?
      if (rf12::buf.sendrecv==FeatureReport.id<<4) {
	if (rf12::buf.type_len==0x21) {	// 1 Byte mit ACK-Anforderung?
          keychange=(InputReport.k^rf12::buf.data[0])&0x3F;
	  InputReport.k^=keychange;
	  rf12::buf.type_len=0x10;	// Ack mit Länge 0
	  rf12::buf.sendrecv=FeatureReport.id;	// zum Absender, Hich-Nibble = 0 (Master)
	  rf12::txdata(rf12::buf);	// zurücksenden
	  resend_countdown = InputReport.k ? 140 : 0;	// erwarte nächsten Tastendruck in 2200 ms
	  led_countdown = 10;
	}else if (rf12::buf.type_len==0x81 && byte(rf12::buf.data[0]-1)<12) {
	  rf12::channel=FeatureReport.ch=rf12::buf.data[0];	// Frequenz-Hopping!
	  rf12::setfreq();
	  led_countdown = 20;
	}else red_on();
      }else red_on();
    }else red_on();
    green_off();	// Irgendetwas wurde empfangen
  }
}

static void OnTimer() {
  if (led_countdown && !--led_countdown) {
    rf12::xfer(0);	// schaltet rote LED aus
    green_on();
  }
  if (resend_countdown && !--resend_countdown) {
    keychange=InputReport.k;
    InputReport.k=0;
    led_countdown=15;
    red_on();
  }
}

static void tuneOscillator() {
  static uchar lastTick;
#define SOLL 14	// usbSofDiff sollte 16500/8%256 = 14,5 sein
#define MABW 7	// Abweichung kleiner ±6 ist OK und wird ignoriert
  if (GPIOR0==1) {		// Ohne Überlauf?
    schar t = GPIOR1-lastTick-SOLL;	// zentriere Ergebnis um die Null
    if (t<-MABW) OSCCAL++;	// schneller machen
    if (t> MABW) OSCCAL--;	// langsamer machen
  }
  cli();
  lastTick=GPIOR1;
  sei();
  GPIOR0=0;			// noch im cli()
}

int main() {
  byte mcusr = MCUSR;	// Reset-Ursache einfangen
  OSCCAL= 160;
  MCUSR = 0;
  ACSR  = 0x80;		// Analogvergleicher abschalten: –70µA
  PRR   = 0x01;		// ADC wird nicht benötigt
  rf12::powerdown();
  WDTCR = 0x08;	// Watchdog mit kürzestmöglicher Zeit (16 ms) aktivieren
  if (mcusr & 1<<WDRF	// Watchdog war Reset-Ursache?
  && USBIN & USBMASK) {	// kein SE0 (Aufwecken?) dann weiterschlafen!
    wdt_reset();
    MCUCR = 0x30;	// SLEEP_MODE_PWR_DOWN (schaltet Oszillator ab)
    sleep_cpu();	// Schlafen bis zum nächsten Watchdog-Reset
  }			// ergibt Verhältnis Wach : Schlaf = 1,4 : 16
  MCUCR = 0x20;		// SLEEP_MODE_IDLE
  HardwareInit();
  PCMSK = 9;		// USB D-
  usbInit();
  sei();

  for (;;) {	// Endlos-Hauptschleife, wird höchstens vom Watchdog abgebrochen
    if (USBIN&USBMASK) sleep_cpu();// CPU schläft bis SOF; 16 ms kein SOF führt zum Reset
    usbPoll();
    if (GPIOR0) {
      wdt_reset();
      tuneOscillator();
    }
//    _delay_us(20);
    check_ether();
    if (TIFR&0x04) {	// Zeitgeber 1: Unterbrechungsanforderung?
      TIFR=0x04;	// löschen
      OnTimer();	// alle 1/F_CPU*1024*256 = 16 ms ausführen
    }
    if (usbInterruptIsReady() && keychange) {	// Pegelwechsel aufgetreten?
      usbSetInterrupt(InputReport,sizeof InputReport);
      keychange=0;
    }
  }
}
Detected encoding: UTF-80