/ RNode_Firmware_CE_G2 / sx126x.cpp
sx126x.cpp
1 // Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. 2 // Licensed under the MIT license. 3 4 #include "Boards.h" 5 6 #if MODEM == SX1262 7 #include "sx126x.h" 8 9 #if MCU_VARIANT == MCU_ESP32 10 #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) 11 #include "soc/rtc_wdt.h" 12 #endif 13 #define ISR_VECT IRAM_ATTR 14 #else 15 #define ISR_VECT 16 #endif 17 18 #define OP_RF_FREQ_6X 0x86 19 #define OP_SLEEP_6X 0x84 20 #define OP_STANDBY_6X 0x80 21 #define OP_TX_6X 0x83 22 #define OP_RX_6X 0x82 23 #define OP_PA_CONFIG_6X 0x95 24 #define OP_SET_IRQ_FLAGS_6X 0x08 // Also provides info such as 25 // preamble detection, etc for 26 // knowing when it's safe to switch 27 // antenna modes 28 #define OP_CLEAR_IRQ_STATUS_6X 0x02 29 #define OP_GET_IRQ_STATUS_6X 0x12 30 #define OP_RX_BUFFER_STATUS_6X 0x13 31 #define OP_PACKET_STATUS_6X 0x14 // Get snr & rssi of last packet 32 #define OP_CURRENT_RSSI_6X 0x15 33 #define OP_MODULATION_PARAMS_6X 0x8B // BW, SF, CR, etc. 34 #define OP_PACKET_PARAMS_6X 0x8C // CRC, preamble, payload length, etc. 35 #define OP_STATUS_6X 0xC0 36 #define OP_TX_PARAMS_6X 0x8E // Set dbm, etc 37 #define OP_PACKET_TYPE_6X 0x8A 38 #define OP_BUFFER_BASE_ADDR_6X 0x8F 39 #define OP_READ_REGISTER_6X 0x1D 40 #define OP_WRITE_REGISTER_6X 0x0D 41 #define OP_DIO3_TCXO_CTRL_6X 0x97 42 #define OP_DIO2_RF_CTRL_6X 0x9D 43 #define OP_CAD_PARAMS 0x88 44 #define OP_CALIBRATE_6X 0x89 45 #define OP_RX_TX_FALLBACK_MODE_6X 0x93 46 #define OP_REGULATOR_MODE_6X 0x96 47 #define OP_CALIBRATE_IMAGE_6X 0x98 48 49 #define MASK_CALIBRATE_ALL 0x7f 50 51 #define IRQ_TX_DONE_MASK_6X 0x01 52 #define IRQ_RX_DONE_MASK_6X 0x02 53 #define IRQ_HEADER_DET_MASK_6X 0x10 54 #define IRQ_PREAMBLE_DET_MASK_6X 0x04 55 #define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40 56 #define IRQ_ALL_MASK_6X 0b0100001111111111 57 58 #define MODE_LONG_RANGE_MODE_6X 0x01 59 60 #define OP_FIFO_WRITE_6X 0x0E 61 #define OP_FIFO_READ_6X 0x1E 62 #define REG_OCP_6X 0x08E7 63 #define REG_LNA_6X 0x08AC // No agc in sx1262 64 #define REG_SYNC_WORD_MSB_6X 0x0740 65 #define REG_SYNC_WORD_LSB_6X 0x0741 66 #define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98 67 #define REG_RANDOM_GEN_6X 0x0819 68 69 #define MODE_TCXO_3_3V_6X 0x07 70 #define MODE_TCXO_3_0V_6X 0x06 71 #define MODE_TCXO_2_7V_6X 0x06 72 #define MODE_TCXO_2_4V_6X 0x06 73 #define MODE_TCXO_2_2V_6X 0x03 74 #define MODE_TCXO_1_8V_6X 0x02 75 #define MODE_TCXO_1_7V_6X 0x01 76 #define MODE_TCXO_1_6V_6X 0x00 77 78 #define MODE_STDBY_RC_6X 0x00 79 #define MODE_STDBY_XOSC_6X 0x01 80 #define MODE_FALLBACK_STDBY_RC_6X 0x20 81 #define MODE_IMPLICIT_HEADER 0x01 82 #define MODE_EXPLICIT_HEADER 0x00 83 84 #define SYNC_WORD_6X 0x1424 85 86 #define XTAL_FREQ_6X (double)32000000 87 #define FREQ_DIV_6X (double)pow(2.0, 25.0) 88 #define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X) 89 90 #if BOARD_MODEL == BOARD_TECHO 91 SPIClass spim3 = SPIClass(NRF_SPIM3, pin_miso, pin_sclk, pin_mosi) ; 92 #define SPI spim3 93 94 #elif defined(NRF52840_XXAA) 95 extern SPIClass spiModem; 96 #define SPI spiModem 97 #endif 98 99 extern SPIClass SPI; 100 101 #define MAX_PKT_LENGTH 255 102 103 sx126x::sx126x() : 104 #if BOARD_MODEL == BOARD_STATION_G2 105 _spiSettings(10E6, MSBFIRST, SPI_MODE0), // 10 MHz for Station G2 PCB signal integrity per spec 106 #else 107 _spiSettings(16E6, MSBFIRST, SPI_MODE0), 108 #endif 109 _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), 110 _frequency(0), 111 _txp(0), 112 _sf(0x07), 113 _bw(0x04), 114 _cr(0x01), 115 _ldro(0x00), 116 _packetIndex(0), 117 _preambleLength(18), 118 _implicitHeaderMode(0), 119 _payloadLength(255), 120 _crcMode(1), 121 _fifo_tx_addr_ptr(0), 122 _fifo_rx_addr_ptr(0), 123 _packet({0}), 124 _preinit_done(false), 125 _onReceive(NULL) 126 { setTimeout(0); } 127 128 bool sx126x::preInit() { 129 pinMode(_ss, OUTPUT); 130 digitalWrite(_ss, HIGH); 131 132 #if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_XIAO_S3 || BOARD_MODEL == BOARD_STATION_G2 133 SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs); 134 #elif BOARD_MODEL == BOARD_TECHO 135 SPI.setPins(pin_miso, pin_sclk, pin_mosi); 136 SPI.begin(); 137 #else 138 SPI.begin(); 139 #endif 140 141 // Check version (retry for up to 2 seconds) 142 // TODO: Actually read version registers, not syncwords 143 long start = millis(); 144 uint8_t syncmsb; 145 uint8_t synclsb; 146 while (((millis() - start) < 2000) && (millis() >= start)) { 147 syncmsb = readRegister(REG_SYNC_WORD_MSB_6X); 148 synclsb = readRegister(REG_SYNC_WORD_LSB_6X); 149 if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) { 150 break; 151 } 152 delay(100); 153 } 154 if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) { 155 return false; 156 } 157 158 _preinit_done = true; 159 return true; 160 } 161 162 uint8_t ISR_VECT sx126x::readRegister(uint16_t address) { 163 return singleTransfer(OP_READ_REGISTER_6X, address, 0x00); 164 } 165 166 void sx126x::writeRegister(uint16_t address, uint8_t value) { 167 singleTransfer(OP_WRITE_REGISTER_6X, address, value); 168 } 169 170 uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) { 171 waitOnBusy(); 172 173 uint8_t response; 174 digitalWrite(_ss, LOW); 175 SPI.beginTransaction(_spiSettings); 176 SPI.transfer(opcode); 177 SPI.transfer((address & 0xFF00) >> 8); 178 SPI.transfer(address & 0x00FF); 179 if (opcode == OP_READ_REGISTER_6X) { SPI.transfer(0x00); } 180 response = SPI.transfer(value); 181 SPI.endTransaction(); 182 183 digitalWrite(_ss, HIGH); 184 185 return response; 186 } 187 188 void sx126x::rxAntEnable() { 189 if (_rxen != -1) { digitalWrite(_rxen, HIGH); } 190 } 191 192 void sx126x::loraMode() { 193 // Enable lora mode on the SX1262 chip 194 uint8_t mode = MODE_LONG_RANGE_MODE_6X; 195 executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); 196 } 197 198 void sx126x::waitOnBusy() { 199 unsigned long time = millis(); 200 if (_busy != -1) { 201 while (digitalRead(_busy) == HIGH) { 202 if (millis() >= (time + 100)) { break; } 203 } 204 } 205 } 206 207 void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) { 208 waitOnBusy(); 209 digitalWrite(_ss, LOW); 210 SPI.beginTransaction(_spiSettings); 211 SPI.transfer(opcode); 212 for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); } 213 SPI.endTransaction(); 214 digitalWrite(_ss, HIGH); 215 } 216 217 void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) { 218 waitOnBusy(); 219 digitalWrite(_ss, LOW); 220 SPI.beginTransaction(_spiSettings); 221 SPI.transfer(opcode); 222 SPI.transfer(0x00); 223 for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); } 224 SPI.endTransaction(); 225 digitalWrite(_ss, HIGH); 226 } 227 228 void sx126x::writeBuffer(const uint8_t* buffer, size_t size) { 229 waitOnBusy(); 230 digitalWrite(_ss, LOW); 231 SPI.beginTransaction(_spiSettings); 232 SPI.transfer(OP_FIFO_WRITE_6X); 233 SPI.transfer(_fifo_tx_addr_ptr); 234 for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; } 235 SPI.endTransaction(); 236 digitalWrite(_ss, HIGH); 237 } 238 239 void sx126x::readBuffer(uint8_t* buffer, size_t size) { 240 waitOnBusy(); 241 digitalWrite(_ss, LOW); 242 SPI.beginTransaction(_spiSettings); 243 SPI.transfer(OP_FIFO_READ_6X); 244 SPI.transfer(_fifo_rx_addr_ptr); 245 SPI.transfer(0x00); 246 for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); } 247 SPI.endTransaction(); 248 digitalWrite(_ss, HIGH); 249 } 250 251 void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) { 252 // Because there is no access to these registers on the sx1262, we have 253 // to set all these parameters at once or not at all. 254 uint8_t buf[8]; 255 buf[0] = sf; 256 buf[1] = bw; 257 buf[2] = cr; 258 buf[3] = ldro; // Low data rate toggle 259 buf[4] = 0x00; // Unused params in LoRa mode 260 buf[5] = 0x00; 261 buf[6] = 0x00; 262 buf[7] = 0x00; 263 executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8); 264 } 265 266 void sx126x::setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) { 267 // Because there is no access to these registers on the sx1262, we have 268 // to set all these parameters at once or not at all. 269 uint8_t buf[9]; 270 buf[0] = uint8_t((preamble_symbols & 0xFF00) >> 8); 271 buf[1] = uint8_t((preamble_symbols & 0x00FF)); 272 buf[2] = headermode; 273 buf[3] = payload_length; 274 buf[4] = crc; 275 buf[5] = 0x00; // standard IQ setting (no inversion) 276 buf[6] = 0x00; // unused params 277 buf[7] = 0x00; 278 buf[8] = 0x00; 279 executeOpcode(OP_PACKET_PARAMS_6X, buf, 9); 280 } 281 282 void sx126x::reset(void) { 283 if (_reset != -1) { 284 pinMode(_reset, OUTPUT); 285 digitalWrite(_reset, LOW); 286 delay(10); 287 digitalWrite(_reset, HIGH); 288 delay(10); 289 } 290 } 291 292 void sx126x::calibrate(void) { 293 // Put in STDBY_RC mode before calibration 294 uint8_t mode_byte = MODE_STDBY_RC_6X; 295 executeOpcode(OP_STANDBY_6X, &mode_byte, 1); 296 297 // Calibrate RC64k, RC13M, PLL, ADC and image 298 uint8_t calibrate = MASK_CALIBRATE_ALL; 299 executeOpcode(OP_CALIBRATE_6X, &calibrate, 1); 300 301 delay(5); 302 waitOnBusy(); 303 } 304 305 void sx126x::calibrate_image(long frequency) { 306 uint8_t image_freq[2] = {0}; 307 if (frequency >= 430E6 && frequency <= 440E6) { image_freq[0] = 0x6B; image_freq[1] = 0x6F; } 308 else if (frequency >= 470E6 && frequency <= 510E6) { image_freq[0] = 0x75; image_freq[1] = 0x81; } 309 else if (frequency >= 779E6 && frequency <= 787E6) { image_freq[0] = 0xC1; image_freq[1] = 0xC5; } 310 else if (frequency >= 863E6 && frequency <= 870E6) { image_freq[0] = 0xD7; image_freq[1] = 0xDB; } 311 else if (frequency >= 902E6 && frequency <= 928E6) { image_freq[0] = 0xE1; image_freq[1] = 0xE9; } // TODO: Allow higher freq calibration 312 executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2); 313 waitOnBusy(); 314 } 315 316 int sx126x::begin(long frequency) { 317 reset(); 318 319 if (_busy != -1) { pinMode(_busy, INPUT); } 320 if (!_preinit_done) { if (!preInit()) { return false; } } 321 if (_rxen != -1) { pinMode(_rxen, OUTPUT); } 322 323 calibrate(); 324 calibrate_image(frequency); 325 enableTCXO(); 326 loraMode(); 327 standby(); 328 329 // Set sync word 330 setSyncWord(SYNC_WORD_6X); 331 332 #if DIO2_AS_RF_SWITCH 333 // enable dio2 rf switch 334 uint8_t byte = 0x01; 335 executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1); 336 #endif 337 338 rxAntEnable(); 339 setFrequency(frequency); 340 setTxPower(2); 341 enableCrc(); 342 writeRegister(REG_LNA_6X, 0x96); // Set LNA boost 343 uint8_t basebuf[2] = {0}; // Set base addresses 344 executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2); 345 346 setModulationParams(_sf, _bw, _cr, _ldro); 347 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); 348 349 #if HAS_LORA_PA 350 #if LORA_PA_GC1109 351 // Enable Vfem_ctl for supply to 352 // PA power net. 353 pinMode(LORA_PA_PWR_EN, OUTPUT); 354 digitalWrite(LORA_PA_PWR_EN, HIGH); 355 356 // Enable PA LNA and TX standby 357 pinMode(LORA_PA_CSD, OUTPUT); 358 digitalWrite(LORA_PA_CSD, HIGH); 359 360 // Keep PA CPS low until actual 361 // transmit. Does it save power? 362 // Who knows? Will have to measure. 363 pinMode(LORA_PA_CPS, OUTPUT); 364 digitalWrite(LORA_PA_CPS, LOW); 365 366 // On Heltec V4, the PA CTX pin 367 // is driven by the SX1262 DIO2 368 // pin directly, so we do not 369 // need to manually raise this. 370 #endif 371 #endif 372 373 return 1; 374 } 375 376 void sx126x::end() { sleep(); SPI.end(); _preinit_done = false; } 377 378 int sx126x::beginPacket(int implicitHeader) { 379 #if HAS_LORA_PA 380 #if LORA_PA_GC1109 381 // Enable PA CPS for transmit 382 digitalWrite(LORA_PA_CPS, HIGH); 383 #endif 384 #endif 385 386 standby(); 387 if (implicitHeader) { implicitHeaderMode(); } 388 else { explicitHeaderMode(); } 389 390 _payloadLength = 0; 391 _fifo_tx_addr_ptr = 0; 392 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); 393 394 return 1; 395 } 396 397 int sx126x::endPacket() { 398 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); 399 uint8_t timeout[3] = {0}; // Put in single TX mode 400 executeOpcode(OP_TX_6X, timeout, 3); 401 402 uint8_t buf[2]; 403 buf[0] = 0x00; 404 buf[1] = 0x00; 405 executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); 406 407 // Wait for TX done 408 bool timed_out = false; 409 uint32_t w_timeout = millis()+LORA_MODEM_TIMEOUT_MS; 410 while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0)) { 411 buf[0] = 0x00; 412 buf[1] = 0x00; 413 executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); 414 yield(); 415 } 416 417 if (!(millis() < w_timeout)) { timed_out = true; } 418 419 // Clear IRQs 420 uint8_t mask[2]; 421 mask[0] = 0x00; 422 mask[1] = IRQ_TX_DONE_MASK_6X; 423 executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2); 424 if (timed_out) { return 0; } else { return 1; } 425 } 426 427 unsigned long preamble_detected_at = 0; 428 extern long lora_preamble_time_ms; 429 extern long lora_header_time_ms; 430 bool false_preamble_detected = false; 431 432 bool sx126x::dcd() { 433 uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); 434 uint32_t now = millis(); 435 436 bool header_detected = false; 437 bool carrier_detected = false; 438 439 if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { header_detected = true; carrier_detected = true; } 440 else { header_detected = false; } 441 442 if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) { 443 carrier_detected = true; 444 if (preamble_detected_at == 0) { preamble_detected_at = now; } 445 if (now - preamble_detected_at > lora_preamble_time_ms + lora_header_time_ms) { 446 preamble_detected_at = 0; 447 if (!header_detected) { false_preamble_detected = true; } 448 uint8_t clearbuf[2] = {0}; 449 clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; 450 executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); 451 } 452 } 453 454 // TODO: Maybe there's a way of unlatching the RSSI 455 // status without re-activating receive mode? 456 if (false_preamble_detected) { sx126x_modem.receive(); false_preamble_detected = false; } 457 return carrier_detected; 458 } 459 460 uint8_t sx126x::currentRssiRaw() { 461 uint8_t byte = 0; 462 executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); 463 return byte; 464 } 465 466 int ISR_VECT sx126x::currentRssi() { 467 uint8_t byte = 0; 468 executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); 469 int rssi = -(int(byte)) / 2; 470 #if HAS_LORA_LNA 471 rssi -= LORA_LNA_GAIN; 472 #endif 473 return rssi; 474 } 475 476 uint8_t sx126x::packetRssiRaw() { 477 uint8_t buf[3] = {0}; 478 executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); 479 return buf[2]; 480 } 481 482 int ISR_VECT sx126x::packetRssi() { 483 // TODO: May need more calculations here 484 uint8_t buf[3] = {0}; 485 executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); 486 int pkt_rssi = -buf[0] / 2; 487 #if HAS_LORA_LNA 488 pkt_rssi -= LORA_LNA_GAIN; 489 #endif 490 return pkt_rssi; 491 } 492 493 int ISR_VECT sx126x::packetRssi(uint8_t pkt_snr_raw) { 494 // TODO: May need more calculations here 495 uint8_t buf[3] = {0}; 496 executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); 497 int pkt_rssi = -buf[0] / 2; 498 return pkt_rssi; 499 } 500 501 uint8_t ISR_VECT sx126x::packetSnrRaw() { 502 uint8_t buf[3] = {0}; 503 executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); 504 return buf[1]; 505 } 506 507 float ISR_VECT sx126x::packetSnr() { 508 uint8_t buf[3] = {0}; 509 executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); 510 return float(buf[1]) * 0.25; 511 } 512 513 long sx126x::packetFrequencyError() { 514 // TODO: Implement this, no idea how to check it on the sx1262 515 const float fError = 0.0; 516 return static_cast<long>(fError); 517 } 518 519 size_t sx126x::write(uint8_t byte) { return write(&byte, sizeof(byte)); } 520 size_t sx126x::write(const uint8_t *buffer, size_t size) { 521 if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; } 522 writeBuffer(buffer, size); 523 _payloadLength = _payloadLength + size; 524 return size; 525 } 526 527 int ISR_VECT sx126x::available() { 528 uint8_t buf[2] = {0}; 529 executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2); 530 return buf[0] - _packetIndex; 531 } 532 533 int ISR_VECT sx126x::read(){ 534 if (!available()) { return -1; } 535 if (_packetIndex == 0) { 536 uint8_t rxbuf[2] = {0}; 537 executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); 538 int size = rxbuf[0]; 539 _fifo_rx_addr_ptr = rxbuf[1]; 540 readBuffer(_packet, size); 541 } 542 543 uint8_t byte = _packet[_packetIndex]; 544 _packetIndex++; 545 return byte; 546 } 547 548 int sx126x::peek() { 549 if (!available()) { return -1; } 550 if (_packetIndex == 0) { 551 uint8_t rxbuf[2] = {0}; 552 executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); 553 int size = rxbuf[0]; 554 _fifo_rx_addr_ptr = rxbuf[1]; 555 readBuffer(_packet, size); 556 } 557 558 uint8_t b = _packet[_packetIndex]; 559 return b; 560 } 561 562 void sx126x::flush() { } 563 564 void sx126x::onReceive(void(*callback)(int)){ 565 _onReceive = callback; 566 567 if (callback) { 568 pinMode(_dio0, INPUT); 569 uint8_t buf[8]; // Set preamble and header detection irqs, plus dio0 mask 570 buf[0] = 0xFF; // Set irq masks, enable all 571 buf[1] = 0xFF; 572 buf[2] = 0x00; // Set dio0 masks 573 buf[3] = IRQ_RX_DONE_MASK_6X; 574 buf[4] = 0x00; // Set dio1 masks 575 buf[5] = 0x00; 576 buf[6] = 0x00; // Set dio2 masks 577 buf[7] = 0x00; 578 executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8); 579 580 #ifdef SPI_HAS_NOTUSINGINTERRUPT 581 SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); 582 #endif 583 attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING); 584 585 } else { 586 detachInterrupt(digitalPinToInterrupt(_dio0)); 587 #ifdef SPI_HAS_NOTUSINGINTERRUPT 588 SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); 589 #endif 590 } 591 } 592 593 void sx126x::receive(int size) { 594 #if HAS_LORA_PA 595 #if LORA_PA_GC1109 596 // Disable PA CPS for receive 597 digitalWrite(LORA_PA_CPS, LOW); 598 #endif 599 #endif 600 601 if (size > 0) { 602 implicitHeaderMode(); 603 _payloadLength = size; 604 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); 605 } else { explicitHeaderMode(); } 606 607 if (_rxen != -1) { rxAntEnable(); } 608 uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // Continuous mode 609 executeOpcode(OP_RX_6X, mode, 3); 610 } 611 612 void sx126x::standby() { 613 uint8_t byte = MODE_STDBY_XOSC_6X; // STDBY_XOSC 614 executeOpcode(OP_STANDBY_6X, &byte, 1); 615 } 616 617 void sx126x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_6X, &byte, 1); } 618 619 void sx126x::enableTCXO() { 620 #if HAS_TCXO 621 #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_XIAO_S3 622 uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF}; 623 #elif BOARD_MODEL == BOARD_TBEAM 624 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 625 #elif BOARD_MODEL == BOARD_TDECK 626 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 627 #elif BOARD_MODEL == BOARD_TBEAM_S_V1 628 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 629 #elif BOARD_MODEL == BOARD_T3S3 630 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 631 #elif BOARD_MODEL == BOARD_HELTEC_T114 632 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 633 #elif BOARD_MODEL == BOARD_TECHO 634 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 635 #elif BOARD_MODEL == BOARD_HELTEC32_V4 636 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 637 #elif BOARD_MODEL == BOARD_STATION_G2 638 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; 639 #endif 640 executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4); 641 #if BOARD_MODEL == BOARD_STATION_G2 642 delay(5); // TCXO stabilization delay for Station G2 (typ. 2ms, 5ms for margin) 643 #endif 644 #endif 645 } 646 647 // TODO: Once enabled, SX1262 needs a complete reset to disable TCXO 648 void sx126x::disableTCXO() { } 649 650 void sx126x::setTxPower(int level, int outputPin) { 651 // Currently no low power mode for SX1262 implemented, assuming PA boost 652 653 // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2 654 // RegTxClampConfig = @address 0x08D8 655 writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1)); 656 657 uint8_t pa_buf[4]; 658 pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs 659 pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262 660 pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261) 661 pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet) 662 executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power 663 664 if (level > 22) { level = 22; } 665 else if (level < -9) { level = -9; } 666 writeRegister(REG_OCP_6X, OCP_TUNED); // Use board-specific tuned OCP 667 668 uint8_t tx_buf[2]; 669 tx_buf[0] = level; 670 tx_buf[1] = 0x02; // PA ramping time - 40 microseconds 671 executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2); 672 673 _txp = level; 674 } 675 676 uint8_t sx126x::getTxPower() { return _txp; } 677 678 void sx126x::setFrequency(long frequency) { 679 _frequency = frequency; 680 uint8_t buf[4]; 681 uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X); 682 buf[0] = ((freq >> 24) & 0xFF); 683 buf[1] = ((freq >> 16) & 0xFF); 684 buf[2] = ((freq >> 8) & 0xFF); 685 buf[3] = (freq & 0xFF); 686 executeOpcode(OP_RF_FREQ_6X, buf, 4); 687 } 688 689 uint32_t sx126x::getFrequency() { 690 // We can't read the frequency on the sx1262 / 80 691 uint32_t frequency = _frequency; 692 return frequency; 693 } 694 695 void sx126x::setSpreadingFactor(int sf) { 696 if (sf < 5) { sf = 5; } 697 else if (sf > 12) { sf = 12; } 698 _sf = sf; 699 700 handleLowDataRate(); 701 setModulationParams(sf, _bw, _cr, _ldro); 702 } 703 704 long sx126x::getSignalBandwidth() { 705 int bw = _bw; 706 switch (bw) { 707 case 0x00: return 7.8E3; 708 case 0x01: return 15.6E3; 709 case 0x02: return 31.25E3; 710 case 0x03: return 62.5E3; 711 case 0x04: return 125E3; 712 case 0x05: return 250E3; 713 case 0x06: return 500E3; 714 case 0x08: return 10.4E3; 715 case 0x09: return 20.8E3; 716 case 0x0A: return 41.7E3; 717 } 718 return 0; 719 } 720 721 extern bool lora_low_datarate; 722 void sx126x::handleLowDataRate() { 723 if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) 724 { _ldro = 0x01; lora_low_datarate = true; } 725 else { _ldro = 0x00; lora_low_datarate = false; } 726 } 727 728 // TODO: Check if there's anything the sx1262 can do here 729 void sx126x::optimizeModemSensitivity(){ } 730 731 void sx126x::setSignalBandwidth(long sbw) { 732 if (sbw <= 7.8E3) { _bw = 0x00; } 733 else if (sbw <= 10.4E3) { _bw = 0x08; } 734 else if (sbw <= 15.6E3) { _bw = 0x01; } 735 else if (sbw <= 20.8E3) { _bw = 0x09; } 736 else if (sbw <= 31.25E3) { _bw = 0x02; } 737 else if (sbw <= 41.7E3) { _bw = 0x0A; } 738 else if (sbw <= 62.5E3) { _bw = 0x03; } 739 else if (sbw <= 125E3) { _bw = 0x04; } 740 else if (sbw <= 250E3) { _bw = 0x05; } 741 else { _bw = 0x06; } 742 743 handleLowDataRate(); 744 setModulationParams(_sf, _bw, _cr, _ldro); 745 optimizeModemSensitivity(); 746 } 747 748 void sx126x::setCodingRate4(int denominator) { 749 if (denominator < 5) { denominator = 5; } 750 else if (denominator > 8) { denominator = 8; } 751 int cr = denominator - 4; 752 _cr = cr; 753 setModulationParams(_sf, _bw, cr, _ldro); 754 } 755 756 void sx126x::setPreambleLength(long preamble_symbols) { 757 _preambleLength = preamble_symbols; 758 setPacketParams(preamble_symbols, _implicitHeaderMode, _payloadLength, _crcMode); 759 } 760 761 void sx126x::setSyncWord(uint16_t sw) { 762 // TODO: Why was this hardcoded instead of using the config value? 763 // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8); 764 // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF); 765 writeRegister(REG_SYNC_WORD_MSB_6X, 0x14); 766 writeRegister(REG_SYNC_WORD_LSB_6X, 0x24); 767 } 768 769 void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) { 770 _ss = ss; 771 _reset = reset; 772 _dio0 = dio0; 773 _busy = busy; 774 _rxen = rxen; 775 } 776 777 void sx126x::dumpRegisters(Stream& out) { 778 for (int i = 0; i < 128; i++) { 779 out.print("0x"); 780 out.print(i, HEX); 781 out.print(": 0x"); 782 out.println(readRegister(i), HEX); 783 } 784 } 785 786 void ISR_VECT sx126x::handleDio0Rise() { 787 uint8_t buf[2]; 788 buf[0] = 0x00; 789 buf[1] = 0x00; 790 executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); 791 executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2); 792 793 if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) { 794 _packetIndex = 0; 795 uint8_t rxbuf[2] = {0}; // Read packet length 796 executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); 797 int packetLength = rxbuf[0]; 798 if (_onReceive) { _onReceive(packetLength); } 799 } 800 } 801 802 void ISR_VECT sx126x::onDio0Rise() { sx126x_modem.handleDio0Rise(); } 803 void sx126x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); } 804 void sx126x::enableCrc() { _crcMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } 805 void sx126x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } 806 void sx126x::explicitHeaderMode() { _implicitHeaderMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } 807 void sx126x::implicitHeaderMode() { _implicitHeaderMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } 808 byte sx126x::random() { return readRegister(REG_RANDOM_GEN_6X); } 809 810 sx126x sx126x_modem; 811 812 #endif