Line data Source code
1 : #include "SX126x.h"
2 : #include <string.h>
3 : #include <math.h>
4 : #if !RADIOLIB_EXCLUDE_SX126X
5 :
6 18 : SX126x::SX126x(Module* mod) : PhysicalLayer() {
7 18 : this->freqStep = RADIOLIB_SX126X_FREQUENCY_STEP_SIZE;
8 18 : this->maxPacketLength = RADIOLIB_SX126X_MAX_PACKET_LENGTH;
9 18 : this->mod = mod;
10 18 : this->standbyXOSC = false;
11 18 : this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_SX126X_IRQ_TX_DONE;
12 18 : this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_SX126X_IRQ_RX_DONE;
13 18 : this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED;
14 18 : this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID;
15 18 : this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_SX126X_IRQ_HEADER_VALID;
16 18 : this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_SX126X_IRQ_HEADER_ERR;
17 18 : this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_SX126X_IRQ_CRC_ERR;
18 18 : this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_SX126X_IRQ_CAD_DONE;
19 18 : this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_SX126X_IRQ_CAD_DETECTED;
20 18 : this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_SX126X_IRQ_TIMEOUT;
21 18 : }
22 :
23 0 : int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength) {
24 : // BW in kHz and SF are required in order to calculate LDRO for setModulationParams
25 : // set the defaults, this will get overwritten later anyway
26 0 : this->bandwidthKhz = 500.0;
27 0 : this->spreadingFactor = 9;
28 :
29 : // initialize configuration variables (will be overwritten during public settings configuration)
30 0 : this->bandwidth = RADIOLIB_SX126X_LORA_BW_500_0; // initialized to 500 kHz, since lower values will interfere with LLCC68
31 0 : this->codingRate = RADIOLIB_SX126X_LORA_CR_4_7;
32 0 : this->ldrOptimize = 0x00;
33 0 : this->crcTypeLoRa = RADIOLIB_SX126X_LORA_CRC_ON;
34 0 : this->preambleLengthLoRa = preambleLength;
35 0 : this->tcxoDelay = 0;
36 0 : this->headerType = RADIOLIB_SX126X_LORA_HEADER_EXPLICIT;
37 0 : this->implicitLen = 0xFF;
38 :
39 : // set module properties and perform initial setup
40 0 : int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_LORA);
41 0 : RADIOLIB_ASSERT(state);
42 :
43 : // configure publicly accessible settings
44 0 : state = setCodingRate(cr);
45 0 : RADIOLIB_ASSERT(state);
46 :
47 0 : state = setSyncWord(syncWord);
48 0 : RADIOLIB_ASSERT(state);
49 :
50 0 : state = setPreambleLength(preambleLength);
51 0 : RADIOLIB_ASSERT(state);
52 :
53 : // set publicly accessible settings that are not a part of begin method
54 0 : state = setCurrentLimit(60.0);
55 0 : RADIOLIB_ASSERT(state);
56 :
57 0 : state = setDio2AsRfSwitch(true);
58 0 : RADIOLIB_ASSERT(state);
59 :
60 0 : state = setCRC(2);
61 0 : RADIOLIB_ASSERT(state);
62 :
63 0 : state = invertIQ(false);
64 0 : RADIOLIB_ASSERT(state);
65 :
66 0 : return(state);
67 : }
68 :
69 0 : int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleLength) {
70 : // initialize configuration variables (will be overwritten during public settings configuration)
71 0 : this->bitRate = 21333; // 48.0 kbps
72 0 : this->frequencyDev = 52428; // 50.0 kHz
73 0 : this->rxBandwidth = RADIOLIB_SX126X_GFSK_RX_BW_156_2;
74 0 : this->rxBandwidthKhz = 156.2;
75 0 : this->pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_5;
76 0 : this->crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV; // CCITT CRC configuration
77 0 : this->preambleLengthFSK = preambleLength;
78 :
79 : // set module properties and perform initial setup
80 0 : int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_GFSK);
81 0 : RADIOLIB_ASSERT(state);
82 :
83 : // configure publicly accessible settings
84 0 : state = setBitRate(br);
85 0 : RADIOLIB_ASSERT(state);
86 :
87 0 : state = setFrequencyDeviation(freqDev);
88 0 : RADIOLIB_ASSERT(state);
89 :
90 0 : state = setRxBandwidth(rxBw);
91 0 : RADIOLIB_ASSERT(state);
92 :
93 0 : state = setCurrentLimit(60.0);
94 0 : RADIOLIB_ASSERT(state);
95 :
96 0 : state = setPreambleLength(preambleLength);
97 0 : RADIOLIB_ASSERT(state);
98 :
99 : // set publicly accessible settings that are not a part of begin method
100 0 : uint8_t sync[] = {0x12, 0xAD};
101 0 : state = setSyncWord(sync, 2);
102 0 : RADIOLIB_ASSERT(state);
103 :
104 0 : state = setDataShaping(RADIOLIB_SHAPING_NONE);
105 0 : RADIOLIB_ASSERT(state);
106 :
107 0 : state = setEncoding(RADIOLIB_ENCODING_NRZ);
108 0 : RADIOLIB_ASSERT(state);
109 :
110 0 : state = variablePacketLengthMode(RADIOLIB_SX126X_MAX_PACKET_LENGTH);
111 0 : RADIOLIB_ASSERT(state);
112 :
113 0 : state = setCRC(2);
114 0 : RADIOLIB_ASSERT(state);
115 :
116 0 : state = setDio2AsRfSwitch(true);
117 0 : RADIOLIB_ASSERT(state);
118 :
119 0 : return(state);
120 : }
121 :
122 0 : int16_t SX126x::beginBPSK(float br) {
123 : // set module properties and perform initial setup
124 0 : int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_BPSK);
125 0 : RADIOLIB_ASSERT(state);
126 :
127 : // configure publicly accessible settings
128 0 : state = setBitRate(br);
129 0 : RADIOLIB_ASSERT(state);
130 :
131 : // set publicly accessible settings that are not a part of begin method
132 0 : state = setDio2AsRfSwitch(true);
133 0 : RADIOLIB_ASSERT(state);
134 :
135 0 : return(state);
136 : }
137 :
138 0 : int16_t SX126x::beginLRFHSS(uint8_t bw, uint8_t cr, bool narrowGrid) {
139 0 : this->lrFhssGridNonFcc = narrowGrid;
140 :
141 : // set module properties and perform initial setup
142 0 : int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS);
143 0 : RADIOLIB_ASSERT(state);
144 :
145 : // set publicly accessible settings that are not a part of begin method
146 0 : state = setCurrentLimit(60.0);
147 0 : RADIOLIB_ASSERT(state);
148 :
149 0 : state = setDio2AsRfSwitch(true);
150 0 : RADIOLIB_ASSERT(state);
151 :
152 : // set all packet params to 0 (packet engine is disabled in LR-FHSS mode)
153 0 : state = setPacketParamsFSK(0, 0, 0, 0, 0, 0, 0, 0);
154 0 : RADIOLIB_ASSERT(state);
155 :
156 : // set bit rate
157 0 : this->rxBandwidth = 0;
158 0 : this->frequencyDev = 0;
159 0 : this->pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_1;
160 0 : state = setBitRate(0.48828125f);
161 0 : RADIOLIB_ASSERT(state);
162 :
163 0 : return(setLrFhssConfig(bw, cr));
164 : }
165 :
166 0 : int16_t SX126x::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16_t hopSeqId) {
167 : // check and cache all parameters
168 0 : RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_SX126X_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_SX126X_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE);
169 0 : this->lrFhssCr = cr;
170 0 : RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_SX126X_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_SX126X_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH);
171 0 : this->lrFhssBw = bw;
172 0 : RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE);
173 0 : this->lrFhssHdrCount = hdrCount;
174 0 : RADIOLIB_CHECK_RANGE((int16_t)hopSeqId, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING);
175 0 : this->lrFhssHopSeqId = hopSeqId;
176 0 : return(RADIOLIB_ERR_NONE);
177 : }
178 :
179 0 : int16_t SX126x::reset(bool verify) {
180 : // run the reset sequence
181 0 : this->mod->hal->pinMode(this->mod->getRst(), this->mod->hal->GpioModeOutput);
182 0 : this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelLow);
183 0 : this->mod->hal->delay(1);
184 0 : this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelHigh);
185 :
186 : // return immediately when verification is disabled
187 0 : if(!verify) {
188 0 : return(RADIOLIB_ERR_NONE);
189 : }
190 :
191 : // set mode to standby - SX126x often refuses first few commands after reset
192 0 : RadioLibTime_t start = this->mod->hal->millis();
193 : while(true) {
194 : // try to set mode to standby
195 : // force usage of RC standby here - if this is being called during startup,
196 : // and user set standbyXOSC to true with TCXO, this would attempt to use the TCXO
197 0 : int16_t state = standby(RADIOLIB_SX126X_STANDBY_RC);
198 0 : if(state == RADIOLIB_ERR_NONE) {
199 : // standby command successful
200 0 : return(RADIOLIB_ERR_NONE);
201 : }
202 :
203 : // standby command failed, check timeout and try again
204 0 : if(this->mod->hal->millis() - start >= 1000) {
205 : // timed out, possibly incorrect wiring
206 0 : return(state);
207 : }
208 :
209 : // wait a bit to not spam the module
210 0 : this->mod->hal->delay(10);
211 0 : }
212 : }
213 :
214 3 : int16_t SX126x::transmit(const uint8_t* data, size_t len, uint8_t addr) {
215 : // set mode to standby
216 3 : int16_t state = standby();
217 3 : RADIOLIB_ASSERT(state);
218 :
219 : // check packet length
220 0 : if(this->codingRate > RADIOLIB_SX126X_LORA_CR_4_8) {
221 : // Long Interleaver needs at least 8 bytes
222 0 : if(len < 8) {
223 0 : return(RADIOLIB_ERR_PACKET_TOO_SHORT);
224 : }
225 :
226 : // Long Interleaver supports up to 253 bytes if CRC is enabled
227 0 : if(this->crcTypeLoRa == RADIOLIB_SX126X_LORA_CRC_ON && (len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 2)) {
228 0 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
229 : }
230 : }
231 0 : if(len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
232 0 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
233 : }
234 :
235 : // calculate timeout in ms (5ms + 500 % of expected time-on-air)
236 0 : RadioLibTime_t timeout = 5 + (getTimeOnAir(len) * 5) / 1000;
237 : RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout);
238 :
239 : // start transmission
240 0 : state = startTransmit(data, len, addr);
241 0 : RADIOLIB_ASSERT(state);
242 :
243 : // wait for packet transmission or timeout
244 0 : uint8_t modem = getPacketType();
245 0 : RadioLibTime_t start = this->mod->hal->millis();
246 : while(true) {
247 : // yield for multi-threaded platforms
248 0 : this->mod->hal->yield();
249 :
250 : // check timeout
251 0 : if(this->mod->hal->millis() - start > timeout) {
252 0 : finishTransmit();
253 0 : return(RADIOLIB_ERR_TX_TIMEOUT);
254 : }
255 :
256 : // poll the interrupt pin
257 0 : if(this->mod->hal->digitalRead(this->mod->getIrq())) {
258 : // in LoRa or GFSK, only Tx done interrupt is enabled
259 0 : if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
260 0 : break;
261 : }
262 :
263 : // in LR-FHSS, IRQ signals both Tx done as frequency hop request
264 0 : if(this->getIrqFlags() & RADIOLIB_SX126X_IRQ_TX_DONE) {
265 0 : break;
266 : } else {
267 : // handle frequency hop
268 0 : this->hopLRFHSS();
269 : }
270 : }
271 : }
272 :
273 0 : return(finishTransmit());
274 : }
275 :
276 3 : int16_t SX126x::receive(uint8_t* data, size_t len, RadioLibTime_t timeout) {
277 : // set mode to standby
278 3 : int16_t state = standby();
279 3 : RADIOLIB_ASSERT(state);
280 :
281 0 : RadioLibTime_t timeoutInternal = timeout;
282 0 : if(!timeoutInternal) {
283 : // calculate timeout (500 % of expected time-one-air)
284 0 : size_t maxLen = len;
285 0 : if(len == 0) { maxLen = RADIOLIB_SX126X_MAX_PACKET_LENGTH; }
286 0 : timeoutInternal = (getTimeOnAir(maxLen) * 5) / 1000;
287 : }
288 :
289 : RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeoutInternal);
290 :
291 : // start reception
292 0 : uint32_t timeoutValue = (uint32_t)(((float)timeoutInternal * 1000.0f) / 15.625f);
293 0 : state = startReceive(timeoutValue);
294 0 : RADIOLIB_ASSERT(state);
295 :
296 : // wait for packet reception or timeout
297 0 : bool softTimeout = false;
298 0 : RadioLibTime_t start = this->mod->hal->millis();
299 0 : while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
300 0 : this->mod->hal->yield();
301 : // safety check, the timeout should be done by the radio
302 0 : if(this->mod->hal->millis() - start > timeoutInternal) {
303 0 : softTimeout = true;
304 0 : break;
305 : }
306 : }
307 :
308 : // if it was a timeout, this will return an error code
309 0 : state = standby();
310 0 : if((state != RADIOLIB_ERR_NONE) && (state != RADIOLIB_ERR_SPI_CMD_TIMEOUT)) {
311 0 : return(state);
312 : }
313 :
314 : // check whether this was a timeout or not
315 0 : if(softTimeout || (getIrqFlags() & this->irqMap[RADIOLIB_IRQ_TIMEOUT])) {
316 0 : (void)finishReceive();
317 0 : return(RADIOLIB_ERR_RX_TIMEOUT);
318 : }
319 :
320 : // read the received data
321 0 : return(readData(data, len));
322 : }
323 :
324 3 : int16_t SX126x::transmitDirect(uint32_t frf) {
325 : // set RF switch (if present)
326 3 : this->mod->setRfSwitchState(this->txMode);
327 :
328 : // user requested to start transmitting immediately (required for RTTY)
329 3 : int16_t state = RADIOLIB_ERR_NONE;
330 3 : if(frf != 0) {
331 0 : state = setRfFrequency(frf);
332 : }
333 3 : RADIOLIB_ASSERT(state);
334 :
335 : // direct mode activation intentionally skipped here, as it seems to lead to much worse results
336 3 : return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_CONTINUOUS_WAVE, NULL, 0));
337 : }
338 :
339 3 : int16_t SX126x::receiveDirect() {
340 : // set RF switch (if present)
341 3 : this->mod->setRfSwitchState(Module::MODE_RX);
342 :
343 : // SX126x is unable to output received data directly
344 3 : return(RADIOLIB_ERR_UNKNOWN);
345 : }
346 :
347 0 : int16_t SX126x::directMode() {
348 : // check modem
349 0 : if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
350 0 : return(RADIOLIB_ERR_WRONG_MODEM);
351 : }
352 :
353 : // set mode to standby
354 0 : int16_t state = standby();
355 0 : RADIOLIB_ASSERT(state);
356 :
357 : // disable DIO2 RF switch
358 0 : state = setDio2AsRfSwitch(false);
359 0 : RADIOLIB_ASSERT(state);
360 :
361 : // set DIO2 to clock output and DIO3 to data input
362 : // this is done exclusively by writing magic values to even more magic registers
363 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1, RADIOLIB_SX126X_TX_BITBANG_1_ENABLED, 6, 4);
364 0 : RADIOLIB_ASSERT(state);
365 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0, RADIOLIB_SX126X_TX_BITBANG_0_ENABLED, 3, 0);
366 0 : RADIOLIB_ASSERT(state);
367 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE, RADIOLIB_SX126X_DIO3_OUT_DISABLED, 3, 3);
368 0 : RADIOLIB_ASSERT(state);
369 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_IN_ENABLE, RADIOLIB_SX126X_DIO3_IN_ENABLED, 3, 3);
370 0 : RADIOLIB_ASSERT(state);
371 :
372 : // enable TxDone interrupt
373 0 : state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE, RADIOLIB_SX126X_IRQ_TX_DONE);
374 0 : RADIOLIB_ASSERT(state);
375 :
376 : // set preamble length to the maximum to prevent SX126x from exiting Tx mode for a while
377 0 : state = setPreambleLength(0xFFFF);
378 0 : RADIOLIB_ASSERT(state);
379 :
380 0 : return(state);
381 : }
382 :
383 0 : int16_t SX126x::packetMode() {
384 : // set mode to standby
385 0 : int16_t state = standby();
386 0 : RADIOLIB_ASSERT(state);
387 :
388 : // set preamble length to the default
389 0 : state = setPreambleLength(16);
390 0 : RADIOLIB_ASSERT(state);
391 :
392 : // disable TxDone interrupt
393 0 : state = setDioIrqParams(RADIOLIB_SX126X_IRQ_NONE, RADIOLIB_SX126X_IRQ_NONE);
394 0 : RADIOLIB_ASSERT(state);
395 :
396 : // restore the magic registers
397 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_IN_ENABLE, RADIOLIB_SX126X_DIO3_IN_DISABLED, 3, 3);
398 0 : RADIOLIB_ASSERT(state);
399 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE, RADIOLIB_SX126X_DIO3_OUT_ENABLED, 3, 3);
400 0 : RADIOLIB_ASSERT(state);
401 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0, RADIOLIB_SX126X_TX_BITBANG_0_DISABLED, 3, 0);
402 0 : RADIOLIB_ASSERT(state);
403 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1, RADIOLIB_SX126X_TX_BITBANG_1_DISABLED, 6, 4);
404 0 : RADIOLIB_ASSERT(state);
405 :
406 : // enable DIO2 RF switch
407 0 : state = setDio2AsRfSwitch(true);
408 0 : RADIOLIB_ASSERT(state);
409 :
410 0 : return(state);
411 : }
412 :
413 3 : int16_t SX126x::scanChannel() {
414 3 : ChannelScanConfig_t cfg = {
415 : .cad = {
416 : .symNum = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
417 : .detPeak = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
418 : .detMin = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
419 : .exitMode = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
420 : .timeout = 0,
421 : .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
422 : .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK,
423 : },
424 : };
425 6 : return(this->scanChannel(cfg));
426 : }
427 :
428 6 : int16_t SX126x::scanChannel(const ChannelScanConfig_t &cfg) {
429 : // set mode to CAD
430 6 : int state = startChannelScan(cfg);
431 6 : RADIOLIB_ASSERT(state);
432 :
433 : // wait for channel activity detected or timeout
434 0 : while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
435 0 : this->mod->hal->yield();
436 : }
437 :
438 : // check CAD result
439 0 : return(getChannelScanResult());
440 : }
441 :
442 0 : int16_t SX126x::hopLRFHSS() {
443 0 : if(!(this->getIrqFlags() & RADIOLIB_SX126X_IRQ_LR_FHSS_HOP)) {
444 0 : return(RADIOLIB_ERR_TX_TIMEOUT);
445 : }
446 :
447 0 : int16_t state = this->setLRFHSSHop(this->lrFhssHopNum % 16);
448 0 : RADIOLIB_ASSERT(state);
449 0 : return(clearIrqStatus());
450 : }
451 :
452 3 : int16_t SX126x::finishTransmit() {
453 : // clear interrupt flags
454 3 : int16_t state = clearIrqStatus();
455 3 : RADIOLIB_ASSERT(state);
456 :
457 : // set mode to standby to disable transmitter/RF switch
458 0 : return(standby());
459 : }
460 :
461 3 : int16_t SX126x::finishReceive() {
462 : // set mode to standby to disable RF switch
463 3 : int16_t state = standby();
464 3 : RADIOLIB_ASSERT(state);
465 :
466 : // try to fix timeout error in implicit header mode
467 : // check for modem type and header mode is done in fixImplicitTimeout()
468 0 : state = fixImplicitTimeout();
469 0 : RADIOLIB_ASSERT(state);
470 :
471 : // clear interrupt flags
472 0 : return(clearIrqStatus());
473 : }
474 :
475 3 : int16_t SX126x::startReceive() {
476 3 : return(this->startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0));
477 : }
478 :
479 0 : int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) {
480 : // datasheet claims time to go to sleep is ~500us, same to wake up, compensate for that with 1 ms + TCXO delay
481 0 : uint32_t transitionTime = this->tcxoDelay + 1000;
482 0 : sleepPeriod -= transitionTime;
483 :
484 : // divide by 15.625
485 0 : uint32_t rxPeriodRaw = (rxPeriod * 8) / 125;
486 0 : uint32_t sleepPeriodRaw = (sleepPeriod * 8) / 125;
487 :
488 : // check 24 bit limit and zero value (likely not intended)
489 0 : if((rxPeriodRaw & 0xFF000000) || (rxPeriodRaw == 0)) {
490 0 : return(RADIOLIB_ERR_INVALID_RX_PERIOD);
491 : }
492 :
493 : // this check of the high byte also catches underflow when we subtracted transitionTime
494 0 : if((sleepPeriodRaw & 0xFF000000) || (sleepPeriodRaw == 0)) {
495 0 : return(RADIOLIB_ERR_INVALID_SLEEP_PERIOD);
496 : }
497 :
498 : // set up Rx mode
499 0 : RadioModeConfig_t cfg = {
500 : .receive = {
501 : .timeout = RADIOLIB_SX126X_RX_TIMEOUT_INF,
502 : .irqFlags = irqFlags,
503 : .irqMask = irqMask,
504 : .len = 0,
505 : }
506 0 : };
507 0 : int16_t state = this->stageMode(RADIOLIB_RADIO_MODE_RX, &cfg);
508 0 : RADIOLIB_ASSERT(state);
509 :
510 0 : const uint8_t data[6] = {(uint8_t)((rxPeriodRaw >> 16) & 0xFF), (uint8_t)((rxPeriodRaw >> 8) & 0xFF), (uint8_t)(rxPeriodRaw & 0xFF),
511 0 : (uint8_t)((sleepPeriodRaw >> 16) & 0xFF), (uint8_t)((sleepPeriodRaw >> 8) & 0xFF), (uint8_t)(sleepPeriodRaw & 0xFF)};
512 0 : return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_DUTY_CYCLE, data, 6));
513 : }
514 :
515 0 : int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_t minSymbols, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) {
516 : // calculate the sleep and wake periods
517 0 : uint32_t wakePeriod = 0;
518 0 : uint32_t sleepPeriod = 0;
519 : DataRate_t dr = {
520 : .lora = {
521 0 : .spreadingFactor = this->spreadingFactor,
522 0 : .bandwidth = this->bandwidthKhz,
523 0 : .codingRate = this->codingRate,
524 : }
525 0 : };
526 0 : int16_t state = calculateRxDutyCycle(senderPreambleLength, this->preambleLengthLoRa, minSymbols, &dr, &wakePeriod, &sleepPeriod);
527 0 : RADIOLIB_ASSERT(state);
528 :
529 : // If our sleep period is shorter than our transition time, just use the standard startReceive
530 0 : if(sleepPeriod < this->tcxoDelay + 1016) {
531 0 : return(startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, irqFlags, irqMask));
532 : }
533 :
534 0 : return(startReceiveDutyCycle(wakePeriod, sleepPeriod, irqFlags, irqMask));
535 : }
536 :
537 3 : int16_t SX126x::readData(uint8_t* data, size_t len) {
538 : // this method may get called from receive() after Rx timeout
539 : // if that's the case, the first call will return "SPI command timeout error"
540 : // check the IRQ to be sure this really originated from timeout event
541 3 : int16_t state = this->mod->SPIcheckStream();
542 3 : uint16_t irq = getIrqFlags();
543 3 : if((state == RADIOLIB_ERR_SPI_CMD_TIMEOUT) && (irq & RADIOLIB_SX126X_IRQ_TIMEOUT)) {
544 : // this is definitely Rx timeout
545 0 : return(RADIOLIB_ERR_RX_TIMEOUT);
546 : }
547 3 : RADIOLIB_ASSERT(state);
548 :
549 : // check integrity CRC
550 0 : int16_t crcState = RADIOLIB_ERR_NONE;
551 : // Report CRC mismatch when there's a payload CRC error, or a header error and no valid header (to avoid false alarm from previous packet)
552 0 : if((irq & RADIOLIB_SX126X_IRQ_CRC_ERR) || ((irq & RADIOLIB_SX126X_IRQ_HEADER_ERR) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID))) {
553 0 : crcState = RADIOLIB_ERR_CRC_MISMATCH;
554 : }
555 :
556 : // get packet length and Rx buffer offset
557 0 : uint8_t offset = 0;
558 0 : size_t length = getPacketLength(true, &offset);
559 0 : if((len != 0) && (len < length)) {
560 : // user requested less data than we got, only return what was requested
561 0 : length = len;
562 : }
563 :
564 : // read packet data starting at offset
565 0 : state = readBuffer(data, length, offset);
566 0 : RADIOLIB_ASSERT(state);
567 :
568 : // clear interrupt flags
569 0 : state = clearIrqStatus();
570 :
571 : // check if CRC failed - this is done after reading data to give user the option to keep them
572 0 : RADIOLIB_ASSERT(crcState);
573 :
574 0 : return(state);
575 : }
576 :
577 3 : int16_t SX126x::startChannelScan() {
578 3 : ChannelScanConfig_t cfg = {
579 : .cad = {
580 : .symNum = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
581 : .detPeak = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
582 : .detMin = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
583 : .exitMode = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
584 : .timeout = 0,
585 : .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
586 : .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK,
587 : },
588 : };
589 6 : return(this->startChannelScan(cfg));
590 : }
591 :
592 12 : int16_t SX126x::startChannelScan(const ChannelScanConfig_t &cfg) {
593 : // check active modem
594 12 : if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
595 12 : return(RADIOLIB_ERR_WRONG_MODEM);
596 : }
597 :
598 : // set mode to standby
599 0 : int16_t state = standby();
600 0 : RADIOLIB_ASSERT(state);
601 :
602 : // set RF switch (if present)
603 0 : this->mod->setRfSwitchState(Module::MODE_RX);
604 :
605 : // set DIO pin mapping
606 0 : state = setDioIrqParams(getIrqMapped(cfg.cad.irqFlags), getIrqMapped(cfg.cad.irqMask));
607 0 : RADIOLIB_ASSERT(state);
608 :
609 : // clear interrupt flags
610 0 : state = clearIrqStatus();
611 0 : RADIOLIB_ASSERT(state);
612 :
613 : // set mode to CAD
614 0 : state = setCad(cfg.cad.symNum, cfg.cad.detPeak, cfg.cad.detMin, cfg.cad.exitMode, cfg.cad.timeout);
615 0 : return(state);
616 : }
617 :
618 3 : int16_t SX126x::getChannelScanResult() {
619 : // check active modem
620 3 : if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
621 3 : return(RADIOLIB_ERR_WRONG_MODEM);
622 : }
623 :
624 : // check CAD result
625 0 : uint16_t cadResult = getIrqFlags();
626 0 : if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DETECTED) {
627 : // detected some LoRa activity
628 0 : return(RADIOLIB_LORA_DETECTED);
629 0 : } else if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DONE) {
630 : // channel is free
631 0 : return(RADIOLIB_CHANNEL_FREE);
632 : }
633 :
634 0 : return(RADIOLIB_ERR_UNKNOWN);
635 : }
636 :
637 3 : float SX126x::getRSSI() {
638 3 : return(this->getRSSI(true));
639 : }
640 :
641 3 : float SX126x::getRSSI(bool packet) {
642 3 : if(packet) {
643 : // get last packet RSSI from packet status
644 3 : uint32_t packetStatus = getPacketStatus();
645 3 : uint8_t rssiPkt = packetStatus & 0xFF;
646 3 : return(-1.0 * rssiPkt/2.0);
647 : } else {
648 : // get instantaneous RSSI value
649 0 : uint8_t rssiRaw = 0;
650 0 : this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_RSSI_INST, &rssiRaw, 1);
651 0 : return((float)rssiRaw / (-2.0f));
652 : }
653 : }
654 :
655 3 : float SX126x::getSNR() {
656 : // check active modem
657 3 : if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
658 3 : return(RADIOLIB_ERR_WRONG_MODEM);
659 : }
660 :
661 : // get last packet SNR from packet status
662 0 : uint32_t packetStatus = getPacketStatus();
663 0 : uint8_t snrPkt = (packetStatus >> 8) & 0xFF;
664 0 : if(snrPkt < 128) {
665 0 : return(snrPkt/4.0);
666 : } else {
667 0 : return((snrPkt - 256)/4.0);
668 : }
669 : }
670 :
671 0 : float SX126x::getFrequencyError() {
672 : // check active modem
673 0 : uint8_t modem = getPacketType();
674 0 : if(modem != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
675 0 : return(0.0);
676 : }
677 :
678 : // read the raw frequency error register values
679 0 : uint8_t efeRaw[3] = {0};
680 0 : int16_t state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC, &efeRaw[0], 1);
681 0 : RADIOLIB_ASSERT(state);
682 0 : state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC + 1, &efeRaw[1], 1);
683 0 : RADIOLIB_ASSERT(state);
684 0 : state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC + 2, &efeRaw[2], 1);
685 0 : RADIOLIB_ASSERT(state);
686 0 : uint32_t efe = ((uint32_t) efeRaw[0] << 16) | ((uint32_t) efeRaw[1] << 8) | efeRaw[2];
687 0 : efe &= 0x0FFFFF;
688 :
689 0 : float error = 0;
690 :
691 : // check the first bit
692 0 : if (efe & 0x80000) {
693 : // frequency error is negative
694 0 : efe |= (uint32_t) 0xFFF00000;
695 0 : efe = ~efe + 1;
696 0 : error = 1.55f * (float) efe / (1600.0f / (float) this->bandwidthKhz) * -1.0f;
697 : } else {
698 0 : error = 1.55f * (float) efe / (1600.0f / (float) this->bandwidthKhz);
699 : }
700 :
701 0 : return(error);
702 : }
703 :
704 3 : size_t SX126x::getPacketLength(bool update) {
705 3 : return(this->getPacketLength(update, NULL));
706 : }
707 :
708 3 : size_t SX126x::getPacketLength(bool update, uint8_t* offset) {
709 : (void)update;
710 :
711 : // in implicit mode, return the cached value if the offset was not requested
712 3 : if((getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) && (this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (!offset)) {
713 0 : return(this->implicitLen);
714 : }
715 :
716 : // if offset was requested, or in explicit mode, we always have to perform the SPI transaction
717 3 : uint8_t rxBufStatus[2] = {0, 0};
718 3 : this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_RX_BUFFER_STATUS, rxBufStatus, 2);
719 :
720 3 : if(offset) { *offset = rxBufStatus[1]; }
721 :
722 3 : return((size_t)rxBufStatus[0]);
723 : }
724 :
725 0 : int16_t SX126x::getLoRaRxHeaderInfo(uint8_t* cr, bool* hasCRC) {
726 0 : int16_t state = RADIOLIB_ERR_NONE;
727 :
728 : // check if in explicit header mode
729 0 : if(this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) {
730 0 : return(RADIOLIB_ERR_WRONG_MODEM);
731 : }
732 :
733 0 : if(cr) { *cr = this->mod->SPIgetRegValue(RADIOLIB_SX126X_REG_LORA_RX_CODING_RATE, 6, 4) >> 4; }
734 0 : if(hasCRC) { *hasCRC = (this->mod->SPIgetRegValue(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC, 4, 4) != 0); }
735 :
736 0 : return(state);
737 : }
738 :
739 18 : RadioLibTime_t SX126x::calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len) {
740 : // everything is in microseconds to allow integer arithmetic
741 : // some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact
742 18 : switch (modem) {
743 8 : case RADIOLIB_MODEM_LORA: {
744 8 : uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << dr.lora.spreadingFactor) / (dr.lora.bandwidth * 10) ;
745 8 : uint8_t sfCoeff1_x4 = 17; // (4.25 * 4)
746 8 : uint8_t sfCoeff2 = 8;
747 8 : if(dr.lora.spreadingFactor == 5 || dr.lora.spreadingFactor == 6) {
748 0 : sfCoeff1_x4 = 25; // 6.25 * 4
749 0 : sfCoeff2 = 0;
750 : }
751 8 : uint8_t sfDivisor = 4*dr.lora.spreadingFactor;
752 8 : if(pc.lora.ldrOptimize) {
753 4 : sfDivisor = 4*(dr.lora.spreadingFactor - 2);
754 : }
755 8 : const int8_t bitsPerCrc = 16;
756 8 : const int8_t N_symbol_header = pc.lora.implicitHeader ? 0 : 20;
757 :
758 : // numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8)
759 8 : int16_t bitCount = (int16_t) 8 * len + pc.lora.crcEnabled * bitsPerCrc - 4 * dr.lora.spreadingFactor + sfCoeff2 + N_symbol_header;
760 8 : if(bitCount < 0) {
761 0 : bitCount = 0;
762 : }
763 : // add (sfDivisor) - 1 to the numerator to give integer CEIL(...)
764 8 : uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor);
765 :
766 : // preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit
767 8 : uint32_t nSymbol_x4 = (pc.lora.preambleLength + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * dr.lora.codingRate * 4;
768 :
769 8 : return((symbolLength_us * nSymbol_x4) / 4);
770 : }
771 4 : case RADIOLIB_MODEM_FSK: {
772 4 : return((((float)(pc.fsk.crcLength * 8) + pc.fsk.syncWordLength + pc.fsk.preambleLength + (uint32_t)len * 8) / (dr.fsk.bitRate / 1000.0f)));
773 : }
774 3 : case RADIOLIB_MODEM_LRFHSS: {
775 : // calculate the number of bits based on coding rate
776 : uint16_t N_bits;
777 3 : switch(dr.lrFhss.cr) {
778 0 : case RADIOLIB_SX126X_LR_FHSS_CR_5_6:
779 0 : N_bits = ((len * 6) + 4) / 5; // this is from the official LR11xx driver, but why the extra +4?
780 0 : break;
781 3 : case RADIOLIB_SX126X_LR_FHSS_CR_2_3:
782 3 : N_bits = (len * 3) / 2;
783 3 : break;
784 0 : case RADIOLIB_SX126X_LR_FHSS_CR_1_2:
785 0 : N_bits = len * 2;
786 0 : break;
787 0 : case RADIOLIB_SX126X_LR_FHSS_CR_1_3:
788 0 : N_bits = len * 3;
789 0 : break;
790 0 : default:
791 0 : return(RADIOLIB_ERR_INVALID_CODING_RATE);
792 : }
793 :
794 : // calculate number of bits when accounting for unaligned last block
795 3 : uint16_t N_payBits = (N_bits / RADIOLIB_SX126X_LR_FHSS_FRAG_BITS) * RADIOLIB_SX126X_LR_FHSS_BLOCK_BITS;
796 3 : uint16_t N_lastBlockBits = N_bits % RADIOLIB_SX126X_LR_FHSS_FRAG_BITS;
797 3 : if(N_lastBlockBits) {
798 3 : N_payBits += N_lastBlockBits + 2;
799 : }
800 :
801 : // add header bits
802 3 : uint16_t N_totalBits = (RADIOLIB_SX126X_LR_FHSS_HEADER_BITS * pc.lrFhss.hdrCount) + N_payBits;
803 3 : return(((uint32_t)N_totalBits * 8 * 1000000UL) / 488.28215f);
804 : }
805 3 : default:
806 3 : return(RADIOLIB_ERR_WRONG_MODEM);
807 : }
808 :
809 : return(RADIOLIB_ERR_UNKNOWN);
810 : }
811 :
812 3 : RadioLibTime_t SX126x::getTimeOnAir(size_t len) {
813 3 : uint8_t type = getPacketType();
814 3 : ModemType_t modem = RADIOLIB_MODEM_LORA;
815 3 : DataRate_t dataRate = {};
816 3 : PacketConfig_t packetConfig = {};
817 :
818 3 : if(type == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
819 0 : uint8_t cr = this->codingRate;
820 : // We assume same calculation for short and long interleaving, so map CR values 0-4 and 5-7 to the same values
821 0 : if (cr < 5) {
822 0 : cr = cr + 4;
823 0 : } else if (cr == 7) {
824 0 : cr = cr + 1;
825 : }
826 :
827 0 : dataRate.lora.codingRate = cr;
828 0 : dataRate.lora.spreadingFactor = this->spreadingFactor;
829 0 : dataRate.lora.bandwidth = this->bandwidthKhz;
830 :
831 0 : packetConfig.lora.preambleLength = this->preambleLengthLoRa;
832 0 : packetConfig.lora.crcEnabled = (bool)this->crcTypeLoRa;
833 0 : packetConfig.lora.implicitHeader = this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT;
834 0 : packetConfig.lora.ldrOptimize = (bool)this->ldrOptimize;
835 3 : } else if(type == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
836 0 : modem = RADIOLIB_MODEM_FSK;
837 :
838 0 : dataRate.fsk.bitRate = RADIOLIB_SX126X_CRYSTAL_FREQ * 32.0f * 1000.0f / (float)this->bitRate;
839 0 : dataRate.fsk.freqDev = (float)this->frequencyDev;
840 :
841 0 : uint8_t crcLen = 0;
842 0 : if(this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_1_BYTE || this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_1_BYTE_INV) {
843 0 : crcLen = 1;
844 0 : } else if(this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_2_BYTE || this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV) {
845 0 : crcLen = 2;
846 : }
847 :
848 0 : packetConfig.fsk.preambleLength = this->preambleLengthFSK;
849 0 : packetConfig.fsk.syncWordLength = this->syncWordLength;
850 0 : packetConfig.fsk.crcLength = crcLen;
851 3 : } else if(type == RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
852 0 : modem = RADIOLIB_MODEM_LRFHSS;
853 :
854 0 : dataRate.lrFhss.bw = this->lrFhssBw;
855 0 : dataRate.lrFhss.cr = this->lrFhssCr;
856 0 : dataRate.lrFhss.narrowGrid = this->lrFhssGridNonFcc;
857 :
858 0 : packetConfig.lrFhss.hdrCount = this->lrFhssHdrCount;
859 3 : } else if(type == RADIOLIB_SX126X_PACKET_TYPE_BPSK) {
860 : // BPSK is so experimental it does not have a specific data rate structure
861 : // so just reuse FSK
862 0 : modem = RADIOLIB_MODEM_FSK;
863 :
864 0 : dataRate.fsk.bitRate = RADIOLIB_SX126X_CRYSTAL_FREQ * 32.0f * 1000.0f / (float)this->bitRate;
865 0 : dataRate.fsk.freqDev = 0;
866 :
867 0 : packetConfig.fsk.preambleLength = 0;
868 0 : packetConfig.fsk.syncWordLength = 0;
869 0 : packetConfig.fsk.crcLength = 0;
870 :
871 : } else {
872 3 : return(RADIOLIB_ERR_WRONG_MODEM);
873 :
874 : }
875 :
876 0 : return(calculateTimeOnAir(modem, dataRate, packetConfig, len));
877 : }
878 :
879 3 : RadioLibTime_t SX126x::calculateRxTimeout(RadioLibTime_t timeoutUs) {
880 : // the timeout value is given in units of 15.625 microseconds
881 : // the calling function should provide some extra width, as this number of units is truncated to integer
882 3 : RadioLibTime_t timeout = timeoutUs / 15.625f;
883 3 : return(timeout);
884 : }
885 :
886 6 : uint32_t SX126x::getIrqFlags() {
887 6 : uint8_t data[] = { 0x00, 0x00 };
888 6 : this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_IRQ_STATUS, data, 2);
889 6 : return(((uint32_t)(data[0]) << 8) | data[1]);
890 : }
891 :
892 3 : int16_t SX126x::setIrqFlags(uint32_t irq) {
893 3 : return(this->setDioIrqParams(irq, irq));
894 : }
895 :
896 3 : int16_t SX126x::clearIrqFlags(uint32_t irq) {
897 3 : return(this->clearIrqStatus(irq));
898 : }
899 :
900 3 : uint8_t SX126x::randomByte() {
901 : // set some magic registers
902 3 : this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_LNA, RADIOLIB_SX126X_LNA_RNG_ENABLED, 0, 0);
903 3 : this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_MIXER, RADIOLIB_SX126X_MIXER_RNG_ENABLED, 0, 0);
904 :
905 : // set mode to Rx
906 3 : setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF);
907 :
908 : // wait a bit for the RSSI reading to stabilise
909 3 : this->mod->hal->delay(10);
910 :
911 : // read RSSI value 8 times, always keep just the least significant bit
912 3 : uint8_t randByte = 0x00;
913 27 : for(uint8_t i = 0; i < 8; i++) {
914 24 : uint8_t val = 0x00;
915 24 : readRegister(RADIOLIB_SX126X_REG_RANDOM_NUMBER_0, &val, sizeof(uint8_t));
916 24 : randByte |= ((val & 0x01) << i);
917 : }
918 :
919 : // set mode to standby
920 3 : standby();
921 :
922 : // restore the magic registers
923 3 : this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_LNA, RADIOLIB_SX126X_LNA_RNG_DISABLED, 0, 0);
924 3 : this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_MIXER, RADIOLIB_SX126X_MIXER_RNG_DISABLED, 0, 0);
925 :
926 3 : return(randByte);
927 : }
928 :
929 9 : int16_t SX126x::getModem(ModemType_t* modem) {
930 9 : RADIOLIB_ASSERT_PTR(modem);
931 :
932 9 : uint8_t type = getPacketType();
933 9 : switch(type) {
934 0 : case(RADIOLIB_SX126X_PACKET_TYPE_LORA):
935 0 : *modem = ModemType_t::RADIOLIB_MODEM_LORA;
936 0 : return(RADIOLIB_ERR_NONE);
937 0 : case(RADIOLIB_SX126X_PACKET_TYPE_GFSK):
938 0 : *modem = ModemType_t::RADIOLIB_MODEM_FSK;
939 0 : return(RADIOLIB_ERR_NONE);
940 0 : case(RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS):
941 0 : *modem = ModemType_t::RADIOLIB_MODEM_LRFHSS;
942 0 : return(RADIOLIB_ERR_NONE);
943 : }
944 :
945 9 : return(RADIOLIB_ERR_WRONG_MODEM);
946 : }
947 :
948 12 : int16_t SX126x::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) {
949 : int16_t state;
950 :
951 12 : switch(mode) {
952 9 : case(RADIOLIB_RADIO_MODE_RX): {
953 : // in implicit header mode, use the provided length if it is nonzero
954 : // otherwise we trust the user has previously set the payload length manually
955 9 : if((this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (cfg->receive.len != 0)) {
956 0 : this->implicitLen = cfg->receive.len;
957 : }
958 :
959 : // ensure we are in standby
960 9 : state = standby();
961 9 : RADIOLIB_ASSERT(state);
962 :
963 : // set DIO mapping
964 0 : if(cfg->receive.timeout != RADIOLIB_SX126X_RX_TIMEOUT_INF) {
965 0 : cfg->receive.irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT);
966 : }
967 0 : state = setDioIrqParams(getIrqMapped(cfg->receive.irqFlags), getIrqMapped(cfg->receive.irqMask));
968 0 : RADIOLIB_ASSERT(state);
969 :
970 : // set buffer pointers
971 0 : state = setBufferBaseAddress();
972 0 : RADIOLIB_ASSERT(state);
973 :
974 : // clear interrupt flags
975 0 : state = clearIrqStatus();
976 0 : RADIOLIB_ASSERT(state);
977 :
978 : // restore original packet length
979 0 : uint8_t modem = getPacketType();
980 0 : if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
981 0 : state = setPacketParams(this->preambleLengthLoRa, this->crcTypeLoRa, this->implicitLen, this->headerType, this->invertIQEnabled);
982 0 : } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
983 0 : state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening,
984 0 : this->packetType, (this->packetType == RADIOLIB_SX126X_GFSK_PACKET_FIXED) ? this->implicitLen : RADIOLIB_SX126X_MAX_PACKET_LENGTH);
985 : } else {
986 0 : return(RADIOLIB_ERR_UNKNOWN);
987 : }
988 :
989 : // if max(uint32_t) is used, revert to RxContinuous
990 0 : if(cfg->receive.timeout == 0xFFFFFFFF) {
991 0 : cfg->receive.timeout = 0xFFFFFF;
992 : }
993 0 : this->rxTimeout = cfg->receive.timeout;
994 0 : } break;
995 :
996 3 : case(RADIOLIB_RADIO_MODE_TX): {
997 : // check packet length
998 3 : if(cfg->transmit.len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
999 0 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
1000 : }
1001 :
1002 : // maximum packet length is decreased by 1 when address filtering is active
1003 : if((RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF != RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF) &&
1004 : (cfg->transmit.len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 1)) {
1005 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
1006 : }
1007 :
1008 : // set packet Length
1009 3 : state = RADIOLIB_ERR_NONE;
1010 3 : uint8_t modem = getPacketType();
1011 3 : if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
1012 0 : state = setPacketParams(this->preambleLengthLoRa, this->crcTypeLoRa, cfg->transmit.len, this->headerType, this->invertIQEnabled);
1013 :
1014 3 : } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
1015 0 : state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening, this->packetType, cfg->transmit.len);
1016 :
1017 3 : } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_BPSK) {
1018 0 : uint16_t rampUp = RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_600_BPS;
1019 0 : uint16_t rampDown = RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_600_BPS;
1020 0 : if(this->bitRate == 100) {
1021 0 : rampUp = RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_100_BPS;
1022 0 : rampDown = RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_100_BPS;
1023 : }
1024 0 : state = setPacketParamsBPSK(cfg->transmit.len, rampUp, rampDown, 8*cfg->transmit.len);
1025 :
1026 3 : } else if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
1027 3 : return(RADIOLIB_ERR_UNKNOWN);
1028 :
1029 : }
1030 0 : RADIOLIB_ASSERT(state);
1031 :
1032 : // set DIO mapping
1033 0 : if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
1034 0 : state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT, RADIOLIB_SX126X_IRQ_TX_DONE);
1035 : } else {
1036 0 : state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP, RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP);
1037 : }
1038 0 : RADIOLIB_ASSERT(state);
1039 :
1040 : // set buffer pointers
1041 0 : state = setBufferBaseAddress();
1042 0 : RADIOLIB_ASSERT(state);
1043 :
1044 : // write packet to buffer
1045 0 : if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
1046 0 : state = writeBuffer(cfg->transmit.data, cfg->transmit.len);
1047 :
1048 : } else {
1049 : // first, reset the LR-FHSS state machine
1050 0 : state = resetLRFHSS();
1051 0 : RADIOLIB_ASSERT(state);
1052 :
1053 : // skip hopping for the first 4 - lrFhssHdrCount blocks
1054 0 : for(int i = 0; i < 4 - this->lrFhssHdrCount; ++i ) {
1055 0 : stepLRFHSS();
1056 : }
1057 :
1058 : // in LR-FHSS mode, we need to build the entire packet manually
1059 0 : uint8_t frame[RADIOLIB_SX126X_MAX_PACKET_LENGTH] = { 0 };
1060 0 : size_t frameLen = 0;
1061 0 : this->lrFhssFrameBitsRem = 0;
1062 0 : this->lrFhssFrameHopsRem = 0;
1063 0 : this->lrFhssHopNum = 0;
1064 0 : state = buildLRFHSSPacket(cfg->transmit.data, cfg->transmit.len, frame, &frameLen, &this->lrFhssFrameBitsRem, &this->lrFhssFrameHopsRem);
1065 0 : RADIOLIB_ASSERT(state);
1066 :
1067 : // FIXME check max len for FHSS
1068 0 : state = writeBuffer(frame, frameLen);
1069 0 : RADIOLIB_ASSERT(state);
1070 :
1071 : // activate hopping
1072 0 : const uint8_t hopCfg[] = { RADIOLIB_SX126X_HOPPING_ENABLED, (uint8_t)frameLen, (uint8_t)this->lrFhssFrameHopsRem };
1073 0 : state = writeRegister(RADIOLIB_SX126X_REG_HOPPING_ENABLE, hopCfg, 3);
1074 0 : RADIOLIB_ASSERT(state);
1075 :
1076 : // write the initial hopping table
1077 0 : uint8_t initHops = this->lrFhssFrameHopsRem;
1078 0 : if(initHops > 16) {
1079 0 : initHops = 16;
1080 : };
1081 0 : for(size_t i = 0; i < initHops; i++) {
1082 : // set the hop frequency and symbols
1083 0 : state = this->setLRFHSSHop(i);
1084 0 : RADIOLIB_ASSERT(state);
1085 : }
1086 :
1087 : }
1088 0 : RADIOLIB_ASSERT(state);
1089 :
1090 : // clear interrupt flags
1091 0 : state = clearIrqStatus();
1092 0 : RADIOLIB_ASSERT(state);
1093 :
1094 : // fix sensitivity
1095 0 : state = fixSensitivity();
1096 0 : RADIOLIB_ASSERT(state);
1097 0 : } break;
1098 :
1099 0 : default:
1100 0 : return(RADIOLIB_ERR_UNSUPPORTED);
1101 : }
1102 :
1103 0 : this->stagedMode = mode;
1104 0 : return(state);
1105 : }
1106 :
1107 3 : int16_t SX126x::launchMode() {
1108 : int16_t state;
1109 3 : switch(this->stagedMode) {
1110 3 : case(RADIOLIB_RADIO_MODE_RX): {
1111 3 : this->mod->setRfSwitchState(Module::MODE_RX);
1112 3 : state = setRx(this->rxTimeout);
1113 3 : } break;
1114 :
1115 0 : case(RADIOLIB_RADIO_MODE_TX): {
1116 0 : this->mod->setRfSwitchState(this->txMode);
1117 0 : state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE);
1118 0 : RADIOLIB_ASSERT(state);
1119 :
1120 : // wait for BUSY to go low (= PA ramp up done)
1121 0 : while(this->mod->hal->digitalRead(this->mod->getGpio())) {
1122 0 : this->mod->hal->yield();
1123 : }
1124 0 : } break;
1125 :
1126 0 : default:
1127 0 : return(RADIOLIB_ERR_UNSUPPORTED);
1128 : }
1129 :
1130 3 : this->stagedMode = RADIOLIB_RADIO_MODE_NONE;
1131 3 : return(state);
1132 : }
1133 :
1134 : #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE
1135 0 : void SX126x::setDirectAction(void (*func)(void)) {
1136 0 : setDio1Action(func);
1137 0 : }
1138 :
1139 0 : void SX126x::readBit(uint32_t pin) {
1140 0 : updateDirectBuffer((uint8_t)this->mod->hal->digitalRead(pin));
1141 0 : }
1142 : #endif
1143 :
1144 0 : int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) {
1145 : // set to standby RC mode
1146 0 : int16_t state = standby(RADIOLIB_SX126X_STANDBY_RC);
1147 0 : RADIOLIB_ASSERT(state);
1148 :
1149 : // check the version
1150 : #if RADIOLIB_DEBUG_BASIC
1151 : char ver_pre[16];
1152 : this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast<uint8_t*>(ver_pre));
1153 : RADIOLIB_DEBUG_BASIC_PRINTLN("Pre-update version string: %s", ver_pre);
1154 : #endif
1155 :
1156 : // enable patch update
1157 0 : this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_ENABLED);
1158 :
1159 : // upload the patch
1160 : uint8_t data[4];
1161 0 : for(uint32_t i = 0; i < len / sizeof(uint32_t); i++) {
1162 0 : uint32_t bin = 0;
1163 0 : if(nonvolatile) {
1164 0 : uint32_t* ptr = const_cast<uint32_t*>(patch) + i;
1165 0 : bin = RADIOLIB_NONVOLATILE_READ_DWORD(ptr);
1166 : } else {
1167 0 : bin = patch[i];
1168 : }
1169 0 : data[0] = (bin >> 24) & 0xFF;
1170 0 : data[1] = (bin >> 16) & 0xFF;
1171 0 : data[2] = (bin >> 8) & 0xFF;
1172 0 : data[3] = bin & 0xFF;
1173 0 : this->mod->SPIwriteRegisterBurst(RADIOLIB_SX126X_REG_PATCH_MEMORY_BASE + i*sizeof(uint32_t), data, sizeof(uint32_t));
1174 : }
1175 :
1176 : // disable patch update
1177 0 : this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_DISABLED);
1178 :
1179 : // update
1180 0 : this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_PRAM_UPDATE, NULL, 0);
1181 :
1182 : // check the version again
1183 : #if RADIOLIB_DEBUG_BASIC
1184 : char ver_post[16];
1185 : this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast<uint8_t*>(ver_post));
1186 : RADIOLIB_DEBUG_BASIC_PRINTLN("Post-update version string: %s", ver_post);
1187 : #endif
1188 :
1189 0 : return(state);
1190 : }
1191 :
1192 0 : int16_t SX126x::spectralScanStart(uint16_t numSamples, uint8_t window, uint8_t interval) {
1193 : // abort first - not sure if this is strictly needed, but the example code does this
1194 0 : spectralScanAbort();
1195 :
1196 : // set the RSSI window size
1197 0 : this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, window);
1198 :
1199 : // start Rx with infinite timeout
1200 0 : int16_t state = setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF);
1201 0 : RADIOLIB_ASSERT(state);
1202 :
1203 : // now set the actual spectral scan parameters
1204 0 : const uint8_t data[3] = { (uint8_t)((numSamples >> 8) & 0xFF), (uint8_t)(numSamples & 0xFF), interval };
1205 0 : return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS, data, 3));
1206 : }
1207 :
1208 0 : void SX126x::spectralScanAbort() {
1209 0 : this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x00);
1210 0 : }
1211 :
1212 0 : int16_t SX126x::spectralScanGetStatus() {
1213 0 : uint8_t status = this->mod->SPIreadRegister(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS);
1214 0 : if(status == RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED) {
1215 0 : return(RADIOLIB_ERR_NONE);
1216 : }
1217 0 : return(RADIOLIB_ERR_RANGING_TIMEOUT);
1218 : }
1219 :
1220 0 : int16_t SX126x::spectralScanGetResult(uint16_t* results) {
1221 : // read the raw results
1222 : uint8_t data[2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
1223 0 : this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_RESULT, 2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, data);
1224 :
1225 : // convert it
1226 0 : for(uint8_t i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) {
1227 0 : results[i] = ((uint16_t)data[i*2] << 8) | ((uint16_t)data[i*2 + 1]);
1228 : }
1229 0 : return(RADIOLIB_ERR_NONE);
1230 : }
1231 :
1232 0 : int16_t SX126x::calibrateImage(float freq) {
1233 0 : uint8_t data[2] = { 0, 0 };
1234 :
1235 : // try to match the frequency ranges
1236 0 : int freqBand = (int)freq;
1237 0 : if((freqBand >= 902) && (freqBand <= 928)) {
1238 0 : data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1;
1239 0 : data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2;
1240 0 : } else if((freqBand >= 863) && (freqBand <= 870)) {
1241 0 : data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1;
1242 0 : data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2;
1243 0 : } else if((freqBand >= 779) && (freqBand <= 787)) {
1244 0 : data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1;
1245 0 : data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2;
1246 0 : } else if((freqBand >= 470) && (freqBand <= 510)) {
1247 0 : data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1;
1248 0 : data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2;
1249 0 : } else if((freqBand >= 430) && (freqBand <= 440)) {
1250 0 : data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1;
1251 0 : data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2;
1252 : }
1253 :
1254 : int16_t state;
1255 0 : if(data[0]) {
1256 : // matched with predefined ranges, do the calibration
1257 0 : state = SX126x::calibrateImage(data);
1258 :
1259 : } else {
1260 : // if nothing matched, try custom calibration - the may or may not work
1261 : RADIOLIB_DEBUG_BASIC_PRINTLN("Failed to match predefined frequency range, trying custom");
1262 0 : state = SX126x::calibrateImageRejection(freq - 4.0f, freq + 4.0f);
1263 :
1264 : }
1265 :
1266 0 : return(state);
1267 : }
1268 :
1269 0 : int16_t SX126x::calibrateImageRejection(float freqMin, float freqMax) {
1270 : // calculate the calibration coefficients and calibrate image
1271 0 : uint8_t data[] = { (uint8_t)floor((freqMin - 1.0f) / 4.0f), (uint8_t)ceil((freqMax + 1.0f) / 4.0f) };
1272 0 : data[0] = (data[0] % 2) ? data[0] : data[0] - 1;
1273 0 : data[1] = (data[1] % 2) ? data[1] : data[1] + 1;
1274 0 : return(this->calibrateImage(data));
1275 : }
1276 :
1277 0 : int16_t SX126x::fixSensitivity() {
1278 : // fix receiver sensitivity for 500 kHz LoRa
1279 : // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.1 for details
1280 :
1281 : // read current sensitivity configuration
1282 0 : uint8_t sensitivityConfig = 0;
1283 0 : int16_t state = readRegister(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1);
1284 0 : RADIOLIB_ASSERT(state);
1285 :
1286 : // fix the value for LoRa with 500 kHz bandwidth
1287 0 : if((getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) && (fabsf(this->bandwidthKhz - 500.0f) <= 0.001f)) {
1288 0 : sensitivityConfig &= 0xFB;
1289 : } else {
1290 0 : sensitivityConfig |= 0x04;
1291 : }
1292 0 : return(writeRegister(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1));
1293 : }
1294 :
1295 0 : int16_t SX126x::fixPaClamping(bool enable) {
1296 : // fixes overly eager PA clamping
1297 : // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.2 for details
1298 :
1299 : // read current clamping configuration
1300 0 : uint8_t clampConfig = 0;
1301 0 : int16_t state = readRegister(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1);
1302 0 : RADIOLIB_ASSERT(state);
1303 :
1304 : // apply or undo workaround
1305 0 : if (enable)
1306 0 : clampConfig |= 0x1E;
1307 : else
1308 0 : clampConfig = (clampConfig & ~0x1E) | 0x08;
1309 :
1310 0 : return(writeRegister(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1));
1311 : }
1312 :
1313 0 : int16_t SX126x::fixImplicitTimeout() {
1314 : // fixes timeout in implicit header mode
1315 : // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.3 for details
1316 :
1317 : //check if we're in implicit LoRa mode
1318 0 : if(!((this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA))) {
1319 : // not in the correct mode, nothing to do here
1320 0 : return(RADIOLIB_ERR_NONE);
1321 : }
1322 :
1323 : // stop RTC counter
1324 0 : uint8_t rtcStop = 0x00;
1325 0 : int16_t state = writeRegister(RADIOLIB_SX126X_REG_RTC_CTRL, &rtcStop, 1);
1326 0 : RADIOLIB_ASSERT(state);
1327 :
1328 : // read currently active event
1329 0 : uint8_t rtcEvent = 0;
1330 0 : state = readRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1);
1331 0 : RADIOLIB_ASSERT(state);
1332 :
1333 : // clear events
1334 0 : rtcEvent |= 0x02;
1335 0 : return(writeRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1));
1336 : }
1337 :
1338 0 : int16_t SX126x::fixInvertedIQ(uint8_t iqConfig) {
1339 : // fixes IQ configuration for inverted IQ
1340 : // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4 for details
1341 :
1342 : // read current IQ configuration
1343 0 : uint8_t iqConfigCurrent = 0;
1344 0 : int16_t state = readRegister(RADIOLIB_SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1);
1345 0 : RADIOLIB_ASSERT(state);
1346 :
1347 : // set correct IQ configuration
1348 0 : if(iqConfig == RADIOLIB_SX126X_LORA_IQ_INVERTED) {
1349 0 : iqConfigCurrent &= 0xFB;
1350 : } else {
1351 0 : iqConfigCurrent |= 0x04;
1352 : }
1353 :
1354 : // update with the new value
1355 0 : return(writeRegister(RADIOLIB_SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1));
1356 : }
1357 :
1358 0 : int16_t SX126x::fixGFSK() {
1359 : // method that applies some magic workaround for specific bitrate, frequency deviation,
1360 : // receiver bandwidth and carrier frequencies for GFSK (and resets it in all other cases)
1361 : // this is not documented in the datasheet, only in Semtech repositories for SX126x and LR11xx
1362 :
1363 : // first, check we are using GFSK modem
1364 0 : if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
1365 : // not in GFSK, nothing to do here
1366 0 : return(RADIOLIB_ERR_NONE);
1367 : }
1368 :
1369 : // next, decide what to change based on modulation properties
1370 0 : int16_t state = RADIOLIB_ERR_UNKNOWN;
1371 0 : if(this->bitRate == 1200) {
1372 : // workaround for 1.2 kbps
1373 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_3, 0x00, 4, 4);
1374 :
1375 0 : } else if(this->bitRate == 600) {
1376 : // workaround for 0.6 kbps
1377 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_1, 0x18, 4, 3);
1378 0 : RADIOLIB_ASSERT(state);
1379 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x04, 4, 2);
1380 0 : RADIOLIB_ASSERT(state);
1381 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_3, 0x00, 4, 4);
1382 0 : RADIOLIB_ASSERT(state);
1383 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_4, 0x50, 6, 4);
1384 :
1385 : } else {
1386 : // reset
1387 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_1, 0x08, 4, 3);
1388 0 : RADIOLIB_ASSERT(state);
1389 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x00, 4, 2);
1390 0 : RADIOLIB_ASSERT(state);
1391 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_3, 0x10, 4, 4);
1392 0 : RADIOLIB_ASSERT(state);
1393 0 : state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_4, 0x00, 6, 4);
1394 :
1395 : }
1396 :
1397 0 : return(state);
1398 : }
1399 :
1400 0 : Module* SX126x::getMod() {
1401 0 : return(this->mod);
1402 : }
1403 :
1404 0 : int16_t SX126x::modSetup(uint8_t modem) {
1405 : // set module properties
1406 0 : this->mod->init();
1407 0 : this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput);
1408 0 : this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput);
1409 0 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16;
1410 0 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8;
1411 0 : this->mod->spiConfig.statusPos = 1;
1412 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX126X_CMD_READ_REGISTER;
1413 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX126X_CMD_WRITE_REGISTER;
1414 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX126X_CMD_NOP;
1415 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX126X_CMD_GET_STATUS;
1416 0 : this->mod->spiConfig.stream = true;
1417 0 : this->mod->spiConfig.parseStatusCb = SPIparseStatus;
1418 :
1419 : // find the SX126x chip - this will also reset the module and verify the module
1420 0 : if(!SX126x::findChip(this->chipType)) {
1421 : RADIOLIB_DEBUG_BASIC_PRINTLN("No SX126x found!");
1422 0 : this->mod->term();
1423 0 : return(RADIOLIB_ERR_CHIP_NOT_FOUND);
1424 : }
1425 : RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX126x");
1426 :
1427 0 : int16_t state = RADIOLIB_ERR_NONE;
1428 :
1429 : // set TCXO control, if requested
1430 0 : if(this->tcxoVoltage > 0.0f) {
1431 0 : state = setTCXO(this->tcxoVoltage);
1432 0 : RADIOLIB_ASSERT(state);
1433 : }
1434 :
1435 : // configure settings not accessible by API
1436 0 : state = config(modem);
1437 :
1438 : // if something failed, check the device errors
1439 0 : if(state != RADIOLIB_ERR_NONE) {
1440 : // unless mode is forced to standby, device errors will be 0
1441 0 : (void)standby();
1442 0 : uint16_t errors = getDeviceErrors();
1443 : RADIOLIB_DEBUG_BASIC_PRINTLN("Config failed, device errors: 0x%X", errors);
1444 :
1445 : // SPI command fail and oscillator start error flag indicate incorrectly set oscillator
1446 0 : if((state == RADIOLIB_ERR_SPI_CMD_FAILED) && (errors & RADIOLIB_SX126X_XOSC_START_ERR)) {
1447 : // typically users with XTAL devices will try to call the default begin method
1448 : // disable TCXO and try to run config again
1449 0 : this->tcxoVoltage = 0;
1450 : RADIOLIB_DEBUG_BASIC_PRINTLN("Bad oscillator selected, trying XTAL");
1451 :
1452 0 : state = setTCXO(0);
1453 0 : RADIOLIB_ASSERT(state);
1454 :
1455 0 : state = config(modem);
1456 : }
1457 : }
1458 0 : RADIOLIB_ASSERT(state);
1459 :
1460 0 : if(this->useRegulatorLDO) {
1461 0 : state = setRegulatorLDO();
1462 : } else {
1463 0 : state = setRegulatorDCDC();
1464 : }
1465 0 : return(state);
1466 : }
1467 :
1468 0 : int16_t SX126x::SPIparseStatus(uint8_t in) {
1469 0 : if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_TIMEOUT) {
1470 0 : return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
1471 0 : } else if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_INVALID) {
1472 0 : return(RADIOLIB_ERR_SPI_CMD_INVALID);
1473 0 : } else if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_FAILED) {
1474 0 : return(RADIOLIB_ERR_SPI_CMD_FAILED);
1475 0 : } else if((in == 0x00) || (in == 0xFF)) {
1476 0 : return(RADIOLIB_ERR_CHIP_NOT_FOUND);
1477 : }
1478 0 : return(RADIOLIB_ERR_NONE);
1479 : }
1480 :
1481 0 : bool SX126x::findChip(const char* verStr) {
1482 0 : uint8_t i = 0;
1483 0 : bool flagFound = false;
1484 0 : while((i < 10) && !flagFound) {
1485 : // reset the module
1486 0 : if(this->resetOnStartup) {
1487 0 : reset(true);
1488 : }
1489 :
1490 : // read the version string
1491 0 : char version[16] = { 0 };
1492 0 : this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast<uint8_t*>(version));
1493 :
1494 : // check version register
1495 0 : if(strncmp(verStr, version, 6) == 0) {
1496 : RADIOLIB_DEBUG_BASIC_PRINTLN("Found SX126x: RADIOLIB_SX126X_REG_VERSION_STRING:");
1497 : RADIOLIB_DEBUG_BASIC_HEXDUMP(reinterpret_cast<uint8_t*>(version), 16, RADIOLIB_SX126X_REG_VERSION_STRING);
1498 : RADIOLIB_DEBUG_BASIC_PRINTLN();
1499 0 : flagFound = true;
1500 : } else {
1501 : #if RADIOLIB_DEBUG_BASIC
1502 : RADIOLIB_DEBUG_BASIC_PRINTLN("SX126x not found! (%d of 10 tries) RADIOLIB_SX126X_REG_VERSION_STRING:", i + 1);
1503 : RADIOLIB_DEBUG_BASIC_HEXDUMP(reinterpret_cast<uint8_t*>(version), 16, RADIOLIB_SX126X_REG_VERSION_STRING);
1504 : RADIOLIB_DEBUG_BASIC_PRINTLN("Expected string: %s", verStr);
1505 : #endif
1506 0 : this->mod->hal->delay(10);
1507 0 : i++;
1508 : }
1509 : }
1510 :
1511 0 : return(flagFound);
1512 : }
1513 :
1514 : #endif
|