/* ////////////////////////////////////////////////////////////////////////// Bedienung des RFM12 Transceivermoduls *//////////////////////////////////////////////////////////////////////////// // >>>>>>>>>>>>> Globale Variablen <<<<<<<<<<<<<<<<<<<<<<<< enum brate {b1200,b2400,b4800,b9600,b19200,b38400}; byte RF_TXBuf[64+1]; // RF Sendepuffer byte RF_TXPtr; // Zeiger auf Sende Puffer byte RF_RXBuf[64+1]; // RF Empfangspuffer byte RF_RXPtr; // Zeiger Empfangspuffer int16 RF_RX_DataSets, // Summe empfangene Datensätze RF_RX_Errors; // Summe Fehler beim Empfangen bit f_RF_NewData; // 1 = Es liegen neue Daten vor bit f_RF_RecInProgress; // 1 = Es läuft eine IRQ Übertragung ab // interne IRQ Hilfsvariablen für Empfang von Daten byte RF_FIFO_State; // Zeiger für Empfangs Statemachine im IRQ bit f_RX_FirstByte; // Flag für erstes Byte eines Datensatzes // --------- Prototypen ---------- void RF_Init(); // RFM12 initialisieren void RF_SetTX(); // Sender einschalten void RF_SetRX(); // Empfänger einschalten void RF_Sleep(); // Schlafen legen void RF_Wakeup(); // Aufwachen void RF_Baudrate(brate); // Datenrate einstellen void RF_TX_Power(byte); // Sendeleistung einstellen void RFTX_Send(byte*, byte); // Datensatz senden // --- Intern ---- void RF_INT_RSFIFO(); void RF_SendByte(byte); void RF_CMD(int16); void RF_GetStatus(); byte SPIRF(byte); void RF_Init_SPI(); struct RF_Status { int16 FFIT :1; int16 POR :1; int16 FFOV :1; int16 WKUP :1; int16 EXT :1; int16 LBD :1; int16 FFEM :1; int16 RSSI :1; int16 DQD :1; int16 CRL :1; int16 ATGL :1; int16 SIGN :1; int16 OFFS :4; }; struct RF_Status RFS; // enzhält die Statusinfo // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALLGEMEIN <<<<<<<<<<<<<<<<<<<<<<<<<<<< // Initialisierung des RFM12 Modules void RF_Init() { disable_interrupts(GLOBAL); RF_Init_SPI(); RF_CMD(0x80D7); // EL,EF,12.0pF RF_CMD(0x8208); // !er,!ebb,!ET,ES,EX,!eb,!ew,!DC (in Bereitschaft gehen) RF_CMD(0xC4E7); // @PWR,autotune: -10kHz...+7,5kHz,!st,!fi,OE,EN RF_CMD(0xA640); // A140=434MHz RF_CMD(0xC647); // 4,8kbps RF_CMD(0xC2AC); // Data Filter RF_CMD(0x94A0); // VDI,FAST,134kHz,0dBm,-103dBm RF_CMD(0xCA81); // FIFO8,SYNC,!ff,DR RF_CMD(0x9850); // !mp,9810=30kHz,MAX OUT RF_CMD(0xE000); // NOT USE RF_CMD(0xC800); // NOT USE RF_CMD(0xC000); // kein WDT, kein Batteriewächter RF_RX_DataSets = 0; RF_RX_Errors = 0; enable_interrupts(GLOBAL); } // >>>> RXTX: Sleep Mode <<<<< void RF_Sleep() { RF_CMD(0x8201); // Alles intern abschalten } // >>>>> Modul aufwecken <<<<<< void RF_Wakeup() { RF_CMD(0x80D7); //EL,EF,12.0pF RF_CMD(0x8208); //!er,!ebb,!ET,ES,EX,!eb,!ew,!DC (in Bereitschaft gehen) delay_ms(5); } ///////////////////////////////////////////////////////////////////// /* Frequenz einstellen (Kanal wählen) Die Frequenz wird nach der Formel f0 = 10 * (43+F/4000) [Mhz] berechnet und kann mit 12 Bit eingestellt werden mit F = 96..3903 ergibt sich F: 430,24 Mhz < f < 439,75 Mhz in Schritten zu df = 2,5khz Damit sich keine Überschneidungen ergeben wird eine Schrittweite von 250khz (n=100) gewaehlt, daraus ergeben sich 39 wahlfreie Kanaele auf den gesendet und empfangen werden kann. Kanal 0 = 430,25 Mhz Kanal 1 = 430,5 Mhz Kanal 2 = 430,75 Mhz ..... Kanal 38 = 439,75 Mhz */ void RF_SetChannel(byte kanal) { int16 F; disable_interrupts(GLOBAL); RF_Init_SPI(); if (kanal>38) kanal = 38; // Bereichsfehler Fehler abfangen F = 96+((int16)kanal*100); // Berechnung von F aus Kanal RF_CMD (0xA000|F); // Frequenz setzen enable_interrupts(GLOBAL); } // >>>>>> TX: Setze Baudrate <<<<< // 0= 600, 1=1200, 2=2400, 3=4800, 4=9600, 5=19200, 6=38400 // BR = 344827/(R+1)/(1+cs*7) [baud] cs=1 => 8 cs=0 => 1 void RF_Baudrate(brate baudrate) { disable_interrupts(GLOBAL); // 600 => cs=1 R=0x47 (C7) 0 // 1200 => cs=1 R=0x23 (A3) 1 // 2400 => cs=1 R=0x11 (91) 2 // 4800 => cs=0 R=0x47 3 // 9600 => cs=0 R=0x22 4 // 19200 => cs=0 R=0x11 5 // 38400 => cs=0 R=0x05 6 RF_Init_SPI(); switch (baudrate) { case b1200 : RF_CMD(0xC6A3); // 1200 Baud break; case b2400 : RF_CMD(0xC691); // 2400 Baud break; case b4800 : RF_CMD(0xC647); // 4800 Baud break; case b9600 : RF_CMD(0xC622); // 9600 Baud break; case b19200 : RF_CMD(0xC611); // 19200 Baud break; case b38400 : RF_CMD(0xC605); // 38400 Baud break; default : RF_CMD(0xC647); break; } enable_interrupts(GLOBAL); } // >>>>> RXTX: Hole Statusbits in struct hinein void RF_GetStatus() { disable_interrupts(GLOBAL); RF_Init_SPI(); ChipSelect(devRFM,1); hibyte(RFS) = SPIRF(0x00); lobyte(RFS) = SPIRF(0x00); ChipSelect(devRFM,0); enable_interrupts(GLOBAL); } // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SENDEN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // Umschalten auf Sender void RF_SetTX() { disable_interrupts(INT_EXT1); // nIRQ auf INT1 (Pin B1) deaktivieren clear_interrupt(INT_EXT1); RF_CMD(0x8208); delay_us(500); } // >>>> TX: Setze Sendeleistung <<<< // 0(min) - 7 (max) void RF_TX_Power(byte power) { byte val; RF_Init_SPI(); disable_interrupts(GLOBAL); val = 7-power; RF_CMD(0x9850 | val); //!mp,9810=30kHz enable_interrupts(GLOBAL); } // TX: Senden einer Bytefolge #define RF_TX_ON 0x8239 #define RF_TX_OFF 0x8209 #define RF_CHECKPOS 4 void RFTX_Send(byte* data, byte size) { byte check,k,i; RF_Init_SPI(); disable_interrupts(GLOBAL); disable_interrupts(INT_EXT1); // nIRQ auf INT1 (Pin B1) deaktivieren INT1IF = 0; RF_CMD(0x8208); delay_ms(1); // RF_GetStatus(); // Notwendig, sonst klappt Senden nicht (seltsam!) RF_CMD(0x8238); // Transmitter EIN RF_SendByte(0xAA); // Präambel ins Latch, lockt die PLL RF_SendByte(0xAA); // Präambel ins Latch, lockt die PLL RF_SendByte(0xAA); // Präambel ins Latch, lockt die PLL RF_SendByte(0x2D); // FIFO Schlüssel 2DD4 (öffnet das Tor) RF_SendByte(0xD4); // --- Ab jetzt benutzerdefiniert --- RF_Sendbyte(MASTER_START); RF_Sendbyte(size); // Anzahl folgender Bytes senden (incl Checksumme) // Sendeschleife k = 0; check = 0; for (i=0; i=RF_CHECKPOS) // und zwischendurch immer wieder einfügen { RF_SendByte(check); k=0; } data++; } RF_Sendbyte(check); // Checksumme RF_SendByte(0xAA); // Dummy, nachschieben als Pipelineputzer RF_SendByte(0xAA); // Dummy, nachschieben als Pipelineputzer RF_CMD(RF_TX_OFF); // TX off enable_interrupts(GLOBAL); } // Sendet den Sendepuffer los, RF_TXPtr muss auf letztes Byte zeigen void RF_TX_SendBuffer() { RFTX_Send(RF_TXBuf, RF_TXPtr); } ////////////////////////////////////////////////////////////////////////////// // // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> EMPFANGEN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // ////////////////////////////////////////////////////////////////////////////// // >>>>> RX: Umschalten auf Empfang void RF_SetRX() { disable_interrupts(GLOBAL); RF_Init_SPI(); RF_CMD(0x82C8); //Receiver Chain einschalten, Synth aus. RF_GetStatus(); // !!! Status holen, sonst klappt es nicht !!! EXT_INT_EDGE(1,H_TO_L); // INT1 fallende Flanke clear_interrupt(INT_EXT1); // Flag löschen RF_FIFO_State = 0; f_RX_FirstByte = 1; RF_CMD(0xCA81); // Fifo Reset RF_CMD(0xCA83); enable_interrupts(INT_EXT1); // nIRQ auf INT1 (Pin B1) aktivieren enable_interrupts(GLOBAL); } // Setze DQD Parameter (0..7) void RF_RX_SetDQDFilter(byte dqd) { int16 wert; disable_interrupts(GLOBAL); RF_Init_SPI(); wert = 0xC288; // AL=1, Slow Mode, Digital Filter wert = wert | dqd; RF_CMD(wert); enable_interrupts(GLOBAL); } // Setze LNA Gain, RSSI Threshold und Bandweite /* Bandweite: 1(max) ... 6 (min) LNA Gain : 0(max) ... 3 (min) RSSI : 0(min) ....7 (max) */ void RF_SetRX_LNA(byte bw, byte lna, byte rssi) { int16 wert; disable_interrupts(GLOBAL); RF_Init_SPI(); if (bw>6) bw=6; // Werte prüfen if (lna>3) lna=3; if (rssi>7) rssi=7; wert = 0x9400; // Kommando, VDI, Fast wert = wert | (bw<<5); // BW einmaskieren wert = wert | (lna<<3); // LNA Gain einmaskieren wert = wert | rssi; // RSSI Threshold einmaskieren RF_CMD(wert); enable_interrupts(GLOBAL); } // ============================ INTERRUPT ========================================== // ----- IRQ Routinen Nicht aus Hauptprogramm aufufen !!! // Liest 1 Bytes aus dem FIFO aus byte RF_INT_ReadFIFO() { byte data; ChipSelect(devRFM,1); SPIRF(0xB0); data = SPIRF(0x00); ChipSelect(devRFM,0); return(data); } // IRQ: Fifo resetten void RF_INT_RSFIFO() { RF_CMD(0xCA81); // Fifo Reset RF_CMD(0xCA83); } // Timeoutzähler für Datenempfang, bricht Empfang ab, wenn 50ms kein Byte gekommen ist // damit der Interrupt frei bleibt. #INT_TIMER1 void IRQTimer1() { RF_INT_RSFIFO(); // Fifo sperren setup_timer_1(T1_DISABLED); // Timer 1 abschalten RF_RXPtr = 0; // Datenzeiger rücksetzen RF_FIFO_State = 0; // Statemachine rücksetzen f_RF_RecInProgress = 0; // Empfang beendet anzeigen f_RX_FirstByte = TRUE; // Timeout wieder aktivieren für nächsten Empfang } // >>> IRQ: Empfangsroutine, wenn Datenbytes im Fifo eingetroffen sind <<<< // übernimmt immer den ganzen Datensatz, überschreibt alte Daten #INT_EXT1 void nIRQReceive() { static byte RF_Check,k,RF_RecAnzahl; byte reccheck,data; setup_oscillator(OSC_VOLLGAS); set_timer1(0xffff-8000); // 8ms Timeout (reicht für 1200 baud) // Timeout setzen, falls Datensendung abbricht if (f_RX_FirstByte) { f_RX_FirstByte = FALSE; // Timer 1 starten machen setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); // 1us Zähltakt @32 Mhz clear_interrupt(INT_TIMER1); enable_interrupts(INT_TIMER1); } RF_Init_SPI(); // SPI klarmachen f_RF_RecInProgress = TRUE; // Empfang anzeigen // ----- State 0 Byte abholen (Master Kennung) ------ if (RF_FIFO_State == 0) { if(RF_INT_ReadFIFO()!=MASTER_START) goto enderror; RF_FIFO_State = 1; // Kennung war ok, Buffer füllen RF_RXPtr = 0; goto endirq; } // ---- State 1 (Anzahl Bytes abholen) ---- if (RF_FIFO_State == 1) { RF_RecAnzahl = RF_INT_ReadFIFO(); if (RF_RecAnzahl>sizeof(RF_RXBuf)) // Passt es? goto enderror; RF_check = 0; RF_FIFO_State = 2; k=0; goto endirq; } // ----- State 2 (Datensatz abholen) ---- if (RF_FIFO_State == 2) { data = RF_INT_ReadFIFO(); // Byte abholen if (k++>=4) // Checksumme oder Datum? { if (data!=RF_check) // Passt die Zwischenchecksumme? goto enderror; k=0; } else { RF_RXBuf[RF_RXPtr++] = data; RF_check += data; } if (RF_RXPtr>sizeof(RF_RXBuf)) // Bufferüberlauf droht? goto enderror; if (RF_RXPtr>=RF_RecAnzahl) // Erwartete Anzahl Bytes da? { RF_FIFO_State = 3; // dann Checksumme abholen goto endirq; } goto endirq; } // ----- State 3 (Checksumme prüfen) ------- if (RF_FIFO_State == 3) { reccheck = RF_INT_ReadFIFO(); if (reccheck!=RF_check) goto enderror; // Es wurde ein kompletter Datensatz empfangen setup_timer_1(T1_DISABLED); // Timer 1 abschalten disable_interrupts(INT_TIMER1); f_RX_FirstByte = TRUE; RF_FIFO_State = 0; // Alles ok, dann State zurücksetzen f_RF_RecInProgress = 0; // Empfangs Flag zurücksetzen f_RF_NewData = 1; // Neue Daten im Buffer melden RF_RX_DataSets++; // Datensätze +1 RF_INT_RSFIFO(); goto endirq; } // ----- Raussprung mit Fehler ------ enderror: setup_timer_1(T1_DISABLED); // Timer 1 abschalten disable_interrupts(INT_TIMER1); f_RX_FirstByte = TRUE; RF_FIFO_State = 0; // State rücksetzen f_RF_RecInProgress = FALSE; RF_RX_Errors++; RF_INT_RSFIFO(); endirq: setup_oscillator(OSC_NORMAL); #asm nop #endasm } // ================== Interne Funktionen ============================ /////////////////////////////////////////////////////////////////////////////////// // Konfiguriere die SPI als Master // Speed = 0,1,2 void RF_Init_SPI() { TRISC = TRISC & 0b11010111; // SDO,SCK auf Ausgang TRISC = TRISC | 0b00010000; // SDI Eingang // SPI einrichten SSPCON1 = 0b00000001; // Master Mode, fosc/16 SSPSTAT_SMP = 0; SSPCON1_CKP = 0; // Daten bei fallender Flanke SSPSTAT_CKE = 1; SSPCON1_SSPEN = 1; // SPI bleibt dauerhaft an } // >>> RXTX: Eigene SPI Routine <<<< byte SPIRF(byte data) { byte rec; // Solange SPI beschäftigt ist hämmere ihr die Daten ein bis frei wird LED_AKTIV = 1; do { SSPCON1_WCOL = 0; SSPBUF = data; // Sende Register beschreiben } while (SSPCON1_WCOL); // Datentransfer läuft, jetzt warten bis Byte vom Slave reingekommen while (!SSPSTAT_BF); rec = SSPBUF; LED_AKTIV = 0; return(rec); } // RXTX: >>> Überträgt ein 16Bit Kommando an RFM12 <<<<< void RF_CMD(int16 wort) { RF_Init_SPI(); ChipSelect(devRFM,1); SPIRF(hibyte(wort)); SPIRF(lobyte(wort)); ChipSelect(devRFM,0); } // TX: Datenbyte raussenden (1=warten, 0=sofort senden (nur erstes Byte)) void RF_SendByte(byte data) { ChipSelect(devRFM,1); while (RFM12IRQ); SPIRF(0xB8); SPIRF(data); ChipSelect(devRFM,0); }