/* 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};
//----- 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
/***** LOCAL FUNCTION PROTOTYPES *****/
static uint8_t read_ver(void);
static void reset(void);
/*---------------------------------------------------------------------------*/
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);
}
/*---------------------------------------------------------------------------*/
void radio_init(void)
{
#if 0
TRACE_OUTS("start\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");
HRF_change_mode(HRF_MODE_STANDBY);
HRF_pollreg(HRF_ADDR_IRQFLAGS1, HRF_MASK_MODEREADY, HRF_MASK_MODEREADY);
TRACE_OUTS("testing...\n");
//hrf_test_send_ook_tick();
hrf_test_send_energenie_ook_switch();
//spi_finished();
gpio_finished();
return 0;
#endif
// //
// def init():
// """Initialise the module ready for use"""
// spi.init_defaults()
// trace("RESET")
//
// # Note that if another program left GPIO pins in a different state
// # and did a dirty exit, the reset fails to work and the clear fifo hangs.
// # Might have to make the spi.init() set everything to inputs first,
// # then set to outputs, to make sure that the
// # GPIO registers are in a deterministic start state.
// spi.reset() # send a hardware reset to ensure radio in clean state
//
// HRF_clear_fifo() // not needed?
}
/*---------------------------------------------------------------------------*/
void radio_modulation(RADIO_MODULATION mod)
{
// 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)
{
// def transmitter(fsk=None, ook=None):
// """Change into transmitter mode"""
// global mode
//
// trace("transmitter mode")
// modulation(fsk, ook)
// HRF_change_mode(MODE_TRANSMITER)
// mode = "TRANSMITTER"
// HRF_wait_txready()
}
/*---------------------------------------------------------------------------*/
void radio_transmit(uint8_t* payload, uint8_t len, uint8_t repeats)
{
// NOTE: repeats parameter, needs to configure HRF payload sender to repeat
// packet that number of times.
// NOTE: Payload should already be bit encoded and preambled by time it gets here
// def transmit(payload):
// """Transmit a single payload using the present modulation scheme"""
// if not modulation_fsk:
// HRF_send_OOK_payload(payload, repeats) // might not actually be different
// else:
// HRF_send_payload(payload, repeats)
}
/*---------------------------------------------------------------------------*/
void radio_receiver(RADIO_MODULATION mod)
{
// def receiver(fsk=None, ook=None):
// """Change into receiver mode"""
// global mode
//
// trace("receiver mode")
// modulation(fsk, ook)
// HRF_change_mode(MODE_RECEIVER)
// HRF_wait_ready()
// mode = "RECEIVER"
}
/*---------------------------------------------------------------------------*/
RADIO_RESULT radio_isReceiveWaiting(void)
{
// def isReceiveWaiting():
// """Check to see if a payload is waiting in the receive buffer"""
// return HRF_check_payload()
return RADIO_RESULT_ERR_UNIMPLEMENTED;
}
/*---------------------------------------------------------------------------*/
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;
}
/*---------------------------------------------------------------------------*/
void radio_standby(void)
{
//TODO: change radio mode to STANDBY to turn PA off and preserve power.
}
/*---------------------------------------------------------------------------*/
void radio_finished(void)
{
// def finished():
// """Close the library down cleanly when finished"""
// radio.standby() ??
// spi.finished() ??
// gpio.finished() ??
}
/*---------------------------------------------------------------------------*/
// A hard coded test of switching an Energenie switch on and off
//TODO this is for testing only, will be simplified and moved into radio_transmit.
//
//TODO note that we want to change this to use FIFO level rather than PACKETSENT
//so that we can send arbitrary length packets with arbitrary number of repeats
//(i.e. not limited by the U8 size of the payloadlen register in HRF)
static void hrf_test_send_energenie_ook_switch(void)
{
#if 0
// 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.
/* manual preamble, 20 bit encoded address, 4 encoded data bits */
static uint8_t payload[16] = {
0x80, 0x00, 0x00, 0x00, // preamble pulse with timing violation gap
// Energenie 'random' 20 bit address is 0x6C6C6
// 0110 1100 0110 1100 0110
// 0 encoded as 8 (1000)
// 1 encoded as E (1110)
0x8E, 0xE8, 0xEE, 0x88, 0x8E, 0xE8, 0xEE, 0x88, 0x8E, 0xE8,
// Energenie 'switch 1 ON' command F 1111 (0xEE, 0xEE)
0xEE, 0xEE
// Energenie 'switch 1 OFF' command E 1110 (0xEE, 0xE8)
//0xEE, 0xE8
};
/* Last byte of the payload for switch 1 */
#define ON 0xEE
#define OFF 0xE8
// Limited by U8 size of PAYLOADLEN reg (15*16=240)
#define REPEATS 15
// To get longer repeats, we'll have to design a new 'unlimited'
// payload sender, and use FIFOEMPTY as a way to detect end of transmit.
int i;
uint8_t irqflags1;
uint8_t irqflags2;
TRACE_OUTS("config\n");
HRF_config(config_OOK, CONFIG_OOK_COUNT);
// the full packet/burst consists of repeated payloads
// packetsent will trigger when this number of bytes have been transmitted
HRF_writereg(HRF_ADDR_PAYLOADLEN, sizeof(payload) * 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, sizeof(payload)-1);
uint8_t last_byte = ON;
while (1)
{
/* Bring into transmitter mode and ramp up the PA */
TRACE_OUTS("transmitter mode\n");
HRF_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();
/* Set this as alternate ON or OFF bursts */
payload[sizeof(payload)-1] = last_byte;
TRACE_OUTS("tx repeats in a single burst:");
TRACE_OUTN(last_byte);
TRACE_NL();
// send a number of payload repeats for the whole packet burst
for (i=0; i<REPEATS; i++)
{
HRF_writefifo_burst(payload, sizeof(payload));
// 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);
// 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();
// Read back PAYLOADLENGTH to confirm chip doesn't use it as a counter itself
uint8_t pl = HRF_readreg(HRF_ADDR_PAYLOADLEN);
TRACE_OUTS("payloadlen reg at end:");
TRACE_OUTN(pl);
TRACE_NL();
/* Back to STANDBY, this clears packetsent flag */
// always back to standby, regardless of errors above
// otherwise PA/carrier might be left permanently on.
TRACE_OUTS("standby mode\n");
HRF_change_mode(HRF_MODE_STANDBY);
HRF_pollreg(HRF_ADDR_IRQFLAGS1, HRF_MASK_MODEREADY, HRF_MASK_MODEREADY);
if (((irqflags2 & HRF_MASK_FIFONOTEMPTY) != 0) || ((irqflags2 & HRF_MASK_FIFOOVERRUN) != 0))
{
TRACE_OUTN(irqflags2);
TRACE_NL();
TRACE_FAIL("FIFO not empty or overrun at end of burst");
}
/* Inter-burst delay */
delaysec(1);
/* Toggle next switch state */
if (last_byte == OFF)
{
last_byte = ON;
}
else
{
last_byte = OFF;
}
}
#endif
}
/***** END OF FILE *****/