RadioHead
RH_RF95.cpp@0:e69d086cb053, 2017-06-11 (annotated)
- Committer:
- danjulio
- Date:
- Sun Jun 11 04:05:05 2017 +0000
- Revision:
- 0:e69d086cb053
Initial commit of minimally ported Radiohead library using swspi
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
danjulio | 0:e69d086cb053 | 1 | // RH_RF95.cpp |
danjulio | 0:e69d086cb053 | 2 | // |
danjulio | 0:e69d086cb053 | 3 | // Copyright (C) 2011 Mike McCauley |
danjulio | 0:e69d086cb053 | 4 | // $Id: RH_RF95.cpp,v 1.14 2017/03/04 00:59:41 mikem Exp $ |
danjulio | 0:e69d086cb053 | 5 | // |
danjulio | 0:e69d086cb053 | 6 | // Ported to mbed - support only a single radio - Dan Julio - 5/2017 |
danjulio | 0:e69d086cb053 | 7 | // |
danjulio | 0:e69d086cb053 | 8 | |
danjulio | 0:e69d086cb053 | 9 | #include <RH_RF95.h> |
danjulio | 0:e69d086cb053 | 10 | |
danjulio | 0:e69d086cb053 | 11 | // These are indexed by the values of ModemConfigChoice |
danjulio | 0:e69d086cb053 | 12 | // Stored in flash (program) memory to save SRAM |
danjulio | 0:e69d086cb053 | 13 | static const RH_RF95::ModemConfig MODEM_CONFIG_TABLE[] = |
danjulio | 0:e69d086cb053 | 14 | { |
danjulio | 0:e69d086cb053 | 15 | // 1d, 1e, 26 |
danjulio | 0:e69d086cb053 | 16 | { 0x72, 0x74, 0x00}, // Bw125Cr45Sf128 (the chip default) |
danjulio | 0:e69d086cb053 | 17 | { 0x92, 0x74, 0x00}, // Bw500Cr45Sf128 |
danjulio | 0:e69d086cb053 | 18 | { 0x48, 0x94, 0x00}, // Bw31_25Cr48Sf512 |
danjulio | 0:e69d086cb053 | 19 | { 0x78, 0xc4, 0x00}, // Bw125Cr48Sf4096 |
danjulio | 0:e69d086cb053 | 20 | |
danjulio | 0:e69d086cb053 | 21 | }; |
danjulio | 0:e69d086cb053 | 22 | |
danjulio | 0:e69d086cb053 | 23 | RH_RF95::RH_RF95(swspi& spi, int ssNum) |
danjulio | 0:e69d086cb053 | 24 | : |
danjulio | 0:e69d086cb053 | 25 | _spi(spi), |
danjulio | 0:e69d086cb053 | 26 | _ssn(ssNum), |
danjulio | 0:e69d086cb053 | 27 | _rxBufValid(0) |
danjulio | 0:e69d086cb053 | 28 | { |
danjulio | 0:e69d086cb053 | 29 | } |
danjulio | 0:e69d086cb053 | 30 | |
danjulio | 0:e69d086cb053 | 31 | bool RH_RF95::init() |
danjulio | 0:e69d086cb053 | 32 | { |
danjulio | 0:e69d086cb053 | 33 | // Set sleep mode, so we can also set LORA mode: |
danjulio | 0:e69d086cb053 | 34 | _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE, 0, _ssn); |
danjulio | 0:e69d086cb053 | 35 | wait_ms(10); // Wait for sleep mode to take over from say, CAD |
danjulio | 0:e69d086cb053 | 36 | // Check we are in sleep mode, with LORA set |
danjulio | 0:e69d086cb053 | 37 | if (_spi.spiRead(RH_RF95_REG_01_OP_MODE, 0, _ssn) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE)) |
danjulio | 0:e69d086cb053 | 38 | { |
danjulio | 0:e69d086cb053 | 39 | // printf("REG_01_OP_MODE: 0x%2x\n", _spi.spiRead(RH_RF95_REG_01_OP_MODE, 0, _ssn)); |
danjulio | 0:e69d086cb053 | 40 | return false; // No device present? |
danjulio | 0:e69d086cb053 | 41 | } |
danjulio | 0:e69d086cb053 | 42 | |
danjulio | 0:e69d086cb053 | 43 | // Set up FIFO |
danjulio | 0:e69d086cb053 | 44 | // We configure so that we can use the entire 256 byte FIFO for either receive |
danjulio | 0:e69d086cb053 | 45 | // or transmit, but not both at the same time |
danjulio | 0:e69d086cb053 | 46 | _spi.spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0, 0, _ssn); |
danjulio | 0:e69d086cb053 | 47 | _spi.spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0, 0, _ssn); |
danjulio | 0:e69d086cb053 | 48 | |
danjulio | 0:e69d086cb053 | 49 | // Packet format is preamble + explicit-header + payload + crc |
danjulio | 0:e69d086cb053 | 50 | // Explicit Header Mode |
danjulio | 0:e69d086cb053 | 51 | // payload is TO + FROM + ID + FLAGS + message data |
danjulio | 0:e69d086cb053 | 52 | // RX mode is implmented with RXCONTINUOUS |
danjulio | 0:e69d086cb053 | 53 | // max message data length is 255 - 4 = 251 octets |
danjulio | 0:e69d086cb053 | 54 | |
danjulio | 0:e69d086cb053 | 55 | setModeIdle(); |
danjulio | 0:e69d086cb053 | 56 | |
danjulio | 0:e69d086cb053 | 57 | // Set up default configuration |
danjulio | 0:e69d086cb053 | 58 | // No Sync Words in LORA mode. |
danjulio | 0:e69d086cb053 | 59 | setModemConfig(Bw125Cr45Sf128); // Radio default |
danjulio | 0:e69d086cb053 | 60 | // setModemConfig(Bw125Cr48Sf4096); // slow and reliable? |
danjulio | 0:e69d086cb053 | 61 | setPreambleLength(8); // Default is 8 |
danjulio | 0:e69d086cb053 | 62 | // An innocuous ISM frequency, same as RF22's |
danjulio | 0:e69d086cb053 | 63 | setFrequency(434.0); |
danjulio | 0:e69d086cb053 | 64 | // Lowish power |
danjulio | 0:e69d086cb053 | 65 | setTxPower(13); |
danjulio | 0:e69d086cb053 | 66 | |
danjulio | 0:e69d086cb053 | 67 | return true; |
danjulio | 0:e69d086cb053 | 68 | } |
danjulio | 0:e69d086cb053 | 69 | |
danjulio | 0:e69d086cb053 | 70 | // C++ level interrupt handler for this instance |
danjulio | 0:e69d086cb053 | 71 | // LORA is unusual in that it has several interrupt lines, and not a single, combined one. |
danjulio | 0:e69d086cb053 | 72 | // On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly |
danjulio | 0:e69d086cb053 | 73 | // connnected to the processor. |
danjulio | 0:e69d086cb053 | 74 | // We use this to get RxDone and TxDone interrupts |
danjulio | 0:e69d086cb053 | 75 | void RH_RF95::handleInterrupt() |
danjulio | 0:e69d086cb053 | 76 | { |
danjulio | 0:e69d086cb053 | 77 | // Read the interrupt register |
danjulio | 0:e69d086cb053 | 78 | uint8_t irq_flags = _spi.spiRead(RH_RF95_REG_12_IRQ_FLAGS, 0, _ssn); |
danjulio | 0:e69d086cb053 | 79 | if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR)) |
danjulio | 0:e69d086cb053 | 80 | { |
danjulio | 0:e69d086cb053 | 81 | _rxBad++; |
danjulio | 0:e69d086cb053 | 82 | } |
danjulio | 0:e69d086cb053 | 83 | else if (_mode == RHModeRx && irq_flags & RH_RF95_RX_DONE) |
danjulio | 0:e69d086cb053 | 84 | { |
danjulio | 0:e69d086cb053 | 85 | // Have received a packet |
danjulio | 0:e69d086cb053 | 86 | uint8_t len = _spi.spiRead(RH_RF95_REG_13_RX_NB_BYTES, 0, _ssn); |
danjulio | 0:e69d086cb053 | 87 | |
danjulio | 0:e69d086cb053 | 88 | // Reset the fifo read ptr to the beginning of the packet |
danjulio | 0:e69d086cb053 | 89 | _spi.spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, _spi.spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR, 0, _ssn), 0, _ssn); |
danjulio | 0:e69d086cb053 | 90 | _spi.spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len, 0, _ssn); |
danjulio | 0:e69d086cb053 | 91 | _bufLen = len; |
danjulio | 0:e69d086cb053 | 92 | _spi.spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff, 0, _ssn); // Clear all IRQ flags |
danjulio | 0:e69d086cb053 | 93 | |
danjulio | 0:e69d086cb053 | 94 | // Remember the last signal to noise ratio, LORA mode |
danjulio | 0:e69d086cb053 | 95 | // Per page 111, SX1276/77/78/79 datasheet |
danjulio | 0:e69d086cb053 | 96 | _lastSNR = (int8_t)_spi.spiRead(RH_RF95_REG_19_PKT_SNR_VALUE, 0, _ssn) / 4; |
danjulio | 0:e69d086cb053 | 97 | |
danjulio | 0:e69d086cb053 | 98 | // Remember the RSSI of this packet, LORA mode |
danjulio | 0:e69d086cb053 | 99 | // this is according to the doc, but is it really correct? |
danjulio | 0:e69d086cb053 | 100 | // weakest receiveable signals are reported RSSI at about -66 |
danjulio | 0:e69d086cb053 | 101 | _lastRssi = _spi.spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE, 0, _ssn); |
danjulio | 0:e69d086cb053 | 102 | // Adjust the RSSI, datasheet page 87 |
danjulio | 0:e69d086cb053 | 103 | if (_lastSNR < 0) |
danjulio | 0:e69d086cb053 | 104 | _lastRssi = _lastRssi + _lastSNR; |
danjulio | 0:e69d086cb053 | 105 | else |
danjulio | 0:e69d086cb053 | 106 | _lastRssi = (int)_lastRssi * 16 / 15; |
danjulio | 0:e69d086cb053 | 107 | if (_usingHFport) |
danjulio | 0:e69d086cb053 | 108 | _lastRssi -= 157; |
danjulio | 0:e69d086cb053 | 109 | else |
danjulio | 0:e69d086cb053 | 110 | _lastRssi -= 164; |
danjulio | 0:e69d086cb053 | 111 | |
danjulio | 0:e69d086cb053 | 112 | // We have received a message. |
danjulio | 0:e69d086cb053 | 113 | validateRxBuf(); |
danjulio | 0:e69d086cb053 | 114 | if (_rxBufValid) |
danjulio | 0:e69d086cb053 | 115 | setModeIdle(); // Got one |
danjulio | 0:e69d086cb053 | 116 | } |
danjulio | 0:e69d086cb053 | 117 | else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE) |
danjulio | 0:e69d086cb053 | 118 | { |
danjulio | 0:e69d086cb053 | 119 | _txGood++; |
danjulio | 0:e69d086cb053 | 120 | setModeIdle(); |
danjulio | 0:e69d086cb053 | 121 | } |
danjulio | 0:e69d086cb053 | 122 | else if (_mode == RHModeCad && irq_flags & RH_RF95_CAD_DONE) |
danjulio | 0:e69d086cb053 | 123 | { |
danjulio | 0:e69d086cb053 | 124 | _cad = irq_flags & RH_RF95_CAD_DETECTED; |
danjulio | 0:e69d086cb053 | 125 | setModeIdle(); |
danjulio | 0:e69d086cb053 | 126 | } |
danjulio | 0:e69d086cb053 | 127 | |
danjulio | 0:e69d086cb053 | 128 | _spi.spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff, 0, _ssn); // Clear all IRQ flags |
danjulio | 0:e69d086cb053 | 129 | } |
danjulio | 0:e69d086cb053 | 130 | |
danjulio | 0:e69d086cb053 | 131 | // Check whether the latest received message is complete and uncorrupted |
danjulio | 0:e69d086cb053 | 132 | void RH_RF95::validateRxBuf() |
danjulio | 0:e69d086cb053 | 133 | { |
danjulio | 0:e69d086cb053 | 134 | if (_bufLen < 4) |
danjulio | 0:e69d086cb053 | 135 | return; // Too short to be a real message |
danjulio | 0:e69d086cb053 | 136 | |
danjulio | 0:e69d086cb053 | 137 | // Extract the 4 headers |
danjulio | 0:e69d086cb053 | 138 | _rxHeaderTo = _buf[0]; |
danjulio | 0:e69d086cb053 | 139 | _rxHeaderFrom = _buf[1]; |
danjulio | 0:e69d086cb053 | 140 | _rxHeaderId = _buf[2]; |
danjulio | 0:e69d086cb053 | 141 | _rxHeaderFlags = _buf[3]; |
danjulio | 0:e69d086cb053 | 142 | if (_promiscuous || |
danjulio | 0:e69d086cb053 | 143 | _rxHeaderTo == _thisAddress || |
danjulio | 0:e69d086cb053 | 144 | _rxHeaderTo == RH_BROADCAST_ADDRESS) |
danjulio | 0:e69d086cb053 | 145 | { |
danjulio | 0:e69d086cb053 | 146 | _rxGood++; |
danjulio | 0:e69d086cb053 | 147 | _rxBufValid = true; |
danjulio | 0:e69d086cb053 | 148 | } |
danjulio | 0:e69d086cb053 | 149 | } |
danjulio | 0:e69d086cb053 | 150 | |
danjulio | 0:e69d086cb053 | 151 | bool RH_RF95::available() |
danjulio | 0:e69d086cb053 | 152 | { |
danjulio | 0:e69d086cb053 | 153 | if (_mode == RHModeTx) |
danjulio | 0:e69d086cb053 | 154 | return false; |
danjulio | 0:e69d086cb053 | 155 | |
danjulio | 0:e69d086cb053 | 156 | setModeRx(); |
danjulio | 0:e69d086cb053 | 157 | return _rxBufValid; // Will be set by the interrupt handler when a good message is received |
danjulio | 0:e69d086cb053 | 158 | } |
danjulio | 0:e69d086cb053 | 159 | |
danjulio | 0:e69d086cb053 | 160 | void RH_RF95::clearRxBuf() |
danjulio | 0:e69d086cb053 | 161 | { |
danjulio | 0:e69d086cb053 | 162 | _rxBufValid = false; |
danjulio | 0:e69d086cb053 | 163 | _bufLen = 0; |
danjulio | 0:e69d086cb053 | 164 | } |
danjulio | 0:e69d086cb053 | 165 | |
danjulio | 0:e69d086cb053 | 166 | bool RH_RF95::recv(uint8_t* buf, uint8_t* len) |
danjulio | 0:e69d086cb053 | 167 | { |
danjulio | 0:e69d086cb053 | 168 | if (!available()) |
danjulio | 0:e69d086cb053 | 169 | return false; |
danjulio | 0:e69d086cb053 | 170 | |
danjulio | 0:e69d086cb053 | 171 | if (buf && len) |
danjulio | 0:e69d086cb053 | 172 | { |
danjulio | 0:e69d086cb053 | 173 | // Skip the 4 headers that are at the beginning of the rxBuf |
danjulio | 0:e69d086cb053 | 174 | if (*len > _bufLen-RH_RF95_HEADER_LEN) |
danjulio | 0:e69d086cb053 | 175 | *len = _bufLen-RH_RF95_HEADER_LEN; |
danjulio | 0:e69d086cb053 | 176 | memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len); |
danjulio | 0:e69d086cb053 | 177 | } |
danjulio | 0:e69d086cb053 | 178 | clearRxBuf(); // This message accepted and cleared |
danjulio | 0:e69d086cb053 | 179 | return true; |
danjulio | 0:e69d086cb053 | 180 | } |
danjulio | 0:e69d086cb053 | 181 | |
danjulio | 0:e69d086cb053 | 182 | bool RH_RF95::send(const uint8_t* data, uint8_t len) |
danjulio | 0:e69d086cb053 | 183 | { |
danjulio | 0:e69d086cb053 | 184 | if (len > RH_RF95_MAX_MESSAGE_LEN) |
danjulio | 0:e69d086cb053 | 185 | return false; |
danjulio | 0:e69d086cb053 | 186 | |
danjulio | 0:e69d086cb053 | 187 | waitPacketSent(); // Make sure we dont interrupt an outgoing message |
danjulio | 0:e69d086cb053 | 188 | setModeIdle(); |
danjulio | 0:e69d086cb053 | 189 | |
danjulio | 0:e69d086cb053 | 190 | if (!waitCAD()) |
danjulio | 0:e69d086cb053 | 191 | return false; // Check channel activity |
danjulio | 0:e69d086cb053 | 192 | |
danjulio | 0:e69d086cb053 | 193 | // Position at the beginning of the FIFO |
danjulio | 0:e69d086cb053 | 194 | _spi.spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0, 0, _ssn); |
danjulio | 0:e69d086cb053 | 195 | // The headers |
danjulio | 0:e69d086cb053 | 196 | _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo, 0, _ssn); |
danjulio | 0:e69d086cb053 | 197 | _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom, 0, _ssn); |
danjulio | 0:e69d086cb053 | 198 | _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId, 0, _ssn); |
danjulio | 0:e69d086cb053 | 199 | _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags, 0, _ssn); |
danjulio | 0:e69d086cb053 | 200 | // The message data |
danjulio | 0:e69d086cb053 | 201 | _spi.spiBurstWrite(RH_RF95_REG_00_FIFO, data, len, 0, _ssn); |
danjulio | 0:e69d086cb053 | 202 | _spi.spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN, 0, _ssn); |
danjulio | 0:e69d086cb053 | 203 | |
danjulio | 0:e69d086cb053 | 204 | setModeTx(); // Start the transmitter |
danjulio | 0:e69d086cb053 | 205 | // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY |
danjulio | 0:e69d086cb053 | 206 | return true; |
danjulio | 0:e69d086cb053 | 207 | } |
danjulio | 0:e69d086cb053 | 208 | |
danjulio | 0:e69d086cb053 | 209 | bool RH_RF95::printRegisters() |
danjulio | 0:e69d086cb053 | 210 | { |
danjulio | 0:e69d086cb053 | 211 | 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}; |
danjulio | 0:e69d086cb053 | 212 | |
danjulio | 0:e69d086cb053 | 213 | uint8_t i; |
danjulio | 0:e69d086cb053 | 214 | for (i = 0; i < sizeof(registers); i++) |
danjulio | 0:e69d086cb053 | 215 | { |
danjulio | 0:e69d086cb053 | 216 | printf("0x%x: 0x%x\n\r", registers[i], _spi.spiRead(registers[i], 0, _ssn)); |
danjulio | 0:e69d086cb053 | 217 | } |
danjulio | 0:e69d086cb053 | 218 | return true; |
danjulio | 0:e69d086cb053 | 219 | } |
danjulio | 0:e69d086cb053 | 220 | |
danjulio | 0:e69d086cb053 | 221 | uint8_t RH_RF95::maxMessageLength() |
danjulio | 0:e69d086cb053 | 222 | { |
danjulio | 0:e69d086cb053 | 223 | return RH_RF95_MAX_MESSAGE_LEN; |
danjulio | 0:e69d086cb053 | 224 | } |
danjulio | 0:e69d086cb053 | 225 | |
danjulio | 0:e69d086cb053 | 226 | bool RH_RF95::setFrequency(float centre) |
danjulio | 0:e69d086cb053 | 227 | { |
danjulio | 0:e69d086cb053 | 228 | // Frf = FRF / FSTEP |
danjulio | 0:e69d086cb053 | 229 | uint32_t frf = (centre * 1000000.0f) / RH_RF95_FSTEP; |
danjulio | 0:e69d086cb053 | 230 | _spi.spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff, 0, _ssn); |
danjulio | 0:e69d086cb053 | 231 | _spi.spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff, 0, _ssn); |
danjulio | 0:e69d086cb053 | 232 | _spi.spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff, 0, _ssn); |
danjulio | 0:e69d086cb053 | 233 | _usingHFport = (centre >= 779.0f); |
danjulio | 0:e69d086cb053 | 234 | |
danjulio | 0:e69d086cb053 | 235 | return true; |
danjulio | 0:e69d086cb053 | 236 | } |
danjulio | 0:e69d086cb053 | 237 | |
danjulio | 0:e69d086cb053 | 238 | void RH_RF95::setModeIdle() |
danjulio | 0:e69d086cb053 | 239 | { |
danjulio | 0:e69d086cb053 | 240 | if (_mode != RHModeIdle) |
danjulio | 0:e69d086cb053 | 241 | { |
danjulio | 0:e69d086cb053 | 242 | _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY, 0, _ssn); |
danjulio | 0:e69d086cb053 | 243 | _mode = RHModeIdle; |
danjulio | 0:e69d086cb053 | 244 | } |
danjulio | 0:e69d086cb053 | 245 | } |
danjulio | 0:e69d086cb053 | 246 | |
danjulio | 0:e69d086cb053 | 247 | bool RH_RF95::sleep() |
danjulio | 0:e69d086cb053 | 248 | { |
danjulio | 0:e69d086cb053 | 249 | if (_mode != RHModeSleep) |
danjulio | 0:e69d086cb053 | 250 | { |
danjulio | 0:e69d086cb053 | 251 | _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP, 0, _ssn); |
danjulio | 0:e69d086cb053 | 252 | _mode = RHModeSleep; |
danjulio | 0:e69d086cb053 | 253 | } |
danjulio | 0:e69d086cb053 | 254 | return true; |
danjulio | 0:e69d086cb053 | 255 | } |
danjulio | 0:e69d086cb053 | 256 | |
danjulio | 0:e69d086cb053 | 257 | void RH_RF95::setModeRx() |
danjulio | 0:e69d086cb053 | 258 | { |
danjulio | 0:e69d086cb053 | 259 | if (_mode != RHModeRx) |
danjulio | 0:e69d086cb053 | 260 | { |
danjulio | 0:e69d086cb053 | 261 | _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS, 0, _ssn); |
danjulio | 0:e69d086cb053 | 262 | _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x00, 0, _ssn); // Interrupt on RxDone |
danjulio | 0:e69d086cb053 | 263 | _mode = RHModeRx; |
danjulio | 0:e69d086cb053 | 264 | } |
danjulio | 0:e69d086cb053 | 265 | } |
danjulio | 0:e69d086cb053 | 266 | |
danjulio | 0:e69d086cb053 | 267 | void RH_RF95::setModeTx() |
danjulio | 0:e69d086cb053 | 268 | { |
danjulio | 0:e69d086cb053 | 269 | if (_mode != RHModeTx) |
danjulio | 0:e69d086cb053 | 270 | { |
danjulio | 0:e69d086cb053 | 271 | _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX, 0, _ssn); |
danjulio | 0:e69d086cb053 | 272 | _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x40, 0, _ssn); // Interrupt on TxDone |
danjulio | 0:e69d086cb053 | 273 | _mode = RHModeTx; |
danjulio | 0:e69d086cb053 | 274 | } |
danjulio | 0:e69d086cb053 | 275 | } |
danjulio | 0:e69d086cb053 | 276 | |
danjulio | 0:e69d086cb053 | 277 | void RH_RF95::setTxPower(int8_t power, bool useRFO) |
danjulio | 0:e69d086cb053 | 278 | { |
danjulio | 0:e69d086cb053 | 279 | // Sigh, different behaviours depending on whther the module use PA_BOOST or the RFO pin |
danjulio | 0:e69d086cb053 | 280 | // for the transmitter output |
danjulio | 0:e69d086cb053 | 281 | if (useRFO) |
danjulio | 0:e69d086cb053 | 282 | { |
danjulio | 0:e69d086cb053 | 283 | if (power > 14) |
danjulio | 0:e69d086cb053 | 284 | power = 14; |
danjulio | 0:e69d086cb053 | 285 | if (power < -1) |
danjulio | 0:e69d086cb053 | 286 | power = -1; |
danjulio | 0:e69d086cb053 | 287 | _spi.spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_MAX_POWER | (power + 1), 0, _ssn); |
danjulio | 0:e69d086cb053 | 288 | } |
danjulio | 0:e69d086cb053 | 289 | else |
danjulio | 0:e69d086cb053 | 290 | { |
danjulio | 0:e69d086cb053 | 291 | if (power > 23) |
danjulio | 0:e69d086cb053 | 292 | power = 23; |
danjulio | 0:e69d086cb053 | 293 | if (power < 5) |
danjulio | 0:e69d086cb053 | 294 | power = 5; |
danjulio | 0:e69d086cb053 | 295 | |
danjulio | 0:e69d086cb053 | 296 | // For RH_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf' |
danjulio | 0:e69d086cb053 | 297 | // RH_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will us it |
danjulio | 0:e69d086cb053 | 298 | // for 21, 22 and 23dBm |
danjulio | 0:e69d086cb053 | 299 | if (power > 20) |
danjulio | 0:e69d086cb053 | 300 | { |
danjulio | 0:e69d086cb053 | 301 | _spi.spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE, 0, _ssn); |
danjulio | 0:e69d086cb053 | 302 | power -= 3; |
danjulio | 0:e69d086cb053 | 303 | } |
danjulio | 0:e69d086cb053 | 304 | else |
danjulio | 0:e69d086cb053 | 305 | { |
danjulio | 0:e69d086cb053 | 306 | _spi.spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE, 0, _ssn); |
danjulio | 0:e69d086cb053 | 307 | } |
danjulio | 0:e69d086cb053 | 308 | |
danjulio | 0:e69d086cb053 | 309 | // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST |
danjulio | 0:e69d086cb053 | 310 | // pin is connected, so must use PA_BOOST |
danjulio | 0:e69d086cb053 | 311 | // Pout = 2 + OutputPower. |
danjulio | 0:e69d086cb053 | 312 | // The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm, |
danjulio | 0:e69d086cb053 | 313 | // but OutputPower claims it would be 17dBm. |
danjulio | 0:e69d086cb053 | 314 | // My measurements show 20dBm is correct |
danjulio | 0:e69d086cb053 | 315 | _spi.spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-5), 0, _ssn); |
danjulio | 0:e69d086cb053 | 316 | } |
danjulio | 0:e69d086cb053 | 317 | } |
danjulio | 0:e69d086cb053 | 318 | |
danjulio | 0:e69d086cb053 | 319 | // Sets registers from a canned modem configuration structure |
danjulio | 0:e69d086cb053 | 320 | void RH_RF95::setModemRegisters(const ModemConfig* config) |
danjulio | 0:e69d086cb053 | 321 | { |
danjulio | 0:e69d086cb053 | 322 | _spi.spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d, 0, _ssn); |
danjulio | 0:e69d086cb053 | 323 | _spi.spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e, 0, _ssn); |
danjulio | 0:e69d086cb053 | 324 | _spi.spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, config->reg_26, 0, _ssn); |
danjulio | 0:e69d086cb053 | 325 | } |
danjulio | 0:e69d086cb053 | 326 | |
danjulio | 0:e69d086cb053 | 327 | // Set one of the canned FSK Modem configs |
danjulio | 0:e69d086cb053 | 328 | // Returns true if its a valid choice |
danjulio | 0:e69d086cb053 | 329 | bool RH_RF95::setModemConfig(ModemConfigChoice index) |
danjulio | 0:e69d086cb053 | 330 | { |
danjulio | 0:e69d086cb053 | 331 | if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig))) |
danjulio | 0:e69d086cb053 | 332 | return false; |
danjulio | 0:e69d086cb053 | 333 | |
danjulio | 0:e69d086cb053 | 334 | ModemConfig cfg; |
danjulio | 0:e69d086cb053 | 335 | memcpy(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF95::ModemConfig)); |
danjulio | 0:e69d086cb053 | 336 | setModemRegisters(&cfg); |
danjulio | 0:e69d086cb053 | 337 | |
danjulio | 0:e69d086cb053 | 338 | return true; |
danjulio | 0:e69d086cb053 | 339 | } |
danjulio | 0:e69d086cb053 | 340 | |
danjulio | 0:e69d086cb053 | 341 | void RH_RF95::setPreambleLength(uint16_t bytes) |
danjulio | 0:e69d086cb053 | 342 | { |
danjulio | 0:e69d086cb053 | 343 | _spi.spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8, 0, _ssn); |
danjulio | 0:e69d086cb053 | 344 | _spi.spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff, 0, _ssn); |
danjulio | 0:e69d086cb053 | 345 | } |
danjulio | 0:e69d086cb053 | 346 | |
danjulio | 0:e69d086cb053 | 347 | bool RH_RF95::isChannelActive() |
danjulio | 0:e69d086cb053 | 348 | { |
danjulio | 0:e69d086cb053 | 349 | // Set mode RHModeCad |
danjulio | 0:e69d086cb053 | 350 | if (_mode != RHModeCad) |
danjulio | 0:e69d086cb053 | 351 | { |
danjulio | 0:e69d086cb053 | 352 | _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_CAD, 0, _ssn); |
danjulio | 0:e69d086cb053 | 353 | _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x80, 0, _ssn); // Interrupt on CadDone |
danjulio | 0:e69d086cb053 | 354 | _mode = RHModeCad; |
danjulio | 0:e69d086cb053 | 355 | } |
danjulio | 0:e69d086cb053 | 356 | |
danjulio | 0:e69d086cb053 | 357 | while (_mode == RHModeCad) |
danjulio | 0:e69d086cb053 | 358 | Thread::yield(); |
danjulio | 0:e69d086cb053 | 359 | |
danjulio | 0:e69d086cb053 | 360 | return _cad; |
danjulio | 0:e69d086cb053 | 361 | } |
danjulio | 0:e69d086cb053 | 362 | |
danjulio | 0:e69d086cb053 | 363 | void RH_RF95::enableTCXO() |
danjulio | 0:e69d086cb053 | 364 | { |
danjulio | 0:e69d086cb053 | 365 | while ((_spi.spiRead(RH_RF95_REG_4B_TCXO, 0, _ssn) & RH_RF95_TCXO_TCXO_INPUT_ON) != RH_RF95_TCXO_TCXO_INPUT_ON) |
danjulio | 0:e69d086cb053 | 366 | { |
danjulio | 0:e69d086cb053 | 367 | sleep(); |
danjulio | 0:e69d086cb053 | 368 | _spi.spiWrite(RH_RF95_REG_4B_TCXO, (_spi.spiRead(RH_RF95_REG_4B_TCXO, 0, _ssn) | RH_RF95_TCXO_TCXO_INPUT_ON), 0, _ssn); |
danjulio | 0:e69d086cb053 | 369 | } |
danjulio | 0:e69d086cb053 | 370 | } |
danjulio | 0:e69d086cb053 | 371 | |
danjulio | 0:e69d086cb053 | 372 | // From section 4.1.5 of SX1276/77/78/79 |
danjulio | 0:e69d086cb053 | 373 | // Ferror = FreqError * 2**24 * BW / Fxtal / 500 |
danjulio | 0:e69d086cb053 | 374 | int RH_RF95::frequencyError() |
danjulio | 0:e69d086cb053 | 375 | { |
danjulio | 0:e69d086cb053 | 376 | int32_t freqerror = 0; |
danjulio | 0:e69d086cb053 | 377 | |
danjulio | 0:e69d086cb053 | 378 | // Convert 2.5 bytes (5 nibbles, 20 bits) to 32 bit signed int |
danjulio | 0:e69d086cb053 | 379 | freqerror = _spi.spiRead(RH_RF95_REG_28_FEI_MSB, 0, _ssn) << 16; |
danjulio | 0:e69d086cb053 | 380 | freqerror |= _spi.spiRead(RH_RF95_REG_29_FEI_MID, 0, _ssn) << 8; |
danjulio | 0:e69d086cb053 | 381 | freqerror |= _spi.spiRead(RH_RF95_REG_2A_FEI_LSB, 0, _ssn); |
danjulio | 0:e69d086cb053 | 382 | // Sign extension into top 3 nibbles |
danjulio | 0:e69d086cb053 | 383 | if (freqerror & 0x80000) |
danjulio | 0:e69d086cb053 | 384 | freqerror |= 0xfff00000; |
danjulio | 0:e69d086cb053 | 385 | |
danjulio | 0:e69d086cb053 | 386 | int error = 0; // In hertz |
danjulio | 0:e69d086cb053 | 387 | float bw_tab[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250, 500}; |
danjulio | 0:e69d086cb053 | 388 | uint8_t bwindex = _spi.spiRead(RH_RF95_REG_1D_MODEM_CONFIG1, 0, _ssn) >> 4; |
danjulio | 0:e69d086cb053 | 389 | if (bwindex < (sizeof(bw_tab) / sizeof(float))) |
danjulio | 0:e69d086cb053 | 390 | error = (float)freqerror * bw_tab[bwindex] * ((float)(1L << 24) / (float)RH_RF95_FXOSC / 500.0f); |
danjulio | 0:e69d086cb053 | 391 | // else not defined |
danjulio | 0:e69d086cb053 | 392 | |
danjulio | 0:e69d086cb053 | 393 | return error; |
danjulio | 0:e69d086cb053 | 394 | } |
danjulio | 0:e69d086cb053 | 395 | |
danjulio | 0:e69d086cb053 | 396 | int RH_RF95::lastSNR() |
danjulio | 0:e69d086cb053 | 397 | { |
danjulio | 0:e69d086cb053 | 398 | return _lastSNR; |
danjulio | 0:e69d086cb053 | 399 | } |