Source file: /~heha/basteln/PC/Mikrofonverstärker/OneKey.zip/OneKey.c

/* Name: OneKey.c; TABSIZE=8, ENCODING=UTF-8, LINESEP=LF
 * Projekt: Tastenanschlag per Fernsteuereingang (bspw. Relaiskontakt)
 * Zweck: Bildschirmschoner beenden bei externem Ereignis (Lichtschranke; Einbruch)
 * Autor: Henrik Haftmann
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <avr/sleep.h>

#include "usbdrv.c"

/*
Pin-Zuordnung (entsprechend vorhandener Platine):
PB0 (5) = Taste0 (low-aktiv, zum Auslösen mit Masse verbinden)
PB1 (6) = USB D+
PB2 (7) = USB D- (INT0)
PB3 (2) = Taste3
PB4 (3) = Taste4
PB5 (1) = Taste5 (nur bei gesetzter Reset-Disable-Fuse!)
*/

// Kodezuweisungen entsprechend „USB HID Usage Tables“ (2004) Seite 53 ff.
#define KEY0 0x57	// '+' (Keypad)
#define KEY3 0x47	// ScrollLock
#define KEY4 0x53	// NumLock
//#define KEY5 0x56	// '-' (Keypad) - Kommentar entfernen bei gesetzter Reset-Disable-Fuse!
// Hier wird ganz einfach der Tastenstatus als Bitmap (wie's von PINB kommt)
// an den PC durchgereicht; die Tastenkode-Umsetzung und die Bit-Zuordnung
// erfolgt allein mit dem Report-Beschreiber durch Windows (Linux?).
// Gleichzeitige Tastendrücke sind somit gar kein Problem.
// Der Kontakt muss hinreichend prellarm sein, sonst gibt es Mehrfach-Ereignisse.

static uchar idleRate;		// in 4 ms, nicht ausgewertet
static uchar keystate;		// 1 = Taste gedrückt, 0 = Taste losgelassen

// USB Report-Deskriptor
// Normalerweise handelt sich es hierbei um ein „Keypad“ (Usage = 7),
// nicht um ein „Keyboard“ (Usage = 6). Bei „Keypad“ lädt zwar Win98SE korrekt
// den USB-Tastaturtreiber, aber die Tasten erscheinen funktionslos;
// jedenfalls schaltet die ScrollLock-LED der Haupt-Tastatur (PS/2) nicht.
PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
 0x05, 0x01,	// G USAGE_PAGE (Generic Desktop)
 0x09, 0x06,	// L USAGE (Keyboard)	(„Keypad“ {7} geht unter Win98 nicht, unter W2k schon.)
 0xA1, 0x01,	// M COLLECTION (Application)
 0x05, 0x07,	// G  USAGE_PAGE (Keyboard)
 0x75, 0x01,	// G  REPORT_SIZE (1)
 0x95, 0x01,	// G  REPORT_COUNT (1)
 0x15, 0x00,	// G  LOGICAL_MINIMUM (0)	(Win98 streikt bei „0x14“, lädt Treiber nicht)
 0x25, 0x01,	// G  LOGICAL_MAXIMUM (1)
 0x09, KEY0,	// L  USAGE ()
 0x81, 0x02,	// M  INPUT (Var)
 0x81, 0x01,	// M  INPUT (Const)	(USB-Bit 1 frei halten)
 0x81, 0x01,	// M  INPUT (Const)	(USB-Bit 2 frei halten)
 0x09, KEY3,	// L  USAGE ()
 0x81, 0x02,	// M  INPUT (Var)
 0x09, KEY4,	// L  USAGE ()
 0x81, 0x02,	// M  INPUT (Var)
#ifdef KEY5
 0x09, KEY5,	// L  USAGE ()
 0x81, 0x02,	// M  INPUT (Var)
#else
 0x09, 0x00,	// L  USAGE (0)		(Lückenfüller für gleich bleibende Array-Größe)
 0x81, 0x01,	// M  INPUT (Const)	(Byte auffüllen)
#endif
 0x81, 0x01,	// M  INPUT (Const)	(Byte auffüllen)
 0x81, 0x01,	// M  INPUT (Const)	(Byte auffüllen)
 0xC0};		// M END_COLLECTION

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
   usbMsgPtr = &keystate;
   return 1;
  }else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
   usbMsgPtr = &idleRate;
   return 1;
  }else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
   idleRate = rq->wValue.bytes[1];	// wValueH
  }
 }	/* no vendor specific requests implemented */
 return 0;
}

static void tuneOscillator(void) {
#define SOLL 14	// usbSofDiff should be 16500/8%256 = 14.5
#define MABW 7	// Deviation of less than ±6 is OK and silently ignored
 schar t = usbSofDiff-SOLL;	// center result around signed zero
 if (t<-MABW) OSCCAL++;		// make faster
 if (t> MABW) OSCCAL--;		// make slower
}

int main(void) __attribute__((noreturn));
int main(void) {
 uchar keychange=0;
 uchar mcusr = MCUSR;
 MCUSR = 0;
 ACSR |= 0x80;		// Analogvergleicher abschalten: –70µA
 WDTCR = 0x18;		// Watchdog überlebt RESET: abschalten!
 WDTCR = 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= 0x02;		// Achtel-Taktfrequenz
 PORTB = 0b00111001;	// Pull-Up an allen offenen Anschlüssen
 usbInit();
 WDTCR  = 0x08;		// Watchdog mit kürzestmöglicher Zeit (16 ms) aktivieren
 sei();
 for (;;) {	// Endlos-Hauptschleife, wird höchstens vom Watchdog abgebrochen
  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();
  tuneOscillator();
  if (usbInterruptIsReady() && keychange) {	// Pegelwechsel aufgetreten?
   usbSetInterrupt(&keystate,1);
   keychange=0;
  }
  if (!keychange) {
#ifdef KEY5
   keychange=(~PINB&0b00111001)^keystate;	// 4 Tasten abfragen
#else
   keychange=(~PINB&0b00011001)^keystate;	// 3 Tasten abfragen
#endif
   keystate^=keychange;	// Änderungen merken
  }
 }
}
Detected encoding: UTF-80