|
#include "loramodem.h" |
|
|
|
#include <string.h> |
|
#include <stdio.h> |
|
|
|
#include "periph/spi.h" |
|
|
|
uint8_t read_reg(uint8_t regid) { |
|
int res = spi_acquire(SPI_DEV(0), SPI_HWCS(0), SPI_MODE_0, SPI_CLK_5MHZ); |
|
if (res == SPI_OK) { |
|
uint8_t buf[2] = {(uint8_t)(regid & 0x7f), 0x00}; |
|
spi_transfer_bytes(SPI_DEV(0), SPI_HWCS(0), false, buf, buf, 2); |
|
spi_release(SPI_DEV(0)); |
|
return buf[1]; |
|
} |
|
return 0; |
|
} |
|
|
|
uint8_t write_reg(uint8_t regid, uint8_t value) { |
|
uint8_t buf[2] = {(uint8_t)(regid | 0x80), value}; |
|
int res = spi_acquire(SPI_DEV(0), SPI_HWCS(0), SPI_MODE_0, SPI_CLK_5MHZ); |
|
if (res == SPI_OK) { |
|
spi_transfer_bytes(SPI_DEV(0), SPI_HWCS(0), false, buf, buf, 2); |
|
spi_release(SPI_DEV(0)); |
|
return buf[1]; |
|
} |
|
return 0; |
|
} |
|
|
|
static uint8_t write_reg_burst(uint8_t regid, uint8_t * val, uint8_t len) { |
|
uint8_t buf[1] = {(uint8_t)(regid | 0x80)}; |
|
int res = spi_acquire(SPI_DEV(0), SPI_HWCS(0), SPI_MODE_0, SPI_CLK_5MHZ); |
|
if (res == SPI_OK) { |
|
spi_transfer_bytes(SPI_DEV(0), SPI_HWCS(0), true, buf, NULL, 1); |
|
spi_transfer_bytes(SPI_DEV(0), SPI_HWCS(0), false, val, NULL, len); |
|
spi_release(SPI_DEV(0)); |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
/** |
|
* Reads register at addr, replaces the masked bytes with value and writes all back. |
|
* |
|
* value should already be shifted correctly |
|
*/ |
|
static void write_reg_masked(uint8_t addr, uint8_t mask, uint8_t value) { |
|
write_reg(addr, (mask & value) | (~mask & read_reg(addr))); |
|
} |
|
|
|
int loramodem_setup(void) { |
|
// Check the modem version |
|
uint8_t version = read_reg(REG1276_VERSION); |
|
if (version != VERSION_SX1276) { |
|
printf("Got version %02x\n",version); |
|
return 1; |
|
} |
|
|
|
// Go to sleep mode (required to switch to LoRa mode) |
|
loramodem_set_opmode(OPMODE_SLEEP); |
|
|
|
// Set LoRa mode |
|
write_reg_masked(REG1276_OPMODE, MSK1276_OPMODE_MODULATION, VAL1276_OPMODE_MODULATION_LORA); |
|
|
|
// Enable AGC auto on |
|
write_reg_masked(REG1276_LORA_MODEMCONFIG3, MSK1276_LORA_MODEMCONFIG3_AGCAUTOON, 0xff); |
|
|
|
// Disable channel hopping |
|
write_reg(REG1276_LORA_HOPPERIOD, 0x00); |
|
|
|
// Payload length settings: We want all payloads, independend of their length: |
|
write_reg(REG1276_LORA_MAXPAYLOADLENGTH,0xFF); |
|
|
|
// LNA to max gain: |
|
write_reg_masked(REG1276_LNA, MSK1276_LNA_GAIN, VAL1276_LNA_GAIN_MAX); |
|
write_reg_masked(REG1276_LNA, MSK1276_LNA_BOOST, VAL1276_LNA_BOOST_ON); |
|
|
|
// For TX power |
|
write_reg(REG1276_PACONFIG, |
|
(MSK1276_PACONFIG_PASELECT & VAL1276_PACONFIG_PASELECT_PABOOST) | |
|
(MSK1276_PACONFIG_OUTPUTPOWER & 0xf) |
|
); |
|
write_reg_masked(REG1276_PADAC, MSK1276_PADAC_PADAC, VAL1276_PADAC_PADAC_DEFAULT); |
|
|
|
// Set the FIFO access pointer to the beginning of the RX buffer |
|
write_reg(REG1276_LORA_FIFOADDRPTR, read_reg(REG1276_LORA_FIFORXBASEADDR)); |
|
|
|
// Set the TX Buffer to the RX buffer |
|
write_reg(REG1276_LORA_FIFOTXBASEADDR, 0x00); |
|
|
|
write_reg(REG1276_LORA_SYMBTIMEOUTLSB, 0x08); |
|
write_reg(REG1276_LORA_PAYLOADLENGTH, 0xFF); |
|
|
|
// Setup a default channel |
|
loramodem_set_frequency(868100000); |
|
loramodem_set_sf(7); |
|
loramodem_set_bw(BW125); |
|
loramodem_set_cr(CR4_5); |
|
loramodem_set_syncword(0x12); |
|
|
|
// Go to standby mode |
|
loramodem_set_opmode(OPMODE_STANDBY); |
|
|
|
return 0; |
|
} |
|
|
|
void loramodem_set_frequency(uint32_t freq) { |
|
uint64_t frf = (uint64_t)(((uint64_t)freq) << 19) / 32000000; |
|
write_reg(REG1276_FRFMSB, (uint8_t)(frf>>16) ); |
|
write_reg(REG1276_FRFMID, (uint8_t)(frf>> 8) ); |
|
write_reg(REG1276_FRFLSB, (uint8_t)(frf>> 0) ); |
|
} |
|
|
|
uint32_t loramodem_get_frequency(void) { |
|
uint64_t frf = |
|
((uint64_t)(read_reg(REG1276_FRFMSB)) << 16) + |
|
((uint64_t)(read_reg(REG1276_FRFMID)) << 8) + |
|
(uint64_t)(read_reg(REG1276_FRFLSB)); |
|
frf*=32000000; |
|
return frf >> 19; |
|
} |
|
|
|
void loramodem_set_sf(uint8_t sf) { |
|
write_reg_masked(REG1276_LORA_MODEMCONFIG2, MSK1276_LORA_MODEMCONFIG2_SF, sf << 4); |
|
|
|
// For higher SFs, we should set this in MODEMCONFIG3 too: |
|
uint8_t ldo = sf >= 11 ? 1 : 0; |
|
write_reg_masked(REG1276_LORA_MODEMCONFIG3, MSK1276_LORA_MODEMCONFIG3_LOWDATARATEOPTIMIZE, ldo << 3); |
|
} |
|
|
|
uint8_t loramodem_get_sf(void) { |
|
return (read_reg(REG1276_LORA_MODEMCONFIG2) & MSK1276_LORA_MODEMCONFIG2_SF) >> 4; |
|
} |
|
|
|
void loramodem_set_bw(bw_t bw) { |
|
uint8_t bwVal = VAL1276_LORA_MODEMCONFIG1_BW125; |
|
if (bw == BW250) { |
|
bwVal = VAL1276_LORA_MODEMCONFIG1_BW250; |
|
} else if (bw == BW500) { |
|
bwVal = VAL1276_LORA_MODEMCONFIG1_BW500; |
|
} |
|
write_reg_masked(REG1276_LORA_MODEMCONFIG1, MSK1276_LORA_MODEMCONFIG1_BW, bwVal); |
|
} |
|
|
|
bw_t loramodem_get_bw(void) { |
|
uint8_t res = ((read_reg(REG1276_LORA_MODEMCONFIG1) & MSK1276_LORA_MODEMCONFIG1_BW)); |
|
if (res == VAL1276_LORA_MODEMCONFIG1_BW250) return BW250; |
|
if (res == VAL1276_LORA_MODEMCONFIG1_BW500) return BW500; |
|
return BW125; |
|
} |
|
|
|
void loramodem_set_cr(cr_t cr) { |
|
write_reg_masked(REG1276_LORA_MODEMCONFIG1, MSK1276_LORA_MODEMCONFIG1_CR, cr << 1); |
|
} |
|
|
|
cr_t loramodem_get_cr(void) { |
|
return (cr_t)((read_reg(REG1276_LORA_MODEMCONFIG1) & MSK1276_LORA_MODEMCONFIG1_CR) >> 1); |
|
} |
|
|
|
void loramodem_set_syncword(uint8_t syncword) { |
|
write_reg(REG1276_LORA_SYNCWORD, syncword); |
|
} |
|
|
|
uint8_t loramodem_get_syncword(void) { |
|
return read_reg(REG1276_LORA_SYNCWORD); |
|
} |
|
|
|
void loramodem_set_opmode(opmode_t opmode) { |
|
write_reg_masked(REG1276_OPMODE, MSK1276_OPMODE_MODE, opmode); |
|
} |
|
|
|
opmode_t loramodem_get_opmode(void) { |
|
return (opmode_t)(read_reg(REG1276_OPMODE) & MSK1276_OPMODE_MODE); |
|
} |
|
|
|
int loramodem_receive(loraframe_t *frame) { |
|
// Read and reset RX_DONE |
|
uint8_t irq = write_reg(REG1276_LORA_IRQFLAGS, VAL1276_LORA_IRQFLAGS_RXDONE); |
|
|
|
if ((irq & VAL1276_LORA_IRQFLAGS_RXDONE) > 0) { |
|
// Check for CRC error (we just flag it, but don't discard the message) |
|
frame->crcError = (irq & VAL1276_LORA_IRQFLAGS_PAYLOADCRCERROR) > 0; |
|
|
|
// Reset CRCError if necessary |
|
if (frame->crcError) { |
|
write_reg(REG1276_LORA_IRQFLAGS, VAL1276_LORA_IRQFLAGS_PAYLOADCRCERROR); |
|
} |
|
|
|
// Get position of length of the last message |
|
uint8_t rxCurrentAddr = read_reg(REG1276_LORA_RXCURRENTADDR); |
|
frame->payload_len = read_reg(REG1276_LORA_RXNBBYTES); |
|
|
|
// Adjust the fifo to the message and read it |
|
write_reg(REG1276_LORA_FIFOADDRPTR, rxCurrentAddr); |
|
for(int i = 0; i < frame->payload_len; i++) { |
|
frame->payload[i] = read_reg(REG1276_FIFO); |
|
} |
|
|
|
// Return success |
|
return 0; |
|
} else { |
|
// Nothing received |
|
return 2; |
|
} |
|
} |
|
|
|
int loramodem_transmit(loraframe_t *frame, bool awaitTxDone) { |
|
// Assure standby, so that we have fifo access |
|
loramodem_set_opmode(OPMODE_STANDBY); |
|
|
|
// Clear interrupts |
|
write_reg(REG1276_LORA_IRQFLAGS, 0xff); |
|
|
|
// Assure that we get an interrupt for txDone (otherwise awaitTxDone would lead to an endless loop) |
|
write_reg_masked(REG1276_LORA_IRQFLAGSMASK, VAL1276_LORA_IRQFLAGS_TXDONE, 0x00); |
|
|
|
// Configure the position in the fifo buffer to the beginning |
|
write_reg(REG1276_LORA_FIFOTXBASEADDR, 0x00); |
|
write_reg(REG1276_LORA_FIFOADDRPTR, 0x00); |
|
|
|
// Set the length of the message |
|
write_reg(REG1276_LORA_PAYLOADLENGTH, frame->payload_len); |
|
|
|
// Write the data into the fifo buffer |
|
if (write_reg_burst(REG1276_FIFO, frame->payload, frame->payload_len) == 0) { |
|
// Setting the modem to opmode tx will send the content of the fifo |
|
loramodem_set_opmode(OPMODE_TX); |
|
|
|
// If required, wait for txDone: |
|
if (awaitTxDone) { |
|
while(!loramodem_txdone(true)); |
|
} |
|
return 0; |
|
} else { |
|
return 1; |
|
} |
|
} |
|
|
|
bool loramodem_txdone(bool reset) { |
|
if (reset) { |
|
return (write_reg(REG1276_LORA_IRQFLAGS, VAL1276_LORA_IRQFLAGS_TXDONE) & VAL1276_LORA_IRQFLAGS_TXDONE) != 0; |
|
} else { |
|
return (read_reg(REG1276_LORA_IRQFLAGS) & VAL1276_LORA_IRQFLAGS_TXDONE) != 0; |
|
} |
|
} |