RFM12B-868

ACHTUNG: Ich übernehme keine Haftung für durch in diesem Beitrag bereitgestellten Programmcode verursachte Schäden! Die Benutzung erfolgt ausschließlich auf eigene Gefahr!

Mein Touchpanel habe ich auch mit Sensoren ausgestattet, die bisher nur exemplarisch und sehr dürftig den Wasserstand meiner Zimmerpflanzen überwachen können. Für die Kommunikation mit der restlichen Hardware habe ich mich für eine Funkverbindung entschieden, weil diese sehr flexibel ist und ohne Verdrahtung auskommt. Als Funkmodul verwende ich ein auf dem RF12-Chip basierendes Funkmodul namens RFM12B im 868-MHz-Frequenzband.

Die Ansteuerung der 868-MHz-Variante des Funkmoduls RFM12B war eine langwierige und sehr unangenehme Angelegenheit, nicht zuletzt aufgrund fehlender funktionstüchtiger Beispielcodes und unvollständiger Datenblätter. Letztlich gelang es dennoch nach dem dritten From-Scratch-Neuschreiben des Treibers, mit viel solidarischer Hilfe eines guten Freundes und einer großen Portion Unterstützung aus dem WWW, die ersten Nachrichten erfolgreich über die frische Funkschnittstelle zu übermitteln.

Alles Weitere ließ sich dann glücklicherweise schneller umsetzen als der steinige harte Anfang. Und auch wenn die Verbindungsqualität derzeit noch miserabel ist (die Reichweite beträgt etwa 50 cm) und wünschenswerte Features wie Interrupt-basierte Übertragung fehlen, möchte ich den Code hier zur freien Verfügung aber ohne jegliche Art von Garantien zur Verfügung stellen. In der Hoffnung, anderen damit das Leben ein wenig erleichtern zu können.

Der Treiber besteht aus drei Dateien, "rfm12b.h", "rfm12b.c" und "config.h". Bei der Benutzung müssen unbedingt die geltenden gesetzlichen Bestimmungen für die verwendete Funkfrequenz beachtet werden!

config.h

// _I = INPUT PORT
// _O = OUTPUT PORT
// _P = PORT INDEX
// _D = DATA DIRECTION REGISTER

// SPI SCK (clock, output)
#define RFM12B_SCK_I PINB #define RFM12B_SCK_O PORTB #define RFM12B_SCK_P 2 #define RFM12B_SCK_D DDRB #define RFM12B_SCK_INIT() RFM12B_SCK_D |= (1<<(RFM12B_SCK_P)) #define RFM12B_SCK_LOW() RFM12B_SCK_O &= ~(1<<(RFM12B_SCK_P)) #define RFM12B_SCK_HIGH() RFM12B_SCK_O |= (1<<(RFM12B_SCK_P))
// SPI MISO / SDO (master in slave out, input) #define RFM12B_MISO_I PINB #define RFM12B_MISO_O PORTB #define RFM12B_MISO_P 1 #define RFM12B_MISO_D DDRB #define RFM12B_MISO_INIT() RFM12B_MISO_D &= ~(1<<(RFM12B_MISO_P)) #define RFM12B_MISO_LOW() ~(RFM12B_MISO_HIGH()) #define RFM12B_MISO_HIGH() RFM12B_MISO_I & (1<<(RFM12B_MISO_P))
// SPI MOSI / SDI (master out slave in, output) #define RFM12B_MOSI_I PINB #define RFM12B_MOSI_O PORTB #define RFM12B_MOSI_P 0 #define RFM12B_MOSI_D DDRB #define RFM12B_MOSI_INIT() RFM12B_MOSI_D |= (1<<(RFM12B_MOSI_P)) #define RFM12B_MOSI_LOW() RFM12B_MOSI_O &= ~(1<<(RFM12B_MOSI_P)) #define RFM12B_MOSI_HIGH() RFM12B_MOSI_O |= (1<<(RFM12B_MOSI_P))
// SPI SS / CS (slave select, output) #define RFM12B_SS_I PINB #define RFM12B_SS_O PORTB #define RFM12B_SS_P 4 #define RFM12B_SS_D DDRB #define RFM12B_SS_INIT() RFM12B_SS_D |= (1<<(RFM12B_SS_P)) #define RFM12B_SS_LOW() RFM12B_SS_O &= ~(1<<(RFM12B_SS_P)) #define RFM12B_SS_HIGH() RFM12B_SS_O |= (1<<(RFM12B_SS_P))

rfm12b.h

#include "config.h"
#include <avr/io.h>

