Newer
Older
pyenergenie / src / energenie / drv / radio.c
/* 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)
{
// 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, 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);


    /* 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, 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);


    /* 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 *****/