acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

Si443x.cpp (26036B)

      1 #include "Si443x.h"
      2 #if !defined(RADIOLIB_EXCLUDE_SI443X)
      3 
      4 Si443x::Si443x(Module* mod) : PhysicalLayer(RADIOLIB_SI443X_FREQUENCY_STEP_SIZE, RADIOLIB_SI443X_MAX_PACKET_LENGTH) {
      5   _mod = mod;
      6 }
      7 
      8 Module* Si443x::getMod() {
      9   return(_mod);
     10 }
     11 
     12 int16_t Si443x::begin(float br, float freqDev, float rxBw, uint8_t preambleLen) {
     13   // set module properties
     14   _mod->init();
     15   _mod->pinMode(_mod->getIrq(), INPUT);
     16   _mod->pinMode(_mod->getRst(), OUTPUT);
     17   _mod->digitalWrite(_mod->getRst(), LOW);
     18 
     19   // try to find the Si443x chip
     20   if(!Si443x::findChip()) {
     21     RADIOLIB_DEBUG_PRINTLN(F("No Si443x found!"));
     22     _mod->term();
     23     return(RADIOLIB_ERR_CHIP_NOT_FOUND);
     24   } else {
     25     RADIOLIB_DEBUG_PRINTLN(F("M\tSi443x"));
     26   }
     27 
     28   // reset the device
     29   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_SOFTWARE_RESET);
     30 
     31   // clear POR interrupt
     32   clearIRQFlags();
     33 
     34   // configure settings not accessible by API
     35   int16_t state = config();
     36   RADIOLIB_ASSERT(state);
     37 
     38   // configure publicly accessible settings
     39   state = setBitRate(br);
     40   RADIOLIB_ASSERT(state);
     41 
     42   state = setFrequencyDeviation(freqDev);
     43   RADIOLIB_ASSERT(state);
     44 
     45   state = setRxBandwidth(rxBw);
     46   RADIOLIB_ASSERT(state);
     47 
     48   state = setPreambleLength(preambleLen);
     49   RADIOLIB_ASSERT(state);
     50 
     51   uint8_t syncWord[] = {0x12, 0xAD};
     52   state = setSyncWord(syncWord, sizeof(syncWord));
     53   RADIOLIB_ASSERT(state);
     54 
     55   state = packetMode();
     56   RADIOLIB_ASSERT(state);
     57 
     58   state = setDataShaping(0);
     59   RADIOLIB_ASSERT(state);
     60 
     61   state = setEncoding(0);
     62   RADIOLIB_ASSERT(state);
     63 
     64   state = variablePacketLengthMode();
     65 
     66   return(state);
     67 }
     68 
     69 void Si443x::reset() {
     70   _mod->pinMode(_mod->getRst(), OUTPUT);
     71   _mod->digitalWrite(_mod->getRst(), HIGH);
     72   _mod->delay(1);
     73   _mod->digitalWrite(_mod->getRst(), LOW);
     74   _mod->delay(100);
     75 }
     76 
     77 int16_t Si443x::transmit(uint8_t* data, size_t len, uint8_t addr) {
     78   // calculate timeout (5ms + 500 % of expected time-on-air)
     79   uint32_t timeout = 5000000 + (uint32_t)((((float)(len * 8)) / (_br * 1000.0)) * 5000000.0);
     80 
     81   // start transmission
     82   int16_t state = startTransmit(data, len, addr);
     83   RADIOLIB_ASSERT(state);
     84 
     85   // wait for transmission end or timeout
     86   uint32_t start = _mod->micros();
     87   while(_mod->digitalRead(_mod->getIrq())) {
     88     _mod->yield();
     89     if(_mod->micros() - start > timeout) {
     90       standby();
     91       clearIRQFlags();
     92       return(RADIOLIB_ERR_TX_TIMEOUT);
     93     }
     94   }
     95 
     96   // clear interrupt flags
     97   clearIRQFlags();
     98 
     99   // set mode to standby
    100   standby();
    101 
    102   return(state);
    103 }
    104 
    105 int16_t Si443x::receive(uint8_t* data, size_t len) {
    106   // calculate timeout (500 ms + 400 full 64-byte packets at current bit rate)
    107   uint32_t timeout = 500000 + (1.0/(_br*1000.0))*(RADIOLIB_SI443X_MAX_PACKET_LENGTH*400.0);
    108 
    109   // start reception
    110   int16_t state = startReceive();
    111   RADIOLIB_ASSERT(state);
    112 
    113   // wait for packet reception or timeout
    114   uint32_t start = _mod->micros();
    115   while(_mod->digitalRead(_mod->getIrq())) {
    116     if(_mod->micros() - start > timeout) {
    117       standby();
    118       clearIRQFlags();
    119       return(RADIOLIB_ERR_RX_TIMEOUT);
    120     }
    121   }
    122 
    123   // read packet data
    124   return(readData(data, len));
    125 }
    126 
    127 int16_t Si443x::sleep() {
    128   // set RF switch (if present)
    129   _mod->setRfSwitchState(LOW, LOW);
    130 
    131   // disable wakeup timer interrupt
    132   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_1, 0x00);
    133   RADIOLIB_ASSERT(state);
    134   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_2, 0x00);
    135   RADIOLIB_ASSERT(state);
    136 
    137   // enable wakeup timer to set mode to sleep
    138   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_ENABLE_WAKEUP_TIMER);
    139 
    140   return(state);
    141 }
    142 
    143 int16_t Si443x::standby() {
    144   // set RF switch (if present)
    145   _mod->setRfSwitchState(LOW, LOW);
    146 
    147   return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_XTAL_ON, 7, 0, 10));
    148 }
    149 
    150 int16_t Si443x::transmitDirect(uint32_t frf) {
    151   // set RF switch (if present)
    152   _mod->setRfSwitchState(LOW, HIGH);
    153 
    154   // user requested to start transmitting immediately (required for RTTY)
    155   if(frf != 0) {
    156     // convert the 24-bit frequency to the format accepted by the module
    157     /// \todo integers only
    158     float newFreq = frf / 6400.0;
    159 
    160     // check high/low band
    161     uint8_t bandSelect = RADIOLIB_SI443X_BAND_SELECT_LOW;
    162     uint8_t freqBand = (newFreq / 10) - 24;
    163     if(newFreq >= 480.0) {
    164       bandSelect = RADIOLIB_SI443X_BAND_SELECT_HIGH;
    165       freqBand = (newFreq / 20) - 24;
    166     }
    167 
    168     // calculate register values
    169     uint16_t freqCarrier = ((newFreq / (10 * ((bandSelect >> 5) + 1))) - freqBand - 24) * (uint32_t)64000;
    170 
    171     // update registers
    172     _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_FREQUENCY_BAND_SELECT, RADIOLIB_SI443X_SIDE_BAND_SELECT_LOW | bandSelect | freqBand);
    173     _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_NOM_CARRIER_FREQUENCY_1, (uint8_t)((freqCarrier & 0xFF00) >> 8));
    174     _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_NOM_CARRIER_FREQUENCY_0, (uint8_t)(freqCarrier & 0xFF));
    175 
    176     // start direct transmission
    177     directMode();
    178     _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_TX_ON | RADIOLIB_SI443X_XTAL_ON);
    179 
    180     return(RADIOLIB_ERR_NONE);
    181   }
    182 
    183   // activate direct mode
    184   int16_t state = directMode();
    185   RADIOLIB_ASSERT(state);
    186 
    187   // start transmitting
    188   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_TX_ON | RADIOLIB_SI443X_XTAL_ON);
    189   return(state);
    190 }
    191 
    192 int16_t Si443x::receiveDirect() {
    193   // set RF switch (if present)
    194   _mod->setRfSwitchState(HIGH, LOW);
    195 
    196   // activate direct mode
    197   int16_t state = directMode();
    198   RADIOLIB_ASSERT(state);
    199 
    200   // start receiving
    201   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_RX_ON | RADIOLIB_SI443X_XTAL_ON);
    202   return(state);
    203 }
    204 
    205 int16_t Si443x::packetMode() {
    206   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_2, RADIOLIB_SI443X_MODULATION_FSK, 1, 0);
    207   RADIOLIB_ASSERT(state);
    208 
    209   return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_2, RADIOLIB_SI443X_TX_DATA_SOURCE_FIFO, 5, 4));
    210 }
    211 
    212 void Si443x::setIrqAction(void (*func)(void)) {
    213   _mod->attachInterrupt(RADIOLIB_DIGITAL_PIN_TO_INTERRUPT(_mod->getIrq()), func, FALLING);
    214 }
    215 
    216 void Si443x::clearIrqAction() {
    217   _mod->detachInterrupt(RADIOLIB_DIGITAL_PIN_TO_INTERRUPT(_mod->getIrq()));
    218 }
    219 
    220 int16_t Si443x::startTransmit(uint8_t* data, size_t len, uint8_t addr) {
    221   // check packet length
    222   if(len > RADIOLIB_SI443X_MAX_PACKET_LENGTH) {
    223     return(RADIOLIB_ERR_PACKET_TOO_LONG);
    224   }
    225 
    226   // set mode to standby
    227   int16_t state = standby();
    228   RADIOLIB_ASSERT(state);
    229 
    230   // clear Tx FIFO
    231   _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_2, RADIOLIB_SI443X_TX_FIFO_RESET, 0, 0);
    232   _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_2, RADIOLIB_SI443X_TX_FIFO_CLEAR, 0, 0);
    233 
    234   // clear interrupt flags
    235   clearIRQFlags();
    236 
    237   // set packet length
    238   if (_packetLengthConfig == RADIOLIB_SI443X_FIXED_PACKET_LENGTH_OFF) {
    239     _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_TRANSMIT_PACKET_LENGTH, len);
    240   }
    241 
    242   /// \todo use header as address field?
    243   (void)addr;
    244 
    245   // write packet to FIFO
    246   _mod->SPIwriteRegisterBurst(RADIOLIB_SI443X_REG_FIFO_ACCESS, data, len);
    247 
    248   // set RF switch (if present)
    249   _mod->setRfSwitchState(LOW, HIGH);
    250 
    251   // set interrupt mapping
    252   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_1, RADIOLIB_SI443X_PACKET_SENT_ENABLED);
    253   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_2, 0x00);
    254 
    255   // set mode to transmit
    256   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_TX_ON | RADIOLIB_SI443X_XTAL_ON);
    257 
    258   return(state);
    259 }
    260 
    261 int16_t Si443x::startReceive() {
    262   // set mode to standby
    263   int16_t state = standby();
    264   RADIOLIB_ASSERT(state);
    265 
    266   // clear Rx FIFO
    267   _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_2, RADIOLIB_SI443X_RX_FIFO_RESET, 1, 1);
    268   _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_2, RADIOLIB_SI443X_RX_FIFO_CLEAR, 1, 1);
    269 
    270   // clear interrupt flags
    271   clearIRQFlags();
    272 
    273   // set RF switch (if present)
    274   _mod->setRfSwitchState(HIGH, LOW);
    275 
    276   // set interrupt mapping
    277   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_1, RADIOLIB_SI443X_VALID_PACKET_RECEIVED_ENABLED | RADIOLIB_SI443X_CRC_ERROR_ENABLED);
    278   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_2, 0x00);
    279 
    280   // set mode to receive
    281   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_RX_ON | RADIOLIB_SI443X_XTAL_ON);
    282 
    283   return(state);
    284 }
    285 
    286 int16_t Si443x::readData(uint8_t* data, size_t len) {
    287   // clear interrupt flags
    288   clearIRQFlags();
    289 
    290   // get packet length
    291   size_t length = getPacketLength();
    292   size_t dumpLen = 0;
    293   if((len != 0) && (len < length)) {
    294     // user requested less data than we got, only return what was requested
    295     dumpLen = length - len;
    296     length = len;
    297   }
    298 
    299   // read packet data
    300   _mod->SPIreadRegisterBurst(RADIOLIB_SI443X_REG_FIFO_ACCESS, length, data);
    301 
    302   // dump the bytes that weren't requested
    303   if(dumpLen != 0) {
    304     clearFIFO(dumpLen);
    305   }
    306 
    307   // clear internal flag so getPacketLength can return the new packet length
    308   _packetLengthQueried = false;
    309 
    310   // set mode to standby
    311   int16_t state = standby();
    312   RADIOLIB_ASSERT(state);
    313 
    314   // clear interrupt flags
    315   clearIRQFlags();
    316 
    317   return(RADIOLIB_ERR_NONE);
    318 }
    319 
    320 int16_t Si443x::setBitRate(float br) {
    321   RADIOLIB_CHECK_RANGE(br, 0.123, 256.0, RADIOLIB_ERR_INVALID_BIT_RATE);
    322 
    323   // check high data rate
    324   uint8_t dataRateMode = RADIOLIB_SI443X_LOW_DATA_RATE_MODE;
    325   uint8_t exp = 21;
    326   if(br >= 30.0) {
    327     // bit rate above 30 kbps
    328     dataRateMode = RADIOLIB_SI443X_HIGH_DATA_RATE_MODE;
    329     exp = 16;
    330   }
    331 
    332   // calculate raw data rate value
    333   uint16_t txDr = (br * ((uint32_t)1 << exp)) / 1000.0;
    334 
    335   // update registers
    336   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, dataRateMode, 5, 5);
    337   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_TX_DATA_RATE_1, (uint8_t)((txDr & 0xFF00) >> 8));
    338   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_TX_DATA_RATE_0, (uint8_t)(txDr & 0xFF));
    339 
    340   if(state == RADIOLIB_ERR_NONE) {
    341     _br = br;
    342   }
    343   RADIOLIB_ASSERT(state);
    344 
    345   // update clock recovery
    346   state = updateClockRecovery();
    347 
    348   return(state);
    349 }
    350 
    351 int16_t Si443x::setFrequencyDeviation(float freqDev) {
    352   // set frequency deviation to lowest available setting (required for digimodes)
    353   float newFreqDev = freqDev;
    354   if(freqDev < 0.0) {
    355     newFreqDev = 0.625;
    356   }
    357 
    358   RADIOLIB_CHECK_RANGE(newFreqDev, 0.625, 320.0, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
    359 
    360   // calculate raw frequency deviation value
    361   uint16_t fdev = (uint16_t)(newFreqDev / 0.625);
    362 
    363   // update registers
    364   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_2, (uint8_t)((fdev & 0x0100) >> 6), 2, 2);
    365   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_FREQUENCY_DEVIATION, (uint8_t)(fdev & 0xFF));
    366 
    367   if(state == RADIOLIB_ERR_NONE) {
    368     _freqDev = newFreqDev;
    369   }
    370 
    371   return(state);
    372 }
    373 
    374 int16_t Si443x::setRxBandwidth(float rxBw) {
    375   RADIOLIB_CHECK_RANGE(rxBw, 2.6, 620.7, RADIOLIB_ERR_INVALID_RX_BANDWIDTH);
    376 
    377   // decide which approximation to use for decimation rate and filter tap calculation
    378   uint8_t bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_OFF;
    379   uint8_t decRate = RADIOLIB_SI443X_IF_FILTER_DEC_RATE;
    380   uint8_t filterSet = RADIOLIB_SI443X_IF_FILTER_COEFF_SET;
    381 
    382   // this is the "well-behaved" section - can be linearly approximated
    383   if((rxBw >= 2.6) && (rxBw <= 4.5)) {
    384     decRate = 5;
    385     filterSet = ((rxBw - 2.1429)/0.3250 + 0.5);
    386   } else if((rxBw > 4.5) && (rxBw <= 8.8)) {
    387     decRate = 4;
    388     filterSet = ((rxBw - 3.9857)/0.6643 + 0.5);
    389   } else if((rxBw > 8.8) && (rxBw <= 17.5)) {
    390     decRate = 3;
    391     filterSet = ((rxBw - 7.6714)/1.3536 + 0.5);
    392   } else if((rxBw > 17.5) && (rxBw <= 34.7)) {
    393     decRate = 2;
    394     filterSet = ((rxBw - 15.2000)/2.6893 + 0.5);
    395   } else if((rxBw > 34.7) && (rxBw <= 69.2)) {
    396     decRate = 1;
    397     filterSet = ((rxBw - 30.2430)/5.3679 + 0.5);
    398   } else if((rxBw > 69.2) && (rxBw <= 137.9)) {
    399     decRate = 0;
    400     filterSet = ((rxBw - 60.286)/10.7000 + 0.5);
    401 
    402   // this is the "Lord help thee who tread 'ere" section - no way to approximate this mess
    403   /// \todo float tolerance equality as macro?
    404   } else if(fabs(rxBw - 142.8) <= 0.001) {
    405     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    406     decRate = 1;
    407     filterSet = 4;
    408   } else if(fabs(rxBw - 167.8) <= 0.001) {
    409     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    410     decRate = 1;
    411     filterSet = 5;
    412   } else if(fabs(rxBw - 181.1) <= 0.001) {
    413     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    414     decRate = 1;
    415     filterSet = 6;
    416   } else if(fabs(rxBw - 191.5) <= 0.001) {
    417     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    418     decRate = 0;
    419     filterSet = 15;
    420   } else if(fabs(rxBw - 225.1) <= 0.001) {
    421     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    422     decRate = 0;
    423     filterSet = 1;
    424   } else if(fabs(rxBw - 248.8) <= 0.001) {
    425     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    426     decRate = 0;
    427     filterSet = 2;
    428   } else if(fabs(rxBw - 269.3) <= 0.001) {
    429     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    430     decRate = 0;
    431     filterSet = 3;
    432   } else if(fabs(rxBw - 284.8) <= 0.001) {
    433     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    434     decRate = 0;
    435     filterSet = 4;
    436   } else if(fabs(rxBw -335.5) <= 0.001) {
    437     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    438     decRate = 0;
    439     filterSet = 8;
    440   } else if(fabs(rxBw - 391.8) <= 0.001) {
    441     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    442     decRate = 0;
    443     filterSet = 9;
    444   } else if(fabs(rxBw - 420.2) <= 0.001) {
    445     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    446     decRate = 0;
    447     filterSet = 10;
    448   } else if(fabs(rxBw - 468.4) <= 0.001) {
    449     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    450     decRate = 0;
    451     filterSet = 11;
    452   } else if(fabs(rxBw - 518.8) <= 0.001) {
    453     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    454     decRate = 0;
    455     filterSet = 12;
    456   } else if(fabs(rxBw - 577.0) <= 0.001) {
    457     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    458     decRate = 0;
    459     filterSet = 13;
    460   } else if(fabs(rxBw - 620.7) <= 0.001) {
    461     bypass = RADIOLIB_SI443X_BYPASS_DEC_BY_3_ON;
    462     decRate = 0;
    463     filterSet = 14;
    464   } else {
    465     return(RADIOLIB_ERR_INVALID_RX_BANDWIDTH);
    466   }
    467 
    468   // shift decimation rate bits
    469   decRate <<= 4;
    470 
    471   // update register
    472   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_IF_FILTER_BANDWIDTH, bypass | decRate | filterSet);
    473   RADIOLIB_ASSERT(state);
    474 
    475   // update clock recovery
    476   state = updateClockRecovery();
    477 
    478   return(state);
    479 }
    480 
    481 int16_t Si443x::setSyncWord(uint8_t* syncWord, size_t len) {
    482   RADIOLIB_CHECK_RANGE(len, 1, 4, RADIOLIB_ERR_INVALID_SYNC_WORD);
    483 
    484   // set mode to standby
    485   int16_t state = standby();
    486   RADIOLIB_ASSERT(state);
    487 
    488   // set sync word length
    489   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_HEADER_CONTROL_2, (uint8_t)(len - 1) << 1, 2, 1);
    490   RADIOLIB_ASSERT(state);
    491 
    492   // set sync word bytes
    493   _mod->SPIwriteRegisterBurst(RADIOLIB_SI443X_REG_SYNC_WORD_3, syncWord, len);
    494 
    495   return(state);
    496 }
    497 
    498 int16_t Si443x::setPreambleLength(uint8_t preambleLen) {
    499   // Si443x configures preamble length in 4-bit nibbles
    500   if(preambleLen % 4 != 0) {
    501     return(RADIOLIB_ERR_INVALID_PREAMBLE_LENGTH);
    502   }
    503 
    504   // set default preamble length
    505   uint8_t preLenNibbles = preambleLen / 4;
    506   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_PREAMBLE_LENGTH, preLenNibbles);
    507   RADIOLIB_ASSERT(state);
    508 
    509   // set default preamble detection threshold to 5/8 of preamble length (in units of 4 bits)
    510   uint8_t preThreshold = 5*preLenNibbles / 8;
    511   return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_PREAMBLE_DET_CONTROL, preThreshold << 3, 7, 3));
    512 }
    513 
    514 size_t Si443x::getPacketLength(bool update) {
    515   if(!_packetLengthQueried && update) {
    516     if (_packetLengthConfig == RADIOLIB_SI443X_FIXED_PACKET_LENGTH_ON) {
    517       _packetLength = _mod->SPIreadRegister(RADIOLIB_SI443X_REG_TRANSMIT_PACKET_LENGTH);
    518     } else {
    519       _packetLength = _mod->SPIreadRegister(RADIOLIB_SI443X_REG_RECEIVED_PACKET_LENGTH);
    520     }
    521     _packetLengthQueried = true;
    522   }
    523 
    524   return(_packetLength);
    525 }
    526 
    527 int16_t Si443x::setEncoding(uint8_t encoding) {
    528   // set mode to standby
    529   int16_t state = standby();
    530   RADIOLIB_ASSERT(state);
    531 
    532   // set encoding
    533   /// \todo - add inverted Manchester?
    534   switch(encoding) {
    535     case RADIOLIB_ENCODING_NRZ:
    536       return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_OFF, 2, 0));
    537     case RADIOLIB_ENCODING_MANCHESTER:
    538       return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_ON | RADIOLIB_SI443X_WHITENING_OFF, 2, 0));
    539     case RADIOLIB_ENCODING_WHITENING:
    540       return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_ON, 2, 0));
    541     default:
    542       return(RADIOLIB_ERR_INVALID_ENCODING);
    543   }
    544 }
    545 
    546 int16_t Si443x::setDataShaping(uint8_t sh) {
    547   // set mode to standby
    548   int16_t state = standby();
    549   RADIOLIB_ASSERT(state);
    550 
    551   // set data shaping
    552   switch(sh) {
    553     case RADIOLIB_SHAPING_NONE:
    554       return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_OFF, 2, 0));
    555     case RADIOLIB_SHAPING_0_3:
    556     case RADIOLIB_SHAPING_0_5:
    557     case RADIOLIB_SHAPING_1_0:
    558       /// \todo implement fiter configuration - docs claim this should be possible, but seems undocumented
    559       return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_ON, 2, 0));
    560     default:
    561       return(RADIOLIB_ERR_INVALID_ENCODING);
    562   }
    563 }
    564 
    565 void Si443x::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) {
    566   _mod->setRfSwitchPins(rxEn, txEn);
    567 }
    568 
    569 uint8_t Si443x::randomByte() {
    570   // set mode to Rx
    571   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_RX_ON | RADIOLIB_SI443X_XTAL_ON);
    572 
    573   // wait a bit for the RSSI reading to stabilise
    574   _mod->delay(10);
    575 
    576   // read RSSI value 8 times, always keep just the least significant bit
    577   uint8_t randByte = 0x00;
    578   for(uint8_t i = 0; i < 8; i++) {
    579     randByte |= ((_mod->SPIreadRegister(RADIOLIB_SI443X_REG_RSSI) & 0x01) << i);
    580   }
    581 
    582   // set mode to standby
    583   standby();
    584 
    585   return(randByte);
    586 }
    587 
    588 int16_t Si443x::getChipVersion() {
    589   return(_mod->SPIgetRegValue(RADIOLIB_SI443X_REG_DEVICE_VERSION));
    590 }
    591 
    592 #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE)
    593 void Si443x::setDirectAction(void (*func)(void)) {
    594   setIrqAction(func);
    595 }
    596 
    597 void Si443x::readBit(RADIOLIB_PIN_TYPE pin) {
    598   updateDirectBuffer((uint8_t)digitalRead(pin));
    599 }
    600 #endif
    601 
    602 int16_t Si443x::fixedPacketLengthMode(uint8_t len) {
    603   return(Si443x::setPacketMode(RADIOLIB_SI443X_FIXED_PACKET_LENGTH_ON, len));
    604 }
    605 
    606 int16_t Si443x::variablePacketLengthMode(uint8_t maxLen) {
    607   return(Si443x::setPacketMode(RADIOLIB_SI443X_FIXED_PACKET_LENGTH_OFF, maxLen));
    608 }
    609 
    610 int16_t Si443x::setFrequencyRaw(float newFreq) {
    611   // set mode to standby
    612   int16_t state = standby();
    613   RADIOLIB_ASSERT(state);
    614 
    615   // check high/low band
    616   uint8_t bandSelect = RADIOLIB_SI443X_BAND_SELECT_LOW;
    617   uint8_t freqBand = (newFreq / 10) - 24;
    618   uint8_t afcLimiter = 80;
    619   _freq = newFreq;
    620   if(newFreq >= 480.0) {
    621     bandSelect = RADIOLIB_SI443X_BAND_SELECT_HIGH;
    622     freqBand = (newFreq / 20) - 24;
    623     afcLimiter = 40;
    624   }
    625 
    626   // calculate register values
    627   uint16_t freqCarrier = ((newFreq / (10 * ((bandSelect >> 5) + 1))) - freqBand - 24) * (uint32_t)64000;
    628 
    629   // update registers
    630   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_FREQUENCY_BAND_SELECT, bandSelect | freqBand, 5, 0);
    631   state |= _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_NOM_CARRIER_FREQUENCY_1, (uint8_t)((freqCarrier & 0xFF00) >> 8));
    632   state |= _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_NOM_CARRIER_FREQUENCY_0, (uint8_t)(freqCarrier & 0xFF));
    633   state |= _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_AFC_LIMITER, afcLimiter);
    634 
    635   return(state);
    636 }
    637 
    638 int16_t Si443x::setPacketMode(uint8_t mode, uint8_t len) {
    639   // check packet length
    640   if (len > RADIOLIB_SI443X_MAX_PACKET_LENGTH) {
    641     return(RADIOLIB_ERR_PACKET_TOO_LONG);
    642   }
    643 
    644   // set to fixed packet length
    645   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_HEADER_CONTROL_2, mode, 3, 3);
    646   RADIOLIB_ASSERT(state);
    647 
    648   // set length to register
    649   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_TRANSMIT_PACKET_LENGTH, len);
    650   RADIOLIB_ASSERT(state);
    651 
    652   // update cached value
    653   _packetLengthConfig = mode;
    654   return(state);
    655 }
    656 
    657 bool Si443x::findChip() {
    658   uint8_t i = 0;
    659   bool flagFound = false;
    660   while((i < 10) && !flagFound) {
    661     // reset the module
    662     reset();
    663 
    664     // check version register
    665     uint8_t version = _mod->SPIreadRegister(RADIOLIB_SI443X_REG_DEVICE_VERSION);
    666     if(version == RADIOLIB_SI443X_DEVICE_VERSION) {
    667       flagFound = true;
    668     } else {
    669       #if defined(RADIOLIB_DEBUG)
    670         RADIOLIB_DEBUG_PRINT(F("Si443x not found! ("));
    671         RADIOLIB_DEBUG_PRINT(i + 1);
    672         RADIOLIB_DEBUG_PRINT(F(" of 10 tries) RADIOLIB_SI443X_REG_DEVICE_VERSION == "));
    673 
    674         char buffHex[5];
    675         sprintf(buffHex, "0x%02X", version);
    676         RADIOLIB_DEBUG_PRINT(buffHex);
    677         RADIOLIB_DEBUG_PRINT(F(", expected 0x00"));
    678         RADIOLIB_DEBUG_PRINTLN(RADIOLIB_SI443X_DEVICE_VERSION, HEX);
    679       #endif
    680       _mod->delay(10);
    681       i++;
    682     }
    683   }
    684 
    685   return(flagFound);
    686 }
    687 
    688 void Si443x::clearIRQFlags() {
    689   uint8_t buff[2];
    690   _mod->SPIreadRegisterBurst(RADIOLIB_SI443X_REG_INTERRUPT_STATUS_1, 2, buff);
    691 }
    692 
    693 void Si443x::clearFIFO(size_t count) {
    694   while(count) {
    695     _mod->SPIreadRegister(RADIOLIB_SI443X_REG_FIFO_ACCESS);
    696     count--;
    697   }
    698 }
    699 
    700 int16_t Si443x::config() {
    701   // set mode to standby
    702   int16_t state = standby();
    703   RADIOLIB_ASSERT(state);
    704 
    705   // disable POR and chip ready interrupts
    706   _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_2, 0x00);
    707 
    708   // enable AGC
    709   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_AGC_OVERRIDE_1, RADIOLIB_SI443X_AGC_GAIN_INCREASE_ON | RADIOLIB_SI443X_AGC_ON, 6, 5);
    710   RADIOLIB_ASSERT(state);
    711 
    712   // disable packet header
    713   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_HEADER_CONTROL_2, RADIOLIB_SI443X_SYNC_WORD_TIMEOUT_OFF | RADIOLIB_SI443X_HEADER_LENGTH_HEADER_NONE, 7, 4);
    714   RADIOLIB_ASSERT(state);
    715 
    716   // set antenna switching
    717   _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_GPIO0_CONFIG, RADIOLIB_SI443X_GPIOX_TX_STATE_OUT, 4, 0);
    718   _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_GPIO1_CONFIG, RADIOLIB_SI443X_GPIOX_RX_STATE_OUT, 4, 0);
    719 
    720   // disable packet header checking
    721   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_HEADER_CONTROL_1, RADIOLIB_SI443X_BROADCAST_ADDR_CHECK_NONE | RADIOLIB_SI443X_RECEIVED_HEADER_CHECK_NONE);
    722   RADIOLIB_ASSERT(state);
    723 
    724   return(state);
    725 }
    726 
    727 int16_t Si443x::updateClockRecovery() {
    728   // get the parameters
    729   uint8_t bypass = _mod->SPIgetRegValue(RADIOLIB_SI443X_REG_IF_FILTER_BANDWIDTH, 7, 7) >> 7;
    730   uint8_t decRate = _mod->SPIgetRegValue(RADIOLIB_SI443X_REG_IF_FILTER_BANDWIDTH, 6, 4) >> 4;
    731   uint8_t manch = _mod->SPIgetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, 1, 1) >> 1;
    732 
    733   // calculate oversampling ratio, NCO offset and clock recovery gain
    734   int8_t ndecExp = (int8_t)decRate - 3;
    735   float ndec = 0;
    736   if(ndecExp > 0) {
    737     ndec = (uint16_t)1 << ndecExp;
    738   } else {
    739     ndecExp *= -1;
    740     ndec = 1.0/(float)((uint16_t)1 << ndecExp);
    741   }
    742   float rxOsr = ((float)(500 * (1 + 2*bypass))) / (ndec * _br * ((float)(1 + manch)));
    743   uint32_t ncoOff = (_br * (1 + manch) * ((uint32_t)(1) << (20 + decRate))) / (500 * (1 + 2*bypass));
    744   uint16_t crGain = 2 + (((float)(65536.0 * (1 + manch)) * _br) / (rxOsr * (_freqDev / 0.625)));
    745   uint16_t rxOsr_fixed = (uint16_t)rxOsr;
    746 
    747   // print that whole mess
    748   RADIOLIB_DEBUG_PRINTLN(bypass, HEX);
    749   RADIOLIB_DEBUG_PRINTLN(decRate, HEX);
    750   RADIOLIB_DEBUG_PRINTLN(manch, HEX);
    751   RADIOLIB_DEBUG_PRINT(rxOsr, 3);
    752   RADIOLIB_DEBUG_PRINT('\t');
    753   RADIOLIB_DEBUG_PRINT(rxOsr_fixed);
    754   RADIOLIB_DEBUG_PRINT('\t');
    755   RADIOLIB_DEBUG_PRINTLN(rxOsr_fixed, HEX);
    756   RADIOLIB_DEBUG_PRINT(ncoOff);
    757   RADIOLIB_DEBUG_PRINT('\t');
    758   RADIOLIB_DEBUG_PRINTLN(ncoOff, HEX);
    759   RADIOLIB_DEBUG_PRINT(crGain);
    760   RADIOLIB_DEBUG_PRINT('\t');
    761   RADIOLIB_DEBUG_PRINTLN(crGain, HEX);
    762 
    763   // update oversampling ratio
    764   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OFFSET_2, (uint8_t)((rxOsr_fixed & 0x0700) >> 3), 7, 5);
    765   RADIOLIB_ASSERT(state);
    766   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OVERSAMP_RATIO, (uint8_t)(rxOsr_fixed & 0x00FF));
    767   RADIOLIB_ASSERT(state);
    768 
    769   // update NCO offset
    770   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OFFSET_2, (uint8_t)((ncoOff & 0x0F0000) >> 16), 3, 0);
    771   RADIOLIB_ASSERT(state);
    772   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OFFSET_1, (uint8_t)((ncoOff & 0x00FF00) >> 8));
    773   RADIOLIB_ASSERT(state);
    774   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OFFSET_0, (uint8_t)(ncoOff & 0x0000FF));
    775   RADIOLIB_ASSERT(state);
    776 
    777   // update clock recovery loop gain
    778   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_1, (uint8_t)((crGain & 0x0700) >> 8), 2, 0);
    779   RADIOLIB_ASSERT(state);
    780   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_0, (uint8_t)(crGain & 0x00FF));
    781   RADIOLIB_ASSERT(state);
    782 
    783   return(state);
    784 }
    785 
    786 int16_t Si443x::directMode() {
    787   int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_2, RADIOLIB_SI443X_TX_DATA_SOURCE_GPIO, 5, 4);
    788   RADIOLIB_ASSERT(state);
    789 
    790   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_GPIO1_CONFIG, RADIOLIB_SI443X_GPIOX_TX_RX_DATA_CLK_OUT, 4, 0);
    791   RADIOLIB_ASSERT(state);
    792 
    793   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_GPIO2_CONFIG, RADIOLIB_SI443X_GPIOX_TX_DATA_IN, 4, 0);
    794   RADIOLIB_ASSERT(state);
    795 
    796   state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_2, RADIOLIB_SI443X_MODULATION_FSK, 1, 0);
    797   return(state);
    798 }
    799 
    800 #endif