Line data Source code
1 : #include "LR_common.h"
2 :
3 : #include <string.h>
4 :
5 18 : LRxxxx::LRxxxx(Module* mod) : PhysicalLayer() {
6 18 : this->mod = mod;
7 18 : this->mod->spiConfig.stream = true;
8 18 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16;
9 18 : this->mod->spiConfig.statusPos = 0;
10 18 : this->mod->spiConfig.parseStatusCb = SPIparseStatus;
11 18 : this->mod->spiConfig.checkStatusCb = SPIcheckStatus;
12 18 : }
13 :
14 5 : uint32_t LRxxxx::getIrqStatus() {
15 : // there is no dedicated "get IRQ" command, the IRQ bits are sent after the status bytes
16 5 : uint8_t buff[6] = { 0 };
17 5 : Module::BitWidth_t statusWidth = mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS];
18 5 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_0;
19 5 : mod->SPItransferStream(NULL, 0, false, NULL, buff, sizeof(buff), true);
20 5 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = statusWidth;
21 5 : uint32_t irq = ((uint32_t)(buff[2]) << 24) | ((uint32_t)(buff[3]) << 16) | ((uint32_t)(buff[4]) << 8) | (uint32_t)buff[5];
22 5 : return(irq);
23 : }
24 :
25 3 : RadioLibTime_t LRxxxx::getToA(size_t len, ModemType_t modem) {
26 3 : DataRate_t dr = {};
27 3 : PacketConfig_t pc = {};
28 3 : size_t packetLen = len;
29 3 : switch(modem) {
30 0 : case ModemType_t::RADIOLIB_MODEM_LORA: {
31 0 : uint8_t cr = this->codingRate;
32 : // We assume same calculation for short and long interleaving, so map CR values 0-4 and 5-7 to the same values
33 0 : if (cr < 5) {
34 0 : cr = cr + 4;
35 0 : } else if (cr == 7) {
36 0 : cr = cr + 1;
37 : }
38 :
39 0 : dr.lora.spreadingFactor = this->spreadingFactor;
40 0 : dr.lora.bandwidth = this->bandwidthHz;
41 0 : dr.lora.codingRate = cr;
42 :
43 0 : pc.lora.preambleLength = this->preambleLengthLoRa;
44 0 : pc.lora.implicitHeader = (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT);
45 0 : pc.lora.crcEnabled = (this->crcTypeLoRa == RADIOLIB_LRXXXX_LORA_CRC_ENABLED);
46 0 : pc.lora.ldrOptimize = (bool)this->ldrOptimize;
47 0 : break;
48 : }
49 :
50 0 : case ModemType_t::RADIOLIB_MODEM_FSK: {
51 0 : dr.fsk.bitRate = this->bitRate;
52 0 : dr.fsk.freqDev = this->frequencyDev;
53 0 : pc.fsk.preambleLength = this->preambleLengthGFSK;
54 0 : pc.fsk.syncWordLength = this->syncWordLength;
55 0 : pc.fsk.crcLength = this->crcLenGFSK;
56 0 : if(this->packetType != 0) {
57 : //! \todo [LRxxxx] Implement ToA calculation for extended GFSK modes (9/15-bit packet lengths)
58 0 : packetLen++;
59 : }
60 0 : break;
61 : }
62 :
63 0 : case ModemType_t::RADIOLIB_MODEM_LRFHSS: {
64 0 : dr.lrFhss.bw = this->lrFhssBw;
65 0 : dr.lrFhss.cr = this->lrFhssCr;
66 0 : dr.lrFhss.narrowGrid = (this->lrFhssGrid == RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_NON_FCC) ? true : false;
67 :
68 0 : pc.lrFhss.hdrCount = this->lrFhssHdrCount;
69 0 : break;
70 : }
71 :
72 3 : default:
73 3 : return(RADIOLIB_ERR_WRONG_MODEM);
74 : }
75 :
76 0 : return(this->calculateTimeOnAir(modem, dr, pc, packetLen));
77 : }
78 :
79 5 : RadioLibTime_t LRxxxx::calculateRxTimeout(RadioLibTime_t timeoutUs) {
80 : // the timeout value is given in units of 30.52 microseconds
81 : // the calling function should provide some extra width, as this number of units is truncated to integer
82 5 : RadioLibTime_t timeout = (RadioLibTime_t) ((float) timeoutUs * (1.0f / 30.52f));
83 : // or can be: RadioLibTime_t timeout = (RadioLibTime_t) (((uint64_t) timeoutUs * 34360) >> 20);
84 5 : return(timeout);
85 : }
86 :
87 0 : int16_t LRxxxx::reset() {
88 : // run the reset sequence
89 0 : this->mod->hal->pinMode(this->mod->getRst(), this->mod->hal->GpioModeOutput);
90 0 : this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelLow);
91 0 : this->mod->hal->delay(10);
92 0 : this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelHigh);
93 :
94 : // the typical transition duration should be 273 ms
95 0 : this->mod->hal->delay(300);
96 :
97 : // wait for BUSY to go low
98 0 : RadioLibTime_t start = this->mod->hal->millis();
99 0 : while(this->mod->hal->digitalRead(this->mod->getGpio())) {
100 0 : this->mod->hal->yield();
101 0 : if(this->mod->hal->millis() - start >= 3000) {
102 : RADIOLIB_DEBUG_BASIC_PRINTLN("BUSY pin timeout after reset!");
103 0 : return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
104 : }
105 : }
106 :
107 0 : return(RADIOLIB_ERR_NONE);
108 : }
109 :
110 5 : int16_t LRxxxx::getStatus(uint8_t* stat1, uint8_t* stat2, uint32_t* irq) {
111 5 : uint8_t buff[6] = { 0 };
112 :
113 : // the status check command doesn't return status in the same place as other read commands
114 : // but only as the first byte (as with any other command), hence LRxxxx::SPIcommand can't be used
115 : // it also seems to ignore the actual command, and just sending in bunch of NOPs will work
116 : // the status bytes are the thing being read here, so they must not be skipped as a reply prefix
117 5 : Module::BitWidth_t statusWidth = this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS];
118 5 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_0;
119 5 : int16_t state = this->mod->SPItransferStream(NULL, 0, false, NULL, buff, sizeof(buff), true);
120 5 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = statusWidth;
121 :
122 : // pass the replies
123 5 : if(stat1) { *stat1 = buff[0]; }
124 5 : if(stat2) { *stat2 = buff[1]; }
125 5 : if(irq) { *irq = ((uint32_t)(buff[2]) << 24) | ((uint32_t)(buff[3]) << 16) | ((uint32_t)(buff[4]) << 8) | (uint32_t)buff[5]; }
126 :
127 5 : return(state);
128 : }
129 :
130 0 : int16_t LRxxxx::lrFhssBuildFrame(uint16_t cmd, uint8_t hdrCount, uint8_t cr, uint8_t grid, uint8_t hop, uint8_t bw, uint16_t hopSeq, int8_t devOffset, const uint8_t* payload, size_t len) {
131 : // check maximum size
132 0 : const uint8_t maxLen[4][4] = {
133 : { 189, 178, 167, 155, },
134 : { 151, 142, 133, 123, },
135 : { 112, 105, 99, 92, },
136 : { 74, 69, 65, 60, },
137 : };
138 0 : if((cr > RADIOLIB_LR_FHSS_CR_1_3) || ((hdrCount - 1) > (int)sizeof(maxLen[0])) || (len > maxLen[cr][hdrCount - 1])) {
139 0 : return(RADIOLIB_ERR_SPI_CMD_INVALID);
140 : }
141 :
142 : // build buffers
143 0 : size_t buffLen = 9 + len;
144 : #if RADIOLIB_STATIC_ONLY
145 : uint8_t dataBuff[9 + 190];
146 : #else
147 0 : uint8_t* dataBuff = new uint8_t[buffLen];
148 : #endif
149 :
150 : // set properties of the packet
151 0 : dataBuff[0] = hdrCount;
152 0 : dataBuff[1] = cr;
153 0 : dataBuff[2] = RADIOLIB_LRXXXX_LR_FHSS_MOD_TYPE_GMSK;
154 0 : dataBuff[3] = grid;
155 0 : dataBuff[4] = hop;
156 0 : dataBuff[5] = bw;
157 0 : dataBuff[6] = (uint8_t)((hopSeq >> 8) & 0x01);
158 0 : dataBuff[7] = (uint8_t)(hopSeq & 0xFF);
159 0 : dataBuff[8] = devOffset;
160 0 : memcpy(&dataBuff[9], payload, len);
161 :
162 0 : int16_t state = this->SPIcommand(cmd, true, dataBuff, buffLen);
163 : #if !RADIOLIB_STATIC_ONLY
164 0 : delete[] dataBuff;
165 : #endif
166 0 : return(state);
167 : }
168 :
169 3 : uint8_t LRxxxx::roundRampTime(uint32_t rampTimeUs) {
170 : uint8_t regVal;
171 :
172 : // Round up the ramp time to nearest discrete register value
173 3 : if(rampTimeUs <= 2) {
174 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_2U;
175 3 : } else if(rampTimeUs <= 4) {
176 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_4U;
177 3 : } else if(rampTimeUs <= 8) {
178 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_8U;
179 3 : } else if(rampTimeUs <= 16) {
180 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_16U;
181 3 : } else if(rampTimeUs <= 32) {
182 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_32U;
183 3 : } else if(rampTimeUs <= 48) {
184 3 : regVal = RADIOLIB_LRXXXX_PA_RAMP_48U;
185 0 : } else if(rampTimeUs <= 64) {
186 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_64U;
187 0 : } else if(rampTimeUs <= 80) {
188 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_80U;
189 0 : } else if(rampTimeUs <= 96) {
190 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_96U;
191 0 : } else if(rampTimeUs <= 112) {
192 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_112U;
193 0 : } else if(rampTimeUs <= 128) {
194 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_128U;
195 0 : } else if(rampTimeUs <= 144) {
196 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_144U;
197 0 : } else if(rampTimeUs <= 160) {
198 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_160U;
199 0 : } else if(rampTimeUs <= 176) {
200 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_176U;
201 0 : } else if(rampTimeUs <= 192) {
202 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_192U;
203 0 : } else if(rampTimeUs <= 208) {
204 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_208U;
205 0 : } else if(rampTimeUs <= 240) {
206 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_240U;
207 0 : } else if(rampTimeUs <= 272) {
208 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_272U;
209 : } else { // 304
210 0 : regVal = RADIOLIB_LRXXXX_PA_RAMP_304U;
211 : }
212 :
213 3 : return regVal;
214 : }
215 :
216 10 : int16_t LRxxxx::setU32(uint16_t cmd, uint32_t u32) {
217 : uint8_t buff[] = {
218 10 : (uint8_t)((u32 >> 24) & 0xFF), (uint8_t)((u32 >> 16) & 0xFF),
219 10 : (uint8_t)((u32 >> 8) & 0xFF), (uint8_t)(u32 & 0xFF),
220 10 : };
221 20 : return(this->SPIcommand(cmd, true, buff, sizeof(buff)));
222 : }
223 :
224 3270 : int16_t LRxxxx::SPIparseStatus(uint8_t in) {
225 3270 : if((in & 0b00001110) == RADIOLIB_LRXXXX_STAT_1_CMD_PERR) {
226 0 : return(RADIOLIB_ERR_SPI_CMD_INVALID);
227 3270 : } else if((in & 0b00001110) == RADIOLIB_LRXXXX_STAT_1_CMD_FAIL) {
228 0 : return(RADIOLIB_ERR_SPI_CMD_FAILED);
229 3270 : } else if((in == 0x00) || (in == 0xFF)) {
230 3270 : return(RADIOLIB_ERR_CHIP_NOT_FOUND);
231 : }
232 0 : return(RADIOLIB_ERR_NONE);
233 : }
234 :
235 0 : int16_t LRxxxx::SPIcheckStatus(Module* mod) {
236 : // the status check command doesn't return status in the same place as other read commands,
237 : // but only as the first byte (as with any other command), hence LR11x0::SPIcommand can't be used
238 : // it also seems to ignore the actual command, and just sending in bunch of NOPs will work
239 0 : uint8_t buff[6] = { 0 };
240 0 : Module::BitWidth_t statusWidth = mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS];
241 0 : mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_0;
242 0 : int16_t state = mod->SPItransferStream(NULL, 0, false, NULL, buff, sizeof(buff), true);
243 0 : mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = statusWidth;
244 0 : RADIOLIB_ASSERT(state);
245 0 : return(LRxxxx::SPIparseStatus(buff[0]));
246 : }
247 :
248 0 : int16_t LRxxxx::writeCommon(uint16_t cmd, uint32_t addrOffset, const uint32_t* data, size_t len, bool nonvolatile) {
249 : // build buffers - later we need to ensure endians are correct,
250 : // so there is probably no way to do this without copying buffers and iterating
251 : // LR2021 has 24-bit address, whereas LR11x0 has 32-bit
252 0 : const size_t addrLen = (this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] >= Module::BITS_32) ? 4 : 3;
253 0 : size_t buffLen = addrLen + len*sizeof(uint32_t);
254 : #if RADIOLIB_STATIC_ONLY
255 : uint8_t dataBuff[sizeof(uint32_t) + RADIOLIB_LRXXXX_SPI_MAX_READ_WRITE_LEN];
256 : #else
257 0 : uint8_t* dataBuff = new uint8_t[buffLen];
258 : #endif
259 :
260 : // set the address or offset
261 0 : uint8_t* dataBuffPtr = reinterpret_cast<uint8_t*>(dataBuff);
262 0 : if(addrLen == 4) {
263 0 : *(dataBuffPtr++) = (uint8_t)((addrOffset >> 24) & 0xFF);
264 : }
265 :
266 0 : *(dataBuffPtr++) = (uint8_t)((addrOffset >> 16) & 0xFF);
267 0 : *(dataBuffPtr++) = (uint8_t)((addrOffset >> 8) & 0xFF);
268 0 : *(dataBuffPtr++) = (uint8_t)(addrOffset & 0xFF);
269 :
270 : // convert endians
271 0 : for(size_t i = 0; i < len; i++) {
272 0 : uint32_t bin = 0;
273 0 : if(nonvolatile) {
274 0 : uint32_t* ptr = const_cast<uint32_t*>(data) + i;
275 0 : bin = RADIOLIB_NONVOLATILE_READ_DWORD(ptr);
276 : } else {
277 0 : bin = data[i];
278 : }
279 0 : *(dataBuffPtr++) = (uint8_t)((bin >> 24) & 0xFF);
280 0 : *(dataBuffPtr++) = (uint8_t)((bin >> 16) & 0xFF);
281 0 : *(dataBuffPtr++) = (uint8_t)((bin >> 8) & 0xFF);
282 0 : *(dataBuffPtr++) = (uint8_t)(bin & 0xFF);
283 : }
284 :
285 0 : int16_t state = this->mod->SPIwriteStream(cmd, dataBuff, buffLen, true, false);
286 : #if !RADIOLIB_STATIC_ONLY
287 0 : delete[] dataBuff;
288 : #endif
289 0 : return(state);
290 : }
291 :
292 204 : int16_t LRxxxx::SPIcommand(uint16_t cmd, bool write, uint8_t* data, size_t len, const uint8_t* out, size_t outLen) {
293 204 : int16_t state = RADIOLIB_ERR_UNKNOWN;
294 204 : if(!write) {
295 : // the SPI interface of LR11x0 requires two separate transactions for reading
296 : // send the 16-bit command
297 142 : state = this->mod->SPIwriteStream(cmd, out, outLen, true, false);
298 142 : RADIOLIB_ASSERT(state);
299 :
300 : // read the result without command
301 0 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_0;
302 0 : state = this->mod->SPIreadStream(RADIOLIB_LRXXXX_CMD_NOP, data, len, true, false);
303 0 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16;
304 :
305 : } else {
306 : // write is just a single transaction
307 62 : state = this->mod->SPIwriteStream(cmd, data, len, true, true);
308 :
309 : }
310 :
311 62 : return(state);
312 : }
|