This driver is a stripped down version of the Radiohead 1.45 driver, and covers fewer radios. Threading and an event queue have been added to make the ISR's more stable across architectures. Specifically The STM32L4 parts
Dependents: Threaded_LoRa_Modem
RH_RF95.cpp@3:6ffa8c82a713, 2021-05-31 (annotated)
- Committer:
- rlanders73
- Date:
- Mon May 31 02:49:19 2021 +0000
- Revision:
- 3:6ffa8c82a713
- Parent:
- 1:dfeb5e8b199a
- Child:
- 4:61db9344ce8c
Initial working version that is focused on the RF95 radio.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rlanders73 | 3:6ffa8c82a713 | 1 | // RH_RF95.cpp |
davidr99 | 0:ab4e012489ef | 2 | // |
davidr99 | 0:ab4e012489ef | 3 | // Copyright (C) 2011 Mike McCauley |
davidr99 | 0:ab4e012489ef | 4 | // $Id: RH_RF95.cpp,v 1.8 2015/08/12 23:18:51 mikem Exp $ |
davidr99 | 0:ab4e012489ef | 5 | |
davidr99 | 0:ab4e012489ef | 6 | #include <RH_RF95.h> |
davidr99 | 0:ab4e012489ef | 7 | |
rlanders73 | 3:6ffa8c82a713 | 8 | // Interrupt vectors for up to 3 interrupt pins |
davidr99 | 0:ab4e012489ef | 9 | // Each interrupt can be handled by a different instance of RH_RF95, allowing you to have |
rlanders73 | 3:6ffa8c82a713 | 10 | // multiple LoRa radios |
davidr99 | 0:ab4e012489ef | 11 | RH_RF95* RH_RF95::_deviceForInterrupt[RH_RF95_NUM_INTERRUPTS] = {0, 0, 0}; |
davidr99 | 0:ab4e012489ef | 12 | uint8_t RH_RF95::_interruptCount = 0; // Index into _deviceForInterrupt for next device |
davidr99 | 0:ab4e012489ef | 13 | |
rlanders73 | 3:6ffa8c82a713 | 14 | // The mbed driver leverages threading and a queue to handle the ISR's |
rlanders73 | 3:6ffa8c82a713 | 15 | #if (RH_PLATFORM == RH_PLATFORM_MBED) |
rlanders73 | 3:6ffa8c82a713 | 16 | EventQueue queue(32 * EVENTS_EVENT_SIZE); |
rlanders73 | 3:6ffa8c82a713 | 17 | Thread _isrThread;//( osPriorityHigh, OS_STACK_SIZE / 2 ); |
rlanders73 | 3:6ffa8c82a713 | 18 | #endif |
davidr99 | 0:ab4e012489ef | 19 | // These are indexed by the values of ModemConfigChoice |
davidr99 | 0:ab4e012489ef | 20 | // Stored in flash (program) memory to save SRAM |
davidr99 | 0:ab4e012489ef | 21 | PROGMEM static const RH_RF95::ModemConfig MODEM_CONFIG_TABLE[] = |
davidr99 | 0:ab4e012489ef | 22 | { |
davidr99 | 0:ab4e012489ef | 23 | // 1d, 1e, 26 |
davidr99 | 0:ab4e012489ef | 24 | { 0x72, 0x74, 0x00}, // Bw125Cr45Sf128 (the chip default) |
davidr99 | 0:ab4e012489ef | 25 | { 0x92, 0x74, 0x00}, // Bw500Cr45Sf128 |
davidr99 | 0:ab4e012489ef | 26 | { 0x48, 0x94, 0x00}, // Bw31_25Cr48Sf512 |
davidr99 | 0:ab4e012489ef | 27 | { 0x78, 0xc4, 0x00}, // Bw125Cr48Sf4096 |
davidr99 | 0:ab4e012489ef | 28 | |
davidr99 | 0:ab4e012489ef | 29 | }; |
davidr99 | 0:ab4e012489ef | 30 | |
davidr99 | 0:ab4e012489ef | 31 | RH_RF95::RH_RF95(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi) |
davidr99 | 0:ab4e012489ef | 32 | : |
davidr99 | 0:ab4e012489ef | 33 | RHSPIDriver(slaveSelectPin, spi), |
davidr99 | 0:ab4e012489ef | 34 | _rxBufValid(0), |
davidr99 | 0:ab4e012489ef | 35 | _interruptPin(interruptPin) |
davidr99 | 0:ab4e012489ef | 36 | { |
davidr99 | 0:ab4e012489ef | 37 | _myInterruptIndex = 0xff; // Not allocated yet |
davidr99 | 0:ab4e012489ef | 38 | } |
davidr99 | 0:ab4e012489ef | 39 | |
davidr99 | 0:ab4e012489ef | 40 | bool RH_RF95::init() |
davidr99 | 0:ab4e012489ef | 41 | { |
rlanders73 | 3:6ffa8c82a713 | 42 | // printf("Initializing...\n"); |
davidr99 | 0:ab4e012489ef | 43 | if (!RHSPIDriver::init()) |
davidr99 | 0:ab4e012489ef | 44 | return false; |
davidr99 | 0:ab4e012489ef | 45 | |
davidr99 | 0:ab4e012489ef | 46 | #if (RH_PLATFORM != RH_PLATFORM_MBED) |
davidr99 | 0:ab4e012489ef | 47 | // Determine the interrupt number that corresponds to the interruptPin |
davidr99 | 0:ab4e012489ef | 48 | int interruptNumber = digitalPinToInterrupt(_interruptPin); |
davidr99 | 0:ab4e012489ef | 49 | if (interruptNumber == NOT_AN_INTERRUPT) |
davidr99 | 0:ab4e012489ef | 50 | return false; |
rlanders73 | 3:6ffa8c82a713 | 51 | #else |
rlanders73 | 3:6ffa8c82a713 | 52 | _isrThread.start(callback(&queue, &EventQueue::dispatch_forever)); |
davidr99 | 0:ab4e012489ef | 53 | #endif |
davidr99 | 0:ab4e012489ef | 54 | |
rlanders73 | 1:dfeb5e8b199a | 55 | |
davidr99 | 0:ab4e012489ef | 56 | // No way to check the device type :-( |
davidr99 | 0:ab4e012489ef | 57 | |
davidr99 | 0:ab4e012489ef | 58 | // Set sleep mode, so we can also set LORA mode: |
davidr99 | 0:ab4e012489ef | 59 | spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE); |
davidr99 | 0:ab4e012489ef | 60 | delay(10); // Wait for sleep mode to take over from say, CAD |
davidr99 | 0:ab4e012489ef | 61 | // Check we are in sleep mode, with LORA set |
davidr99 | 0:ab4e012489ef | 62 | if (spiRead(RH_RF95_REG_01_OP_MODE) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE)) |
davidr99 | 0:ab4e012489ef | 63 | { |
rlanders73 | 3:6ffa8c82a713 | 64 | printf("Invalid Mode:%x\n",spiRead(RH_RF95_REG_01_OP_MODE)); |
davidr99 | 0:ab4e012489ef | 65 | return false; // No device present? |
davidr99 | 0:ab4e012489ef | 66 | } |
davidr99 | 0:ab4e012489ef | 67 | |
davidr99 | 0:ab4e012489ef | 68 | #if (RH_PLATFORM != RH_PLATFORM_MBED) |
davidr99 | 0:ab4e012489ef | 69 | // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy |
davidr99 | 0:ab4e012489ef | 70 | // ARM M4 requires the below. else pin interrupt doesn't work properly. |
davidr99 | 0:ab4e012489ef | 71 | // On all other platforms, its innocuous, belt and braces |
davidr99 | 0:ab4e012489ef | 72 | pinMode(_interruptPin, INPUT); |
rlanders73 | 3:6ffa8c82a713 | 73 | #else |
rlanders73 | 3:6ffa8c82a713 | 74 | _interruptPin.mode(PullDown); |
davidr99 | 0:ab4e012489ef | 75 | #endif |
davidr99 | 0:ab4e012489ef | 76 | |
davidr99 | 0:ab4e012489ef | 77 | // Set up interrupt handler |
davidr99 | 0:ab4e012489ef | 78 | // Since there are a limited number of interrupt glue functions isr*() available, |
davidr99 | 0:ab4e012489ef | 79 | // we can only support a limited number of devices simultaneously |
davidr99 | 0:ab4e012489ef | 80 | // ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the |
davidr99 | 0:ab4e012489ef | 81 | // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping |
davidr99 | 0:ab4e012489ef | 82 | // yourself based on knwledge of what Arduino board you are running on. |
davidr99 | 0:ab4e012489ef | 83 | if (_myInterruptIndex == 0xff) |
davidr99 | 0:ab4e012489ef | 84 | { |
davidr99 | 0:ab4e012489ef | 85 | // First run, no interrupt allocated yet |
davidr99 | 0:ab4e012489ef | 86 | if (_interruptCount <= RH_RF95_NUM_INTERRUPTS) |
davidr99 | 0:ab4e012489ef | 87 | _myInterruptIndex = _interruptCount++; |
davidr99 | 0:ab4e012489ef | 88 | else |
davidr99 | 0:ab4e012489ef | 89 | return false; // Too many devices, not enough interrupt vectors |
davidr99 | 0:ab4e012489ef | 90 | } |
davidr99 | 0:ab4e012489ef | 91 | _deviceForInterrupt[_myInterruptIndex] = this; |
davidr99 | 0:ab4e012489ef | 92 | |
davidr99 | 0:ab4e012489ef | 93 | #if (RH_PLATFORM == RH_PLATFORM_MBED) |
davidr99 | 0:ab4e012489ef | 94 | if (_myInterruptIndex == 0) |
rlanders73 | 3:6ffa8c82a713 | 95 | _interruptPin.rise(queue.event(isr0)); |
davidr99 | 0:ab4e012489ef | 96 | else if (_myInterruptIndex == 1) |
rlanders73 | 3:6ffa8c82a713 | 97 | _interruptPin.rise(queue.event(isr1)); |
davidr99 | 0:ab4e012489ef | 98 | else if (_myInterruptIndex == 2) |
rlanders73 | 3:6ffa8c82a713 | 99 | _interruptPin.rise(queue.event(isr2)); |
davidr99 | 0:ab4e012489ef | 100 | else |
davidr99 | 0:ab4e012489ef | 101 | return false; // Too many devices, not enough interrupt vectors |
davidr99 | 0:ab4e012489ef | 102 | #else |
davidr99 | 0:ab4e012489ef | 103 | if (_myInterruptIndex == 0) |
davidr99 | 0:ab4e012489ef | 104 | attachInterrupt(interruptNumber, isr0, RISING); |
davidr99 | 0:ab4e012489ef | 105 | else if (_myInterruptIndex == 1) |
davidr99 | 0:ab4e012489ef | 106 | attachInterrupt(interruptNumber, isr1, RISING); |
davidr99 | 0:ab4e012489ef | 107 | else if (_myInterruptIndex == 2) |
davidr99 | 0:ab4e012489ef | 108 | attachInterrupt(interruptNumber, isr2, RISING); |
davidr99 | 0:ab4e012489ef | 109 | else |
davidr99 | 0:ab4e012489ef | 110 | return false; // Too many devices, not enough interrupt vectors |
davidr99 | 0:ab4e012489ef | 111 | #endif |
davidr99 | 0:ab4e012489ef | 112 | // Set up FIFO |
davidr99 | 0:ab4e012489ef | 113 | // We configure so that we can use the entire 256 byte FIFO for either receive |
davidr99 | 0:ab4e012489ef | 114 | // or transmit, but not both at the same time |
rlanders73 | 3:6ffa8c82a713 | 115 | // printf("using ISR %d\n",_myInterruptIndex); |
davidr99 | 0:ab4e012489ef | 116 | spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0); |
davidr99 | 0:ab4e012489ef | 117 | spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0); |
davidr99 | 0:ab4e012489ef | 118 | |
davidr99 | 0:ab4e012489ef | 119 | // Packet format is preamble + explicit-header + payload + crc |
davidr99 | 0:ab4e012489ef | 120 | // Explicit Header Mode |
davidr99 | 0:ab4e012489ef | 121 | // payload is TO + FROM + ID + FLAGS + message data |
davidr99 | 0:ab4e012489ef | 122 | // RX mode is implmented with RXCONTINUOUS |
davidr99 | 0:ab4e012489ef | 123 | // max message data length is 255 - 4 = 251 octets |
davidr99 | 0:ab4e012489ef | 124 | |
davidr99 | 0:ab4e012489ef | 125 | setModeIdle(); |
davidr99 | 0:ab4e012489ef | 126 | |
davidr99 | 0:ab4e012489ef | 127 | // Set up default configuration |
davidr99 | 0:ab4e012489ef | 128 | // No Sync Words in LORA mode. |
davidr99 | 0:ab4e012489ef | 129 | setModemConfig(Bw125Cr45Sf128); // Radio default |
davidr99 | 0:ab4e012489ef | 130 | // setModemConfig(Bw125Cr48Sf4096); // slow and reliable? |
davidr99 | 0:ab4e012489ef | 131 | setPreambleLength(8); // Default is 8 |
davidr99 | 0:ab4e012489ef | 132 | // An innocuous ISM frequency, same as RF22's |
davidr99 | 0:ab4e012489ef | 133 | setFrequency(434.0); |
davidr99 | 0:ab4e012489ef | 134 | // Lowish power |
davidr99 | 0:ab4e012489ef | 135 | setTxPower(13); |
rlanders73 | 3:6ffa8c82a713 | 136 | // printf("Ready!\n"); |
davidr99 | 0:ab4e012489ef | 137 | return true; |
davidr99 | 0:ab4e012489ef | 138 | } |
davidr99 | 0:ab4e012489ef | 139 | |
davidr99 | 0:ab4e012489ef | 140 | // C++ level interrupt handler for this instance |
davidr99 | 0:ab4e012489ef | 141 | // LORA is unusual in that it has several interrupt lines, and not a single, combined one. |
davidr99 | 0:ab4e012489ef | 142 | // On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly |
davidr99 | 0:ab4e012489ef | 143 | // connnected to the processor. |
davidr99 | 0:ab4e012489ef | 144 | // We use this to get RxDone and TxDone interrupts |
davidr99 | 0:ab4e012489ef | 145 | void RH_RF95::handleInterrupt() |
davidr99 | 0:ab4e012489ef | 146 | { |
rlanders73 | 3:6ffa8c82a713 | 147 | // if( doISR == false) |
rlanders73 | 3:6ffa8c82a713 | 148 | // return; |
rlanders73 | 3:6ffa8c82a713 | 149 | // |
rlanders73 | 3:6ffa8c82a713 | 150 | // doISR = false; |
davidr99 | 0:ab4e012489ef | 151 | // Read the interrupt register |
davidr99 | 0:ab4e012489ef | 152 | uint8_t irq_flags = spiRead(RH_RF95_REG_12_IRQ_FLAGS); |
davidr99 | 0:ab4e012489ef | 153 | if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR)) |
davidr99 | 0:ab4e012489ef | 154 | { |
davidr99 | 0:ab4e012489ef | 155 | _rxBad++; |
davidr99 | 0:ab4e012489ef | 156 | } |
davidr99 | 0:ab4e012489ef | 157 | else if (_mode == RHModeRx && irq_flags & RH_RF95_RX_DONE) |
davidr99 | 0:ab4e012489ef | 158 | { |
davidr99 | 0:ab4e012489ef | 159 | // Have received a packet |
davidr99 | 0:ab4e012489ef | 160 | uint8_t len = spiRead(RH_RF95_REG_13_RX_NB_BYTES); |
davidr99 | 0:ab4e012489ef | 161 | |
davidr99 | 0:ab4e012489ef | 162 | // Reset the fifo read ptr to the beginning of the packet |
davidr99 | 0:ab4e012489ef | 163 | spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)); |
davidr99 | 0:ab4e012489ef | 164 | spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len); |
davidr99 | 0:ab4e012489ef | 165 | _bufLen = len; |
davidr99 | 0:ab4e012489ef | 166 | spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags |
davidr99 | 0:ab4e012489ef | 167 | |
davidr99 | 0:ab4e012489ef | 168 | // Remember the RSSI of this packet |
davidr99 | 0:ab4e012489ef | 169 | // this is according to the doc, but is it really correct? |
davidr99 | 0:ab4e012489ef | 170 | // weakest receiveable signals are reported RSSI at about -66 |
davidr99 | 0:ab4e012489ef | 171 | _lastRssi = spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE) - 137; |
davidr99 | 0:ab4e012489ef | 172 | |
davidr99 | 0:ab4e012489ef | 173 | // We have received a message. |
davidr99 | 0:ab4e012489ef | 174 | validateRxBuf(); |
davidr99 | 0:ab4e012489ef | 175 | if (_rxBufValid) |
davidr99 | 0:ab4e012489ef | 176 | setModeIdle(); // Got one |
davidr99 | 0:ab4e012489ef | 177 | } |
davidr99 | 0:ab4e012489ef | 178 | else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE) |
davidr99 | 0:ab4e012489ef | 179 | { |
davidr99 | 0:ab4e012489ef | 180 | _txGood++; |
davidr99 | 0:ab4e012489ef | 181 | setModeIdle(); |
davidr99 | 0:ab4e012489ef | 182 | } |
davidr99 | 0:ab4e012489ef | 183 | |
davidr99 | 0:ab4e012489ef | 184 | spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags |
davidr99 | 0:ab4e012489ef | 185 | } |
davidr99 | 0:ab4e012489ef | 186 | |
rlanders73 | 3:6ffa8c82a713 | 187 | void RH_RF95::manageISR(){ |
rlanders73 | 3:6ffa8c82a713 | 188 | while(1) |
rlanders73 | 3:6ffa8c82a713 | 189 | _deviceForInterrupt[0]->handleInterrupt(); |
rlanders73 | 3:6ffa8c82a713 | 190 | } |
rlanders73 | 3:6ffa8c82a713 | 191 | |
davidr99 | 0:ab4e012489ef | 192 | // These are low level functions that call the interrupt handler for the correct |
davidr99 | 0:ab4e012489ef | 193 | // instance of RH_RF95. |
davidr99 | 0:ab4e012489ef | 194 | // 3 interrupts allows us to have 3 different devices |
davidr99 | 0:ab4e012489ef | 195 | void RH_RF95::isr0() |
davidr99 | 0:ab4e012489ef | 196 | { |
davidr99 | 0:ab4e012489ef | 197 | if (_deviceForInterrupt[0]) |
davidr99 | 0:ab4e012489ef | 198 | _deviceForInterrupt[0]->handleInterrupt(); |
rlanders73 | 3:6ffa8c82a713 | 199 | // doISR = true; |
rlanders73 | 3:6ffa8c82a713 | 200 | |
davidr99 | 0:ab4e012489ef | 201 | } |
davidr99 | 0:ab4e012489ef | 202 | void RH_RF95::isr1() |
davidr99 | 0:ab4e012489ef | 203 | { |
davidr99 | 0:ab4e012489ef | 204 | if (_deviceForInterrupt[1]) |
davidr99 | 0:ab4e012489ef | 205 | _deviceForInterrupt[1]->handleInterrupt(); |
davidr99 | 0:ab4e012489ef | 206 | } |
davidr99 | 0:ab4e012489ef | 207 | void RH_RF95::isr2() |
davidr99 | 0:ab4e012489ef | 208 | { |
davidr99 | 0:ab4e012489ef | 209 | if (_deviceForInterrupt[2]) |
davidr99 | 0:ab4e012489ef | 210 | _deviceForInterrupt[2]->handleInterrupt(); |
davidr99 | 0:ab4e012489ef | 211 | } |
davidr99 | 0:ab4e012489ef | 212 | |
davidr99 | 0:ab4e012489ef | 213 | // Check whether the latest received message is complete and uncorrupted |
davidr99 | 0:ab4e012489ef | 214 | void RH_RF95::validateRxBuf() |
davidr99 | 0:ab4e012489ef | 215 | { |
davidr99 | 0:ab4e012489ef | 216 | if (_bufLen < 4) |
davidr99 | 0:ab4e012489ef | 217 | return; // Too short to be a real message |
davidr99 | 0:ab4e012489ef | 218 | // Extract the 4 headers |
davidr99 | 0:ab4e012489ef | 219 | _rxHeaderTo = _buf[0]; |
davidr99 | 0:ab4e012489ef | 220 | _rxHeaderFrom = _buf[1]; |
davidr99 | 0:ab4e012489ef | 221 | _rxHeaderId = _buf[2]; |
davidr99 | 0:ab4e012489ef | 222 | _rxHeaderFlags = _buf[3]; |
davidr99 | 0:ab4e012489ef | 223 | if (_promiscuous || |
davidr99 | 0:ab4e012489ef | 224 | _rxHeaderTo == _thisAddress || |
davidr99 | 0:ab4e012489ef | 225 | _rxHeaderTo == RH_BROADCAST_ADDRESS) |
davidr99 | 0:ab4e012489ef | 226 | { |
davidr99 | 0:ab4e012489ef | 227 | _rxGood++; |
davidr99 | 0:ab4e012489ef | 228 | _rxBufValid = true; |
davidr99 | 0:ab4e012489ef | 229 | } |
davidr99 | 0:ab4e012489ef | 230 | } |
davidr99 | 0:ab4e012489ef | 231 | |
davidr99 | 0:ab4e012489ef | 232 | bool RH_RF95::available() |
davidr99 | 0:ab4e012489ef | 233 | { |
rlanders73 | 1:dfeb5e8b199a | 234 | if (_mode == RHModeTx){ |
rlanders73 | 1:dfeb5e8b199a | 235 | return false; |
rlanders73 | 1:dfeb5e8b199a | 236 | } |
rlanders73 | 1:dfeb5e8b199a | 237 | |
davidr99 | 0:ab4e012489ef | 238 | setModeRx(); |
davidr99 | 0:ab4e012489ef | 239 | return _rxBufValid; // Will be set by the interrupt handler when a good message is received |
davidr99 | 0:ab4e012489ef | 240 | } |
davidr99 | 0:ab4e012489ef | 241 | |
davidr99 | 0:ab4e012489ef | 242 | void RH_RF95::clearRxBuf() |
davidr99 | 0:ab4e012489ef | 243 | { |
davidr99 | 0:ab4e012489ef | 244 | ATOMIC_BLOCK_START; |
davidr99 | 0:ab4e012489ef | 245 | _rxBufValid = false; |
davidr99 | 0:ab4e012489ef | 246 | _bufLen = 0; |
davidr99 | 0:ab4e012489ef | 247 | ATOMIC_BLOCK_END; |
davidr99 | 0:ab4e012489ef | 248 | } |
davidr99 | 0:ab4e012489ef | 249 | |
davidr99 | 0:ab4e012489ef | 250 | bool RH_RF95::recv(uint8_t* buf, uint8_t* len) |
davidr99 | 0:ab4e012489ef | 251 | { |
davidr99 | 0:ab4e012489ef | 252 | if (!available()) |
davidr99 | 0:ab4e012489ef | 253 | return false; |
davidr99 | 0:ab4e012489ef | 254 | if (buf && len) |
davidr99 | 0:ab4e012489ef | 255 | { |
davidr99 | 0:ab4e012489ef | 256 | ATOMIC_BLOCK_START; |
davidr99 | 0:ab4e012489ef | 257 | // Skip the 4 headers that are at the beginning of the rxBuf |
davidr99 | 0:ab4e012489ef | 258 | if (*len > _bufLen-RH_RF95_HEADER_LEN) |
davidr99 | 0:ab4e012489ef | 259 | *len = _bufLen-RH_RF95_HEADER_LEN; |
davidr99 | 0:ab4e012489ef | 260 | memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len); |
davidr99 | 0:ab4e012489ef | 261 | ATOMIC_BLOCK_END; |
davidr99 | 0:ab4e012489ef | 262 | } |
davidr99 | 0:ab4e012489ef | 263 | clearRxBuf(); // This message accepted and cleared |
davidr99 | 0:ab4e012489ef | 264 | return true; |
davidr99 | 0:ab4e012489ef | 265 | } |
davidr99 | 0:ab4e012489ef | 266 | |
davidr99 | 0:ab4e012489ef | 267 | bool RH_RF95::send(const uint8_t* data, uint8_t len) |
davidr99 | 0:ab4e012489ef | 268 | { |
davidr99 | 0:ab4e012489ef | 269 | if (len > RH_RF95_MAX_MESSAGE_LEN) |
davidr99 | 0:ab4e012489ef | 270 | return false; |
davidr99 | 0:ab4e012489ef | 271 | |
davidr99 | 0:ab4e012489ef | 272 | waitPacketSent(); // Make sure we dont interrupt an outgoing message |
davidr99 | 0:ab4e012489ef | 273 | setModeIdle(); |
davidr99 | 0:ab4e012489ef | 274 | |
davidr99 | 0:ab4e012489ef | 275 | // Position at the beginning of the FIFO |
davidr99 | 0:ab4e012489ef | 276 | spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0); |
davidr99 | 0:ab4e012489ef | 277 | // The headers |
davidr99 | 0:ab4e012489ef | 278 | spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo); |
davidr99 | 0:ab4e012489ef | 279 | spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom); |
davidr99 | 0:ab4e012489ef | 280 | spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId); |
davidr99 | 0:ab4e012489ef | 281 | spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags); |
davidr99 | 0:ab4e012489ef | 282 | // The message data |
davidr99 | 0:ab4e012489ef | 283 | spiBurstWrite(RH_RF95_REG_00_FIFO, data, len); |
davidr99 | 0:ab4e012489ef | 284 | spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN); |
davidr99 | 0:ab4e012489ef | 285 | |
davidr99 | 0:ab4e012489ef | 286 | setModeTx(); // Start the transmitter |
davidr99 | 0:ab4e012489ef | 287 | // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY |
davidr99 | 0:ab4e012489ef | 288 | return true; |
davidr99 | 0:ab4e012489ef | 289 | } |
davidr99 | 0:ab4e012489ef | 290 | |
davidr99 | 0:ab4e012489ef | 291 | bool RH_RF95::printRegisters() |
davidr99 | 0:ab4e012489ef | 292 | { |
davidr99 | 0:ab4e012489ef | 293 | #ifdef RH_HAVE_SERIAL |
davidr99 | 0:ab4e012489ef | 294 | 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}; |
davidr99 | 0:ab4e012489ef | 295 | |
davidr99 | 0:ab4e012489ef | 296 | uint8_t i; |
davidr99 | 0:ab4e012489ef | 297 | for (i = 0; i < sizeof(registers); i++) |
davidr99 | 0:ab4e012489ef | 298 | { |
davidr99 | 0:ab4e012489ef | 299 | Serial.print(registers[i], HEX); |
davidr99 | 0:ab4e012489ef | 300 | Serial.print(": "); |
davidr99 | 0:ab4e012489ef | 301 | Serial.println(spiRead(registers[i]), HEX); |
davidr99 | 0:ab4e012489ef | 302 | } |
davidr99 | 0:ab4e012489ef | 303 | #endif |
davidr99 | 0:ab4e012489ef | 304 | return true; |
davidr99 | 0:ab4e012489ef | 305 | } |
davidr99 | 0:ab4e012489ef | 306 | |
davidr99 | 0:ab4e012489ef | 307 | uint8_t RH_RF95::maxMessageLength() |
davidr99 | 0:ab4e012489ef | 308 | { |
davidr99 | 0:ab4e012489ef | 309 | return RH_RF95_MAX_MESSAGE_LEN; |
davidr99 | 0:ab4e012489ef | 310 | } |
davidr99 | 0:ab4e012489ef | 311 | |
davidr99 | 0:ab4e012489ef | 312 | bool RH_RF95::setFrequency(float centre) |
davidr99 | 0:ab4e012489ef | 313 | { |
davidr99 | 0:ab4e012489ef | 314 | // Frf = FRF / FSTEP |
davidr99 | 0:ab4e012489ef | 315 | uint32_t frf = (centre * 1000000.0) / RH_RF95_FSTEP; |
davidr99 | 0:ab4e012489ef | 316 | spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff); |
davidr99 | 0:ab4e012489ef | 317 | spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff); |
davidr99 | 0:ab4e012489ef | 318 | spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff); |
davidr99 | 0:ab4e012489ef | 319 | |
davidr99 | 0:ab4e012489ef | 320 | return true; |
davidr99 | 0:ab4e012489ef | 321 | } |
davidr99 | 0:ab4e012489ef | 322 | |
davidr99 | 0:ab4e012489ef | 323 | void RH_RF95::setModeIdle() |
davidr99 | 0:ab4e012489ef | 324 | { |
davidr99 | 0:ab4e012489ef | 325 | if (_mode != RHModeIdle) |
davidr99 | 0:ab4e012489ef | 326 | { |
rlanders73 | 3:6ffa8c82a713 | 327 | // printf("---- Idle\n"); |
rlanders73 | 1:dfeb5e8b199a | 328 | spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY); |
rlanders73 | 3:6ffa8c82a713 | 329 | delay(10); |
rlanders73 | 1:dfeb5e8b199a | 330 | _mode = RHModeIdle; |
davidr99 | 0:ab4e012489ef | 331 | } |
davidr99 | 0:ab4e012489ef | 332 | } |
davidr99 | 0:ab4e012489ef | 333 | |
davidr99 | 0:ab4e012489ef | 334 | bool RH_RF95::sleep() |
davidr99 | 0:ab4e012489ef | 335 | { |
rlanders73 | 1:dfeb5e8b199a | 336 | |
davidr99 | 0:ab4e012489ef | 337 | if (_mode != RHModeSleep) |
davidr99 | 0:ab4e012489ef | 338 | { |
rlanders73 | 3:6ffa8c82a713 | 339 | // printf("---- Sleep\n"); |
rlanders73 | 1:dfeb5e8b199a | 340 | spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP); |
rlanders73 | 3:6ffa8c82a713 | 341 | delay(10); |
rlanders73 | 1:dfeb5e8b199a | 342 | _mode = RHModeSleep; |
davidr99 | 0:ab4e012489ef | 343 | } |
davidr99 | 0:ab4e012489ef | 344 | return true; |
davidr99 | 0:ab4e012489ef | 345 | } |
davidr99 | 0:ab4e012489ef | 346 | |
davidr99 | 0:ab4e012489ef | 347 | void RH_RF95::setModeRx() |
davidr99 | 0:ab4e012489ef | 348 | { |
davidr99 | 0:ab4e012489ef | 349 | if (_mode != RHModeRx) |
davidr99 | 0:ab4e012489ef | 350 | { |
rlanders73 | 3:6ffa8c82a713 | 351 | // printf("---- RX\n"); |
rlanders73 | 1:dfeb5e8b199a | 352 | spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS); |
rlanders73 | 3:6ffa8c82a713 | 353 | spiWrite(RH_RF95_REG_40_DIO_MAPPING1, RH_RF95_DIOMAPPING1_DIO0MAPPING_00); // Interrupt on RxDone |
rlanders73 | 3:6ffa8c82a713 | 354 | delay(10); |
rlanders73 | 1:dfeb5e8b199a | 355 | _mode = RHModeRx; |
davidr99 | 0:ab4e012489ef | 356 | } |
davidr99 | 0:ab4e012489ef | 357 | } |
davidr99 | 0:ab4e012489ef | 358 | |
davidr99 | 0:ab4e012489ef | 359 | void RH_RF95::setModeTx() |
davidr99 | 0:ab4e012489ef | 360 | { |
davidr99 | 0:ab4e012489ef | 361 | if (_mode != RHModeTx) |
davidr99 | 0:ab4e012489ef | 362 | { |
rlanders73 | 3:6ffa8c82a713 | 363 | // printf("---- TX\n"); |
rlanders73 | 1:dfeb5e8b199a | 364 | spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX); |
rlanders73 | 1:dfeb5e8b199a | 365 | spiWrite(RH_RF95_REG_40_DIO_MAPPING1, RH_RF95_DIOMAPPING1_DIO0MAPPING_00); // Interrupt on TxDone |
rlanders73 | 3:6ffa8c82a713 | 366 | delay(10); |
rlanders73 | 1:dfeb5e8b199a | 367 | _mode = RHModeTx; |
davidr99 | 0:ab4e012489ef | 368 | } |
davidr99 | 0:ab4e012489ef | 369 | } |
davidr99 | 0:ab4e012489ef | 370 | |
davidr99 | 0:ab4e012489ef | 371 | void RH_RF95::setTxPower(int8_t power) |
davidr99 | 0:ab4e012489ef | 372 | { |
davidr99 | 0:ab4e012489ef | 373 | if (power > 23) |
davidr99 | 0:ab4e012489ef | 374 | power = 23; |
davidr99 | 0:ab4e012489ef | 375 | if (power < 5) |
davidr99 | 0:ab4e012489ef | 376 | power = 5; |
davidr99 | 0:ab4e012489ef | 377 | |
davidr99 | 0:ab4e012489ef | 378 | // For RH_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf' |
davidr99 | 0:ab4e012489ef | 379 | // RH_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will us it |
davidr99 | 0:ab4e012489ef | 380 | // for 21, 22 and 23dBm |
davidr99 | 0:ab4e012489ef | 381 | if (power > 20) |
davidr99 | 0:ab4e012489ef | 382 | { |
davidr99 | 0:ab4e012489ef | 383 | spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE); |
davidr99 | 0:ab4e012489ef | 384 | power -= 3; |
davidr99 | 0:ab4e012489ef | 385 | } |
davidr99 | 0:ab4e012489ef | 386 | else |
davidr99 | 0:ab4e012489ef | 387 | { |
davidr99 | 0:ab4e012489ef | 388 | spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE); |
davidr99 | 0:ab4e012489ef | 389 | } |
davidr99 | 0:ab4e012489ef | 390 | |
davidr99 | 0:ab4e012489ef | 391 | // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST |
davidr99 | 0:ab4e012489ef | 392 | // pin is connected, so must use PA_BOOST |
davidr99 | 0:ab4e012489ef | 393 | // Pout = 2 + OutputPower. |
davidr99 | 0:ab4e012489ef | 394 | // The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm, |
davidr99 | 0:ab4e012489ef | 395 | // but OutputPower claims it would be 17dBm. |
davidr99 | 0:ab4e012489ef | 396 | // My measurements show 20dBm is correct |
davidr99 | 0:ab4e012489ef | 397 | spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-5)); |
davidr99 | 0:ab4e012489ef | 398 | } |
davidr99 | 0:ab4e012489ef | 399 | |
davidr99 | 0:ab4e012489ef | 400 | // Sets registers from a canned modem configuration structure |
davidr99 | 0:ab4e012489ef | 401 | void RH_RF95::setModemRegisters(const ModemConfig* config) |
davidr99 | 0:ab4e012489ef | 402 | { |
davidr99 | 0:ab4e012489ef | 403 | spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d); |
davidr99 | 0:ab4e012489ef | 404 | spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e); |
davidr99 | 0:ab4e012489ef | 405 | spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, config->reg_26); |
davidr99 | 0:ab4e012489ef | 406 | } |
davidr99 | 0:ab4e012489ef | 407 | |
davidr99 | 0:ab4e012489ef | 408 | // Set one of the canned FSK Modem configs |
davidr99 | 0:ab4e012489ef | 409 | // Returns true if its a valid choice |
davidr99 | 0:ab4e012489ef | 410 | bool RH_RF95::setModemConfig(ModemConfigChoice index) |
davidr99 | 0:ab4e012489ef | 411 | { |
davidr99 | 0:ab4e012489ef | 412 | if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig))) |
davidr99 | 0:ab4e012489ef | 413 | return false; |
davidr99 | 0:ab4e012489ef | 414 | |
davidr99 | 0:ab4e012489ef | 415 | ModemConfig cfg; |
davidr99 | 0:ab4e012489ef | 416 | memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF95::ModemConfig)); |
davidr99 | 0:ab4e012489ef | 417 | setModemRegisters(&cfg); |
davidr99 | 0:ab4e012489ef | 418 | |
davidr99 | 0:ab4e012489ef | 419 | return true; |
davidr99 | 0:ab4e012489ef | 420 | } |
davidr99 | 0:ab4e012489ef | 421 | |
davidr99 | 0:ab4e012489ef | 422 | void RH_RF95::setPreambleLength(uint16_t bytes) |
davidr99 | 0:ab4e012489ef | 423 | { |
davidr99 | 0:ab4e012489ef | 424 | spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8); |
davidr99 | 0:ab4e012489ef | 425 | spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff); |
davidr99 | 0:ab4e012489ef | 426 | } |
davidr99 | 0:ab4e012489ef | 427 |