/* Name: notaus.cpp; TABSIZE=8, ENCODING=UTF-8, LINESEP=LF
* Projekt: Modell 1:14 Autonome Fahrzeuge im Bergwerk
* Zweck: Freigabe der Steuerung nur bei angestecktem USB-Gerät und gelöstem Taster
* Autor: Henrik Haftmann
* Auswertung: Joystick mit 2 Tasten und 0 Hebeln ausfindig machen
* Taste0 = 1, Taste1 = 0: Not-Aus gelöst
* Taste0 = 0, Taste1 = 1: Not-Aus gedrückt
* Alle anderen Fälle: Fehlfunktion (bspw. Draht ab; Schaltvorgang)
*220917 erstellt
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/fuse.h>
#include <avr/signature.h>
#include "usbdrv.c"
FUSES={
0b11000001, // PLL-Oszillator 16 MHz, schnell hochfahren, kein Taktteiler
0b11011101, // BrownOut bei 2,7 V
0b11111111, // unverändert
};
/* Pin-Zuordnung (ähnlich FunkUsb.c)
Pin Port Funk. Verwendung
1 PB5 Reset frei
2 PB3 Rastende Taste: Low wenn gelöst
3 PB4 Rastende Taste: Low wenn gedrückt
4 GND Masse
5 PB0 frei
6 PB1 USB D+
7 PB2 USB D-, INT0 für SOF
8 Ucc 3,3 V über 1 rote LED, die die Stromaufnahme anzeigt
*/
static uchar keystate; // 1 = Taste gedrückt, 0 = Taste losgelassen
// Nur Bit 0 und 1, per Hardware gegensätzlich
// USB Report-Deskriptor für Joystick
PROGMEM const uchar usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
0x05, 1, // G Usage Page (Generic Desktop)
0x09, 4, // L Usage (Joystick)
0xA1, 1, // M Collection (Application)
0x05, 9, // G Usage Page (Buttons)
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 Tasten)
0x19, 1, // L Usage Minimum (1) = Taste 1
0x29, 2, // L Usage Maximum (2) = Taste 2
0x81, 2, // M Input (Var)
0x95, 6, // G Report Count (6 ungenutzte Bits)
0x81, 1, // M Input (Const)
0xC0}; // M End Collection
uchar usbFunctionSetup(uchar data[8]) {
if (data[0]==0xA1 && data[1]==1) {
usbMsgPtr = &keystate;
return 1;
}
return 0;
}
// Diese Routine beachtet Grenzen von OSCCAL und die zwei Bereiche
// der ATtinyX5 nicht! Bis jetzt geht's trotzdem.
static void tuneOscillator(uchar ms) {
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 (ms==1) {
schar t = GPIOR1-lastTick-SOLL; // zentriere Ergebnis um die Null
if (t<-MABW) OSCCAL++; // schneller machen
if (t> MABW) OSCCAL--; // langsamer machen
}
lastTick=GPIOR1;
}
int main() {
uchar mcusr = MCUSR;
MCUSR = 0;
ACSR |= 0x80; // Analogvergleicher abschalten: –70 µA
WDTCR = 0x18; // Watchdog überlebt RESET: abschalten!
WDTCR = 0;
PRR = 0b00001011; // USI, ADC, Timer1 aus
PORTB = 0b00111001; // Pull-Up an alle Eingänge
GIMSK = 0x40;
if (mcusr & (1 << WDRF)) { // Wenn die Reset-Ursache der Watchdog war...
if (USBIN & USBMASK) { // kein SE0-State?
MCUCR = 0x30; // SLEEP_MODE_PWR_DOWN und Pegelinterrupt an INT0
sei(); // Interrupts müssen für WakeUp eingeschaltet sein
sleep_cpu(); // Oszillator anhalten, warten auf INT0-LOW-Pegel
cli(); // INT0 aufgetreten (kann nur USB-RESET == SE0 sein)
}
}
MCUCR = 0x20; // SLEEP_MODE_STANDBY
TCCR0B= 0x02; // Achtel-Taktfrequenz
usbInit();
WDTCR = 0x08; // Watchdog mit kürzestmöglicher Zeit (16 ms) aktivieren
uchar sof=GPIOR0; // SOF-Zähler modulo 256
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(GPIOR0-sof);
sof = GPIOR0;
if (usbInterruptIsReady()) { // Nach (unter Windows) 8 ms
uchar chg=~PINB>>3&3^keystate; // PB3 und PB4 abfragen
// Ohne Idle-Rate kommt der anfänglich gedrückte Knopf nicht (unter Windows) an.
// Müsste eigentlich per Protokollanalyse debugt werden, aber so geht's auch.
if (chg || !sof) { // Idle-Rate: 256 ms
keystate^=chg; // Änderungen merken
usbSetInterrupt(&keystate,1);
}
}
}
}
Detected encoding: UTF-8 | 0
|