/** \param [val] The wakeup timer period value to be passed to the rf12. \n * See the rf12 datasheet for valid values. */ void rfm12_set_wakeup_timer(uint16_t val) { //set wakeup timer rfm12_data (RFM12_CMD_WAKEUP | (val & 0x1FFF)); //reset wakeup timer rfm12_data(RFM12_CMD_PWRMGT | (PWRMGT_DEFAULT & ~RFM12_PWRMGT_EW)); rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT); }
/** * Call a SW reset in case the module hangs (does not receive any data). */ void rfm12_sw_reset(void) { RFM12_INT_OFF(); rfm12_data(RFM12_CMD_RESET_FE); _delay_ms(200); // datasheet lists 150ms max. rfm12_data(RFM12_CMD_RESET_FF); _delay_ms(200); RFM12_INT_ON(); }
/** When enabling ASK tx mode, this function puts the internal state machine * into transmit mode and disables the interrupt. * Otherwise it will restore normale operation. * * \param [setting] Pass 1 to enable the raw mode, 0 to disable it. * \note You need to define RFM12_TRANSMIT_ASK as 1 to enable this. * \warning This will interfere with the wakeup timer feature. * \todo Use power management shadow register if the wakeup timer feature is enabled. * \see rfm12_tx_on() and rfm12_tx_off() */ void rfm12_ask_tx_mode(uint8_t setting) { if (setting) { #if 0 /* disable the receiver */ rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT); /* fill preamble into buffer */ rfm12_data(RFM12_CMD_TX | PREAMBLE); rfm12_data(RFM12_CMD_TX | PREAMBLE); #endif ctrl.rfm12_state = STATE_TX; RFM12_INT_OFF(); } else { /* re-enable the receiver */ rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT | RFM12_PWRMGT_ER); RFM12_INT_ON(); ctrl.rfm12_state = STATE_RX_IDLE; } }
/**This function takes care of all module initialization, including: * - Setup of the used frequency band and external capacitor * - Setting the exact frequency (channel) * - Setting the transmission data rate * - Configuring various module related rx parameters, including the amplification * - Enabling the digital data filter * - Enabling the use of the modules fifo, as well as enabling sync pattern detection * - Configuring the automatic frequency correction * - Setting the transmit power * * This initialization function also sets up various library internal configuration structs and * puts the module into receive mode before returning. * * \note Please note that the transmit power and receive amplification values are currently hard coded. * Have a look into rfm12_hw.h for possible settings. */ void rfm12_init(void) { RFM12_INT_OFF(); // in case rfm12_init is called twice, make sure rfm module does not interfere //initialize spi SS_RELEASE(); DDR_SS |= (1<<BIT_SS); spi_init(); //enable internal data register and fifo //setup selected band rfm12_data(RFM12_CMD_CFG | RFM12_CFG_EL | RFM12_CFG_EF | RFM12_BASEBAND | RFM12_XTAL_12PF); //set power default state (usually disable clock output) //do not write the power register two times in a short time //as it seems to need some recovery rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT); //set frequency rfm12_data(RFM12_CMD_FREQUENCY | RFM12_FREQUENCY_CALC(FREQ) ); //set data rate rfm12_data(RFM12_CMD_DATARATE | DATARATE_VALUE ); //set rx parameters: int-in/vdi-out pin is vdi-out, //Bandwith, LNA, RSSI rfm12_data(RFM12_CMD_RXCTRL | RFM12_RXCTRL_P16_VDI | RFM12_RXCTRL_VDI_FAST | RFM12_RXCTRL_BW_400 | RFM12_RXCTRL_LNA_6 | RFM12_RXCTRL_RSSI_79 ); //automatic clock lock control(AL), digital Filter(!S), //Data quality detector value 3, slow clock recovery lock rfm12_data(RFM12_CMD_DATAFILTER | RFM12_DATAFILTER_AL | 3); //2 Byte Sync Pattern, Start fifo fill when sychron pattern received, //disable sensitive reset, Fifo filled interrupt at 8 bits rfm12_data(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4)); //set AFC to automatic, (+4 or -3)*2.5kHz Limit, fine mode, active and enabled rfm12_data(RFM12_CMD_AFC | RFM12_AFC_AUTO_KEEP | RFM12_AFC_LIMIT_4 | RFM12_AFC_FI | RFM12_AFC_OE | RFM12_AFC_EN); //set TX Power to -0dB, frequency shift = +-125kHz rfm12_data(RFM12_CMD_TXCONF | RFM12_TXCONF_POWER_0 | RFM12_TXCONF_FS_CALC(125000) ); //disable low dutycycle mode rfm12_data(RFM12_CMD_DUTYCYCLE); //disable wakeup timer rfm12_data(RFM12_CMD_WAKEUP); //store the syncronization pattern to the transmission buffer //the sync pattern is used by the receiver to distinguish noise from real transmissions //the sync pattern is hardcoded into the receiver rf_tx_buffer.sync[0] = SYNC_MSB; rf_tx_buffer.sync[1] = SYNC_LSB; //if receive mode is not disabled (default) #if !(RFM12_TRANSMIT_ONLY) //init buffer pointers ctrl.rf_buffer_out = &rf_rx_buffers[0]; ctrl.rf_buffer_in = &rf_rx_buffers[0]; //ctrl.buffer_in_num = 0; //ctrl.buffer_out_num = 0; #endif /* !(RFM12_TRANSMIT_ONLY) */ //low battery detector feature initialization #if RFM12_LOW_BATT_DETECTOR ctrl.low_batt = RFM12_BATT_OKAY; #endif /* RFM12_LOW_BATT_DETECTOR */ //enable rf receiver chain, if receiving is not disabled (default) //the magic is done via defines rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_RECEIVE); //wakeup timer feature setup #if RFM12_USE_WAKEUP_TIMER //set power management shadow register to receiver chain enabled or disabled //the define correctly handles the transmit only mode ctrl.pwrmgt_shadow = (RFM12_CMD_PWRMGT | PWRMGT_RECEIVE); #endif /* RFM12_USE_WAKEUP_TIMER */ //ASK receive mode feature initialization #if RFM12_RECEIVE_ASK adc_init(); #endif //setup interrupt for falling edge trigger RFM12_INT_SETUP(); //clear int flag rfm12_read(RFM12_CMD_STATUS); RFM12_INT_FLAG |= (1<<RFM12_FLAG_BIT); //init receiver fifo, we now begin receiving. rfm12_data(CLEAR_FIFO); rfm12_data(ACCEPT_DATA); //activate the interrupt RFM12_INT_ON(); }
/** This function has to be called periodically. * It will read the rfm12 status register to check if a carrier is being received, * which would indicate activity on the chosen radio channel. \n * If there has been no activity for long enough, the channel is believed to be free. * * When there is a packet waiting for transmission and the collision avoidance * algorithm indicates that the air is free, then the interrupt control variables are * setup for packet transmission and the rfm12 is switched to transmit mode. * This function also fills the rfm12 tx fifo with a preamble. * * \warning Warning, if you do not call this function periodically, then no packet will get transmitted. * \see rfm12_tx() and rfm12_start_tx() */ void rfm12_tick(void) { //collision detection is enabled by default #if !(RFM12_NOCOLLISIONDETECTION) uint16_t status; //start with a channel free count of 16, this is necessary for the ASK receive feature to work static uint8_t channel_free_count = 16; //static local variables produce smaller code size than globals #endif //debug #if RFM12_UART_DEBUG static uint8_t oldstate; uint8_t state = ctrl.rfm12_state; if (oldstate != state) { uart_putstr ("mode change: "); switch (state) { case STATE_RX_IDLE: uart_putc ('i'); break; case STATE_RX_ACTIVE: uart_putc ('r'); break; case STATE_TX: uart_putc ('t'); break; default: uart_putc ('?'); } uart_putstr ("\r\n"); oldstate = state; } #endif //don't disturb RFM12 if transmitting or receiving if(ctrl.rfm12_state != STATE_RX_IDLE) { return; } //collision detection is enabled by default #if !(RFM12_NOCOLLISIONDETECTION) //disable the interrupt (as we're working directly with the transceiver now) //hint: we could be losing an interrupt here //solutions: check status flag if int is set, launch int and exit ... OR implement packet retransmission RFM12_INT_OFF(); status = rfm12_read(RFM12_CMD_STATUS); RFM12_INT_ON(); //check if we see a carrier if(status & RFM12_STATUS_RSSI) { //yes: reset free counter and return channel_free_count = CHANNEL_FREE_TIME; return; } //no: decrement counter channel_free_count--; //is the channel free long enough ? if(channel_free_count != 0) { return; } //reset the channel free count for the next decrement (during the next call..) channel_free_count = 1; #endif //do we have something to transmit? if(ctrl.txstate == STATUS_OCCUPIED) { //yes: start transmitting //disable the interrupt (as we're working directly with the transceiver now) //hint: we could be losing an interrupt here, too //we could also disturb an ongoing reception, //if it just started some cpu cycles ago //(as the check for this case is some lines (cpu cycles) above) //anyhow, we MUST transmit at some point... RFM12_INT_OFF(); //disable receiver - if you don't do this, tx packets will get lost //as the fifo seems to be in use by the receiver rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT); //calculate number of bytes to be sent by ISR //2 sync bytes + len byte + type byte + checksum + message length + 1 dummy byte ctrl.num_bytes = rf_tx_buffer.len + 6; //reset byte sent counter ctrl.bytecount = 0; //set mode for interrupt handler ctrl.rfm12_state = STATE_TX; //wakeup timer feature #if RFM12_USE_WAKEUP_TIMER ctrl.pwrmgt_shadow = (RFM12_CMD_PWRMGT | PWRMGT_DEFAULT | RFM12_PWRMGT_ET); #endif /* RFM12_USE_WAKEUP_TIMER */ //fill 2byte 0xAA preamble into data register //the preamble helps the receivers AFC circuit to lock onto the exact frequency //(hint: the tx FIFO [if el is enabled] is two staged, so we can safely write 2 bytes before starting) rfm12_data(RFM12_CMD_TX | PREAMBLE); rfm12_data(RFM12_CMD_TX | PREAMBLE); //set ET in power register to enable transmission (hint: TX starts now) rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT | RFM12_PWRMGT_ET); //enable the interrupt to continue the transmission RFM12_INT_ON(); } }
/** \param [val] The register value to be passed to the rf12. \n * See the rf12 datasheet for valid values. * \see rfm12_get_batt_status() */ void rfm12_set_batt_detector(uint16_t val) { //set the low battery detector and microcontroller clock divider register rfm12_data (RFM12_CMD_LBDMCD | (val & 0x01FF)); }
/** This will usually stop a transmission. * This function is used to emulate amplitude modulated signals. * * \note You need to define RFM12_TRANSMIT_ASK as 1 to enable this. * \warning This will interfere with the wakeup timer feature. * \todo Use power management shadow register if the wakeup timer feature is enabled. * \see rfm12_tx_on() and rfm12_ask_tx_mode() */ inline void rfm12_tx_off(void) { /* turn off everything. */ rfm12_data(RFM12_CMD_PWRMGT); }
/** This will send out the current buffer contents. * This function is used to emulate amplitude modulated signals. * * \note You need to define RFM12_TRANSMIT_ASK as 1 to enable this. * \warning This will interfere with the wakeup timer feature. * \todo Use power management shadow register if the wakeup timer feature is enabled. * \see rfm12_tx_off() and rfm12_ask_tx_mode() */ inline void rfm12_tx_on(void) { /* set enable transmission bit now. */ rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_DEFAULT | RFM12_PWRMGT_ET | RFM12_PWRMGT_ES | RFM12_PWRMGT_EX); }
/** The frequency has to be specified using the RFM12_FREQUENCY_CALC_433(x) macro. * * Please refer to the rfm12 library configuration header for a demo macro usage. \n * The frequency calculation macro can be found in rfm12_hw.h. * It is not included as a function for code-size reasons. */ void rfm12_set_frequency (uint16_t in_freq) { rfm12_data(RFM12_CMD_FREQUENCY | in_freq ); }
/** The data rate has to be specified using the following macros: * - RFM12_DATARATE_CALC_HIGH(x) for rates >= 2700 Baud * - RFM12_DATARATE_CALC_LOW(x) for rates from 340 to < 2700 Baud * * Please refer to the rfm12 library configuration header for a demo macro usage. \n * The data rate calculation macros can be found in rfm12_hw.h. * They are not included as a function for code-size reasons. */ void rfm12_set_rate (uint16_t in_datarate) { rfm12_data(RFM12_CMD_DATARATE | DATARATE_VALUE ); }
//#warning "RFM12_INT_VECT enabled" ISR(RFM12_INT_VECT, ISR_NOBLOCK) #endif { RFM12_INT_OFF(); uint8_t status; //debug #if RFM12_UART_DEBUG >= 2 uart_putc('i'); #endif //if receive mode is not disabled (default) #if !(RFM12_TRANSMIT_ONLY) static uint8_t checksum; //static local variables produce smaller code size than globals #endif /* !(RFM12_TRANSMIT_ONLY) */ //first we read the first byte of the status register //to get the interrupt flags status = rfm12_read_int_flags_inline(); //low battery detector feature #if RFM12_LOW_BATT_DETECTOR if(status & (RFM12_STATUS_LBD>>8)) { //debug #if RFM12_UART_DEBUG >= 2 uart_putc('L'); #endif //set status variable to low battery ctrl.low_batt = RFM12_BATT_LOW; } #endif /* RFM12_LOW_BATT_DETECTOR */ //wakeup timer feature #if RFM12_USE_WAKEUP_TIMER if(status & (RFM12_STATUS_WKUP>>8)) { //debug #if RFM12_UART_DEBUG >= 2 uart_putc('W'); #endif //restart the wakeup timer by toggling the bit on and off rfm12_data(ctrl.pwrmgt_shadow & ~RFM12_PWRMGT_EW); rfm12_data(ctrl.pwrmgt_shadow); } #endif /* RFM12_USE_WAKEUP_TIMER */ //check if the fifo interrupt occurred if(!(status & (RFM12_STATUS_FFIT>>8))) goto END; //see what we have to do (start rx, rx or tx) switch(ctrl.rfm12_state) { case STATE_RX_IDLE: //if receive mode is not disabled (default) #if !(RFM12_TRANSMIT_ONLY) //init the bytecounter - remember, we will read the length byte, so this must be 1 ctrl.bytecount = 1; //read the length byte, and write it to the checksum //remember, the first byte is the length byte checksum = rfm12_read_fifo_inline(); //add the packet overhead and store it into a working variable ctrl.num_bytes = checksum + PACKET_OVERHEAD; //debug #if RFM12_UART_DEBUG >= 2 uart_putc('I'); uart_putc(checksum); #endif //see whether our buffer is free //FIXME: put this into global statekeeping struct, the free state can be set by the function which pulls the packet, i guess if(ctrl.rf_buffer_in->status == STATUS_FREE) { //the current receive buffer is empty, so we start receiving ctrl.rfm12_state = STATE_RX_ACTIVE; //store the received length into the packet buffer //FIXME: why the hell do we need this?! //in principle, the length is stored alongside with the buffer.. the only problem is, that the buffer might be cleared during reception ctrl.rf_buffer_in->len = checksum; //end the interrupt without resetting the fifo goto END; } /* if we're here, the buffer is full, so we ignore this transmission by resetting the fifo (at the end of the function) */ #endif /* !(RFM12_TRANSMIT_ONLY) */ break; case STATE_RX_ACTIVE: //if receive mode is not disabled (default) #if !(RFM12_TRANSMIT_ONLY) //check if transmission is complete if(ctrl.bytecount < ctrl.num_bytes) { uint8_t data; //read a byte data = rfm12_read_fifo_inline(); //debug #if RFM12_UART_DEBUG >= 2 uart_putc('R'); uart_putc(data); #endif //xor the remaining bytes onto the checksum //note: only the header will be effectively checked checksum ^= data; //put next byte into buffer, if there is enough space if(ctrl.bytecount < (RFM12_RX_BUFFER_SIZE + 3)) { //hackhack: begin writing to struct at offsetof len (& ctrl.rf_buffer_in->len)[ctrl.bytecount] = data; } //check header against checksum if (ctrl.bytecount == 2 && checksum != 0xff) { //if the checksum does not match, reset the fifo break; } //increment bytecount ctrl.bytecount++; //end the interrupt without resetting the fifo goto END; } /* if we're here, receiving is done */ /* the fifo will be reset at the end of the function */ //debug #if RFM12_UART_DEBUG >= 2 uart_putc('D'); #endif //indicate that the buffer is ready to be used ctrl.rf_buffer_in->status = STATUS_COMPLETE; //switch to other buffer ctrl.buffer_in_num = (ctrl.buffer_in_num + 1) % 2; ctrl.rf_buffer_in = &rf_rx_buffers[ctrl.buffer_in_num]; #endif /* !(RFM12_TRANSMIT_ONLY) */ break; case STATE_TX: //debug #if RFM12_UART_DEBUG >= 2 uart_putc('T'); #endif if(ctrl.bytecount < ctrl.num_bytes) { //load the next byte from our buffer struct. rfm12_data_inline( (RFM12_CMD_TX>>8), rf_tx_buffer.sync[ctrl.bytecount++]); //end the interrupt without resetting the fifo goto END; } /* if we're here, we're finished transmitting the bytes */ /* the fifo will be reset at the end of the function */ //flag the buffer as free again ctrl.txstate = STATUS_FREE; #if RFM12_UART_DEBUG >= 2 uart_putc('f'); #endif //wakeup timer feature #if RFM12_USE_WAKEUP_TIMER //clear wakeup timer once rfm12_data(ctrl.pwrmgt_shadow & ~RFM12_PWRMGT_EW); //set shadow register to default receive state //the define correctly handles the transmit only mode ctrl.pwrmgt_shadow = (RFM12_CMD_PWRMGT | PWRMGT_RECEIVE); #endif /* RFM12_USE_WAKEUP_TIMER */ //turn off the transmitter and enable receiver //the receiver is not enabled in transmit only mode //if the wakeup timer is used, this will re-enable the wakeup timer bit //the magic is done via defines rfm12_data(RFM12_CMD_PWRMGT | PWRMGT_RECEIVE); //load a dummy byte to clear int status rfm12_data_inline( (RFM12_CMD_TX>>8), 0xaa); break; }