LCOV - code coverage report
Current view: top level - src/modules/SX126x - SX126x_commands.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 53 178 29.8 %
Date: 2026-11-08 19:43:08 Functions: 13 35 37.1 %

          Line data    Source code
       1             : #include "SX126x.h"
       2             : 
       3             : // this file contains implementation of all commands
       4             : // supported by the SX126x SPI interface
       5             : // in most cases, the names of methods match those in the datasheet
       6             : // however, sometimes slight changes had to be made in order to
       7             : // better fit the RadioLib API
       8             : 
       9             : #if !RADIOLIB_EXCLUDE_SX126X
      10             : 
      11           0 : int16_t SX126x::resetAGC() {
      12             :   // warm sleep to power down the analog frontend
      13           0 :   int16_t state = sleep(true);
      14           0 :   RADIOLIB_ASSERT(state);
      15             : 
      16             :   // wake to RC standby
      17           0 :   state = standby(RADIOLIB_SX126X_STANDBY_RC, true);
      18           0 :   RADIOLIB_ASSERT(state);
      19             : 
      20             :   // recalibrate all blocks
      21           0 :   state = calibrate(RADIOLIB_SX126X_CALIBRATE_ALL);
      22           0 :   RADIOLIB_ASSERT(state);
      23             : 
      24             :   // re-calibrate image rejection for the operating frequency
      25           0 :   state = calibrateImage(this->freqHz);
      26           0 :   RADIOLIB_ASSERT(state);
      27             : 
      28             :   // re-apply DIO2 RF switch if it was configured
      29           0 :   if(this->dio2RfSwitch) {
      30           0 :     state = setDio2AsRfSwitch(true);
      31           0 :     RADIOLIB_ASSERT(state);
      32             :   }
      33             : 
      34             :   // re-apply RX boosted gain if it was configured
      35           0 :   if(this->rxBoostedGainMode) {
      36           0 :     state = setRxBoostedGainMode(true);
      37           0 :     RADIOLIB_ASSERT(state);
      38             :   }
      39             : 
      40           0 :   return(RADIOLIB_ERR_NONE);
      41             : }
      42             : 
      43           0 : int16_t SX126x::calibrate(uint8_t params) {
      44           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, &params, 1, true, false));
      45             : }
      46             : 
      47           3 : int16_t SX126x::sleep() {
      48           3 :   return(SX126x::sleep(true));
      49             : }
      50             : 
      51           3 : int16_t SX126x::sleep(bool retainConfig) {
      52             :   // set RF switch (if present)
      53           3 :   this->mod->setRfSwitchState(Module::MODE_IDLE);
      54             : 
      55           3 :   uint8_t sleepMode = RADIOLIB_SX126X_SLEEP_START_WARM | RADIOLIB_SX126X_SLEEP_RTC_OFF;
      56           3 :   if(!retainConfig) {
      57           0 :     sleepMode = RADIOLIB_SX126X_SLEEP_START_COLD | RADIOLIB_SX126X_SLEEP_RTC_OFF;
      58             :   }
      59           3 :   int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SLEEP, &sleepMode, 1, false, false);
      60             : 
      61             :   // wait for SX126x to safely enter sleep mode
      62           3 :   this->mod->hal->delay(1);
      63             : 
      64           3 :   return(state);
      65             : }
      66             : 
      67          24 : int16_t SX126x::standby() {
      68          24 :   return(SX126x::standby(this->standbyXOSC ? RADIOLIB_SX126X_STANDBY_XOSC : RADIOLIB_SX126X_STANDBY_RC, true));
      69             : }
      70             : 
      71           3 : int16_t SX126x::standby(uint8_t mode) {
      72           3 :   return(SX126x::standby(mode, true));
      73             : }
      74             : 
      75          27 : int16_t SX126x::standby(uint8_t mode, bool wakeup) {
      76             :   // set RF switch (if present)
      77          27 :   this->mod->setRfSwitchState(Module::MODE_IDLE);
      78             : 
      79          27 :   if(wakeup) {
      80             :     // send a NOP command - this pulls the NSS low to exit the sleep mode,
      81             :     // while preventing interference with possible other SPI transactions
      82             :     // see https://github.com/jgromes/RadioLib/discussions/1364
      83          27 :     (void)this->mod->SPIwriteStream((uint16_t)RADIOLIB_SX126X_CMD_NOP, NULL, 0, false, false);
      84             :   }
      85             : 
      86          27 :   const uint8_t data[] = { mode };
      87          54 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_STANDBY, data, 1));
      88             : }
      89             : 
      90           0 : int16_t SX126x::setFs() {
      91           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_FS, NULL, 0));
      92             : }
      93             : 
      94           0 : int16_t SX126x::setTx(uint32_t timeout) {
      95           0 :   const uint8_t data[] = { (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF)} ;
      96           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX, data, 3));
      97             : }
      98             : 
      99           6 : int16_t SX126x::setRx(uint32_t timeout) {
     100           6 :   const uint8_t data[] = { (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF) };
     101          12 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX, data, 3, true, false));
     102             : }
     103             : 
     104             : // this implementation is not documented in the datasheet, but taken from LoRaMac-node
     105             : // https://github.com/Lora-net/LoRaMac-node/blob/dcbcfb329b4a343ab007bc19ac43a8dc952b3354/src/radio/sx126x/sx126x.c#L367
     106           0 : int16_t SX126x::setLoRaSymbNumTimeout(uint8_t symbolNum) {
     107             :   // the symbol number timeout is encoded as a mantissa-exponent pair
     108             :   // before being passed to the command
     109           0 :   uint8_t mant = (symbolNum > RADIOLIB_SX126X_MAX_LORA_SYMB_NUM_TIMEOUT ? RADIOLIB_SX126X_MAX_LORA_SYMB_NUM_TIMEOUT : symbolNum) >> 1;
     110           0 :   uint8_t exp = 0;
     111           0 :   while(mant > 31) {
     112           0 :     mant = (mant + 3) >> 2;
     113           0 :     exp++;
     114             :   }
     115             : 
     116             :   // pass the encoded value to the command
     117           0 :   uint8_t reg = mant << (2*exp + 1);
     118           0 :   int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT, &reg, 1);
     119           0 :   RADIOLIB_ASSERT(state);
     120             : 
     121             :   // for non-zero timeouts, the mantissa-exponent value must also be written to a register
     122           0 :   if(symbolNum != 0) {
     123           0 :     reg = exp + (mant << 3);
     124           0 :     state = this->writeRegister(RADIOLIB_SX126X_REG_LORA_SYNC_TIMEOUT, &reg, 1);
     125             :   }
     126           0 :   return(state);
     127             : }
     128             : 
     129           0 : int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout) {
     130             :   // default CAD parameters are selected according to recommendations on Semtech DS.SX1261-2.W.APP rev. 1.1, page 92.
     131             : 
     132             :   // build the packet with default configuration
     133             :   uint8_t data[7];
     134           0 :   data[0] = RADIOLIB_SX126X_CAD_ON_4_SYMB;
     135           0 :   data[1] = this->spreadingFactor + 13;
     136           0 :   data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
     137           0 :   data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY;
     138           0 :   uint32_t timeout_raw = (float)timeout / 15.625f;
     139           0 :   data[4] = (uint8_t)((timeout_raw >> 16) & 0xFF);
     140           0 :   data[5] = (uint8_t)((timeout_raw >> 8) & 0xFF);
     141           0 :   data[6] = (uint8_t)(timeout_raw & 0xFF);
     142             : 
     143             :   // set user-provided values
     144           0 :   if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
     145           0 :     data[0] = symbolNum;
     146             :   }
     147             : 
     148           0 :   if(detPeak != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
     149           0 :     data[1] = detPeak;
     150             :   }
     151             : 
     152           0 :   if(detMin != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
     153           0 :     data[2] = detMin;
     154             :   }
     155             : 
     156           0 :   if(exitMode != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
     157           0 :     data[3] = exitMode;
     158             :   }
     159             : 
     160             :   // configure parameters
     161           0 :   int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7);
     162           0 :   RADIOLIB_ASSERT(state);
     163             : 
     164             :   // start CAD
     165           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD, NULL, 0));
     166             : }
     167             : 
     168           0 : int16_t SX126x::setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax, uint8_t paLut) {
     169           0 :   const uint8_t data[] = { paDutyCycle, hpMax, deviceSel, paLut };
     170           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PA_CONFIG, data, 4));
     171             : }
     172             : 
     173           0 : int16_t SX126x::writeRegister(uint16_t addr, const uint8_t* data, uint8_t numBytes) {
     174           0 :   this->mod->SPIwriteRegisterBurst(addr, data, numBytes);
     175           0 :   return(RADIOLIB_ERR_NONE);
     176             : }
     177             : 
     178          27 : int16_t SX126x::readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) {
     179             :   // send the command
     180          27 :   this->mod->SPIreadRegisterBurst(addr, numBytes, data);
     181             : 
     182             :   // check the status
     183          27 :   int16_t state = this->mod->SPIcheckStream();
     184          27 :   return(state);
     185             : }
     186             : 
     187           0 : int16_t SX126x::writeBuffer(const uint8_t* data, uint8_t numBytes, uint8_t offset) {
     188           0 :   const uint8_t cmd[] = { RADIOLIB_SX126X_CMD_WRITE_BUFFER, offset };
     189           0 :   return(this->mod->SPIwriteStream(cmd, 2, data, numBytes));
     190             : }
     191             : 
     192           0 : int16_t SX126x::readBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset) {
     193           0 :   const uint8_t cmd[] = { RADIOLIB_SX126X_CMD_READ_BUFFER, offset };
     194           0 :   return(this->mod->SPIreadStream(cmd, 2, data, numBytes));
     195             : }
     196             : 
     197           3 : int16_t SX126x::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask) {
     198           3 :   const uint8_t data[8] = {(uint8_t)((irqMask >> 8) & 0xFF), (uint8_t)(irqMask & 0xFF),
     199           3 :                      (uint8_t)((dio1Mask >> 8) & 0xFF), (uint8_t)(dio1Mask & 0xFF),
     200           3 :                      (uint8_t)((dio2Mask >> 8) & 0xFF), (uint8_t)(dio2Mask & 0xFF),
     201           3 :                      (uint8_t)((dio3Mask >> 8) & 0xFF), (uint8_t)(dio3Mask & 0xFF)};
     202           6 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS, data, 8));
     203             : }
     204             : 
     205           6 : int16_t SX126x::clearIrqStatus(uint16_t clearIrqParams) {
     206           6 :   const uint8_t data[] = { (uint8_t)((clearIrqParams >> 8) & 0xFF), (uint8_t)(clearIrqParams & 0xFF) };
     207          12 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CLEAR_IRQ_STATUS, data, 2));
     208             : }
     209             : 
     210           0 : int16_t SX126x::setRfFrequency(uint32_t frf) {
     211           0 :   const uint8_t data[] = { (uint8_t)((frf >> 24) & 0xFF), (uint8_t)((frf >> 16) & 0xFF), (uint8_t)((frf >> 8) & 0xFF), (uint8_t)(frf & 0xFF) };
     212           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RF_FREQUENCY, data, 4));
     213             : }
     214             : 
     215           0 : int16_t SX126x::calibrateImage(const uint8_t* data) {
     216           0 :   int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data, 2);
     217             : 
     218             :   // if something failed, show the device errors
     219             :   #if RADIOLIB_DEBUG_BASIC
     220             :   if(state != RADIOLIB_ERR_NONE) {
     221             :     // unless mode is forced to standby, device errors will be 0
     222             :     standby();
     223             :     uint16_t errors = getDeviceErrors();
     224             :     RADIOLIB_DEBUG_BASIC_PRINTLN("Calibration failed, device errors: 0x%X", errors);
     225             :   }
     226             :   #endif
     227           0 :   return(state);
     228             : }
     229             : 
     230          60 : uint8_t SX126x::getPacketType() {
     231          60 :   uint8_t data = 0xFF;
     232          60 :   this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_PACKET_TYPE, &data, 1);
     233          60 :   return(data);
     234             : }
     235             : 
     236           0 : int16_t SX126x::setTxParams(uint8_t pwr, uint8_t rampTime) {
     237           0 :   const uint8_t data[] = { pwr, rampTime };
     238           0 :   int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_PARAMS, data, 2);
     239           0 :   if(state == RADIOLIB_ERR_NONE) {
     240           0 :     this->pwr = pwr;
     241             :   }
     242           0 :   return(state);
     243             : }
     244             : 
     245           0 : int16_t SX126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro) {
     246             :   // calculate symbol length and enable low data rate optimization, if auto-configuration is enabled
     247           0 :   if(this->ldroAuto) {
     248           0 :     uint64_t symbolLengthUs = ((uint64_t)1000000 * ((uint64_t)1 << this->spreadingFactor)) / (uint64_t)this->bandwidthHz;
     249           0 :     if(symbolLengthUs >= 16000) {
     250           0 :       this->ldrOptimize = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON;
     251             :     } else {
     252           0 :       this->ldrOptimize = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF;
     253             :     }
     254             :   } else {
     255           0 :     this->ldrOptimize = ldro;
     256             :   }
     257             :   // 500/9/8  - 0x09 0x04 0x03 0x00 - SF9, BW125, 4/8
     258             :   // 500/11/8 - 0x0B 0x04 0x03 0x00 - SF11 BW125, 4/7
     259           0 :   const uint8_t data[4] = {sf, bw, cr, this->ldrOptimize};
     260           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, 4));
     261             : }
     262             : 
     263           0 : int16_t SX126x::setModulationParamsFSK(uint32_t br, uint8_t sh, uint8_t rxBw, uint32_t freqDev) {
     264           0 :   const uint8_t data[8] = {(uint8_t)((br >> 16) & 0xFF), (uint8_t)((br >> 8) & 0xFF), (uint8_t)(br & 0xFF),
     265             :                      sh, rxBw,
     266           0 :                      (uint8_t)((freqDev >> 16) & 0xFF), (uint8_t)((freqDev >> 8) & 0xFF), (uint8_t)(freqDev & 0xFF)};
     267           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, 8));
     268             : }
     269             : 
     270           0 : int16_t SX126x::setModulationParamsBPSK(uint32_t br, uint8_t sh) {
     271           0 :   const uint8_t data[] = {(uint8_t)((br >> 16) & 0xFF), (uint8_t)((br >> 8) & 0xFF), (uint8_t)(br & 0xFF), sh};
     272           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, sizeof(data)));
     273             : }
     274             : 
     275           0 : int16_t SX126x::setPacketParams(uint16_t preambleLen, uint8_t crcType, uint8_t payloadLen, uint8_t hdrType, uint8_t invertIQ) {
     276           0 :   int16_t state = fixInvertedIQ(invertIQ);
     277           0 :   RADIOLIB_ASSERT(state);
     278           0 :   const uint8_t data[6] = {(uint8_t)((preambleLen >> 8) & 0xFF), (uint8_t)(preambleLen & 0xFF), hdrType, payloadLen, crcType, invertIQ};
     279           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, 6));
     280             : }
     281             : 
     282           0 : int16_t SX126x::setPacketParamsFSK(uint16_t preambleLen, uint8_t preambleDetectorLen, uint8_t crcType, uint8_t syncWordLen, uint8_t addrCmp, uint8_t whiten, uint8_t packType, uint8_t payloadLen) {
     283           0 :   const uint8_t data[9] = {(uint8_t)((preambleLen >> 8) & 0xFF), (uint8_t)(preambleLen & 0xFF),
     284             :                      preambleDetectorLen, syncWordLen, addrCmp,
     285           0 :                      packType, payloadLen, crcType, whiten};
     286           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, 9));
     287             : }
     288             : 
     289           0 : int16_t SX126x::setPacketParamsBPSK(uint8_t payloadLen, uint16_t rampUpDelay, uint16_t rampDownDelay, uint16_t payloadLenBits) {
     290             :   const uint8_t data[] = { payloadLen,
     291           0 :     (uint8_t)((rampUpDelay >> 8) & 0xFF), (uint8_t)(rampUpDelay & 0xFF),
     292           0 :     (uint8_t)((rampDownDelay >> 8) & 0xFF), (uint8_t)(rampDownDelay & 0xFF),
     293           0 :     (uint8_t)((payloadLenBits >> 8) & 0xFF), (uint8_t)(payloadLenBits & 0xFF)
     294           0 :   };
     295             :   
     296             :   // this one is a bit different, it seems to be split into command transaction and then a register write
     297           0 :   int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, sizeof(uint8_t));
     298           0 :   RADIOLIB_ASSERT(state);
     299           0 :   return(this->writeRegister(RADIOLIB_SX126X_REG_BPSK_PACKET_PARAMS, &data[1], sizeof(data) - sizeof(uint8_t)));
     300             : }
     301             : 
     302           3 : int16_t SX126x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) {
     303           3 :   const uint8_t data[2] = {txBaseAddress, rxBaseAddress};
     304           6 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_BUFFER_BASE_ADDRESS, data, 2));
     305             : }
     306             : 
     307           0 : int16_t SX126x::setRegulatorMode(uint8_t mode) {
     308           0 :   const uint8_t data[1] = {mode};
     309           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_REGULATOR_MODE, data, 1));
     310             : }
     311             : 
     312           3 : uint8_t SX126x::getStatus() {
     313           3 :   const uint8_t cmd = RADIOLIB_SX126X_CMD_GET_STATUS;
     314           3 :   uint8_t data = 0;
     315             : 
     316             :   // temporarily set status width to 0, since in this case, status byte is the thing we are trying to read
     317           3 :   this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_0;
     318           3 :   (void)this->mod->SPItransferStream(&cmd, 1, false, NULL, &data, 1, true);
     319             : 
     320             :   // restore and return the value
     321           3 :   this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8;
     322           3 :   return(data);
     323             : }
     324             : 
     325           3 : uint32_t SX126x::getPacketStatus() {
     326           3 :   uint8_t data[3] = {0, 0, 0};
     327           3 :   this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_PACKET_STATUS, data, 3);
     328           3 :   return((((uint32_t)data[0]) << 16) | (((uint32_t)data[1]) << 8) | (uint32_t)data[2]);
     329             : }
     330             : 
     331           0 : uint16_t SX126x::getDeviceErrors() {
     332           0 :   uint8_t data[2] = {0, 0};
     333           0 :   this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_DEVICE_ERRORS, data, 2);
     334           0 :   uint16_t opError = (((uint16_t)data[0] & 0xFF) << 8) | ((uint16_t)data[1]);
     335           0 :   return(opError);
     336             : }
     337             : 
     338           0 : int16_t SX126x::clearDeviceErrors() {
     339           0 :   const uint8_t data[2] = {RADIOLIB_SX126X_CMD_NOP, RADIOLIB_SX126X_CMD_NOP};
     340           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CLEAR_DEVICE_ERRORS, data, 2));
     341             : }
     342             : 
     343             : #endif

Generated by: LCOV version 1.14