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