Skyward boardcore
Loading...
Searching...
No Matches
SX1278Lora.cpp
Go to the documentation of this file.
1/* Copyright (c) 2022 Skyward Experimental Rocketry
2 * Author: Davide Mor
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "SX1278Lora.h"
24
25#include <utils/Debug.h>
26
27#include "SX1278LoraTimings.h"
28
29namespace Boardcore
30{
31
32using namespace SX1278;
33using namespace SX1278::Lora;
34
35static constexpr uint8_t MAX_PAYLOAD_LENGTH = 0xff;
36static constexpr uint8_t FIFO_TX_BASE_ADDR = 0x00;
37static constexpr uint8_t FIFO_RX_BASE_ADDR = 0x00;
38
39// This doesn't really matter, as we are going to change it anyway
40static constexpr DioMapping DEFAULT_MAPPING =
41 DioMapping(0, 0, 0, 0, 0, 0, false);
42
43// This registers allow for optmized rx spurious response
45{
46 int freq_rf = 0;
49 bool automatic_if_on = false;
52
54 {
55 ErrataRegistersValues values = {};
56
57 switch (bw)
58 {
60 freq_rf += 7810;
61 break;
63 freq_rf += 10420;
64 break;
66 freq_rf += 15620;
67 break;
69 freq_rf += 20830;
70 break;
72 freq_rf += 41670;
73 break;
74 default:
75 break;
76 }
77
78 switch (bw)
79 {
81 values.reg_if_freq_2 = 0x48;
82 values.reg_if_freq_1 = 0x00;
83 break;
89 values.reg_if_freq_2 = 0x44;
90 values.reg_if_freq_1 = 0x00;
91 break;
95 values.reg_if_freq_2 = 0x40;
96 values.reg_if_freq_1 = 0x00;
97 break;
99 values.reg_if_freq_2 = -1;
100 values.reg_if_freq_1 = -1;
101 break;
102 default:
103 break;
104 }
105
107
108 bool freq_rf_low_range = 410000000 <= freq_rf && freq_rf <= 525000000;
109 bool freq_rf_high_range = 862000000 <= freq_rf && freq_rf <= 1020000000;
110
111 if (bw == RegModemConfig1::BW_HZ_500000 && freq_rf_low_range)
112 {
113 values.reg_high_bw_optimize_1 = 0x02;
114 values.reg_high_bw_optimize_2 = 0x64;
115 }
116 else if (bw == RegModemConfig1::BW_HZ_500000 && freq_rf_high_range)
117 {
118 values.reg_high_bw_optimize_1 = 0x02;
119 values.reg_high_bw_optimize_2 = 0x7f;
120 }
121 else
122 {
123 values.reg_high_bw_optimize_1 = 0x03;
124 values.reg_high_bw_optimize_2 = -1;
125 }
126
127 values.freq_rf = freq_rf;
128
129 return values;
130 }
131};
132
134{
135 // First probe for the device
136 if (!checkVersion())
137 return Error::BAD_VALUE;
138
139 Error err;
140 if ((err = configure(config)) != Error::NONE)
141 return err;
142
143 return Error::NONE;
144}
145
147{
148 Lock guard(*this);
150
152 if (version == 0x12)
153 {
154 return true;
155 }
156 else
157 {
158 LOG_ERR(logger, "Wrong chip id: {}", version);
159 return false;
160 }
161}
162
164{
165 // Check that the configuration is actually valid
167 int min_power = pa_boost ? 2 : 0;
169
170 assert(config.power >= min_power && config.power <= max_power &&
171 "[sx1278] Configured power invalid for given frontend!");
172 assert(((config.ocp >= 0 && config.ocp <= 120) ||
173 (config.ocp >= 130 && config.ocp <= 240)) &&
174 "[sx1278] Invalid ocp!");
175 assert(config.freq_rf >= MIN_FREQ_RF && config.freq_rf <= MAX_FREQ_RF &&
176 "[sx1278] Invalid freq_rf");
177
178 // First make sure the device is in lora mode and in standby
179 enterLoraMode();
180
181 // Then make sure the device remains in standby and not in sleep
183 InterruptTrigger::RISING_EDGE, false, false);
184
185 // Lock the bus
186 Lock guard(*this);
189
192 static_cast<RegModemConfig1::Cr>(config.coding_rate);
194 static_cast<RegModemConfig2::Sf>(config.spreading_factor);
195
196 int freq_rf = std::max(std::min(config.freq_rf, MAX_FREQ_RF), MIN_FREQ_RF);
197 int ocp = config.ocp <= 120 ? std::max(std::min(config.ocp, 120), 0)
198 : std::max(std::min(config.ocp, 240), 130);
199 int power = std::max(std::min(config.power, max_power), min_power);
200
201 bool low_data_rate_optimize = config.low_data_rate_optimize;
202
203 // Mandated for when the symbol length exceeds 16ms
205 low_data_rate_optimize = true;
206
207 // Setup high BW errata values
210
211 crc_enabled = config.enable_crc;
212
213 {
215
216 // Setup FIFO sections
217 spi.writeRegister(REG_FIFO_TX_BASE_ADDR, FIFO_TX_BASE_ADDR);
218 spi.writeRegister(REG_FIFO_RX_BASE_ADDR, FIFO_RX_BASE_ADDR);
219 spi.writeRegister(REG_MAX_PAYLOAD_LENGTH, MAX_PAYLOAD_LENGTH);
220
221 // Setup frequency
224
225 // Setup reg power amplifier
226 const int MAX_POWER = 0b111;
227 if (!pa_boost)
228 {
229 // No power amplifier boost
231 RegPaConfig::make(power, MAX_POWER, false));
233 }
234 else if (power != 20)
235 {
236 // Run power amplifier boost but not at full power
238 RegPaConfig::make(power - 2, MAX_POWER, true));
240 }
241 else
242 {
243 // Run power amplifier at full power
245 RegPaConfig::make(0b1111, MAX_POWER, true));
247 }
248
249 // Setup reg over-current protection
250 if (config.ocp == 0)
251 spi.writeRegister(REG_OCP, RegOcp::make(0, false));
252 else if (ocp <= 120)
253 spi.writeRegister(REG_OCP, RegOcp::make((ocp - 45) / 5, true));
254 else
255 spi.writeRegister(REG_OCP, RegOcp::make((ocp + 30) / 10, true));
256
257 // Setup generic configuration registers
259 RegModemConfig1::make(false, cr, bw));
261 RegModemConfig2::make(crc_enabled, false, sf));
263 RegModemConfig3::make(true, low_data_rate_optimize));
264
265 // Setup detect optimize (lots of magic values here)
266 spi.writeRegister(
268 RegDetectOptimize::make(0x03, errata_values.automatic_if_on));
270
271 // (just change it to something that isn't the default)
272 spi.writeRegister(REG_SYNC_WORD, 0x69);
273
274 // Setup weird errata registers (see errata note)
275 if (errata_values.reg_high_bw_optimize_1 != -1)
276 {
278 errata_values.reg_high_bw_optimize_1);
279 }
280 if (errata_values.reg_high_bw_optimize_2 != -1)
281 {
283 errata_values.reg_high_bw_optimize_2);
284 }
285 if (errata_values.reg_if_freq_1 != -1)
286 spi.writeRegister(REG_IF_FREQ_1, errata_values.reg_if_freq_1);
287 if (errata_values.reg_if_freq_2 != -1)
288 spi.writeRegister(REG_IF_FREQ_2, errata_values.reg_if_freq_2);
289 }
290
291 return Error::NONE;
292}
293
295{
296 Lock guard(*this);
297
298 // Use continuous because it doesn't go into timeout (and you cannot
299 // disable it...)
301 DioMapping(0, 0, 0, 0, 0, 0),
302 InterruptTrigger::RISING_EDGE, false, true);
303
305
306 uint8_t len;
307 {
309 len = spi.readRegister(REG_RX_NB_BYTES);
310 }
311
312 if (len > max_len ||
313 (crc_enabled &&
315 {
316 return -1;
317 }
318
319 // Finally read the contents of the fifo
320 readFifo(FIFO_RX_BASE_ADDR, pkt, len);
321 return len;
322}
323
324bool SX1278Lora::send(uint8_t* pkt, size_t len)
325{
326 if (len > MTU)
327 return false;
328
329 Lock guard(*this);
330
331 {
333
335 writeFifo(FIFO_TX_BASE_ADDR, pkt, len);
336 }
337
338 {
339 // Now enter in mode TX to send the packet
341 DioMapping(1, 0, 0, 0, 0, 0),
342 InterruptTrigger::RISING_EDGE, true, false);
343
344 // Wait for the transmission to end
346 }
347
348 return true;
349}
350
352{
353 float rssi;
354 {
355 Lock guard(*this);
357 rssi =
358 static_cast<float>(spi.readRegister(REG_PKT_RSSI_VALUE)) - 164.0f;
359 }
360
361 float snr = getLastRxSnr();
362 // Handle packets below the noise floor
363 if (snr < 0.0f)
364 rssi += snr * 0.25f;
365
366 return rssi;
367}
368
370{
371 Lock guard(*this);
373 return static_cast<float>(
374 static_cast<int8_t>(spi.readRegister(REG_PKT_SNR_VALUE))) /
375 4.0f;
376}
377
378void SX1278Lora::enterLoraMode()
379{
380 Lock guard(*this);
382
383 // First enter LoRa sleep
384 spi.writeRegister(REG_OP_MODE,
386 miosix::Thread::sleep(1);
387
388 // Then transition to standby
389 spi.writeRegister(REG_OP_MODE,
391 miosix::Thread::sleep(1);
392}
393
394void SX1278Lora::readFifo(uint8_t addr, uint8_t* dst, uint8_t size)
395{
396 SPITransaction spi(getSpiSlave());
397 spi.writeRegister(REG_FIFO_ADDR_PTR, addr);
398 spi.readRegisters(REG_FIFO, dst, size);
399}
400
401void SX1278Lora::writeFifo(uint8_t addr, uint8_t* src, uint8_t size)
402{
403 SPITransaction spi(getSpiSlave());
404 spi.writeRegister(REG_FIFO_ADDR_PTR, addr);
405 spi.writeRegisters(REG_FIFO, src, size);
406}
407
408ISX1278::IrqFlags SX1278Lora::getIrqFlags()
409{
410 SPITransaction spi(getSpiSlave());
411 return spi.readRegister(REG_IRQ_FLAGS);
412}
413
414void SX1278Lora::resetIrqFlags(IrqFlags flags)
415{
416 SPITransaction spi(getSpiSlave());
417 // Register is write clear
418 spi.writeRegister(REG_IRQ_FLAGS, flags);
419}
420
421void SX1278Lora::setMode(ISX1278::Mode mode)
422{
423 SPITransaction spi(getSpiSlave());
424
425 spi.writeRegister(
427 RegOpMode::make(static_cast<RegOpMode::Mode>(mode), true, false));
428}
429
430void SX1278Lora::setMapping(SX1278::DioMapping mapping)
431{
432 SPITransaction spi(getSpiSlave());
433 spi.writeRegister16(REG_DIO_MAPPING_1, mapping.raw);
434}
435
436} // namespace Boardcore
#define LOG_ERR(logger,...)
Provides high-level access to the SPI Bus for a single transaction.
uint8_t readRegister(uint8_t reg)
Reads an 8 bit register.
void writeRegister24(uint8_t reg, uint32_t data)
Writes a 24 bit register.
void writeRegister(uint8_t reg, uint8_t data)
Writes an 8 bit register.
virtual bool isOnPaBoost()=0
Is this frontend connected to PA_BOOST or RFO_LF/_HF?
virtual int maxInPower()=0
What is the maximum power supported by this frontend?
RAII scoped bus lock guard.
RAII scoped mode lock, requires a previous lock.
void setDefaultMode(Mode mode, DioMapping mapping, InterruptTrigger dio1_trigger, bool set_tx_frontend_on, bool set_rx_frontend_on)
Set default device mode.
IrqFlags checkForIrqAndReset(IrqFlags set_irq, IrqFlags reset_irq)
Returns a mask containing triggered interrupts.
IrqFlags waitForIrq(LockMode &guard, IrqFlags set_irq, IrqFlags reset_irq, bool unlock=false)
Wait for generic irq.
float getLastRxSnr() override
Get the RSSI in dBm, during last packet receive.
bool send(uint8_t *pkt, size_t len) override
Send a packet. The function must block until the packet is sent (successfully or not)
ssize_t receive(uint8_t *pkt, size_t max_len) override
Wait until a new packet is received.
virtual Error init(const Config &config)
Setup the device.
virtual Error configure(const Config &config)
Configure this device on the fly.
static constexpr size_t MTU
Definition SX1278Lora.h:36
float getLastRxRssi() override
Get the RSSI in dBm, during last packet receive.
constexpr uint8_t make(uint8_t ocp_trim, bool ocp_on)
Definition SX1278Defs.h:223
constexpr uint8_t make(Mode mode, bool low_frequency_mode_on, ModulationType modulation_type)
Definition SX1278Defs.h:161
constexpr uint8_t make(uint8_t output_power, uint8_t max_power, bool pa_select)
Definition SX1278Defs.h:173
constexpr uint8_t make(PaDac pa_dac)
Definition SX1278Defs.h:438
constexpr uint8_t make(uint8_t detection_optimize, bool automatic_if_on)
Definition SX1278Defs.h:712
constexpr uint8_t make(bool implicit_mode_on, Cr coding_rate, Bw bw)
Definition SX1278Defs.h:669
constexpr uint32_t bandwidthToInt(Bw bw)
Definition SX1278Defs.h:631
constexpr uint8_t make(bool rx_payload_crc_on, bool tx_continuous_mode, Sf spreading_factor)
Definition SX1278Defs.h:690
constexpr uint8_t make(bool agc_auto_on, bool low_data_rate_optimize)
Definition SX1278Defs.h:702
constexpr uint32_t symbolDuration(uint32_t spreading_factor, uint32_t bandwidth)
Computes the symbol duration in microseconds.
constexpr float FSTEP
Frequency step (Hz) used in some calculations.
Definition SX1278Defs.h:43
RegDioMapping::Mapping DioMapping
constexpr int MIN_FREQ_RF
Definition SX1278Defs.h:61
constexpr int MAX_FREQ_RF
Definition SX1278Defs.h:62
This file includes all the types the logdecoder script will decode.
constexpr DioMapping DEFAULT_MAPPING
Definition SX1278Fsk.cpp:44
static ErrataRegistersValues calculate(RegModemConfig1::Bw bw, int freq_rf)
Requested SX1278 configuration.
Definition SX1278Lora.h:42