/* Name: fe.cpp (Sandkasten-Empfänger, eine Art AccessPoint)
* Projekt: Drahtlose VR-Sandkasten
* Tabweite: 8, Kodierung: UTF-8, Zeilenenden: LF
*/
#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 <avr/fuse.h>
#include <avr/signature.h>
extern "C"{
#include "usbdrv.c"
}
#include "../rf12/rf12.h"
FUSES={
0b11000001, // Interner High-Speed-Oszillator 16 MHz, hochfahren mit BOD, kein Taktteiler
0b01010101, // RESET als Portpin, BrownOut bei 2,7 V
0b11111111, // unverändert
};
/************
* Hardware *
************/
/* Anschlüsse des Mikrocontrollers ATtiny45:
PB0 (5) DI RFM12 SDO
PB1 (6) DO RFM12 SDI, grüne LED nach 00
PB2 (7) SCK RFM12 SCK, rote LED nach 00
PB3 (2) PCINT3 USB D-, 1,5 kΩ nach 3P3
PB4 (3) USB D+
PB5 (1) RFM12 !SEL
Ucc (8) 3P3 3,3 V vom LP2950-33
┴ (4) 00 Masse
*/
/*******
* USB *
*******/
#define DW(x) (x)&0xFF,(x)>>8
#define DD(x) DW((x)&0xFFFF),DW((x)>>16)
// USB Report-Deskriptor
PROGMEM const uchar 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,6, // G Report Count (6)
0x05,0x09, // G Usage Page (Buttons)
0x19,1, // L Usage Minimum (Button 1)
0x29,6, // L Usage Maximum (Button 6)
0x81,0x02, // M Input (Var)
0x95,2, // G Report Count (2)
0x81,0x01, // M Input (Const)
0x05,0x06, // G Usage Page (Generic Device Controls)
0x09,0x21, // L Usage (Wireless Channel)
0x75,8, // G Report Size (8 bit)
0x95,1, // G Report Count (1)
0x15,1, // G Logical Minimum (1)
0x25,12, // G Logical Maximum (12)
0xA4, // G Push
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)
0xB1,0x02, // M Feature (Var)
0xB4, // G Pop – entfernt physikalisches Maß
0x09,0x22, // L Usage (Wireless ID)
0x25,15, // G Logical Maximum (15)
0xB1,0x02, // M Feature (Var)
0xC0}; // M End Collection
static struct {
byte k; // Tastenstatus
operator byte*() {return (byte*)this;}
}InputReport;
// Wireless_ID ist (nur) mit EEPROM-Programmiergerät änderbar,
// Kanalnummer per Hopping-Paket vom Funksender aus
EEMEM struct ee_t{
byte ch,id;
operator byte*() {return (byte*)this;}
}ee={(WIRELESS_ID-1)%12+1,WIRELESS_ID};
static ee_t FeatureReport={(WIRELESS_ID-1)%12+1,WIRELESS_ID};
/*******
* LED *
*******/
// grün: empfangsbereit
// grün verlöschend: Empfangssignal (zugeordnete Wireless_ID)
// rot: Fehler, Timeout (blockiert RFM12-Kommunikation)
const bool LED_BY_SPI=true;
static void green_off() {
if (LED_BY_SPI) { // Variante 1
USIDR = 0; // SPI-Senderegister-MSB mit 0-Bit laden
}else{ // Variante 2
PORTB&=~0x02;
USICR = 0; // Portpin dem SPI entreißen
}
}
static void green_on() {
if (LED_BY_SPI) { // Variante 1
USIDR = 0x80; // SPI-Senderegister-MSB mit 0-Bit laden
}else{ // Variante 2
PORTB|= 0x02;
USICR = 0; // Portpin dem SPI entreißen
}
}
static void red_on() { // Rote LED einschalten, grün geht dabei aus
rf12::ss_hi();
USICR = 0; // USI deaktivieren
PORTB|= 0x04;
}
/*********************
* OUT data received *
*********************/
uchar usbFunctionWrite(uchar *data, uchar len) {
if (len>=2) {
byte b=data[0];
if (byte(b-1)<12 && FeatureReport.ch != b) {
rf12::channel = FeatureReport.ch = b;
eeprom_write_byte(&ee.ch,b);
}
b=data[1];
if (byte(b-1)<15 && FeatureReport.id != b) {
FeatureReport.id = b;
eeprom_write_byte(&ee.id,b);
}
}
return 1;
}
/***********************
* SETUP data received *
***********************/
uchar usbFunctionSetup(uchar data[8]) {
usbRequest_t *rq = (usbRequest_t*)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 = InputReport;
return sizeof InputReport;
}else if (rq->wValue.bytes[1] == 3) { // Feature
usbMsgPtr = FeatureReport;
return sizeof FeatureReport;
}
}else if (rq->bRequest == USBRQ_HID_SET_REPORT) { // wValueH: ReportType, wValueL: ReportID
if (rq->wValue.bytes[1] == 3) { // Feature
return 0xFF; // OUT entgegennehmen
}
}
}
return 0;
}
/*************************************
* Initialisierung und Hauptschleife *
*************************************/
static void HardwareInit() {
byte b = eeprom_read_byte(&ee.ch);
if (byte(b-1)<12) rf12::channel = FeatureReport.ch = b;
b = eeprom_read_byte(&ee.id);
if (byte(b-1)<15) FeatureReport.id = b;
DDRB = 0x26;
rf12::powerup();
rf12::init();
rf12::xfer(0); // (unnötig)
TCCR0B= 0x02; // Zeitgeber 0 mit Vorteiler 8 (erforderlich für USB)
TCCR1 = 0x0B; // Zeitgeber 1 mit Vorteiler 1024
rf12::rxmode(); // Lauscher auf Empfang
rf12::xfer(0); // Interrupt loseisen (unnötig)
green_on();
}
static byte keychange; // !=0 wenn InputReport zu senden ist
static byte led_countdown; // für beide LEDs
static byte resend_countdown; // hier: Wenn der Sender nicht wiederholt, Tasten lösen
static void check_ether() {
if (rf12::data()) { // Stehen Daten zum Abholen bereit??
led_countdown = 30; // Mit dem nächsten rxdata() ist die grüne LED unkontrolliert
if (!rf12::rxdata(rf12::buf)) { // Datenblock lesen, Prüfsumme OK?
if (rf12::buf.sendrecv==FeatureReport.id<<4) {
if (rf12::buf.type_len==0x21) { // 1 Byte mit ACK-Anforderung?
keychange=(InputReport.k^rf12::buf.data[0])&0x3F;
InputReport.k^=keychange;
rf12::buf.type_len=0x10; // Ack mit Länge 0
rf12::buf.sendrecv=FeatureReport.id; // zum Absender, Hich-Nibble = 0 (Master)
rf12::txdata(rf12::buf); // zurücksenden
resend_countdown = InputReport.k ? 140 : 0; // erwarte nächsten Tastendruck in 2200 ms
led_countdown = 10;
}else if (rf12::buf.type_len==0x81 && byte(rf12::buf.data[0]-1)<12) {
rf12::channel=FeatureReport.ch=rf12::buf.data[0]; // Frequenz-Hopping!
rf12::setfreq();
led_countdown = 20;
}else red_on();
}else red_on();
}else red_on();
green_off(); // Irgendetwas wurde empfangen
}
}
static void OnTimer() {
if (led_countdown && !--led_countdown) {
rf12::xfer(0); // schaltet rote LED aus
green_on();
}
if (resend_countdown && !--resend_countdown) {
keychange=InputReport.k;
InputReport.k=0;
led_countdown=15;
red_on();
}
}
static void tuneOscillator() {
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 (GPIOR0==1) { // Ohne Überlauf?
schar t = GPIOR1-lastTick-SOLL; // zentriere Ergebnis um die Null
if (t<-MABW) OSCCAL++; // schneller machen
if (t> MABW) OSCCAL--; // langsamer machen
}
cli();
lastTick=GPIOR1;
sei();
GPIOR0=0; // noch im cli()
}
int main() {
byte mcusr = MCUSR; // Reset-Ursache einfangen
OSCCAL= 160;
MCUSR = 0;
ACSR = 0x80; // Analogvergleicher abschalten: –70µA
PRR = 0x01; // ADC wird nicht benötigt
rf12::powerdown();
WDTCR = 0x08; // Watchdog mit kürzestmöglicher Zeit (16 ms) aktivieren
if (mcusr & 1<<WDRF // Watchdog war Reset-Ursache?
&& USBIN & USBMASK) { // kein SE0 (Aufwecken?) dann weiterschlafen!
wdt_reset();
MCUCR = 0x30; // SLEEP_MODE_PWR_DOWN (schaltet Oszillator ab)
sleep_cpu(); // Schlafen bis zum nächsten Watchdog-Reset
} // ergibt Verhältnis Wach : Schlaf = 1,4 : 16
MCUCR = 0x20; // SLEEP_MODE_IDLE
HardwareInit();
PCMSK = 9; // USB D-
usbInit();
sei();
for (;;) { // Endlos-Hauptschleife, wird höchstens vom Watchdog abgebrochen
if (USBIN&USBMASK) sleep_cpu();// CPU schläft bis SOF; 16 ms kein SOF führt zum Reset
usbPoll();
if (GPIOR0) {
wdt_reset();
tuneOscillator();
}
// _delay_us(20);
check_ether();
if (TIFR&0x04) { // Zeitgeber 1: Unterbrechungsanforderung?
TIFR=0x04; // löschen
OnTimer(); // alle 1/F_CPU*1024*256 = 16 ms ausführen
}
if (usbInterruptIsReady() && keychange) { // Pegelwechsel aufgetreten?
usbSetInterrupt(InputReport,sizeof InputReport);
keychange=0;
}
}
}
| Detected encoding: UTF-8 | 0
|