PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

RH_MRF89.cpp 18KB

hace 3 años
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. // RH_MRF89.cpp
  2. //
  3. // Copyright (C) 2015 Mike McCauley
  4. // $Id: RH_MRF89.cpp,v 1.7 2015/12/31 04:23:12 mikem Exp $
  5. #include <RH_MRF89.h>
  6. #define BAND_915
  7. #define DATA_RATE_200
  8. #define LNA_GAIN LNA_GAIN_0_DB
  9. #define TX_POWER TX_POWER_13_DB
  10. // Interrupt vectors for the 3 Arduino interrupt pins
  11. // Each interrupt can be handled by a different instance of RH_MRF89, allowing you to have
  12. // 2 or more LORAs per Arduino
  13. RH_MRF89* RH_MRF89::_deviceForInterrupt[RH_MRF89_NUM_INTERRUPTS] = {0, 0, 0};
  14. uint8_t RH_MRF89::_interruptCount = 0; // Index into _deviceForInterrupt for next device
  15. // These are indexed by the values of ModemConfigChoice
  16. // Values based on sample modulation values from MRF89XA.h
  17. // TXIPOLFV set to be more than Fd
  18. PROGMEM static const RH_MRF89::ModemConfig MODEM_CONFIG_TABLE[] =
  19. {
  20. // MODSEL, FDVAL, BRVAL, FILCREG=(PASFILV|BUTFILV), TXIPOLFV
  21. // FSK, No Manchester, Whitening
  22. { RH_MRF89_MODSEL_FSK, 0x0B, 0x63, 0x40 | 0x01, 0x20 }, // FSK_Rb2Fd33
  23. { RH_MRF89_MODSEL_FSK, 0x0B, 0x27, 0x40 | 0x01, 0x20 }, // FSK_Rb5Fd33
  24. { RH_MRF89_MODSEL_FSK, 0x0B, 0x13, 0x40 | 0x01, 0x20 }, // FSK_Rb10Fd33
  25. { RH_MRF89_MODSEL_FSK, 0x09, 0x09, 0x70 | 0x02, 0x20 }, // FSK_Rb20Fd40
  26. { RH_MRF89_MODSEL_FSK, 0x04, 0x04, 0xB0 | 0x05, 0x40 }, // FSK_Rb40Fd80
  27. { RH_MRF89_MODSEL_FSK, 0x03, 0x03, 0xD0 | 0x06, 0x40 }, // FSK_Rb50Fd100
  28. { RH_MRF89_MODSEL_FSK, 0x02, 0x02, 0xE0 | 0x09, 0x60 }, // FSK_Rb66Fd133
  29. { RH_MRF89_MODSEL_FSK, 0x01, 0x01, 0xF0 | 0x0F, 0x80 }, // FSK_Rb100Fd200
  30. { RH_MRF89_MODSEL_FSK, 0x01, 0x00, 0xF0 | 0x0F, 0x80 } // FSK_Rb200Fd200
  31. };
  32. RH_MRF89::RH_MRF89(uint8_t csconPin, uint8_t csdatPin, uint8_t interruptPin, RHGenericSPI& spi)
  33. :
  34. RHNRFSPIDriver(csconPin, spi),
  35. _csconPin(csconPin),
  36. _csdatPin(csdatPin),
  37. _interruptPin(interruptPin)
  38. {
  39. _myInterruptIndex = 0xff; // Not allocated yet
  40. }
  41. bool RH_MRF89::init()
  42. {
  43. // MRF89 data cant handle SPI greater than 1MHz.
  44. // Sigh on teensy at 1MHz, need special delay after writes, see RHNRFSPIDriver::spiWrite
  45. _spi.setFrequency(RHGenericSPI::Frequency1MHz);
  46. if (!RHNRFSPIDriver::init())
  47. return false;
  48. // Initialise the chip select pins
  49. pinMode(_csconPin, OUTPUT);
  50. digitalWrite(_csconPin, HIGH);
  51. pinMode(_csdatPin, OUTPUT);
  52. digitalWrite(_csdatPin, HIGH);
  53. // Determine the interrupt number that corresponds to the interruptPin
  54. int interruptNumber = digitalPinToInterrupt(_interruptPin);
  55. if (interruptNumber == NOT_AN_INTERRUPT)
  56. return false;
  57. #ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
  58. interruptNumber = _interruptPin;
  59. #endif
  60. // Make sure we are not in some unexpected mode from a previous run
  61. setOpMode(RH_MRF89_CMOD_STANDBY);
  62. // No way to check the device type but lets trivially check there is something there
  63. // by trying to change a register:
  64. spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0xaa);
  65. if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0xaa)
  66. return false;
  67. spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0x3); // Back to the default for FDEV
  68. if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0x3)
  69. return false;
  70. // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
  71. // ARM M4 requires the below. else pin interrupt doesn't work properly.
  72. // On all other platforms, its innocuous, belt and braces
  73. pinMode(_interruptPin, INPUT);
  74. // Set up interrupt handler
  75. // Since there are a limited number of interrupt glue functions isr*() available,
  76. // we can only support a limited number of devices simultaneously
  77. // On some devices, notably most Arduinos, the interrupt pin passed in is actually the
  78. // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
  79. // yourself based on knowledge of what Arduino board you are running on.
  80. if (_myInterruptIndex == 0xff)
  81. {
  82. // First run, no interrupt allocated yet
  83. if (_interruptCount <= RH_MRF89_NUM_INTERRUPTS)
  84. _myInterruptIndex = _interruptCount++;
  85. else
  86. return false; // Too many devices, not enough interrupt vectors
  87. }
  88. _deviceForInterrupt[_myInterruptIndex] = this;
  89. if (_myInterruptIndex == 0)
  90. attachInterrupt(interruptNumber, isr0, RISING);
  91. else if (_myInterruptIndex == 1)
  92. attachInterrupt(interruptNumber, isr1, RISING);
  93. else if (_myInterruptIndex == 2)
  94. attachInterrupt(interruptNumber, isr2, RISING);
  95. else
  96. return false; // Too many devices, not enough interrupt vectors
  97. // When used with the MRF89XAM9A module, per 75017B.pdf section 1.3, need:
  98. // crystal freq = 12.8MHz
  99. // clock output disabled
  100. // frequency bands 902-915 or 915-928
  101. // VCOT 60mV
  102. // OOK max 28kbps
  103. // Based on 70622C.pdf, section 3.12:
  104. spiWriteRegister(RH_MRF89_REG_00_GCONREG, RH_MRF89_CMOD_STANDBY | RH_MRF89_FBS_950_960 | RH_MRF89_VCOT_60MV);
  105. spiWriteRegister(RH_MRF89_REG_01_DMODREG, RH_MRF89_MODSEL_FSK | RH_MRF89_OPMODE_PACKET); // FSK, Packet mode, LNA 0dB
  106. spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0); // Set by setModemConfig
  107. spiWriteRegister(RH_MRF89_REG_03_BRSREG, 0); // Set by setModemConfig
  108. spiWriteRegister(RH_MRF89_REG_04_FLTHREG, 0); // Set by setModemConfig (OOK only)
  109. spiWriteRegister(RH_MRF89_REG_05_FIFOCREG, RH_MRF89_FSIZE_64);
  110. spiWriteRegister(RH_MRF89_REG_06_R1CREG, 0); // Set by setFrequency
  111. spiWriteRegister(RH_MRF89_REG_07_P1CREG, 0); // Set by setFrequency
  112. spiWriteRegister(RH_MRF89_REG_08_S1CREG, 0); // Set by setFrequency
  113. spiWriteRegister(RH_MRF89_REG_09_R2CREG, 0); // Frequency set 2 not used
  114. spiWriteRegister(RH_MRF89_REG_0A_P2CREG, 0); // Frequency set 2 not used
  115. spiWriteRegister(RH_MRF89_REG_0B_S2CREG, 0); // Frequency set 2 not used
  116. spiWriteRegister(RH_MRF89_REG_0C_PACREG, RH_MRF89_PARC_23);
  117. // IRQ0 rx mode: SYNC (not used)
  118. // IRQ1 rx mode: CRCOK
  119. // IRQ1 tx mode: TXDONE
  120. spiWriteRegister(RH_MRF89_REG_0D_FTXRXIREG, RH_MRF89_IRQ0RXS_PACKET_SYNC | RH_MRF89_IRQ1RXS_PACKET_CRCOK | RH_MRF89_IRQ1TX);
  121. spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, RH_MRF89_LENPLL);
  122. spiWriteRegister(RH_MRF89_REG_0F_RSTHIREG, 0x00); // default not used if no RSSI interrupts
  123. spiWriteRegister(RH_MRF89_REG_10_FILCREG, 0); // Set by setModemConfig
  124. spiWriteRegister(RH_MRF89_REG_11_PFCREG, 0x38);// 100kHz, recommended, but not used, see RH_MRF89_REG_12_SYNCREG OOK only?
  125. spiWriteRegister(RH_MRF89_REG_12_SYNCREG, RH_MRF89_SYNCREN | RH_MRF89_SYNCWSZ_32); // No polyphase, no bsync, sync, 0 errors
  126. spiWriteRegister(RH_MRF89_REG_13_RSVREG, 0x07);//default
  127. // spiWriteRegister(RH_MRF89_REG_14_RSTSREG, 0x00); // NO, read only
  128. spiWriteRegister(RH_MRF89_REG_15_OOKCREG, 0x00); // Set by setModemConfig OOK only
  129. spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG, 0x69); // Set by setSyncWords
  130. spiWriteRegister(RH_MRF89_REG_17_SYNCV23REG, 0x81); // Set by setSyncWords
  131. spiWriteRegister(RH_MRF89_REG_18_SYNCV15REG, 0x7E); // Set by setSyncWords
  132. spiWriteRegister(RH_MRF89_REG_19_SYNCV07REG, 0x96); // Set by setSyncWords
  133. // TXIPOLFV set by setModemConfig. power set by setTxPower
  134. spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, 0xf0 | RH_MRF89_TXOPVAL_13DBM); // TX cutoff freq=375kHz,
  135. spiWriteRegister(RH_MRF89_REG_1B_CLKOREG, 0x00); // Disable clock output to save power
  136. spiWriteRegister(RH_MRF89_REG_1C_PLOADREG, 0x40); // payload=64bytes (no RX-filtering on packet length)
  137. spiWriteRegister(RH_MRF89_REG_1D_NADDSREG, 0x00); // Node Address (0=default) Not used
  138. spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, RH_MRF89_PKTLENF | RH_MRF89_PRESIZE_4 | RH_MRF89_WHITEON | RH_MRF89_CHKCRCEN | RH_MRF89_ADDFIL_OFF);
  139. spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, 0x00); // default (FIFO access in standby=write, clear FIFO on CRC mismatch)
  140. // Looking OK now
  141. // Set some suitable defaults:
  142. setPreambleLength(3); // The default
  143. uint8_t syncwords[] = { 0x69, 0x81, 0x7e, 0x96 }; // Same as RH_MRF89XA
  144. setSyncWords(syncwords, sizeof(syncwords));
  145. setTxPower(RH_MRF89_TXOPVAL_1DBM);
  146. if (!setFrequency(915.4))
  147. return false;
  148. // Some slow, reliable default speed and modulation
  149. if (!setModemConfig(FSK_Rb20Fd40))
  150. return false;
  151. return true;
  152. }
  153. bool RH_MRF89::printRegisters()
  154. {
  155. #ifdef RH_HAVE_SERIAL
  156. uint8_t i;
  157. for (i = 0; i <= 0x1f; i++)
  158. {
  159. Serial.print(i, HEX);
  160. Serial.print(": ");
  161. Serial.println(spiReadRegister(i), HEX);
  162. }
  163. #endif
  164. return true;
  165. }
  166. // C++ level interrupt handler for this instance
  167. // MRF89XA is unusual in that it has 2 interrupt lines, and not a single, combined one.
  168. // Only one of the several interrupt lines (IRQ1) from the RFM95 needs to be
  169. // connnected to the processor.
  170. // We use this to get CRCOK and TXDONE interrupts
  171. void RH_MRF89::handleInterrupt()
  172. {
  173. // Serial.println("I");
  174. if (_mode == RHModeTx)
  175. {
  176. // Serial.println("T");
  177. // TXDONE
  178. // Transmit is complete
  179. _txGood++;
  180. setModeIdle();
  181. }
  182. else if (_mode == RHModeRx)
  183. {
  184. // Serial.println("R");
  185. // CRCOK
  186. // We have received a packet.
  187. // First byte in FIFO is packet length
  188. // REVISIT: Capture last rssi from RSTSREG
  189. // based roughly on Figure 3-9
  190. _lastRssi = (spiReadRegister(RH_MRF89_REG_14_RSTSREG) >> 1) - 120;
  191. _bufLen = spiReadData();
  192. if (_bufLen < 4)
  193. {
  194. // Drain the FIFO
  195. uint8_t i;
  196. for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++)
  197. spiReadData();
  198. clearRxBuf();
  199. return;
  200. }
  201. // Now drain all the data from the FIFO into _buf
  202. uint8_t i;
  203. for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++)
  204. _buf[i] = spiReadData();
  205. // All good. See if its for us
  206. validateRxBuf();
  207. if (_rxBufValid)
  208. setModeIdle(); // Got one
  209. }
  210. }
  211. // These are low level functions that call the interrupt handler for the correct
  212. // instance of RH_MRF89.
  213. // 3 interrupts allows us to have 3 different devices
  214. void RH_MRF89::isr0()
  215. {
  216. if (_deviceForInterrupt[0])
  217. _deviceForInterrupt[0]->handleInterrupt();
  218. }
  219. void RH_MRF89::isr1()
  220. {
  221. if (_deviceForInterrupt[1])
  222. _deviceForInterrupt[1]->handleInterrupt();
  223. }
  224. void RH_MRF89::isr2()
  225. {
  226. if (_deviceForInterrupt[2])
  227. _deviceForInterrupt[2]->handleInterrupt();
  228. }
  229. uint8_t RH_MRF89::spiReadRegister(uint8_t reg)
  230. {
  231. // Tell the chip we want to talk to the configuration registers
  232. setSlaveSelectPin(_csconPin);
  233. digitalWrite(_csdatPin, HIGH);
  234. return spiRead(((reg & 0x1f) << 1) | RH_MRF89_SPI_READ_MASK);
  235. }
  236. uint8_t RH_MRF89::spiWriteRegister(uint8_t reg, uint8_t val)
  237. {
  238. // Tell the chip we want to talk to the configuration registers
  239. setSlaveSelectPin(_csconPin);
  240. digitalWrite(_csdatPin, HIGH);
  241. // Hmmm, on teensy 3.1, needed some special behaviour in RHNRFSPIDriver::spiWrite
  242. // because otherwise, CSCON returns high before the final clock goes low,
  243. // which prevents the MRF89XA spi write succeeding. Clock must be low when CSCON goes high.
  244. return spiWrite(((reg & 0x1f) << 1), val);
  245. }
  246. uint8_t RH_MRF89::spiWriteData(uint8_t data)
  247. {
  248. spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO
  249. setSlaveSelectPin(_csdatPin);
  250. digitalWrite(_csconPin, HIGH);
  251. return spiCommand(data);
  252. }
  253. uint8_t RH_MRF89::spiWriteData(const uint8_t* data, uint8_t len)
  254. {
  255. spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO
  256. setSlaveSelectPin(_csdatPin);
  257. digitalWrite(_csconPin, HIGH);
  258. uint8_t status = 0;
  259. ATOMIC_BLOCK_START;
  260. _spi.beginTransaction();
  261. digitalWrite(_slaveSelectPin, LOW);
  262. while (len--)
  263. _spi.transfer(*data++);
  264. digitalWrite(_slaveSelectPin, HIGH);
  265. _spi.endTransaction();
  266. ATOMIC_BLOCK_END;
  267. return status;
  268. }
  269. uint8_t RH_MRF89::spiReadData()
  270. {
  271. spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC | RH_MRF89_FRWAXS); // Read from FIFO
  272. setSlaveSelectPin(_csdatPin);
  273. digitalWrite(_csconPin, HIGH);
  274. return spiCommand(0);
  275. }
  276. void RH_MRF89::setOpMode(uint8_t mode)
  277. {
  278. // REVISIT: do we need to have time delays when switching between modes?
  279. uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG);
  280. val = (val & ~RH_MRF89_CMOD) | (mode & RH_MRF89_CMOD);
  281. spiWriteRegister(RH_MRF89_REG_00_GCONREG, val);
  282. }
  283. void RH_MRF89::setModeIdle()
  284. {
  285. if (_mode != RHModeIdle)
  286. {
  287. setOpMode(RH_MRF89_CMOD_STANDBY);
  288. _mode = RHModeIdle;
  289. }
  290. }
  291. bool RH_MRF89::sleep()
  292. {
  293. if (_mode != RHModeSleep)
  294. {
  295. setOpMode(RH_MRF89_CMOD_SLEEP);
  296. _mode = RHModeSleep;
  297. }
  298. return true;
  299. }
  300. void RH_MRF89::setModeRx()
  301. {
  302. if (_mode != RHModeRx)
  303. {
  304. setOpMode(RH_MRF89_CMOD_RECEIVE);
  305. _mode = RHModeRx;
  306. }
  307. }
  308. void RH_MRF89::setModeTx()
  309. {
  310. if (_mode != RHModeTx)
  311. {
  312. setOpMode(RH_MRF89_CMOD_TRANSMIT);
  313. _mode = RHModeTx;
  314. }
  315. }
  316. void RH_MRF89::setTxPower(uint8_t power)
  317. {
  318. uint8_t txconreg = spiReadRegister(RH_MRF89_REG_1A_TXCONREG);
  319. txconreg |= (power & RH_MRF89_TXOPVAL);
  320. spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, txconreg);
  321. }
  322. bool RH_MRF89::available()
  323. {
  324. if (_mode == RHModeTx)
  325. return false;
  326. setModeRx();
  327. return _rxBufValid; // Will be set by the interrupt handler when a good message is received
  328. }
  329. bool RH_MRF89::recv(uint8_t* buf, uint8_t* len)
  330. {
  331. if (!available())
  332. return false;
  333. if (buf && len)
  334. {
  335. ATOMIC_BLOCK_START;
  336. // Skip the 4 headers that are at the beginning of the rxBuf
  337. if (*len > _bufLen - RH_MRF89_HEADER_LEN)
  338. *len = _bufLen - RH_MRF89_HEADER_LEN;
  339. memcpy(buf, _buf + RH_MRF89_HEADER_LEN, *len);
  340. ATOMIC_BLOCK_END;
  341. }
  342. clearRxBuf(); // This message accepted and cleared
  343. return true;
  344. }
  345. bool RH_MRF89::send(const uint8_t* data, uint8_t len)
  346. {
  347. if (len > RH_MRF89_MAX_MESSAGE_LEN)
  348. return false;
  349. waitPacketSent(); // Make sure we dont interrupt an outgoing message
  350. setModeIdle();
  351. // First octet is the length of the chip payload
  352. // 0 length messages are transmitted but never trigger a receive!
  353. spiWriteData(len + RH_MRF89_HEADER_LEN);
  354. spiWriteData(_txHeaderTo);
  355. spiWriteData(_txHeaderFrom);
  356. spiWriteData(_txHeaderId);
  357. spiWriteData(_txHeaderFlags);
  358. spiWriteData(data, len);
  359. setModeTx(); // Start transmitting
  360. return true;
  361. }
  362. uint8_t RH_MRF89::maxMessageLength()
  363. {
  364. return RH_MRF89_MAX_MESSAGE_LEN;
  365. }
  366. // Check whether the latest received message is complete and uncorrupted
  367. void RH_MRF89::validateRxBuf()
  368. {
  369. if (_bufLen < 4)
  370. return; // Too short to be a real message
  371. // Extract the 4 headers
  372. _rxHeaderTo = _buf[0];
  373. _rxHeaderFrom = _buf[1];
  374. _rxHeaderId = _buf[2];
  375. _rxHeaderFlags = _buf[3];
  376. if (_promiscuous ||
  377. _rxHeaderTo == _thisAddress ||
  378. _rxHeaderTo == RH_BROADCAST_ADDRESS)
  379. {
  380. _rxGood++;
  381. _rxBufValid = true;
  382. }
  383. }
  384. void RH_MRF89::clearRxBuf()
  385. {
  386. ATOMIC_BLOCK_START;
  387. _rxBufValid = false;
  388. _bufLen = 0;
  389. ATOMIC_BLOCK_END;
  390. }
  391. bool RH_MRF89::verifyPLLLock()
  392. {
  393. // Verify PLL-lock per instructions in Note 1 section 3.12
  394. // Need to do this after changing frequency.
  395. uint8_t ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG);
  396. spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, ftpriVal | RH_MRF89_LSTSPLL); // Clear PLL lock bit
  397. setOpMode(RH_MRF89_CMOD_FS);
  398. unsigned long ulStartTime = millis();
  399. while ((millis() - ulStartTime < 1000))
  400. {
  401. ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG);
  402. if ((ftpriVal & RH_MRF89_LSTSPLL) != 0)
  403. break;
  404. }
  405. setOpMode(RH_MRF89_CMOD_STANDBY);
  406. return ((ftpriVal & RH_MRF89_LSTSPLL) != 0);
  407. }
  408. bool RH_MRF89::setFrequency(float centre)
  409. {
  410. // REVISIT: FSK only: its different for OOK :-(
  411. uint8_t FBS;
  412. if (centre >= 902.0 && centre < 915.0)
  413. {
  414. FBS = RH_MRF89_FBS_902_915;
  415. }
  416. else if (centre >= 915.0 && centre <= 928.0)
  417. {
  418. FBS = RH_MRF89_FBS_915_928;
  419. }
  420. else if (centre >= 950.0 && centre <= 960.0)
  421. {
  422. // Not all modules support this frequency band:
  423. // The MRF98XAM9A does not
  424. FBS = RH_MRF89_FBS_950_960;
  425. }
  426. // else if (centre >= 863.0 && centre <= 870.0)
  427. // {
  428. // // Not all modules support this frequency band:
  429. // // The MRF98XAM9A does not
  430. // FBS = RH_MRF89_FBS_950_960; // Yes same as above
  431. // }
  432. else
  433. {
  434. // Cant do this freq
  435. return false;
  436. }
  437. // Based on frequency calcs done in MRF89XA.h
  438. // uint8_t R = 100; // Recommended
  439. uint8_t R = 119; // Also recommended :-(
  440. uint32_t centre_kHz = centre * 1000;
  441. uint32_t xtal_kHz = (RH_MRF89_XTAL_FREQ * 1000);
  442. uint32_t compare = (centre_kHz * 8 * (R + 1)) / (9 * xtal_kHz);
  443. uint8_t P = ((compare - 75) / 76) + 1;
  444. uint8_t S = compare - (75 * (P + 1));
  445. // Now set the new register values:
  446. uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG);
  447. val = (val & ~RH_MRF89_FBS) | (FBS & RH_MRF89_FBS);
  448. spiWriteRegister(RH_MRF89_REG_00_GCONREG, val);
  449. spiWriteRegister(RH_MRF89_REG_06_R1CREG, R);
  450. spiWriteRegister(RH_MRF89_REG_07_P1CREG, P);
  451. spiWriteRegister(RH_MRF89_REG_08_S1CREG, S);
  452. return verifyPLLLock();
  453. }
  454. // Set one of the canned FSK Modem configs
  455. // Returns true if its a valid choice
  456. bool RH_MRF89::setModemConfig(ModemConfigChoice index)
  457. {
  458. if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
  459. return false;
  460. RH_MRF89::ModemConfig cfg;
  461. memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(cfg));
  462. // Now update the registers
  463. uint8_t val = spiReadRegister(RH_MRF89_REG_01_DMODREG);
  464. val = (val & ~RH_MRF89_MODSEL) | cfg.MODSEL;
  465. spiWriteRegister(RH_MRF89_REG_01_DMODREG, val);
  466. spiWriteRegister(RH_MRF89_REG_02_FDEVREG, cfg.FDVAL);
  467. spiWriteRegister(RH_MRF89_REG_03_BRSREG, cfg.BRVAL);
  468. spiWriteRegister(RH_MRF89_REG_10_FILCREG, cfg.FILCREG);
  469. // The sample configs in MRF89XA.h all use TXIPOLFV = 0xf0 => 375kHz, which is too wide for most modulations
  470. val = spiReadRegister(RH_MRF89_REG_1A_TXCONREG);
  471. val = (val & ~RH_MRF89_TXIPOLFV) | (cfg.TXIPOLFV & RH_MRF89_TXIPOLFV);
  472. spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, val);
  473. return true;
  474. }
  475. void RH_MRF89::setPreambleLength(uint8_t bytes)
  476. {
  477. if (bytes >= 1 && bytes <= 4)
  478. {
  479. bytes--;
  480. uint8_t pktcreg = spiReadRegister(RH_MRF89_REG_1E_PKTCREG);
  481. pktcreg = (pktcreg & ~RH_MRF89_PRESIZE) | ((bytes << 5) & RH_MRF89_PRESIZE);
  482. spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, pktcreg);
  483. }
  484. }
  485. void RH_MRF89::setSyncWords(const uint8_t* syncWords, uint8_t len)
  486. {
  487. if (syncWords && (len > 0 and len <= 4))
  488. {
  489. uint8_t syncreg = spiReadRegister(RH_MRF89_REG_12_SYNCREG);
  490. syncreg = (syncreg & ~RH_MRF89_SYNCWSZ) | (((len - 1) << 3) & RH_MRF89_SYNCWSZ);
  491. spiWriteRegister(RH_MRF89_REG_12_SYNCREG, syncreg);
  492. uint8_t i;
  493. for (i = 0; i < 4; i++)
  494. {
  495. if (len > i)
  496. spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG + i, syncWords[i]);
  497. }
  498. }
  499. }