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