439 lines
13 KiB
C
439 lines
13 KiB
C
/***************************************************************************
|
|
* Copyright (C) 11/2014 by Olaf Rempel *
|
|
* razzor@kopf-tisch.de *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; version 2 of the License, *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <util/crc16.h>
|
|
|
|
#include "target.h"
|
|
#include "rfm12_hw.h"
|
|
#include "rfm12.h"
|
|
|
|
/* *********************************************************************** */
|
|
|
|
#define RFM12_PREAMBLE 0xAA
|
|
#define RFM12_SYNC_LSB 0xD4
|
|
#define RFM12_SYNC_MSB 0x2D
|
|
|
|
#define RFM12_BASEBAND RFM12_BAND_868
|
|
#define RFM12_XTAL_LOAD RFM12_XTAL_11_5PF
|
|
#define RFM12_FREQUENCY_CALC RFM12_FREQUENCY_CALC_868
|
|
#define RFM12_FREQUENCY 869800000UL
|
|
#define RFM12_DATARATE RFM12_DATARATE_CALC_HIGH(19200.0)
|
|
#define RFM12_FILTER_BW RFM12_RXCTRL_BW_400
|
|
#define RFM12_LNA_GAIN RFM12_RXCTRL_LNA_6
|
|
#define RFM12_RSSI_THRESHOLD RFM12_RXCTRL_RSSI_79
|
|
#define RFM12_POWER RFM12_TXCONF_POWER_0
|
|
#define RFM12_FSK_SHIFT 125000
|
|
|
|
/* *********************************************************************** */
|
|
|
|
#define RFM12_DATA_STATE_FREE 0x00
|
|
#define RFM12_DATA_STATE_USED 0x01
|
|
|
|
struct rfm12_data
|
|
{
|
|
uint8_t state; /* RFM12_DATA_STATE_* */
|
|
struct rfm12_packet packet;
|
|
};
|
|
|
|
#define RFM12_CTX_STATE_INAVTIVE 0x00
|
|
#define RFM12_CTX_STATE_RX_IDLE 0x01
|
|
#define RFM12_CTX_STATE_RX_ACTIVE 0x02
|
|
#define RFM12_CTX_STATE_TX_ACTIVE 0x03
|
|
|
|
struct rfm12_context
|
|
{
|
|
uint8_t state; /* RFM12_CTX_STATE_* */
|
|
uint8_t ch_free_ticks; /* number of ticks channel is unused */
|
|
uint8_t own_address;
|
|
|
|
uint8_t rx_idx_in; /* pkt receiving */
|
|
uint8_t rx_idx_out; /* pkt given to app */
|
|
uint8_t rx_checksum; /* receive checksum */
|
|
uint8_t rx_data_idx; /* byte position inside rx[].packet */
|
|
|
|
uint8_t tx_data_idx; /* byte position inside tx.packet */
|
|
|
|
struct rfm12_data rx[2];
|
|
struct rfm12_data tx;
|
|
};
|
|
|
|
static struct rfm12_context rfm12_ctx;
|
|
|
|
/* *********************************************************************** */
|
|
|
|
static uint16_t rfm12_data(uint16_t txdata)
|
|
{
|
|
uint16_t retval;
|
|
|
|
RFM12_CS_ACTIVE();
|
|
|
|
SPDR = (txdata >> 8);
|
|
while (!(SPSR & (1<<SPIF)));
|
|
retval = (SPDR << 8);
|
|
|
|
SPDR = (txdata & 0xFF);
|
|
while (!(SPSR & (1<<SPIF)));
|
|
retval |= SPDR;
|
|
|
|
RFM12_CS_INACTIVE();
|
|
|
|
return retval;
|
|
} /* rfm12_data */
|
|
|
|
|
|
ISR(RFM12_INT_VECT)
|
|
{
|
|
uint16_t rfm12_status;
|
|
uint8_t rfm12_reset_fifo = 0;
|
|
uint8_t data;
|
|
|
|
/* disable interrupt */
|
|
RFM12_INT_OFF();
|
|
|
|
/* clear interrupt flag */
|
|
RFM12_INT_CLEAR();
|
|
|
|
/* read upper status byte (interrupt flags) */
|
|
rfm12_status = rfm12_data(RFM12_CMD_STATUS);
|
|
|
|
/* FIFO interrupt */
|
|
if (rfm12_status & RFM12_STATUS_FFIT)
|
|
{
|
|
switch (rfm12_ctx.state)
|
|
{
|
|
case RFM12_CTX_STATE_RX_IDLE:
|
|
data = rfm12_data(RFM12_CMD_READ);
|
|
|
|
/* check if buffer is free */
|
|
if (rfm12_ctx.rx[rfm12_ctx.rx_idx_in].state != RFM12_DATA_STATE_FREE)
|
|
{
|
|
rfm12_reset_fifo = 1;
|
|
break;
|
|
}
|
|
|
|
/* store first header byte */
|
|
rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.sync[RFM12_PKT_SYNC_SIZE] = data;
|
|
|
|
/* store state */
|
|
rfm12_ctx.state = RFM12_CTX_STATE_RX_ACTIVE;
|
|
rfm12_ctx.rx_data_idx = 1;
|
|
rfm12_ctx.rx_checksum = data;
|
|
break;
|
|
|
|
case RFM12_CTX_STATE_RX_ACTIVE:
|
|
data = rfm12_data(RFM12_CMD_READ);
|
|
|
|
/* check buffer size */
|
|
if (rfm12_ctx.rx_data_idx >= (RFM12_PKT_HEADER_SIZE + RFM12_PKT_MAX_DATA_SIZE))
|
|
{
|
|
rfm12_reset_fifo = 1;
|
|
break;
|
|
}
|
|
|
|
/* store data */
|
|
(& rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.sync[RFM12_PKT_SYNC_SIZE])[rfm12_ctx.rx_data_idx++] = data;
|
|
|
|
/* calculate checksum */
|
|
rfm12_ctx.rx_checksum ^= data;
|
|
|
|
/* check if header address, data_length and checksum are ok */
|
|
if ((rfm12_ctx.rx_data_idx == RFM12_PKT_HEADER_SIZE) &&
|
|
((rfm12_ctx.rx_checksum != 0xFF) ||
|
|
(rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.dest_address != rfm12_ctx.own_address) ||
|
|
(rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.source_address == rfm12_ctx.own_address) ||
|
|
(rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.data_length > RFM12_PKT_MAX_DATA_SIZE)
|
|
))
|
|
{
|
|
rfm12_reset_fifo = 1;
|
|
break;
|
|
}
|
|
|
|
/* packet complete? */
|
|
if (rfm12_ctx.rx_data_idx == (RFM12_PKT_HEADER_SIZE + rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.data_length))
|
|
{
|
|
/* mark buffer as full */
|
|
rfm12_ctx.rx[rfm12_ctx.rx_idx_in].state = RFM12_DATA_STATE_USED;
|
|
|
|
/* switch to other buffer */
|
|
rfm12_ctx.rx_idx_in ^= 1;
|
|
|
|
/* receiving is complete, reset fifo anyway */
|
|
rfm12_reset_fifo = 1;
|
|
}
|
|
break;
|
|
|
|
case RFM12_CTX_STATE_TX_ACTIVE:
|
|
/* send one additional byte! (<= not <) */
|
|
if (rfm12_ctx.tx_data_idx <= (RFM12_PKT_SYNC_SIZE + RFM12_PKT_HEADER_SIZE + rfm12_ctx.tx.packet.data_length))
|
|
{
|
|
rfm12_data(RFM12_CMD_TX | rfm12_ctx.tx.packet.sync[rfm12_ctx.tx_data_idx++]);
|
|
}
|
|
else
|
|
{
|
|
/* enable RX */
|
|
rfm12_data(RFM12_CMD_PWRMGT | RFM12_PWRMGT_ER | RFM12_PWRMGT_DC);
|
|
|
|
/* TX dummy byte to clear interrupt */
|
|
rfm12_data(RFM12_CMD_TX | RFM12_PREAMBLE);
|
|
|
|
/* mark buffer as empty */
|
|
rfm12_ctx.tx.state = RFM12_DATA_STATE_FREE;
|
|
|
|
/* transmit is complete, reset fifo */
|
|
rfm12_reset_fifo = 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rfm12_reset_fifo = 1;
|
|
break;
|
|
}
|
|
|
|
if (rfm12_reset_fifo)
|
|
{
|
|
/* flush fifo and wait for sync pattern */
|
|
rfm12_data(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4));
|
|
rfm12_data(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4) | RFM12_FIFORESET_FF);
|
|
|
|
/* wait for RX data */
|
|
rfm12_ctx.state = RFM12_CTX_STATE_RX_IDLE;
|
|
}
|
|
}
|
|
|
|
/* enable interrupt again */
|
|
RFM12_INT_ON();
|
|
} /* INT1_vect */
|
|
|
|
|
|
void rfm12_tick(uint8_t channel_free_time)
|
|
{
|
|
uint16_t status;
|
|
|
|
/* receiver not idle, come back later */
|
|
if (rfm12_ctx.state != RFM12_CTX_STATE_RX_IDLE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RFM12_INT_OFF();
|
|
status = rfm12_data(RFM12_CMD_STATUS);
|
|
RFM12_INT_ON();
|
|
|
|
/* check if module receives a carrier */
|
|
if (status & RFM12_STATUS_RSSI)
|
|
{
|
|
rfm12_ctx.ch_free_ticks = 0;
|
|
return;
|
|
}
|
|
else if (rfm12_ctx.ch_free_ticks <= channel_free_time)
|
|
{
|
|
rfm12_ctx.ch_free_ticks++;
|
|
return;
|
|
}
|
|
|
|
if (rfm12_ctx.tx.state == RFM12_DATA_STATE_USED)
|
|
{
|
|
RFM12_INT_OFF();
|
|
|
|
/* disable receiver */
|
|
rfm12_data(RFM12_CMD_PWRMGT | RFM12_PWRMGT_DC);
|
|
|
|
/* put preamble in fifo */
|
|
rfm12_data(RFM12_CMD_TX | RFM12_PREAMBLE);
|
|
rfm12_data(RFM12_CMD_TX | RFM12_PREAMBLE);
|
|
|
|
/* start TX */
|
|
rfm12_data(RFM12_CMD_PWRMGT | RFM12_PWRMGT_ET | RFM12_PWRMGT_DC);
|
|
|
|
/* change state */
|
|
rfm12_ctx.state = RFM12_CTX_STATE_TX_ACTIVE;
|
|
rfm12_ctx.tx_data_idx = 0;
|
|
|
|
RFM12_INT_ON();
|
|
}
|
|
} /* rfm12_tick */
|
|
|
|
|
|
static uint16_t rfm12_calc_crc(const struct rfm12_packet *pkt)
|
|
{
|
|
uint16_t crc = 0x0000;
|
|
uint8_t i;
|
|
|
|
const uint8_t *tmp = pkt->data;
|
|
for (i = 0; i < pkt->data_length; i++)
|
|
crc = _crc_ccitt_update(crc, *tmp++);
|
|
|
|
return crc;
|
|
} /* pkt_check_crc */
|
|
|
|
|
|
struct rfm12_packet * rfm12_get_txpkt(void)
|
|
{
|
|
if (rfm12_ctx.tx.state != RFM12_DATA_STATE_FREE)
|
|
{
|
|
return (void *)0;
|
|
}
|
|
|
|
return &rfm12_ctx.tx.packet;
|
|
} /* rfm12_get_txpkt */
|
|
|
|
|
|
uint8_t rfm12_start_tx(void)
|
|
{
|
|
struct rfm12_packet *pkt = &rfm12_ctx.tx.packet;
|
|
|
|
if ((rfm12_ctx.tx.state != RFM12_DATA_STATE_FREE) &&
|
|
(pkt->data_length > RFM12_PKT_MAX_DATA_SIZE)
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* calculate data crc */
|
|
uint16_t *data_crc = (uint16_t *)(pkt->data + pkt->data_length);
|
|
*data_crc = rfm12_calc_crc(pkt);
|
|
pkt->data_length += 2;
|
|
|
|
/* setup packet */
|
|
pkt->sync[0] = RFM12_SYNC_MSB;
|
|
pkt->sync[1] = RFM12_SYNC_LSB;
|
|
pkt->source_address = rfm12_ctx.own_address;
|
|
pkt->header_checksum = pkt->dest_address ^ pkt->source_address ^ pkt->data_length ^ 0xFF;
|
|
|
|
/* change state */
|
|
rfm12_ctx.tx.state = RFM12_DATA_STATE_USED;
|
|
|
|
return 1;
|
|
} /* rfm12_start_tx */
|
|
|
|
|
|
struct rfm12_packet * rfm12_get_rxpkt(void)
|
|
{
|
|
if (rfm12_ctx.rx[rfm12_ctx.rx_idx_out].state != RFM12_DATA_STATE_USED)
|
|
{
|
|
return (void *)0;
|
|
}
|
|
|
|
/* calculate data crc */
|
|
struct rfm12_packet *pkt = &rfm12_ctx.rx[rfm12_ctx.rx_idx_out].packet;
|
|
|
|
pkt->data_length -= 2;
|
|
|
|
uint16_t *data_crc = (uint16_t *)(pkt->data + pkt->data_length);
|
|
|
|
if (*data_crc != rfm12_calc_crc(pkt))
|
|
{
|
|
rfm12_clear_rx();
|
|
return (void *)0;
|
|
}
|
|
|
|
return pkt;
|
|
} /* rfm12_get_rxpkt */
|
|
|
|
|
|
void rfm12_clear_rx(void)
|
|
{
|
|
/* mark buffer as empty */
|
|
rfm12_ctx.rx[rfm12_ctx.rx_idx_out].state = RFM12_DATA_STATE_FREE;
|
|
|
|
/* switch to other buffer */
|
|
rfm12_ctx.rx_idx_out ^= 1;
|
|
} /* rfm12_clear_rx */
|
|
|
|
|
|
static const uint16_t init_cmds[] PROGMEM =
|
|
{
|
|
/* set power default state (disable clock output) */
|
|
(RFM12_CMD_PWRMGT | RFM12_PWRMGT_DC),
|
|
|
|
/* dummy write after power management change, prevent lockup of module */
|
|
(RFM12_CMD_TX),
|
|
|
|
/* enable internal data register and fifo, setup selected band */
|
|
(RFM12_CMD_CFG | RFM12_CFG_EL | RFM12_CFG_EF | RFM12_BASEBAND | RFM12_XTAL_LOAD),
|
|
|
|
/* set frequency */
|
|
(RFM12_CMD_FREQUENCY | RFM12_FREQUENCY_CALC(RFM12_FREQUENCY)),
|
|
|
|
/* set data rate */
|
|
(RFM12_CMD_DATARATE | RFM12_DATARATE),
|
|
|
|
/* set rx parameters: vdi-out, bandwidth, LNA, RSSI */
|
|
(RFM12_CMD_RXCTRL | RFM12_RXCTRL_P16_VDI | RFM12_RXCTRL_VDI_FAST | RFM12_FILTER_BW | RFM12_LNA_GAIN | RFM12_RSSI_THRESHOLD),
|
|
|
|
/* automatic clock lock control, digital Filter,
|
|
* Data quality detector value 3, slow clock recovery lock
|
|
*/
|
|
(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_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4)),
|
|
|
|
/* set AFC to automatic, (+4 or -3)*2.5kHz Limit, fine mode, active and enabled */
|
|
(RFM12_CMD_AFC | RFM12_AFC_AUTO_KEEP | RFM12_AFC_LIMIT_4 | RFM12_AFC_FI | RFM12_AFC_OE | RFM12_AFC_EN),
|
|
|
|
/* set TX Power, frequency shift */
|
|
(RFM12_CMD_TXCONF | RFM12_POWER | RFM12_TXCONF_FS_CALC(RFM12_FSK_SHIFT)),
|
|
|
|
/* disable low dutycycle mode */
|
|
(RFM12_CMD_DUTYCYCLE),
|
|
|
|
/* disable wakeup timer */
|
|
(RFM12_CMD_WAKEUP),
|
|
|
|
/* enable rf receiver chain */
|
|
(RFM12_CMD_PWRMGT | RFM12_PWRMGT_ER | RFM12_PWRMGT_DC),
|
|
|
|
/* flush fifo, start receiving */
|
|
(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4)),
|
|
(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4) | RFM12_FIFORESET_FF),
|
|
};
|
|
|
|
|
|
void rfm12_init(uint8_t own_address)
|
|
{
|
|
uint8_t i;
|
|
|
|
/* init chipselect GPIO */
|
|
RFM12_CS_INIT();
|
|
RFM12_CS_INACTIVE();
|
|
|
|
/* init internal SPI */
|
|
RFM12_SPI_INIT();
|
|
|
|
/* send init commands */
|
|
for (i = 0; i < ( sizeof(init_cmds) / 2) ; i++)
|
|
{
|
|
rfm12_data(pgm_read_word(&init_cmds[i]));
|
|
}
|
|
|
|
/* store own address */
|
|
rfm12_ctx.own_address = own_address;
|
|
rfm12_ctx.state = RFM12_CTX_STATE_RX_IDLE;
|
|
|
|
/* initalize & activate interrupt */
|
|
RFM12_INT_INIT();
|
|
RFM12_INT_CLEAR();
|
|
RFM12_INT_ON();
|
|
} /* rfm12_init */
|