| 1 | /* $Id: rfm12.c,v 1.2 2010/07/24 20:55:48 simimeie Exp $ |
| 2 | * Functions for communicating with the rfm12(b) module |
| 3 | */ |
| 4 | |
| 5 | #include <avr/io.h> |
| 6 | #include "rfm12.h" |
| 7 | |
| 8 | /* Unfortunately, the manufacturer documentation for chip and module is |
| 9 | * almost unreadable crap. |
| 10 | * This one is better: http://www.mikrocontroller.net/articles/RFM12 |
| 11 | */ |
| 12 | /* Note: the signal is inverted, thus pulling this high DEselects the chip */ |
| 13 | #define spi_ss_hi() { PORTC |= _BV(0); } |
| 14 | /* Consequently, this SELECTS the chip */ |
| 15 | #define spi_ss_lo() { PORTC &= (uint8_t)~_BV(0); } |
| 16 | |
| 17 | /* write and (at the same time) read a word from RFM12. |
| 18 | * The module uses 16 bit commands and replies that are |
| 19 | * synchronous to the command - except for the status read. |
| 20 | * Note: This does not handle chip select! */ |
| 21 | static uint16_t rfm12_spi_wrword(uint16_t v) { |
| 22 | uint16_t res; |
| 23 | /* Start transmission */ |
| 24 | SPDR = v >> 8; |
| 25 | /* Wait for transmission complete */ |
| 26 | while ((SPSR & _BV(SPIF)) == 0) { |
| 27 | } |
| 28 | res = (uint16_t)(SPDR) << 8; |
| 29 | SPDR = v & 0xff; |
| 30 | /* Wait for transmission complete */ |
| 31 | while ((SPSR & _BV(SPIF)) == 0) { |
| 32 | } |
| 33 | res |= SPDR & 0xff; |
| 34 | return res; |
| 35 | } |
| 36 | |
| 37 | void rfm12_sendcommand(uint16_t cmd) { |
| 38 | spi_ss_lo(); |
| 39 | rfm12_spi_wrword(cmd); |
| 40 | spi_ss_hi(); |
| 41 | } |
| 42 | |
| 43 | uint32_t rfm12_readstatus(void) { |
| 44 | uint32_t res; |
| 45 | spi_ss_lo(); |
| 46 | res = (uint32_t)rfm12_spi_wrword(0x0000) << 16; |
| 47 | res |= rfm12_spi_wrword(0x0000); |
| 48 | spi_ss_hi(); |
| 49 | return res; |
| 50 | } |
| 51 | |
| 52 | void rfm12_init(void) { |
| 53 | /* Set MOSI, SCK and SS output, MISO input */ |
| 54 | /* Frickel Alert: Also set PB2 output. It is not used, but noise on the |
| 55 | * pin could reset our SPI mode to slave (!!!) if it is left as input. */ |
| 56 | DDRB |= _BV(3) | _BV(5) | _BV(2); |
| 57 | DDRC |= _BV(0); |
| 58 | DDRB &= (uint8_t)~_BV(4); |
| 59 | spi_ss_hi(); |
| 60 | #if (CPUFREQ <= 10000000UL) |
| 61 | /* Enable SPI, Master, set clock rate fck/4. |
| 62 | * This works as long as our own clockrate is below 10 MHz - because |
| 63 | * 2.5 MHz is the maximum the rfm12b can do. */ |
| 64 | SPCR |= _BV(SPE) | _BV(MSTR); |
| 65 | #else |
| 66 | /* For higher clock rates set fck/16. */ |
| 67 | SPCR |= _BV(SPE) | _BV(MSTR) | _BV(SPR0); |
| 68 | #endif /* CPUFREQ */ |
| 69 | /* Now send some initialization commands. */ |
| 70 | /* Mostly taken over from http://www.joachim-neu.de/post/70/rfm12b-868/ |
| 71 | * Seems to be mostly black magic anyways. */ |
| 72 | rfm12_sendcommand(0x80E7); /* EL, EF, 868band, 12.0pF */ |
| 73 | rfm12_sendcommand(0x8219); /* !er, !ebb, !ET, ES, EX, !eb, !ew, DC */ |
| 74 | /* Frequency setting: 0xAnnn; the 12 nnn-bits of value are |
| 75 | * calculated like this: v = ((freq / 20) - 43) * 4000 |
| 76 | */ |
| 77 | rfm12_sendcommand(0xA7BC); /* 869,900 MHz */ |
| 78 | rfm12_sendcommand(0xC647); /* 4.8kbps FIXME? */ |
| 79 | rfm12_sendcommand(0x94C0); /* VDI, FAST, 67kHz, 0dBm, -103dBm */ |
| 80 | rfm12_sendcommand(0xC2AC); /* AL, !ml, DIG, DQD4 */ |
| 81 | rfm12_sendcommand(0xCA81);//FIFO8,SYNC,!ff,DR |
| 82 | rfm12_sendcommand(0xCED4);//SYNC=2DD4; |
| 83 | rfm12_sendcommand(0xC483);//@PWR,NO RSTRIC,!st,!fi,OE,EN |
| 84 | rfm12_sendcommand(0x9820);// !mp,45kHz,MAX OUT*/ |
| 85 | rfm12_sendcommand(0xCC77);//OB1,OB0, LPX,!ddy,DDIT,BW0 |
| 86 | rfm12_sendcommand(0xE000);//NOT USE |
| 87 | rfm12_sendcommand(0xC800);//NOT USE |
| 88 | rfm12_sendcommand(0xC040);//1.66MHz,2.2V |
| 89 | } |