/* Programm für VR-FlyStick (Bestückungsvariante C: mit Funktransceiver 433 MHz)
* Batteriebetrieb! Hardware: RFM12 (Pollin) sowie:
* Das Gerät benutzt 2 Knöpfe und 2 LEDs.
* grün = Senden OK, rot = Senden ging schief (kein Empfänger, Empfänger zu weit o.ä,)
* Kanalauswahl-Modus: beide Tasten 4 s gedrückt halten: LED = rot permanent
* zuerst Blink-Ausgabe der aktuellen Kanal-Nummer in Gelb zum Mitzählen
* obere Taste schaltet Kanal um 1 weiter, LED gelb pro Tastendruck
* längeres LED gelb wenn Kanal 1 erreicht
* untere Taste loslassen:
* obere nicht gedrückt = Ende, Speichern und Aussenden Frequenz-Hopping-Paket
* obere gedrückt = nicht speichern (Abbruchmöglichkeit)
* Gemessener Ruhestromverbrauch: 0,71 µA (120330)
*
* tabsize = 8, encoding = utf-8?, lineends = LF
*/
#include <string.h>
#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 <util/delay.h>
#include "../rf12/rf12.h"
/************
* Hardware *
************/
/*
Verwendung der Ports:
PA0 (13) - Taster T1 (oben)
PA1 (12) - Taster T2 (unten)
PA2 (11) - LED grün
PA3 (10) - LED rot
PA4 (9) (SCK) RFM12: SCK Programmieranschluss
PA5 (8) (MISO) RFM12: SDO Programmieranschluss
PA6 (7) (MOSI) RFM12: SDI Programmieranschluss
PA7 (6) - RFM12: !SEL und PullUp 47 kΩ
PB0 (2) - RFM12: FFIT (ungenutzt)
PB1 (3) - RFM12: !IRQ (ungenutzt)
PB2 (5) - RFM12: VDI (ungenutzt)
PB3 (4) /RESET RFM12: !RES Programmieranschluss
*/
BYTE keystate; // Tastenstatus, Bit 7 = Toggle-Bit bei Wiederholungen
ISR(PCINT0_vect) { // Pegelwechsel an PINA (Tasten)
}
ISR(TIM0_OVF_vect) { // für sinnvoll funktionierenden SLEEP-Befehl (16 ms)
}
/*************************************
* Initialisierung und Hauptschleife *
*************************************/
static void HardwareInit() {
ACSR = 0x80; // Analogvergleicher abschalten: –70µA
PORTA = 0x83;
PORTB = 0; // keine PullUps
DDRA = 0xDC; // SEL, MOSI, SCK, LEDs als Ausgänge
if (PINA&3==3) { // keine Taste gedrückt: Tiefschlaf!
rf12_powerdown();
PRR = 0x0F; // alles AUS
MCUCR = 0x30; // Power-Down: alles aus außer Pin-Change (Tasten)
GIMSK = 0x10; // Pin-Change an PINA
PCMSK0= 0x03; // beide Tasten
GIFR = 0x10; // anhängigen Interrupt löschen
sei();
PORTA|= 0x80; // !SEL = High wegen PullUp
sleep_cpu(); // warten auf Tastendruck
cli();
GIMSK = 0;
rf12_powerup(); // Quarz aktivieren (ca. 2 mA)
rf12_init(); // Baudrate u.v.a.m. einstellen
rf12_xfer(0); // Status auslesen (probeweise, IRQ löschen)
}
MCUCR = 0x20; // Idle
PRR = 0x0B; // Timer0 reaktivieren
TIFR0 = 0x01; // Überlaufbit löschen
TIMSK0= 0x01;
TCCR0B= 0x03; // Vorteiler 64, Überlauf alle 16 ms (Tastenabfragetakt)
sei();
}
int __attribute__((noreturn)) main(void) {
BYTE keychange=0;
BYTE resend_countdown=0;
// gedrückte Taste alle 1 s wiederholt aussenden, sonst denkt der Empfänger,
// der Sender sei tot, und nimmt den Tastendruck zurück
BYTE retry_countdown=0;
// 3 Sendeversuche unternehmen (die mit einem erfolgreichen ACK enden müssen)
// sonst bleibt die rote LED an zur Fehleranzeige
BYTE led_countdown=0;
BYTE prog_countdown=0;
BYTE b=eeprom_read_byte((void*)0xFFF0);
if ((BYTE)(b-1)<12) rf12_channel=b; // Arbeitskanal setzen
for (;;) { // Endlos-Hauptschleife
if (keystate&3 || retry_countdown || led_countdown);
else HardwareInit();
sleep_cpu(); // CPU schläft bis zu 16 ms, keine weiteren Interrupts
if (!keychange) {
keychange=(~PINA^keystate)&0x03;
if (keychange) prog_countdown = PINA&3 ? 0 : 255;
else if (!--resend_countdown) keychange=0x80;
}
if (prog_countdown && !--prog_countdown) {
// modale Schleife, untere Taste muss die ganze Zeit gedrückt bleiben!
PORTA |= 0x08; // rote LED permanent ein
rf12_powerdown(); //
// Kanalnummer ausspucken (grüne LED blinken lassen)
b=rf12_channel;
do{
BYTE k=10; do {sleep_cpu(); if (PINA&2) goto raus;} while(--k);
PORTA |= 0x04; // grüne LED ein
k=10; do {sleep_cpu(); if (PINA&2) goto raus;} while(--k);
PORTA &=~0x04; // grüne LED aus
}while(--b);
// Tastendrücke auswerten
do{
sleep_cpu();
keychange=(~PINA^keystate)&0x03;
if (~keystate&keychange&1) { // obere Taste gedrückt
rf12_channel = rf12_channel==12 ? 1 : rf12_channel+1;
PORTA|=0x04; // grüne LED ein
led_countdown=10;
if (rf12_channel==1) led_countdown=30; // länger für Kanal 1
}
if (keystate&keychange&1) { // obere Taste losgelassen
led_countdown=1;
}
if (led_countdown && !--led_countdown) PORTA&=~0x04; // grüne LED aus
keystate^=keychange;
keychange=0;
}while (!(PINA&2)); // mit dem Lösen des unteren Knopfes speichern
if (PINA&1) { // oberer Knopf nicht gedrückt?
eeprom_write_byte((void*)0xFFF0,rf12_channel); // abspeichern
rf12buf.type_len=0x81; // 1 Byte Daten
rf12buf.sendrecv=WIRELESS_ID<<4; // lokale ID = 1, Nachricht an Busmaster (0)
rf12buf.data[0]=rf12_channel;
rf12_txdata(rf12buf.all); // für Nahbereich: Empfänger informieren
}
raus:
PORTA&=~0x0C;
}
if (keychange) {
retry_countdown=4;
resend_countdown=61; // Nach jeweils 1 Sekunde wiederholen (KeepAlive)
keystate^=keychange;
keychange=0;
}
if (retry_countdown) {
rf12buf.type_len=0x21; // NeedAck, 1 Byte Daten
rf12buf.sendrecv=WIRELESS_ID<<4; // lokale ID = 1, Nachricht an Busmaster (0)
rf12buf.data[0]=keystate;
rf12_txdata(rf12buf.all);
rf12_xfer(0); // ??
BYTE tic=TCNT0;
do if (PINA&0x20) { // dann Empfang ohne TimeOut möglich
if (!rf12_rxdata(rf12buf.all) // CRC OK
// Die oberen Bits müssen gleich sein, RequestAck muss 0 sein,
// IsAck muss 1 sein, Länge muss (hier) 0 sein
&& rf12buf.type_len==0x10 // ACK-Bit und Länge 0
// Sender und Empfänger müssen vertauscht sein
&& rf12buf.sendrecv==WIRELESS_ID) {// Sender 0, Empfänger 1
PORTA&=~0x08; // rote LED aus
PORTA|= 0x04; // grüne LED ein
retry_countdown=0;
led_countdown=3; // 50 ms
goto ok;
}
break;
}while ((BYTE)(TCNT0-tic)<150);
PORTA|= 0x08; // rote LED ein
PORTA&=~0x04; // grüne LED aus
retry_countdown--;
led_countdown=14; // 0,2 s (erscheint länger wegen Wiederholungen)
ok:
if (!retry_countdown) rf12_powerup();
// Empfänger AUS (spart 11 mA zwischendurch, nur noch LED oder Timer0)
}
if (led_countdown && !--led_countdown) PORTA&=~0x0C;
}
}
Detected encoding: UTF-8 | 0
|