uint16_t RFM12B_spi(uint16_t); void RFM12B_init_hard(); void RFM12B_init_soft(); void RFM12B_put(uint8_t); uint8_t RFM12B_get(); void RFM12B_transmit(uint8_t data[], uint8_t length); void RFM12B_receive(uint8_t *data, uint8_t length);
#define RFM12B_STATUS RFM12B_spi(0x0000) // INTs #define RFM12B_RGIT() (RFM12B_STATUS & 0x8000) #define RFM12B_WAIT_RGIT() while(!RFM12B_RGIT()) { } #define RFM12B_FFIT() (RFM12B_STATUS & 0x8000) #define RFM12B_WAIT_FFIT() while(!RFM12B_FFIT()) { }

rfm12b.c

#include "rfm12b.h"

// send and receive a 16bit-word via spi uint16_t RFM12B_spi(uint16_t cmd) { uint8_t i; uint16_t recv = 0; // start communication RFM12B_SCK_LOW(); RFM12B_SS_LOW(); // read 16 bits for(i=0; i<16; i++) { // send bit if(cmd & 0x8000) RFM12B_MOSI_HIGH(); else RFM12B_MOSI_LOW(); // clock RFM12B_SCK_HIGH(); recv<<=1; // receive bit if(RFM12B_MISO_HIGH()) { recv|=0x0001; } // clock RFM12B_SCK_LOW(); cmd<<=1; } // end communication RFM12B_SS_HIGH(); return recv; }
// initialize hardware (atmel) (CHECKED) void RFM12B_init_hard() { // set default values RFM12B_SS_HIGH(); RFM12B_MOSI_HIGH(); RFM12B_SCK_LOW(); // init ports RFM12B_SCK_INIT(); RFM12B_MOSI_INIT(); RFM12B_MISO_INIT(); RFM12B_SS_INIT(); }
// initialize software (rfm12b) (CHECKED) void RFM12B_init_soft() { RFM12B_spi(0x80D7);//EL,EF,433band,12.0pF RFM12B_spi(0x8239);//!er,!ebb,ET,ES,EX,!eb,!ew,DC /* RFM12B_spi(0xA640);//434MHz*/ RFM12B_spi(0xA74E);//869,35 MHz RFM12B_spi(0xC647);//4.8kbps RFM12B_spi(0x94A0);//VDI,FAST,134kHz,0dBm,-103dBm RFM12B_spi(0xC2AC);//AL,!ml,DIG,DQD4 RFM12B_spi(0xCA81);//FIFO8,SYNC,!ff,DR RFM12B_spi(0xCED4);//SYNC=2DD4; RFM12B_spi(0xC483);//@PWR,NO RSTRIC,!st,!fi,OE,EN /* RFM12B_spi(0x9850);//!mp,90kHz,MAX OUT*/ RFM12B_spi(0x9820);//!mp,45kHz,MAX OUT*/ RFM12B_spi(0xCC77);//OB1,OB0, LPX,!ddy,DDIT,BW0 RFM12B_spi(0xE000);//NOT USE RFM12B_spi(0xC800);//NOT USE RFM12B_spi(0xC040);//1.66MHz,2.2V }
// send a byte (CHECKED) // uint8_t data data to send void RFM12B_put(uint8_t data) { RFM12B_WAIT_RGIT(); RFM12B_spi(0xB800 + data); }
// receive a byte (CHECKED) uint8_t RFM12B_get() { RFM12B_WAIT_FFIT(); return RFM12B_spi(0xB000) & 0x00FF; }
// transmit multiple bytes (CHECKED) // uint8_t data[] data to transmit // uint8_t length length of the data to transmit void RFM12B_transmit(uint8_t data[], uint8_t length) { uint8_t i; // enable TX RFM12B_spi(0x8238); RFM12B_put(0xAA); RFM12B_put(0xAA); RFM12B_put(0xAA); RFM12B_put(0x2D); RFM12B_put(0xD4); for(i = 0; i < length; i++) { RFM12B_put(data[i]); } RFM12B_WAIT_RGIT(); // disable TX RFM12B_spi(0x8208); }
// receive multiple bytes (CHECKED) // uint8_t data[] buffer for received data // uint8_t length length of data to receive void RFM12B_receive(uint8_t *data, uint8_t length) { uint8_t i; // enable RX RFM12B_spi(0x82C8); // set FIFO mode RFM12B_spi(0xCA81); // enable FIFO RFM12B_spi(0xCA83); for(i = 0; i < length; i++) { data[i] = RFM12B_get(); } RFM12B_WAIT_FFIT(); // disable RX RFM12B_spi(0x8208); }

Weitere interessante Ressourcen zum Funkchip RFM12B sind http://jeelabs.net/projects/cafe/wiki/RF12 und http://jeelabs.org/2010/02/23/secure-transmissions/.

Tags: Touchpanel