text: // RH_MRF89.cpp
  //
  // Copyright (C) 2015 Mike McCauley
  // $Id: RH_MRF89.cpp,v 1.8 2017/01/12 23:58:00 mikem Exp $
  
  #include <RH_MRF89.h>
  #define BAND_915
  #define DATA_RATE_200
  #define LNA_GAIN LNA_GAIN_0_DB
  #define TX_POWER TX_POWER_13_DB
  
  // Interrupt vectors for the 3 Arduino interrupt pins
  // Each interrupt can be handled by a different instance of RH_MRF89, allowing you to have
  // 2 or more LORAs per Arduino
  RH_MRF89* RH_MRF89::_deviceForInterrupt[RH_MRF89_NUM_INTERRUPTS] = {0, 0, 0};
  uint8_t RH_MRF89::_interruptCount = 0; // Index into _deviceForInterrupt for next device
  
  // These are indexed by the values of ModemConfigChoice
  // Values based on sample modulation values from MRF89XA.h
  // TXIPOLFV set to be more than Fd
  PROGMEM static const RH_MRF89::ModemConfig MODEM_CONFIG_TABLE[] =
  {
      // MODSEL, FDVAL, BRVAL, FILCREG=(PASFILV|BUTFILV), TXIPOLFV
      // FSK, No Manchester, Whitening
      { RH_MRF89_MODSEL_FSK, 0x0B, 0x63, 0x40 | 0x01, 0x20 }, // FSK_Rb2Fd33
      { RH_MRF89_MODSEL_FSK, 0x0B, 0x27, 0x40 | 0x01, 0x20 }, // FSK_Rb5Fd33
      { RH_MRF89_MODSEL_FSK, 0x0B, 0x13, 0x40 | 0x01, 0x20 }, // FSK_Rb10Fd33
      { RH_MRF89_MODSEL_FSK, 0x09, 0x09, 0x70 | 0x02, 0x20 }, // FSK_Rb20Fd40
      { RH_MRF89_MODSEL_FSK, 0x04, 0x04, 0xB0 | 0x05, 0x40 }, // FSK_Rb40Fd80
      { RH_MRF89_MODSEL_FSK, 0x03, 0x03, 0xD0 | 0x06, 0x40 }, // FSK_Rb50Fd100
      { RH_MRF89_MODSEL_FSK, 0x02, 0x02, 0xE0 | 0x09, 0x60 }, // FSK_Rb66Fd133
      { RH_MRF89_MODSEL_FSK, 0x01, 0x01, 0xF0 | 0x0F, 0x80 }, // FSK_Rb100Fd200
      { RH_MRF89_MODSEL_FSK, 0x01, 0x00, 0xF0 | 0x0F, 0x80 }  // FSK_Rb200Fd200
  
  };
  
  
  RH_MRF89::RH_MRF89(uint8_t csconPin, uint8_t csdatPin, uint8_t interruptPin, RHGenericSPI& spi)
      :
      RHNRFSPIDriver(csconPin, spi),
      _csconPin(csconPin),
      _csdatPin(csdatPin),
      _interruptPin(interruptPin)
  {
      _myInterruptIndex = 0xff; // Not allocated yet
  }
  
  bool RH_MRF89::init()
  {
      // MRF89 data cant handle SPI greater than 1MHz.  
      // Sigh on teensy at 1MHz, need special delay after writes, see RHNRFSPIDriver::spiWrite
      _spi.setFrequency(RHGenericSPI::Frequency1MHz);
      if (!RHNRFSPIDriver::init())
  	return false;
  
      // Initialise the chip select pins
      pinMode(_csconPin, OUTPUT);
      digitalWrite(_csconPin, HIGH);
      pinMode(_csdatPin, OUTPUT);
      digitalWrite(_csdatPin, HIGH);
  
      // Determine the interrupt number that corresponds to the interruptPin
      int interruptNumber = digitalPinToInterrupt(_interruptPin);
      if (interruptNumber == NOT_AN_INTERRUPT)
  	return false;
  #ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
      interruptNumber = _interruptPin;
  #endif
  
      // Make sure we are not in some unexpected mode from a previous run    
      setOpMode(RH_MRF89_CMOD_STANDBY); 
  
      // No way to check the device type but lets trivially check there is something there
      // by trying to change a register:
      spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0xaa);
      if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0xaa)
  	return false;
      spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0x3); // Back to the default for FDEV
      if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0x3)
  	return false;
  
      // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
      // ARM M4 requires the below. else pin interrupt doesn't work properly.
      // On all other platforms, its innocuous, belt and braces
      pinMode(_interruptPin, INPUT); 
  
      // Set up interrupt handler
      // Since there are a limited number of interrupt glue functions isr*() available,
      // we can only support a limited number of devices simultaneously
      // On some devices, notably most Arduinos, the interrupt pin passed in is actually the 
      // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
      // yourself based on knowledge of what Arduino board you are running on.
      if (_myInterruptIndex == 0xff)
      {
  	// First run, no interrupt allocated yet
  	if (_interruptCount <= RH_MRF89_NUM_INTERRUPTS)
  	    _myInterruptIndex = _interruptCount++;
  	else
  	    return false; // Too many devices, not enough interrupt vectors
      }
      _deviceForInterrupt[_myInterruptIndex] = this;
      if (_myInterruptIndex == 0)
  	attachInterrupt(interruptNumber, isr0, RISING);
      else if (_myInterruptIndex == 1)
  	attachInterrupt(interruptNumber, isr1, RISING);
      else if (_myInterruptIndex == 2)
  	attachInterrupt(interruptNumber, isr2, RISING);
      else
  	return false; // Too many devices, not enough interrupt vectors
  
      // When used with the MRF89XAM9A module, per 75017B.pdf section 1.3, need:
      // crystal freq = 12.8MHz
      // clock output disabled
      // frequency bands 902-915 or 915-928
      // VCOT 60mV
      // OOK max 28kbps
      // Based on 70622C.pdf, section 3.12: 
      spiWriteRegister(RH_MRF89_REG_00_GCONREG, RH_MRF89_CMOD_STANDBY | RH_MRF89_FBS_950_960 | RH_MRF89_VCOT_60MV);
      spiWriteRegister(RH_MRF89_REG_01_DMODREG, RH_MRF89_MODSEL_FSK | RH_MRF89_OPMODE_PACKET); // FSK, Packet mode, LNA 0dB
      spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0); // Set by setModemConfig
      spiWriteRegister(RH_MRF89_REG_03_BRSREG,  0); // Set by setModemConfig
      spiWriteRegister(RH_MRF89_REG_04_FLTHREG, 0); // Set by setModemConfig (OOK only)
      spiWriteRegister(RH_MRF89_REG_05_FIFOCREG, RH_MRF89_FSIZE_64);
      spiWriteRegister(RH_MRF89_REG_06_R1CREG, 0); // Set by setFrequency
      spiWriteRegister(RH_MRF89_REG_07_P1CREG, 0); // Set by setFrequency
      spiWriteRegister(RH_MRF89_REG_08_S1CREG, 0); // Set by setFrequency
      spiWriteRegister(RH_MRF89_REG_09_R2CREG, 0); // Frequency set 2 not used
      spiWriteRegister(RH_MRF89_REG_0A_P2CREG, 0); // Frequency set 2 not used
      spiWriteRegister(RH_MRF89_REG_0B_S2CREG, 0); // Frequency set 2 not used
      spiWriteRegister(RH_MRF89_REG_0C_PACREG, RH_MRF89_PARC_23);
      // IRQ0 rx mode: SYNC (not used)
      // IRQ1 rx mode: CRCOK
      // IRQ1 tx mode: TXDONE
      spiWriteRegister(RH_MRF89_REG_0D_FTXRXIREG, RH_MRF89_IRQ0RXS_PACKET_SYNC | RH_MRF89_IRQ1RXS_PACKET_CRCOK | RH_MRF89_IRQ1TX);
      spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, RH_MRF89_LENPLL);
      spiWriteRegister(RH_MRF89_REG_0F_RSTHIREG, 0x00); // default not used if no RSSI interrupts
      spiWriteRegister(RH_MRF89_REG_10_FILCREG, 0); // Set by setModemConfig
  
      spiWriteRegister(RH_MRF89_REG_11_PFCREG, 0x38);// 100kHz, recommended, but not used, see RH_MRF89_REG_12_SYNCREG OOK only?
      spiWriteRegister(RH_MRF89_REG_12_SYNCREG, RH_MRF89_SYNCREN | RH_MRF89_SYNCWSZ_32); // No polyphase, no bsync, sync, 0 errors
      spiWriteRegister(RH_MRF89_REG_13_RSVREG, 0x07);//default
  //    spiWriteRegister(RH_MRF89_REG_14_RSTSREG, 0x00); // NO, read only
      spiWriteRegister(RH_MRF89_REG_15_OOKCREG, 0x00); // Set by setModemConfig OOK only
      spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG, 0x69); // Set by setSyncWords
      spiWriteRegister(RH_MRF89_REG_17_SYNCV23REG, 0x81); // Set by setSyncWords
      spiWriteRegister(RH_MRF89_REG_18_SYNCV15REG, 0x7E); // Set by setSyncWords
      spiWriteRegister(RH_MRF89_REG_19_SYNCV07REG, 0x96); // Set by setSyncWords
      // TXIPOLFV set by setModemConfig. power set by setTxPower
      spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, 0xf0 | RH_MRF89_TXOPVAL_13DBM); // TX cutoff freq=375kHz,
      spiWriteRegister(RH_MRF89_REG_1B_CLKOREG, 0x00); // Disable clock output to save power
      spiWriteRegister(RH_MRF89_REG_1C_PLOADREG, 0x40); // payload=64bytes (no RX-filtering on packet length)
      spiWriteRegister(RH_MRF89_REG_1D_NADDSREG, 0x00); // Node Address (0=default) Not used
      spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, RH_MRF89_PKTLENF | RH_MRF89_PRESIZE_4 | RH_MRF89_WHITEON | RH_MRF89_CHKCRCEN | RH_MRF89_ADDFIL_OFF);
      spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, 0x00); // default (FIFO access in standby=write, clear FIFO on CRC mismatch)
  
      // Looking OK now
      // Set some suitable defaults:
      setPreambleLength(3); // The default
      uint8_t syncwords[] = { 0x69, 0x81, 0x7e, 0x96 }; // Same as RH_MRF89XA
      setSyncWords(syncwords, sizeof(syncwords));
      setTxPower(RH_MRF89_TXOPVAL_1DBM);
      if (!setFrequency(915.4))
  	return false;
      // Some slow, reliable default speed and modulation
      if (!setModemConfig(FSK_Rb20Fd40))
  	return false;
  
      return true;
  }
  
  bool RH_MRF89::printRegisters()
  {
  #ifdef RH_HAVE_SERIAL
      uint8_t i;
      for (i = 0; i <= 0x1f; i++)
      {
  	Serial.print(i, HEX);
  	Serial.print(": ");
  	Serial.println(spiReadRegister(i), HEX);
      }
  #endif
      return true;
  }
  
  // C++ level interrupt handler for this instance
  // MRF89XA is unusual in that it has 2 interrupt lines, and not a single, combined one.
  // Only one of the several interrupt lines (IRQ1) from the RFM95 needs to be
  // connnected to the processor.
  // We use this to get CRCOK and TXDONE  interrupts
  void RH_MRF89::handleInterrupt()
  {
  //    Serial.println("I");
      if (_mode == RHModeTx)
      {
  //    Serial.println("T");
  	// TXDONE
  	// Transmit is complete
  	_txGood++;
  	setModeIdle();
      }
      else if (_mode == RHModeRx)
      {
  //	Serial.println("R");
  	// CRCOK
  	// We have received a packet.
          // First byte in FIFO is packet length
  
  	// REVISIT: Capture last rssi from RSTSREG
  	// based roughly on Figure 3-9
  	_lastRssi = (spiReadRegister(RH_MRF89_REG_14_RSTSREG) >> 1) - 120;
  
  	_bufLen = spiReadData();
  	if (_bufLen < 4)
  	{
  	    // Drain the FIFO
  	    uint8_t i;
  	    for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++)
  		spiReadData();
  	    clearRxBuf();
  	    return;
  	}
  	
  	// Now drain all the data from the FIFO into _buf
  	uint8_t i;
  	for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++)
  	    _buf[i] = spiReadData();
  
  	// All good. See if its for us
  	validateRxBuf(); 
  	if (_rxBufValid)
  	    setModeIdle(); // Got one 
      }
  }
  
  // These are low level functions that call the interrupt handler for the correct
  // instance of RH_MRF89.
  // 3 interrupts allows us to have 3 different devices
  void RH_MRF89::isr0()
  {
      if (_deviceForInterrupt[0])
  	_deviceForInterrupt[0]->handleInterrupt();
  }
  void RH_MRF89::isr1()
  {
      if (_deviceForInterrupt[1])
  	_deviceForInterrupt[1]->handleInterrupt();
  }
  void RH_MRF89::isr2()
  {
      if (_deviceForInterrupt[2])
  	_deviceForInterrupt[2]->handleInterrupt();
  }
  
  uint8_t RH_MRF89::spiReadRegister(uint8_t reg)
  {
      // Tell the chip we want to talk to the configuration registers
      setSlaveSelectPin(_csconPin);
      digitalWrite(_csdatPin, HIGH);
      return spiRead(((reg & 0x1f) << 1) | RH_MRF89_SPI_READ_MASK);
  }
  
  uint8_t RH_MRF89::spiWriteRegister(uint8_t reg, uint8_t val)
  {
      // Tell the chip we want to talk to the configuration registers
      setSlaveSelectPin(_csconPin);
      digitalWrite(_csdatPin, HIGH);
      // Hmmm, on teensy 3.1, needed some special behaviour in RHNRFSPIDriver::spiWrite
      // because otherwise, CSCON returns high before the final clock goes low,
      // which prevents the MRF89XA spi write succeeding. Clock must be low when CSCON goes high.
      return spiWrite(((reg & 0x1f) << 1), val);
  }
  
  uint8_t RH_MRF89::spiWriteData(uint8_t data)
  {
      spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO
      setSlaveSelectPin(_csdatPin);
      digitalWrite(_csconPin, HIGH);
      return spiCommand(data);
  }
  
  uint8_t RH_MRF89::spiWriteData(const uint8_t* data, uint8_t len)
  {
      spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO
      setSlaveSelectPin(_csdatPin);
      digitalWrite(_csconPin, HIGH);
  
      uint8_t status = 0;
      ATOMIC_BLOCK_START;
      digitalWrite(_slaveSelectPin, LOW);
      while (len--)
  	_spi.transfer(*data++);
      digitalWrite(_slaveSelectPin, HIGH);
      ATOMIC_BLOCK_END;
      return status;
  
  }
  
  uint8_t RH_MRF89::spiReadData()
  {
      spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC | RH_MRF89_FRWAXS); // Read from FIFO
      setSlaveSelectPin(_csdatPin);
      digitalWrite(_csconPin, HIGH);
      return spiCommand(0);
  }
  
  void RH_MRF89::setOpMode(uint8_t mode)
  {
      // REVISIT: do we need to have time delays when switching between modes?
      uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG);
      val = (val & ~RH_MRF89_CMOD) | (mode & RH_MRF89_CMOD);
      spiWriteRegister(RH_MRF89_REG_00_GCONREG, val);
  }
  
  void RH_MRF89::setModeIdle()
  {
      if (_mode != RHModeIdle)
      {
  	setOpMode(RH_MRF89_CMOD_STANDBY);
  	_mode = RHModeIdle;
      }
  }
  
  bool RH_MRF89::sleep()
  {
      if (_mode != RHModeSleep)
      {
  	setOpMode(RH_MRF89_CMOD_SLEEP);
  	_mode = RHModeSleep;
      }
      return true;
  }
  
  void RH_MRF89::setModeRx()
  {
      if (_mode != RHModeRx)
      {
  	setOpMode(RH_MRF89_CMOD_RECEIVE);
  	_mode = RHModeRx;
      }
  }
  
  void RH_MRF89::setModeTx()
  {
      if (_mode != RHModeTx)
      {
  	setOpMode(RH_MRF89_CMOD_TRANSMIT);
  	_mode = RHModeTx;
      }
  }
  
  void RH_MRF89::setTxPower(uint8_t power)
  {
      uint8_t txconreg = spiReadRegister(RH_MRF89_REG_1A_TXCONREG);
      txconreg |= (power & RH_MRF89_TXOPVAL);
      spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, txconreg);
  }
  
  bool RH_MRF89::available()
  {
      if (_mode == RHModeTx)
  	return false;
      setModeRx();
  
      return _rxBufValid; // Will be set by the interrupt handler when a good message is received
  }
  
  bool RH_MRF89::recv(uint8_t* buf, uint8_t* len)
  {
      if (!available())
  	return false;
  
      if (buf && len)
      {
  	ATOMIC_BLOCK_START;
  	// Skip the 4 headers that are at the beginning of the rxBuf
  	if (*len > _bufLen - RH_MRF89_HEADER_LEN)
  	    *len = _bufLen - RH_MRF89_HEADER_LEN;
  	memcpy(buf, _buf + RH_MRF89_HEADER_LEN, *len);
  	ATOMIC_BLOCK_END;
      }
      clearRxBuf(); // This message accepted and cleared
  
      return true;
  }
  
  bool RH_MRF89::send(const uint8_t* data, uint8_t len)
  {
      if (len > RH_MRF89_MAX_MESSAGE_LEN)
  	return false;
      
      waitPacketSent(); // Make sure we dont interrupt an outgoing message
      setModeIdle();
      
      if (!waitCAD()) 
  	return false;  // Check channel activity
  
      // First octet is the length of the chip payload
      // 0 length messages are transmitted but never trigger a receive!
      spiWriteData(len + RH_MRF89_HEADER_LEN);
      spiWriteData(_txHeaderTo);
      spiWriteData(_txHeaderFrom);
      spiWriteData(_txHeaderId);
      spiWriteData(_txHeaderFlags);
      spiWriteData(data, len);
      setModeTx(); // Start transmitting
  
      return true;
  }
  
  uint8_t RH_MRF89::maxMessageLength()
  {
      return RH_MRF89_MAX_MESSAGE_LEN;
  }
  
  // Check whether the latest received message is complete and uncorrupted
  void RH_MRF89::validateRxBuf()
  {
      if (_bufLen < 4)
  	return; // Too short to be a real message
      // Extract the 4 headers
      _rxHeaderTo    = _buf[0];
      _rxHeaderFrom  = _buf[1];
      _rxHeaderId    = _buf[2];
      _rxHeaderFlags = _buf[3];
      if (_promiscuous ||
  	_rxHeaderTo == _thisAddress ||
  	_rxHeaderTo == RH_BROADCAST_ADDRESS)
      {
  	_rxGood++;
  	_rxBufValid = true;
      }
  }
  
  void RH_MRF89::clearRxBuf()
  {
      ATOMIC_BLOCK_START;
      _rxBufValid = false;
      _bufLen = 0;
      ATOMIC_BLOCK_END;
  }
  
  bool RH_MRF89::verifyPLLLock()
  {
      // Verify PLL-lock per instructions in Note 1 section 3.12
      // Need to do this after changing frequency.
      uint8_t ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG);
      spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, ftpriVal | RH_MRF89_LSTSPLL); // Clear PLL lock bit
      setOpMode(RH_MRF89_CMOD_FS);
      unsigned long ulStartTime = millis();
      while ((millis() - ulStartTime < 1000))
      {
          ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG);
          if ((ftpriVal & RH_MRF89_LSTSPLL) != 0)
              break;
      }
      setOpMode(RH_MRF89_CMOD_STANDBY);
      return ((ftpriVal & RH_MRF89_LSTSPLL) != 0);
  }
  
  bool RH_MRF89::setFrequency(float centre)
  {
      // REVISIT: FSK only: its different for OOK :-(
  
      uint8_t FBS;
      if (centre >= 902.0 && centre < 915.0)
      {
  	FBS = RH_MRF89_FBS_902_915;
      }
      else if (centre >= 915.0 && centre <= 928.0)
      {
  	FBS = RH_MRF89_FBS_915_928;
      }
      else if (centre >= 950.0 && centre <= 960.0)
      {
  	// Not all modules support this frequency band:
  	// The MRF98XAM9A does not
  	FBS = RH_MRF89_FBS_950_960;
      }
  //    else if (centre >= 863.0 && centre <= 870.0)
  //    {
  //	// Not all modules support this frequency band:
  //	// The MRF98XAM9A does not
  //	FBS = RH_MRF89_FBS_950_960; // Yes same as above
  //    }
      else
      {
  	// Cant do this freq
  	return false;
      }
  
      // Based on frequency calcs done in MRF89XA.h
  //    uint8_t R = 100; // Recommended
      uint8_t R = 119; // Also recommended :-(
      uint32_t centre_kHz = centre * 1000;
      uint32_t xtal_kHz = (RH_MRF89_XTAL_FREQ * 1000);
      uint32_t compare = (centre_kHz * 8 * (R + 1)) / (9 * xtal_kHz);
      uint8_t P = ((compare - 75) / 76) + 1;
      uint8_t S = compare - (75 * (P + 1));
  
      // Now set the new register values:
      uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG);
      val = (val & ~RH_MRF89_FBS) | (FBS & RH_MRF89_FBS);
      spiWriteRegister(RH_MRF89_REG_00_GCONREG, val);
  
      spiWriteRegister(RH_MRF89_REG_06_R1CREG, R); 
      spiWriteRegister(RH_MRF89_REG_07_P1CREG, P); 
      spiWriteRegister(RH_MRF89_REG_08_S1CREG, S); 
  
      return verifyPLLLock();
  }
  
  // Set one of the canned FSK Modem configs
  // Returns true if its a valid choice
  bool RH_MRF89::setModemConfig(ModemConfigChoice index)
  {
      if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
          return false;
  
      RH_MRF89::ModemConfig cfg;
      memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(cfg));
  
      // Now update the registers
      uint8_t val = spiReadRegister(RH_MRF89_REG_01_DMODREG);
      val = (val & ~RH_MRF89_MODSEL) | cfg.MODSEL;
      spiWriteRegister(RH_MRF89_REG_01_DMODREG, val);
  
      spiWriteRegister(RH_MRF89_REG_02_FDEVREG, cfg.FDVAL);
      spiWriteRegister(RH_MRF89_REG_03_BRSREG,  cfg.BRVAL);
      spiWriteRegister(RH_MRF89_REG_10_FILCREG, cfg.FILCREG);
  
      // The sample configs in MRF89XA.h all use TXIPOLFV = 0xf0 => 375kHz, which is too wide for most modulations
      val = spiReadRegister(RH_MRF89_REG_1A_TXCONREG);
      val = (val & ~RH_MRF89_TXIPOLFV) | (cfg.TXIPOLFV & RH_MRF89_TXIPOLFV);
      spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, val);
  
      return true;
  }
  
  void RH_MRF89::setPreambleLength(uint8_t bytes)
  {
      if (bytes >= 1 && bytes <= 4)
      {
  	bytes--;
  	uint8_t pktcreg = spiReadRegister(RH_MRF89_REG_1E_PKTCREG);
  	pktcreg = (pktcreg & ~RH_MRF89_PRESIZE) | ((bytes << 5) & RH_MRF89_PRESIZE);
  	spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, pktcreg);
      }
  }
  
  void RH_MRF89::setSyncWords(const uint8_t* syncWords, uint8_t len)
  {
      if (syncWords && (len > 0 and len <= 4))
      {
  	uint8_t syncreg = spiReadRegister(RH_MRF89_REG_12_SYNCREG);
  	syncreg = (syncreg & ~RH_MRF89_SYNCWSZ) | (((len - 1) << 3) & RH_MRF89_SYNCWSZ);
  	spiWriteRegister(RH_MRF89_REG_12_SYNCREG, syncreg);
  	uint8_t i;
  	for (i = 0; i < 4; i++)
  	{
  	    if (len > i)
  		spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG + i, syncWords[i]);
  	}
      }
  }
  
  