|
#include "lora_minimalist.h" |
|
#include "lora_minimalist_hal.h" |
|
|
|
unsigned char lora_minimalist_recv_buffer[242]; |
|
volatile size_t lora_minimalist_recv_buffer_filled; |
|
|
|
static struct spi_settings * spi_settings; |
|
static volatile enum { STANDBY, TRANSMITTING, RECEIVING } mode, mode_after_tx; |
|
|
|
static void spi_write_register(uint8_t reg, uint8_t val) { |
|
spi_begin_transaction(spi_settings); |
|
|
|
spi_transfer((uint8_t[2]) { reg | 0x80, val }, 2); |
|
|
|
spi_end_transaction(spi_settings); |
|
} |
|
|
|
static uint8_t spi_read_register(uint8_t reg) { |
|
spi_begin_transaction(spi_settings); |
|
|
|
uint8_t buf[2] = { reg & ~0x80, 0 }; |
|
spi_transfer(buf, 2); |
|
|
|
spi_end_transaction(spi_settings); |
|
return buf[1]; |
|
} |
|
|
|
static void spi_write_many(uint8_t reg, const uint8_t * src, size_t len) { |
|
spi_begin_transaction(spi_settings); |
|
|
|
spi_transfer(&(uint8_t) { reg | 0x80 }, 1); |
|
for (size_t ibyte = 0; ibyte < len; ibyte++) |
|
spi_transfer(&(uint8_t) { src[ibyte] }, 1); |
|
|
|
spi_end_transaction(spi_settings); |
|
} |
|
|
|
static void spi_read_many(uint8_t reg, void * dest, size_t len) { |
|
spi_begin_transaction(spi_settings); |
|
|
|
spi_transfer(&(uint8_t) { reg & ~0x80 }, 1); |
|
spi_transfer(dest, len); |
|
|
|
spi_end_transaction(spi_settings); |
|
} |
|
|
|
static void set_tx_power(unsigned char high) { |
|
/* if high, set +20 dBm on PA_BOOST pin */ |
|
spi_write_register(0x4D, high ? 0x07 : 0x04); |
|
spi_write_register(0x09, 0x80 | (high ? 0x0f : 0)); |
|
} |
|
|
|
static void set_standby(void) { |
|
if (STANDBY == mode) return; |
|
|
|
spi_write_register(0x01, 0x01); |
|
|
|
mode = STANDBY; |
|
} |
|
|
|
void lora_minimalist_set_mode_rx(void) { |
|
if (RECEIVING == mode) return; |
|
|
|
/* request rx continuous mode */ |
|
spi_write_register(0x01, 0x05); |
|
|
|
/* configure register 0x40 (RegDioMapping1), to generate an interrupt on dio0 on rxdone */ |
|
spi_write_register(0x40, 0x00); |
|
|
|
mode = RECEIVING; |
|
} |
|
|
|
static volatile unsigned int wake = 0; |
|
|
|
static void handle_interrupt(void) { |
|
/* this is called whenever the given interrupt pin is driven high by dio0 */ |
|
wake++; |
|
} |
|
|
|
void lora_minimalist_react_to_interrupts(void) { |
|
if (!wake) return; |
|
atomic_decrement_unsigned_int(&wake); |
|
|
|
/* which interrupt(s) fired */ |
|
const uint8_t irq_flags = spi_read_register(0x12); |
|
|
|
if (RECEIVING == mode) { |
|
/* read the reghopchannel register */ |
|
const uint8_t crc_present = spi_read_register(0x1C); |
|
|
|
if ((irq_flags & (0x80 | 0x20)) | !(crc_present & 0x40)) { |
|
/* crc error */ |
|
} |
|
else if (irq_flags & 0x40) { |
|
/* good crc */ |
|
|
|
const uint8_t size_filled = spi_read_register(0x13); |
|
|
|
/* reset fifo */ |
|
spi_write_register(0x0D, spi_read_register(0x10)); |
|
|
|
/* read fifo */ |
|
spi_read_many(0x00, (void *)&lora_minimalist_recv_buffer, size_filled); |
|
|
|
/* todo verify message is actually for us */ |
|
|
|
lora_minimalist_recv_buffer_filled = size_filled; |
|
|
|
set_standby(); |
|
} |
|
} |
|
else if (TRANSMITTING == mode && irq_flags & 0x08) { |
|
/* successfully finished transmitting */ |
|
|
|
if (RECEIVING == mode_after_tx) { |
|
lora_minimalist_set_mode_rx(); |
|
mode_after_tx = STANDBY; |
|
} |
|
else if (STANDBY == mode_after_tx) |
|
set_standby(); |
|
} |
|
|
|
/* clear interrupts, gotta do this twice for reasons */ |
|
spi_write_register(0x12, 0xff); |
|
spi_write_register(0x12, 0xff); |
|
} |
|
|
|
unsigned char lora_minimalist_send(const void * data, size_t len, unsigned char rx_after) { |
|
/* verify len is not greater than maximum */ |
|
|
|
/* make sure we don't interrupt a prior outgoing message */ |
|
if (TRANSMITTING == mode) return 0; |
|
|
|
set_standby(); |
|
|
|
/* possible todo: check for channel activity */ |
|
|
|
/* position fifo address pointer at beginning of fifo */ |
|
spi_write_register(0x0D, 0); |
|
|
|
/* actual payload */ |
|
spi_write_many(0x00, data, len); |
|
spi_write_register(0x22, len); |
|
|
|
/* start transmitting and request an interrupt when finished */ |
|
mode = TRANSMITTING; |
|
mode_after_tx = rx_after ? RECEIVING : STANDBY; |
|
spi_write_register(0x01, 0x03); |
|
|
|
/* configure register 0x40 (RegDioMapping1), to generate an interrupt on dio0 on txdone */ |
|
spi_write_register(0x40, 0x40); |
|
|
|
return 1; |
|
} |
|
|
|
char lora_minimalist_init(const unsigned long millis_now, unsigned char pin_cs, unsigned char pin_reset, unsigned char pin_interrupt) { |
|
static char state = 0; |
|
static unsigned long millis_ref = 0; |
|
|
|
if (0 == state) { |
|
spi_settings = spi_init(1000000, 1, 0, pin_cs); |
|
pin_init_for_output(pin_reset); |
|
pin_set(pin_reset, 1); |
|
|
|
millis_ref = millis_now; |
|
state = 1; |
|
} |
|
|
|
if (1 == state) { |
|
if (millis_now - millis_ref < 100) return 0; |
|
|
|
pin_set(pin_reset, 0); |
|
|
|
millis_ref = millis_now; |
|
state++; |
|
} |
|
|
|
if (2 == state) { |
|
if (millis_now - millis_ref < 10) return 0; |
|
|
|
pin_set(pin_reset, 1); |
|
|
|
millis_ref = millis_now; |
|
state++; |
|
} |
|
|
|
if (3 == state) { |
|
if (millis_now - millis_ref < 10) return 0; |
|
|
|
/* enable lora mode, high frequency, stay in sleep */ |
|
spi_write_register(0x01, 0x80); |
|
|
|
millis_ref = millis_now; |
|
state++; |
|
} |
|
|
|
if (4 == state) { |
|
if (millis_now - millis_ref < 10) return 0; |
|
|
|
if (spi_read_register(0x01) != (0x00 | 0x80)) panic(); |
|
|
|
/* use entire fifo for either tx or rx */ |
|
spi_write_register(0x0E, 0); |
|
spi_write_register(0x0F, 0); |
|
|
|
/* set standby mode */ |
|
spi_write_register(0x01, 0x01); |
|
|
|
/* set bandwidth to 125 kHz, cr 4/5 */ |
|
spi_write_register(0x1D, 0x72); |
|
|
|
/* set spreading factor to 7 (128x), rx crc on */ |
|
spi_write_register(0x1E, 0x74); |
|
|
|
/* enable auto agc */ |
|
spi_write_register(0x26, 0x04); |
|
|
|
/* set preample length to 8 */ |
|
const uint16_t preamble_length = 8; |
|
spi_write_register(0x20, preamble_length >> 8); |
|
spi_write_register(0x21, preamble_length & 0xFF); |
|
|
|
/* set centre frequency to 915 MHz, in steps of 61.035216 Hz */ |
|
const uint32_t centre_freq_in_steps = 14991360; |
|
spi_write_register(0x06, (centre_freq_in_steps >> 16) & 0xFF); |
|
spi_write_register(0x07, (centre_freq_in_steps >> 8) & 0xFF); |
|
spi_write_register(0x08, centre_freq_in_steps & 0xFF); |
|
|
|
/* set power to low */ |
|
set_tx_power(0); |
|
|
|
pin_attach_rising_interrupt(pin_interrupt, handle_interrupt); |
|
|
|
millis_ref = millis_now; |
|
state++; |
|
} |
|
|
|
|
|
if (5 == state) { |
|
if (millis_now - millis_ref < 10) return 0; |
|
|
|
state++; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
unsigned long milliseconds_on_air_given_payload_length(const size_t payload_bytes) { |
|
return (2592 + ( (8UL * payload_bytes + 99) / 28) * 640) / 128; |
|
} |