PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

RH_RF95.cpp 12KB

il y a 3 ans
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // RH_RF95.cpp
  2. //
  3. // Copyright (C) 2011 Mike McCauley
  4. // $Id: RH_RF95.cpp,v 1.11 2016/04/04 01:40:12 mikem Exp $
  5. #include <RH_RF95.h>
  6. // Interrupt vectors for the 3 Arduino interrupt pins
  7. // Each interrupt can be handled by a different instance of RH_RF95, allowing you to have
  8. // 2 or more LORAs per Arduino
  9. RH_RF95* RH_RF95::_deviceForInterrupt[RH_RF95_NUM_INTERRUPTS] = {0, 0, 0};
  10. uint8_t RH_RF95::_interruptCount = 0; // Index into _deviceForInterrupt for next device
  11. // These are indexed by the values of ModemConfigChoice
  12. // Stored in flash (program) memory to save SRAM
  13. PROGMEM static const RH_RF95::ModemConfig MODEM_CONFIG_TABLE[] =
  14. {
  15. // 1d, 1e, 26
  16. { 0x72, 0x74, 0x00}, // Bw125Cr45Sf128 (the chip default)
  17. { 0x92, 0x74, 0x00}, // Bw500Cr45Sf128
  18. { 0x48, 0x94, 0x00}, // Bw31_25Cr48Sf512
  19. { 0x78, 0xc4, 0x00}, // Bw125Cr48Sf4096
  20. };
  21. RH_RF95::RH_RF95(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi)
  22. :
  23. RHSPIDriver(slaveSelectPin, spi),
  24. _rxBufValid(0)
  25. {
  26. _interruptPin = interruptPin;
  27. _myInterruptIndex = 0xff; // Not allocated yet
  28. }
  29. bool RH_RF95::init()
  30. {
  31. if (!RHSPIDriver::init())
  32. return false;
  33. //Serial.println("RHSPIDriver::init completed");
  34. // Determine the interrupt number that corresponds to the interruptPin
  35. int interruptNumber = digitalPinToInterrupt(_interruptPin);
  36. if (interruptNumber == NOT_AN_INTERRUPT)
  37. return false;
  38. #ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
  39. interruptNumber = _interruptPin;
  40. #endif
  41. //Serial.println("Attach Interrupt completed");
  42. // No way to check the device type :-(
  43. // Set sleep mode, so we can also set LORA mode:
  44. spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE);
  45. delay(10); // Wait for sleep mode to take over from say, CAD
  46. // Check we are in sleep mode, with LORA set
  47. if (spiRead(RH_RF95_REG_01_OP_MODE) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE))
  48. {
  49. //Serial.println(spiRead(RH_RF95_REG_01_OP_MODE), HEX);
  50. return false; // No device present?
  51. }
  52. // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
  53. // ARM M4 requires the below. else pin interrupt doesn't work properly.
  54. // On all other platforms, its innocuous, belt and braces
  55. pinMode(_interruptPin, INPUT);
  56. // Set up interrupt handler
  57. // Since there are a limited number of interrupt glue functions isr*() available,
  58. // we can only support a limited number of devices simultaneously
  59. // ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the
  60. // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
  61. // yourself based on knwledge of what Arduino board you are running on.
  62. if (_myInterruptIndex == 0xff)
  63. {
  64. // First run, no interrupt allocated yet
  65. if (_interruptCount <= RH_RF95_NUM_INTERRUPTS)
  66. _myInterruptIndex = _interruptCount++;
  67. else
  68. return false; // Too many devices, not enough interrupt vectors
  69. }
  70. _deviceForInterrupt[_myInterruptIndex] = this;
  71. if (_myInterruptIndex == 0)
  72. attachInterrupt(interruptNumber, isr0, RISING);
  73. else if (_myInterruptIndex == 1)
  74. attachInterrupt(interruptNumber, isr1, RISING);
  75. else if (_myInterruptIndex == 2)
  76. attachInterrupt(interruptNumber, isr2, RISING);
  77. else
  78. {
  79. //Serial.println("Interrupt vector too many vectors");
  80. return false; // Too many devices, not enough interrupt vectors
  81. }
  82. // Set up FIFO
  83. // We configure so that we can use the entire 256 byte FIFO for either receive
  84. // or transmit, but not both at the same time
  85. spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0);
  86. spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0);
  87. // Packet format is preamble + explicit-header + payload + crc
  88. // Explicit Header Mode
  89. // payload is TO + FROM + ID + FLAGS + message data
  90. // RX mode is implmented with RXCONTINUOUS
  91. // max message data length is 255 - 4 = 251 octets
  92. setModeIdle();
  93. // Set up default configuration
  94. // No Sync Words in LORA mode.
  95. setModemConfig(Bw125Cr45Sf128); // Radio default
  96. // setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
  97. setPreambleLength(8); // Default is 8
  98. // An innocuous ISM frequency, same as RF22's
  99. setFrequency(434.0);
  100. // Lowish power
  101. setTxPower(13);
  102. return true;
  103. }
  104. // C++ level interrupt handler for this instance
  105. // LORA is unusual in that it has several interrupt lines, and not a single, combined one.
  106. // On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly
  107. // connnected to the processor.
  108. // We use this to get RxDone and TxDone interrupts
  109. void RH_RF95::handleInterrupt()
  110. {
  111. // Read the interrupt register
  112. //Serial.println("HandleInterrupt");
  113. uint8_t irq_flags = spiRead(RH_RF95_REG_12_IRQ_FLAGS);
  114. if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR))
  115. {
  116. _rxBad++;
  117. }
  118. else if (_mode == RHModeRx && irq_flags & RH_RF95_RX_DONE)
  119. {
  120. // Have received a packet
  121. uint8_t len = spiRead(RH_RF95_REG_13_RX_NB_BYTES);
  122. // Reset the fifo read ptr to the beginning of the packet
  123. spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR));
  124. spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len);
  125. _bufLen = len;
  126. spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags
  127. // Remember the RSSI of this packet
  128. // this is according to the doc, but is it really correct?
  129. // weakest receiveable signals are reported RSSI at about -66
  130. _lastRssi = spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE) - 137;
  131. // We have received a message.
  132. validateRxBuf();
  133. if (_rxBufValid)
  134. setModeIdle(); // Got one
  135. }
  136. else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE)
  137. {
  138. _txGood++;
  139. setModeIdle();
  140. }
  141. spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags
  142. }
  143. // These are low level functions that call the interrupt handler for the correct
  144. // instance of RH_RF95.
  145. // 3 interrupts allows us to have 3 different devices
  146. void RH_RF95::isr0()
  147. {
  148. if (_deviceForInterrupt[0])
  149. _deviceForInterrupt[0]->handleInterrupt();
  150. }
  151. void RH_RF95::isr1()
  152. {
  153. if (_deviceForInterrupt[1])
  154. _deviceForInterrupt[1]->handleInterrupt();
  155. }
  156. void RH_RF95::isr2()
  157. {
  158. if (_deviceForInterrupt[2])
  159. _deviceForInterrupt[2]->handleInterrupt();
  160. }
  161. // Check whether the latest received message is complete and uncorrupted
  162. void RH_RF95::validateRxBuf()
  163. {
  164. if (_bufLen < 4)
  165. return; // Too short to be a real message
  166. // Extract the 4 headers
  167. //Serial.println("validateRxBuf >= 4");
  168. _rxHeaderTo = _buf[0];
  169. _rxHeaderFrom = _buf[1];
  170. _rxHeaderId = _buf[2];
  171. _rxHeaderFlags = _buf[3];
  172. if (_promiscuous ||
  173. _rxHeaderTo == _thisAddress ||
  174. _rxHeaderTo == RH_BROADCAST_ADDRESS)
  175. {
  176. _rxGood++;
  177. _rxBufValid = true;
  178. }
  179. }
  180. bool RH_RF95::available()
  181. {
  182. if (_mode == RHModeTx)
  183. return false;
  184. setModeRx();
  185. return _rxBufValid; // Will be set by the interrupt handler when a good message is received
  186. }
  187. void RH_RF95::clearRxBuf()
  188. {
  189. ATOMIC_BLOCK_START;
  190. _rxBufValid = false;
  191. _bufLen = 0;
  192. ATOMIC_BLOCK_END;
  193. }
  194. bool RH_RF95::recv(uint8_t* buf, uint8_t* len)
  195. {
  196. if (!available())
  197. return false;
  198. if (buf && len)
  199. {
  200. ATOMIC_BLOCK_START;
  201. // Skip the 4 headers that are at the beginning of the rxBuf
  202. if (*len > _bufLen-RH_RF95_HEADER_LEN)
  203. *len = _bufLen-RH_RF95_HEADER_LEN;
  204. memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len);
  205. ATOMIC_BLOCK_END;
  206. }
  207. clearRxBuf(); // This message accepted and cleared
  208. return true;
  209. }
  210. bool RH_RF95::send(const uint8_t* data, uint8_t len)
  211. {
  212. if (len > RH_RF95_MAX_MESSAGE_LEN)
  213. return false;
  214. waitPacketSent(); // Make sure we dont interrupt an outgoing message
  215. setModeIdle();
  216. // Position at the beginning of the FIFO
  217. spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0);
  218. // The headers
  219. spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo);
  220. spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom);
  221. spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId);
  222. spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags);
  223. // The message data
  224. spiBurstWrite(RH_RF95_REG_00_FIFO, data, len);
  225. spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN);
  226. setModeTx(); // Start the transmitter
  227. // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
  228. return true;
  229. }
  230. bool RH_RF95::printRegisters()
  231. {
  232. #ifdef RH_HAVE_SERIAL
  233. uint8_t registers[] = { 0x01, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
  234. uint8_t i;
  235. for (i = 0; i < sizeof(registers); i++)
  236. {
  237. Serial.print(registers[i], HEX);
  238. Serial.print(": ");
  239. Serial.println(spiRead(registers[i]), HEX);
  240. }
  241. #endif
  242. return true;
  243. }
  244. uint8_t RH_RF95::maxMessageLength()
  245. {
  246. return RH_RF95_MAX_MESSAGE_LEN;
  247. }
  248. bool RH_RF95::setFrequency(float centre)
  249. {
  250. // Frf = FRF / FSTEP
  251. uint32_t frf = (centre * 1000000.0) / RH_RF95_FSTEP;
  252. spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff);
  253. spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff);
  254. spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff);
  255. return true;
  256. }
  257. void RH_RF95::setModeIdle()
  258. {
  259. if (_mode != RHModeIdle)
  260. {
  261. spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY);
  262. _mode = RHModeIdle;
  263. }
  264. }
  265. bool RH_RF95::sleep()
  266. {
  267. if (_mode != RHModeSleep)
  268. {
  269. spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP);
  270. _mode = RHModeSleep;
  271. }
  272. return true;
  273. }
  274. void RH_RF95::setModeRx()
  275. {
  276. if (_mode != RHModeRx)
  277. {
  278. //Serial.println("SetModeRx");
  279. _mode = RHModeRx;
  280. spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS);
  281. spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone
  282. }
  283. }
  284. void RH_RF95::setModeTx()
  285. {
  286. if (_mode != RHModeTx)
  287. {
  288. _mode = RHModeTx; // set first to avoid possible race condition
  289. spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX);
  290. spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone
  291. }
  292. }
  293. void RH_RF95::setTxPower(int8_t power, bool useRFO)
  294. {
  295. // Sigh, different behaviours depending on whther the module use PA_BOOST or the RFO pin
  296. // for the transmitter output
  297. if (useRFO)
  298. {
  299. if (power > 14)
  300. power = 14;
  301. if (power < -1)
  302. power = -1;
  303. spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_MAX_POWER | (power + 1));
  304. }
  305. else
  306. {
  307. if (power > 23)
  308. power = 23;
  309. if (power < 5)
  310. power = 5;
  311. // For RH_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf'
  312. // RH_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will us it
  313. // for 21, 22 and 23dBm
  314. if (power > 20)
  315. {
  316. spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE);
  317. power -= 3;
  318. }
  319. else
  320. {
  321. spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE);
  322. }
  323. // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST
  324. // pin is connected, so must use PA_BOOST
  325. // Pout = 2 + OutputPower.
  326. // The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm,
  327. // but OutputPower claims it would be 17dBm.
  328. // My measurements show 20dBm is correct
  329. spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-5));
  330. }
  331. }
  332. // Sets registers from a canned modem configuration structure
  333. void RH_RF95::setModemRegisters(const ModemConfig* config)
  334. {
  335. spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d);
  336. spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e);
  337. spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, config->reg_26);
  338. }
  339. // Set one of the canned FSK Modem configs
  340. // Returns true if its a valid choice
  341. bool RH_RF95::setModemConfig(ModemConfigChoice index)
  342. {
  343. if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
  344. return false;
  345. ModemConfig cfg;
  346. memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF95::ModemConfig));
  347. setModemRegisters(&cfg);
  348. return true;
  349. }
  350. void RH_RF95::setPreambleLength(uint16_t bytes)
  351. {
  352. spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8);
  353. spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff);
  354. }