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

/* Programm für VR-FlyStick (Variante B: USB-HID-Joystick)
 * Hardware:
 * Das Gerät benutzt 2 Knöpfe und 2 LEDs.
 * grün = OK, rot = Tastendruck
 *
 * 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 "usbdrv.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	Programmieranschluss
PA5	(8)	MISO	Programmieranschluss
PA6	(7)	MOSI	Programmieranschluss
PA7	(6)	-	-

PB0	(2)	-	-
PB1	(3)	-	USB D+
PB2	(5)	-	USB D-
PB3	(4)	/RESET	Programmieranschluss

Verwendung der Zähler:
Timer0 (8 bit) = für OSCCAL-Synchronisation an USB
*/

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

/*******
 * LED *
 *******/
static uchar Led_T;	// flashing time in ms, 0 = LED is steady
static uchar Led_F;	// flashing frequency in ms (half period)
static uchar Led_C;	// flash-counter (via SOF pulses)

// LED-Blinken (ausgelöst von SETUP-Transfers) ist hier nur Schnicknack
void Led_Start(uchar t) {
 if (!Led_T) {
  Led_C=t;
  PORTA&=~0x0C;		// LED ausschalten
 }
 Led_T=Led_F=t;
}

static void Led_On1ms(void) {
 uchar led_t=Led_T, led_c;	// lokal vorhalten
 uchar mask;
 if (!led_t) return;
 led_c=Led_C;
 mask = InputReport.keystate ? 0x08 : 0x04;	// rot oder grün
 if (!--led_c) {
  PORTA^=mask;			// LED ein- oder ausschalten
  led_c=Led_F;
 }
 if (!--led_t) PORTA|=mask;	// LED einschalten
 Led_T=led_t;
 Led_C=led_c;
}

/*******
 * USB *
 *******/

// 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)
 0xC0};			// M End Collection

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

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);
   }
  }
 }
 return 0;
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/

int __attribute__((noreturn)) main(void) {
 uchar SofCmp=0;
 uchar keychange=0;
 uchar mcusr = MCUSR;
 MCUSR = 0;
 OSCCAL= 0xCF;
 ACSR  = 0x80;		// Analogvergleicher abschalten: –70µA
 WDTCSR = 0x18;		// Watchdog überlebt RESET: abschalten!
 WDTCSR = 0;
 if (mcusr & (1 << WDRF)) {
  if (USBIN & USBMASK) {	// no SE0 state?
   GIMSK = 0x40;
   MCUCR = 0x30;	// SLEEP_MODE_PWR_DOWN and level interrupt for INT0
   sei();		// must be enabled for wakeup
   sleep_cpu();		// stop osc. and let INT0 do the next wakeup
   cli();
  }
 }
 MCUCR = 0x20;		// SLEEP_MODE_STANDBY
 PRR   = 0b00001011;	// USI, ADC, Timer1 aus
 TCCR0B= 0x03;		// Vorteiler 64 (für USB)
 WDTCSR  = 0x08;	// Watchdog mit kürzestmöglicher Zeit (16 ms) aktivieren
 PORTA = 0xF7;		// grüne LED sowie Pullups EIN
 DDRA  = 0x0C;
 PORTB = 0x09;		// Pullups EIN (an offenen Eingängen)
 usbInit();
 sei();

 for (;;) {	// Endlos-Hauptschleife, wird höchstens vom Watchdog abgebrochen
  uchar t=usbSofCount;
  sleep_cpu();	// CPU schläft bis SOF (Ständige ISR-Abarbeitung während SE0!)
  wdt_reset();	// Watchdog sollte nach 3 ms zuschnappen, Minimum des µC ist 15 ms
  usbPoll();
  if (!(t-SofCmp)) continue;
  SofCmp=t;
  Led_On1ms();
  if (usbInterruptIsReady() && keychange) {	// Pegelwechsel aufgetreten?
   usbSetInterrupt((uchar*)&InputReport,sizeof(InputReport));
   keychange=0;
  }
  if (!keychange) {
   keychange=(~PINA&0x03)^InputReport.keystate;
   if (keychange) {
    if (InputReport.keystate^=keychange) {
     PORTA|= 0x08;	// rote LED ein
     PORTA&=~0x04;	// grüne LED aus
    }else{
     PORTA|= 0x04;	// grüne LED ein
     PORTA&=~0x08;	// rote LED aus
    }
   }
  }
 }
}
Detected encoding: UTF-80