/* radio.c 12/04/2016 D.J.Whale
*
* An interface to the Energenie Raspberry Pi Radio.
*/
/***** INCLUDES *****/
#include "system.h"
#include "radio.h"
#include "delay.h"
#include "gpio.h"
#include "spi.h"
#include "hrfm69.h"
#include "trace.h"
/***** CONFIGURATION *****/
// Energenie specific radio config values
//#define RADIO_VAL_SYNCVALUE1FSK 0x2D // 1st byte of Sync word
//#define RADIO_VAL_SYNCVALUE2FSK 0xD4 // 2nd byte of Sync word
//#define RADIO_VAL_SYNCVALUE1OOK 0x80 // 1nd byte of Sync word
//#define RADIO_VAL_PACKETCONFIG1FSK 0xA2 // Variable length, Manchester coding, Addr must match NodeAddress
//#define RADIO_VAL_PACKETCONFIG1FSKNO 0xA0 // Variable length, Manchester coding
//#define RADIO_VAL_PACKETCONFIG1OOK 0 // Fixed length, no Manchester coding
//#define RADIO_VAL_PAYLOADLEN_OOK (13 + 8 * 17) // Payload Length (WRONG!)
//TODO: Not sure, might pass this in? What about on Arduino?
//What about if we have multiple chip selects on same SPI?
//What about if we have multiple spi's on different pins?
/* GPIO assignments for Raspberry Pi using BCM numbering */
#define RESET 25
#define LED_GREEN 27 // (not B rev1)
#define LED_RED 22
#define CS 7 // CE1
#define SCLK 11
#define MOSI 10
#define MISO 9
SPI_CONFIG radioConfig = {CS, SCLK, MOSI, MISO, SPI_SPOL0, SPI_CPOL0, SPI_CPHA0};
//TSETTLE, THOLD, TFREQ};
/***** LOCAL FUNCTION PROTOTYPES *****/
static void _reset(void);
static uint8_t _read_ver(void);
static void _change_mode(uint8_t mode);
static void _wait_ready(void);
static void _wait_txready(void);
static void _config(HRF_CONFIG_REC* config, uint8_t len);
static int _payload_waiting(void);
static void _receive_payload(uint8_t* buf, uint8_t len);
static void _send_payload(uint8_t* payload, uint8_t len, uint8_t repeats);
//----- ENERGENIE SPECIFIC CONFIGURATIONS --------------------------------------
// config_FSK = [
// [ADDR_REGDATAMODUL, VAL_REGDATAMODUL_FSK], # modulation scheme FSK
// [ADDR_FDEVMSB, VAL_FDEVMSB30], # frequency deviation 5kHz 0x0052 -> 30kHz 0x01EC
// [ADDR_FDEVLSB, VAL_FDEVLSB30], # frequency deviation 5kHz 0x0052 -> 30kHz 0x01EC
// [ADDR_FRMSB, VAL_FRMSB434], # carrier freq -> 434.3MHz 0x6C9333
// [ADDR_FRMID, VAL_FRMID434], # carrier freq -> 434.3MHz 0x6C9333
// [ADDR_FRLSB, VAL_FRLSB434], # carrier freq -> 434.3MHz 0x6C9333
// [ADDR_AFCCTRL, VAL_AFCCTRLS], # standard AFC routine
// [ADDR_LNA, VAL_LNA50], # 200ohms, gain by AGC loop -> 50ohms
// [ADDR_RXBW, VAL_RXBW60], # channel filter bandwidth 10kHz -> 60kHz page:26
// [ADDR_BITRATEMSB, 0x1A], # 4800b/s
// [ADDR_BITRATELSB, 0x0B], # 4800b/s
// [ADDR_SYNCCONFIG, VAL_SYNCCONFIG2], # Size of the Synch word = 2 (SyncSize + 1)
// [ADDR_SYNCVALUE1, VAL_SYNCVALUE1FSK], # 1st byte of Sync word
// [ADDR_SYNCVALUE2, VAL_SYNCVALUE2FSK], # 2nd byte of Sync word
// [ADDR_PACKETCONFIG1, VAL_PACKETCONFIG1FSKNO], # Variable length, Manchester coding
// [ADDR_PAYLOADLEN, VAL_PAYLOADLEN66], # max Length in RX, not used in Tx
// [ADDR_NODEADDRESS, 0x06], # Node address used in address filtering TODO???
// [ADDR_FIFOTHRESH, VAL_FIFOTHRESH1], # Condition to start packet transmission: at least one byte in FIFO
// [ADDR_OPMODE, MODE_RECEIVER] # Operating mode to Receiver
// ]
static HRF_CONFIG_REC config_OOK[] = {
{HRF_ADDR_REGDATAMODUL, HRF_VAL_REGDATAMODUL_OOK}, // modulation scheme OOK
{HRF_ADDR_FDEVMSB, 0}, // frequency deviation:0kHz
{HRF_ADDR_FDEVLSB, 0}, // frequency deviation:0kHz
{HRF_ADDR_FRMSB, HRF_VAL_FRMSB433}, // carrier freq:433.92MHz 0x6C7AE1
{HRF_ADDR_FRMID, HRF_VAL_FRMID433}, // carrier freq:433.92MHz 0x6C7AE1
{HRF_ADDR_FRLSB, HRF_VAL_FRLSB433}, // carrier freq:433.92MHz 0x6C7AE1
{HRF_ADDR_RXBW, HRF_VAL_RXBW120}, // channel filter bandwidth:120kHz
{HRF_ADDR_BITRATEMSB, 0x1A}, // bitrate:4800b/s
{HRF_ADDR_BITRATELSB, 0x0B}, // bitrate:4800b/s
{HRF_ADDR_PREAMBLELSB, 0}, // preamble size LSB
{HRF_ADDR_SYNCCONFIG, HRF_VAL_SYNCCONFIG0}, // Size of sync word (disabled)
{HRF_ADDR_PACKETCONFIG1, 0x00}, // Fixed length, no Manchester coding
};
#define CONFIG_OOK_COUNT (sizeof(config_OOK)/sizeof(HRF_CONFIG_REC))
/***** MODULE STATE *****/
// mode = None
// modulation_fsk = None
/***** PRIVATE ***************************************************************/
/*---------------------------------------------------------------------------*/
static void reset(void)
{
gpio_high(RESET);
delayms(150);
gpio_low(RESET);
delayus(100);
}
/*---------------------------------------------------------------------------*/
static uint8_t read_ver(void)
{
return HRF_readreg(HRF_ADDR_VERSION);
}
/*---------------------------------------------------------------------------*/
// Change the operating mode of the HRF radi
static void _change_mode(uint8_t mode)
{
HRF_writereg(HRF_ADDR_OPMODE, mode);
}
/*---------------------------------------------------------------------------*/
// Wait for HRF to be ready after last command
static void _wait_ready(void)
{
HRF_pollreg(HRF_ADDR_IRQFLAGS1, HRF_MASK_MODEREADY, HRF_MASK_MODEREADY);
}
/*---------------------------------------------------------------------------*/
// Load a table of configuration values into HRF registers
static void _config(HRF_CONFIG_REC* config, uint8_t count)
{
while (count-- != 0)
{
HRF_writereg(config->addr, config->value);
config++;
}
}
/*---------------------------------------------------------------------------*/
// Wait for the HRF to be ready, and ready for tx, after last command
static void _wait_txready(void)
{
HRF_pollreg(HRF_ADDR_IRQFLAGS1, HRF_MASK_MODEREADY|HRF_MASK_TXREADY, HRF_MASK_MODEREADY|HRF_MASK_TXREADY);
}
/*---------------------------------------------------------------------------*/
// Check if there is a payload in the FIFO waiting to be processed
static int _payload_waiting(void)
{
//TODO: First read might be superflous, but left in just in case
uint8_t irqflags1 = HRF_readreg(HRF_ADDR_IRQFLAGS1);
uint8_t irqflags2 = HRF_readreg(HRF_ADDR_IRQFLAGS2);
return (irqflags2 & HRF_MASK_PAYLOADRDY) == HRF_MASK_PAYLOADRDY;
}
/***** PUBLIC ****************************************************************/
/*---------------------------------------------------------------------------*/
void radio_init(void)
{
TRACE_OUTS("radio_init\n");
//gpio_init(); done by spi_init at moment
spi_init(&radioConfig);
gpio_setout(RESET);
gpio_low(RESET);
gpio_setout(LED_RED);
gpio_low(LED_RED);
gpio_setout(LED_GREEN);
gpio_low(LED_GREEN);
TRACE_OUTS("reset...\n");
reset();
TRACE_OUTS("reading radiover...\n");
uint8_t rv = read_ver();
TRACE_OUTN(rv);
TRACE_NL();
if (rv != 36)
{
TRACE_FAIL("unexpected radio ver, not 36(dec)\n");
}
TRACE_OUTS("standby mode\n");
_change_mode(HRF_MODE_STANDBY);
HRF_pollreg(HRF_ADDR_IRQFLAGS1, HRF_MASK_MODEREADY, HRF_MASK_MODEREADY);
}
/*---------------------------------------------------------------------------*/
void radio_modulation(RADIO_MODULATION mod)
{
if (mod == RADIO_MODULATION_OOK)
{
_config(config_OOK, CONFIG_OOK_COUNT);
}
else
{
TRACE_FAIL("Unknown modulation requested\n");
}
// def modulation(fsk=None, ook=None):
// """Switch modulation, if needed"""
// global modulation_fsk
//
// # Handle sensible module defaults for earlier versions of user code
// if fsk == None and ook == None:
// # Force FSK mode
// fsk = True
//
// if fsk != None and fsk:
// if modulation_fsk == None or modulation_fsk == False:
// trace("switch to FSK")
// HRF_config(config_FSK)
// modulation_fsk = True
//
// elif ook != None and ook:
// if modulation_fsk == None or modulation_fsk == True:
// trace("switch to OOK")
// HRF_config(config_OOK)
// modulation_fsk = False
}
/*---------------------------------------------------------------------------*/
void radio_transmitter(RADIO_MODULATION mod)
{
TRACE_OUTS("radio_transmitter\n");
radio_modulation(mod);
_change_mode(HRF_MODE_TRANSMITTER);
_wait_txready();
//radio_data.modulation = mod;
}
/*---------------------------------------------------------------------------*/
void radio_transmit(uint8_t* payload, uint8_t len, uint8_t repeats)
{
TRACE_OUTS("radio_transmit\n");
radio_transmitter(RADIO_MODULATION_OOK); //TODO use present state
radio_send_payload(payload, len, repeats);
radio_standby();
}
/*---------------------------------------------------------------------------*/
// Send a payload of data
//TODO: Rewrite this to use FIFOLEV and FIFOEMPTY with payloadlen=0
//rather than PACKETSENT, as it will allow any number of repeats.
void radio_send_payload(uint8_t* payload, uint8_t len, uint8_t repeats)
{
TRACE_OUTS("send_payload\n");
// Note, when PA starts up, radio inserts a 01 at start before any user data
// we might need to pad away from this by sending a sync of many zero bits
// to prevent it being misinterpreted as a preamble, and prevent it causing
// the first bit of the preamble being twice the length it should be in the
// first packet.
// Also need to confirm this bit only occurs when transmit actually starts,
// and not on every FIFO load.
int i;
uint8_t irqflags1;
uint8_t irqflags2;
/* CONFIGURE: Setup the radio for transmit of the correct payload length */
TRACE_OUTS("config\n");
if ((unsigned int)repeats * (unsigned int)len > 255)
{
// This is a temporary situation until the new 'indefinite transmit'
// scheme is implemented using fifolevel only, and ignoring packetsent.
TRACE_FAIL("repeats*payloadlen > 255, can't configure\n");
}
// the full packet/burst consists of repeated payloads
// packetsent will trigger when this number of bytes have been transmitted
HRF_writereg(HRF_ADDR_PAYLOADLEN, len * repeats);
// but the FIFO is filled in 1 message (4+10+2=16 byte) sections
// level triggers when it 'strictly exceeds' level (i.e. 16 bytes starts tx,
// and <=15 bytes triggers fifolevel irqflag to be cleared)
HRF_writereg(HRF_ADDR_FIFOTHRESH, len-1);
/* Bring into transmitter mode and ramp up the PA */
TRACE_OUTS("transmitter mode\n");
_change_mode(HRF_MODE_TRANSMITTER);
TRACE_OUTS("wait for modeready,txready in irqflags1\n");
HRF_pollreg(HRF_ADDR_IRQFLAGS1, HRF_MASK_MODEREADY|HRF_MASK_TXREADY, HRF_MASK_MODEREADY|HRF_MASK_TXREADY);
irqflags1 = HRF_readreg(HRF_ADDR_IRQFLAGS1);
irqflags2 = HRF_readreg(HRF_ADDR_IRQFLAGS2);
TRACE_OUTS("irqflags1,2=");
TRACE_OUTN(irqflags1);
TRACE_OUTC(',');
TRACE_OUTN(irqflags2);
TRACE_NL();
/* TRANSMIT: Transmit a number of bursts back to back */
TRACE_OUTS("tx repeats in a single burst\n");
// send a number of payload repeats for the whole packet burst
for (i=0; i<repeats; i++)
{
HRF_writefifo_burst(payload, len);
// Tx will auto start when fifolevel is exceeded by loading the payload
// so the level register must be correct for the size of the payload
// otherwise transmit will never start.
/* wait for FIFO to not exceed threshold level */
HRF_pollreg(HRF_ADDR_IRQFLAGS2, HRF_MASK_FIFOLEVEL, 0);
}
// wait for packet sent (num bytes tx'ed matches PAYLOADLEN reg)
HRF_pollreg(HRF_ADDR_IRQFLAGS2, HRF_MASK_PACKETSENT, HRF_MASK_PACKETSENT);
/* CONFIRM: Was the transmit ok? */
// Check final flags in case of overruns etc
irqflags1 = HRF_readreg(HRF_ADDR_IRQFLAGS1);
irqflags2 = HRF_readreg(HRF_ADDR_IRQFLAGS2);
TRACE_OUTS("irqflags1,2=");
TRACE_OUTN(irqflags1);
TRACE_OUTC(',');
TRACE_OUTN(irqflags2);
TRACE_NL();
if (((irqflags2 & HRF_MASK_FIFONOTEMPTY) != 0) || ((irqflags2 & HRF_MASK_FIFOOVERRUN) != 0))
{
TRACE_FAIL("FIFO not empty or overrun at end of burst");
}
}
/*---------------------------------------------------------------------------*/
//void radio_receiver(RADIO_MODULATION mod)
//{
// def receiver(fsk=None, ook=None):
// """Change into receiver mode"""
// global mode
//
// trace("receiver mode")
// modulation(fsk, ook)
// _change_mode(MODE_RECEIVER)
// _wait_ready()
// mode = "RECEIVER"
//}
/*---------------------------------------------------------------------------*/
//RADIO_RESULT radio_isReceiveWaiting(void)
//{
// def isReceiveWaiting():
// """Check to see if a payload is waiting in the receive buffer"""
// return check_payload()
// return RADIO_RESULT_ERR_UNIMPLEMENTED;
//}
/*---------------------------------------------------------------------------*/
//TODO high level receive, put into receive, receive a payload, back to standby
//RADIO_RESULT radio_receive(uint8_t* buf, uint8_t len)
//{
// def receive():
// """Receive a single payload from the buffer using the present modulation scheme"""
// return HRF_receive_payload()
// return RADIO_RESULT_ERR_UNIMPLEMENTED;
//}
/*---------------------------------------------------------------------------*/
//TODO: low level receive, just receive a payload
//
//RADIO_RESULT radio_receive_payload(uint8_t* buf, uint8_t len)
//{
// def receive():
// """Receive a single payload from the buffer using the present modulation scheme"""
// return HRF_receive_payload()
// return RADIO_RESULT_ERR_UNIMPLEMENTED;
//}
/*---------------------------------------------------------------------------*/
void radio_standby(void)
{
TRACE_OUTS("radio_standby\n");
_change_mode(HRF_MODE_STANDBY);
_wait_ready();
//radio_data.mode = STANDBY
}
/*---------------------------------------------------------------------------*/
void radio_finished(void)
{
TRACE_OUTS("radio_finished\n");
//spi_finished();
gpio_finished();
}
/***** END OF FILE *****/