Source file: /~heha/mb-iwp/FlyStick/Firmware.zip/AccessPoint/fe.c

/* Name: fe.c (Flystick-Empfänger, eine Art AccessPoint)
 * Projekt: FlyStick-Neuauflage, Funk-Version, Henrik Haftmann, TU Chemnitz
 * Tabweite: 8, Kodierung: UTF-8, Zeilenenden: LF
 * WUNSCH: Kann man erkennen, ob Tastendrücke konsumiert werden?
 */

#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 <util/delay.h>

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

/************
 * Hardware *
 ************/
/*
Verwendung der Ports des ATmega8TQ:
PB0	(12)	ICP	RFM12S !IRQ
PB1	(13)	(OC1A)	RFM12S FFIT (ungenutzt, HIGH)
PB2	(14)	!SS	RFM12S !SEL
PB3	(15)	MOSI	RFM12S SDI, Programmieranschluss
PB4	(16)	MISO	RFM12S SDO, Programmieranschluss
PB5	(17)	SCK	RFM12S SCK, Programmieranschluss
PB6	(7)	XTAL1	Quarz 12 MHz
PB7	(8)	XTAL2	Quarz 12 MHz

PC0	(23)	(ADC0)	grüne LED nach 00 (oder 5P)
PC1	(24)	(ADC1)	rote LED nach 00 (oder 5P) *
PC2	(25)	(ADC2)	-
PC3	(26)	(ADC3)	-
PC4	(27)	(ADC4)	-
PC5	(28)	(ADC5)	-
PC6	(29)	!RESET	RFM12S /RES, Programmieranschluss

PD0	(30)	RxD	Debug
PD1	(31)	TxD	Debug
PD2	(32)	INT0	USB D-
PD3	(1)	(INT1)	USB D+
PD4	(2)	(T0)	RFM12S VDI (ungenutzt, HIGH)
PD5	(9)	(T1)	Bootloader-Zwang wenn LOW; Debug-Anschluss
PD6	(10)	(AIN0)	-
PD7	(11)	(AIN1)	-

* Der Bootloader schaltet die rote LED ein, entweder mit schwachem HIGH
  oder starkem LOW, je nach gemeinsamer Katode oder Anode der Duo-LED.
*/

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

// USB Report-Deskriptor
PROGMEM char 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,2,		// G  Report Count (2)
 0x05,0x09,		// G  Usage Page (Buttons)
 0x19,0x01,		// L  Usage Minimum (Button 1)
 0x29,0x02,		// L  Usage Maximum (Button 2)
 0x81,0x02,		// M  Input (Var)
 0x95,6,		// G  Report Count (6)
 0x81,0x01,		// M  Input (Const)

 0x05,0x06,		// G  Usage Page (Generic Device Controls)
 0x09,0x21,		// L  Usage (Wireless Channel)
 0x15,1,		// G  Logical Minimum (1)
 0x25,12,		// G  Logical Maximum (12)
 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)
 0x75,8,		// G  Report Size (8 bit)
 0x95,1,		// G  Report Count (1)
 0xB1,0x02,		// M  Feature (Var)
 0xC0};			// M End Collection
// TODO: Wireless ID melden

static struct {
 uchar keystate;	// Tastenstatus
}InputReport;

//static struct{
// uchar chan_id;		// Kanal und ID
//}FeatureReport;

/*******
 * LED *
 *******/
// Blink-Anzeigen:
// grün: empfangsbereit
// grün verlöschend: Empfangssignal (zugeordneter FlyStick)
// rot (kurz): Empfangssignal (bspw. falscher FlyStick)
// rot (permanent): Bootloader

static uchar DefPortC=0xFD;	// Portbits wenn GRÜN

/***********************
 * SETUP data received *
 ***********************/

uchar usbFunctionWrite(uchar *data, uchar len) {
 if (len==1) {
  rf12_channel = *data;
  eeprom_write_byte((void*)0xFFF0,rf12_channel);
 }
 return 1;
}
 
 uchar usbFunctionSetup(uchar data[8]) {
 usbRequest_t *rq = (void*)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 = (uchar*)&InputReport;
    return sizeof(InputReport);
   }else if (rq->wValue.bytes[1] == 3) {	// Feature
    usbMsgPtr = &rf12_channel;
    return 1;
   }
  }else if (rq->bRequest == USBRQ_HID_SET_REPORT) { // wValueH: ReportType, wValueL: ReportID
   if (rq->wValue.bytes[1] == 3) {	 	// Feature
    return USB_NO_MSG;
   } 
  }
 }
 return 0;
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/
void init() __attribute__((naked,section(".init3")));
void init() {
 DDRC=0;		// rote LED vom Bootloader frühzeitig ausmachen
}

