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