/* 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-8 | 0
|