static void HardwareInit() {
 BYTE b = eeprom_read_byte((void*)0xFFF0);
 if ((BYTE)(b-1)<12) rf12_channel = b;
 DDRC  = 0x03;		// LEDs als Ausgänge
 DDRD |= 0x02;		// TxD als Ausgang
 rf12_init();
 TCCR0 = 0x05;		// Zeitgeber mit Vorteiler 1024 (für TimeOuts)
 rf12_rxmode();		// Lauscher auf Empfang
 rf12_xfer(0);		// Interrupt loseisen (unnötig)
 PORTC = DefPortC;
}

static void HardwareZeroPower() {
 ACSR  = 0x80;		// Analogvergleicher abschalten: –70µA
 if (!(PORTC&2)) DefPortC=0xFE;	// Aus-Zustand der LED festlegen (vom Bootloader übernehmen)
 PORTB = 0x1F;		// Pull-Ups, außer an SCK (Ruhepegel ist LOW)
 PORTC = DefPortC^0x01;	// LED bzw. Pull-Ups
 PORTD = 0xF3;		// Pull-Ups
 DDRB  = 0x2C;		// SCK, MOSI, SS als Ausgänge
 SPCR  = 0x51;		// SPI Master mit CPOL=CPHA=0, 1,5 MHz Schiebetakt
 SPSR  = 0x01;		// Laut Datenblatt sind 20 MHz Schiebetakt zulässig
			// aber nur für alles andere als FIFO-Daten-Lesen
 PORTB = 0x03;		// !SS auf LOW, MISO ohne PullUp
 rf12_powerdown();
}

uchar keychange;
BYTE LedTimeOut;
BYTE RepTimeOut;

static void check_ether() {
 if (PINB&0x10) {	// Stehen Daten zum Abholen bereit??
  BYTE c=DefPortC;
  c^=0x03;		// nur rot
  LedTimeOut=10;	// langes Aufleuchten bei Fehler (220 ms)
  if (!rf12_rxdata(rf12buf.all)) {	// Datenblock lesen, Prüfsumme OK?
   c^=0x01;		// rot+grün
   if (rf12buf.sendrecv==WIRELESS_ID<<4) {
    if (rf12buf.type_len==0x21) {
     keychange=(InputReport.keystate^rf12buf.data[0])&3;
     InputReport.keystate^=keychange;
     rf12buf.type_len=0x10;	// Ack
     rf12buf.sendrecv=WIRELESS_ID;	// zum Absender
     rf12_txdata(rf12buf.all);	// zurücksenden
     RepTimeOut = InputReport.keystate ? 100 : 0;	// erwarte nächsten Tastendruck in 2200 ms
     c^=0x03;			// LED aus
     LedTimeOut=5;		// kurzes Verlöschen (110 ms)
    }else if (rf12buf.type_len==0x81 && (BYTE)(rf12buf.data[0]-1)<12) {
     rf12_channel=rf12buf.data[0];	// Frequenz-Hopping!
     rf12_setfreq();
     c^=0x03;			// LED aus
     LedTimeOut=10;		// langes Verlöschen (220 ms)
    }
   }
  }
  PORTC = c;
 }
}

static void OnTimer() {
 if (LedTimeOut && !--LedTimeOut) PORTC=DefPortC;	// LED grün
 if (RepTimeOut && !--RepTimeOut) {
  keychange=InputReport.keystate;
  InputReport.keystate=0;
  PORTC = DefPortC^0x03;	// LED rot
  LedTimeOut = 20;		// langes Aufleuchten
 }
}

int __attribute__((noreturn)) main(void) {
 uchar mcucsr = MCUCSR;	// Reset-Ursache einfangen
 MCUCSR= 0;
 HardwareZeroPower();
 WDTCR = 0x08;	// Watchdog mit kürzestmöglicher Zeit (16 ms) aktivieren
 if (mcucsr & 1<<WDRF	// Watchdog war Reset-Ursache?
 && USBIN & USBMASK) {	// kein SE0 (Aufwecken?) dann weiterschlafen!
  wdt_reset();
  MCUCR = 0xA0;		// SLEEP_MODE_PWR_DOWN (schaltet Quarz ab)
  sleep_cpu();		// Schlafen bis zum nächsten Watchdog-Reset
 }			// ergibt Verhältnis Wach : Schlaf = 1,4 : 16
 MCUCR = 0x80;		// SLEEP_MODE_IDLE
 HardwareInit();
 usbInit();
 sei();

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