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