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