mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
sx126x: Driver for Semtech sx126x radio modules and optional RF Switch.
This first version of the driver has been tested with STM32WL SoC, which embeddeds SX1262 radio on the same die.
This commit is contained in:
committed by
Ron Evans
parent
43899e1330
commit
b6c750ccd1
@@ -223,13 +223,15 @@ endif
|
||||
@md5sum ./build/test.elf
|
||||
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/touchpaint/
|
||||
@md5sum ./build/test.elf
|
||||
tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
|
||||
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192 xpt2046 \
|
||||
ft6336
|
||||
ft6336 sx126x
|
||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||
|
||||
unit-test:
|
||||
|
||||
@@ -134,6 +134,7 @@ The following 78 devices are supported.
|
||||
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
|
||||
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
|
||||
| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO |
|
||||
| [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-transceiv-ers/sx1261) | SPI |
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package main
|
||||
|
||||
// This example will periodically enable Continuous "Preamble" and "Wave" modes on 868.1 Mhz
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
|
||||
|
||||
"tinygo.org/x/drivers/sx126x"
|
||||
)
|
||||
|
||||
const FREQ = 868100000
|
||||
|
||||
var (
|
||||
loraRadio *sx126x.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
println("\n# TinyGo Lora continuous Wave/Preamble test")
|
||||
println("# -----------------------------------------")
|
||||
|
||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
// Create the driver
|
||||
loraRadio = sx126x.New(machine.SPI3)
|
||||
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
|
||||
|
||||
// Create RF Switch
|
||||
var radioSwitch rfswitch.CustomSwitch
|
||||
loraRadio.SetRfSwitch(radioSwitch)
|
||||
|
||||
state := loraRadio.DetectDevice()
|
||||
if !state {
|
||||
panic("sx126x not detected. ")
|
||||
}
|
||||
|
||||
// Prepare for Lora operation
|
||||
loraConf := sx126x.LoraConfig{
|
||||
Freq: FREQ,
|
||||
Bw: sx126x.SX126X_LORA_BW_500_0,
|
||||
Sf: sx126x.SX126X_LORA_SF9,
|
||||
Cr: sx126x.SX126X_LORA_CR_4_7,
|
||||
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
|
||||
Preamble: 12,
|
||||
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
|
||||
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
|
||||
Crc: sx126x.SX126X_LORA_CRC_ON,
|
||||
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
|
||||
LoraTxPowerDBm: 14,
|
||||
}
|
||||
loraRadio.LoraConfig(loraConf)
|
||||
|
||||
// Although LoraConfig has already configured most of Lora settings,
|
||||
// the following lines are still required to enable Continuous Preamble/Wave
|
||||
loraRadio.SetPacketType(sx126x.SX126X_PACKET_TYPE_LORA)
|
||||
loraRadio.SetRfFrequency(loraConf.Freq)
|
||||
loraRadio.SetModulationParams(loraConf.Sf, loraConf.Bw, loraConf.Cr, loraConf.Ldr)
|
||||
loraRadio.SetTxParams(loraConf.LoraTxPowerDBm, sx126x.SX126X_PA_RAMP_200U)
|
||||
|
||||
for {
|
||||
println("2 seconds in Continuous Preamble")
|
||||
loraRadio.SetStandby()
|
||||
loraRadio.SetTxContinuousPreamble()
|
||||
time.Sleep(2 * time.Second)
|
||||
println("Continuous Preamble Stopped")
|
||||
|
||||
loraRadio.SetStandby()
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
println("2 seconds in Continuous Wave")
|
||||
loraRadio.SetTxContinuousWave()
|
||||
time.Sleep(2 * time.Second)
|
||||
println(" Continuous Wave Stopped")
|
||||
|
||||
loraRadio.SetStandby()
|
||||
time.Sleep(60 * time.Second)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
// In this example, a Lora packet will be sent every 10s
|
||||
// module will be in RX mode between two transmissions
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
|
||||
|
||||
"tinygo.org/x/drivers/sx126x"
|
||||
)
|
||||
|
||||
const FREQ = 868100000
|
||||
|
||||
const (
|
||||
LORA_DEFAULT_RXTIMEOUT_MS = 1000
|
||||
LORA_DEFAULT_TXTIMEOUT_MS = 5000
|
||||
)
|
||||
|
||||
var (
|
||||
loraRadio *sx126x.Device
|
||||
txmsg = []byte("Hello TinyGO")
|
||||
)
|
||||
|
||||
// radioIntHandler will take care of radio interrupts
|
||||
func radioIntHandler(intr interrupt.Interrupt) {
|
||||
loraRadio.HandleInterrupt()
|
||||
}
|
||||
|
||||
func main() {
|
||||
println("\n# TinyGo Lora RX/TX test")
|
||||
println("# ----------------------")
|
||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
// Create the driver
|
||||
loraRadio = sx126x.New(machine.SPI3)
|
||||
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
|
||||
|
||||
// Create RF Switch
|
||||
var radioSwitch rfswitch.CustomSwitch
|
||||
loraRadio.SetRfSwitch(radioSwitch)
|
||||
|
||||
// Detect the device
|
||||
state := loraRadio.DetectDevice()
|
||||
if !state {
|
||||
panic("sx126x not detected.")
|
||||
}
|
||||
|
||||
// Add interrupt handler for Radio IRQs
|
||||
intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler)
|
||||
intr.Enable()
|
||||
|
||||
loraConf := sx126x.LoraConfig{
|
||||
Freq: FREQ,
|
||||
Bw: sx126x.SX126X_LORA_BW_500_0,
|
||||
Sf: sx126x.SX126X_LORA_SF9,
|
||||
Cr: sx126x.SX126X_LORA_CR_4_7,
|
||||
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
|
||||
Preamble: 12,
|
||||
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
|
||||
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
|
||||
Crc: sx126x.SX126X_LORA_CRC_ON,
|
||||
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
|
||||
LoraTxPowerDBm: 20,
|
||||
}
|
||||
|
||||
loraRadio.LoraConfig(loraConf)
|
||||
|
||||
var count uint
|
||||
for {
|
||||
tStart := time.Now()
|
||||
|
||||
// Blocking RX for LORA_DEFAULT_RXTIMEOUT_MS
|
||||
println("Start Lora RX for 10 sec")
|
||||
for int(time.Now().Sub(tStart).Seconds()) < 10 {
|
||||
buf, err := loraRadio.LoraRx(LORA_DEFAULT_RXTIMEOUT_MS)
|
||||
|
||||
if err != nil {
|
||||
println("RX Error: ", err)
|
||||
} else if buf != nil {
|
||||
println("Packet Received: len=", len(buf), string(buf))
|
||||
}
|
||||
}
|
||||
println("END Lora RX")
|
||||
|
||||
println("LORA TX size=", len(txmsg))
|
||||
err := loraRadio.LoraTx(txmsg, LORA_DEFAULT_TXTIMEOUT_MS)
|
||||
if err != nil {
|
||||
println("TX Error:", err)
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//go:build gnse
|
||||
// +build gnse
|
||||
|
||||
/*
|
||||
Generic Node Sensor Edition
|
||||
RFSwitch
|
||||
|
||||
Disable Switch : PB8=OFF PA0=OFF PA1=OFF
|
||||
Enable RX : PB8=ON PA0=ON PA1=OFF
|
||||
Enable TX RFO LP : PB8=ON PA0=ON PA1=ON
|
||||
Enable TX RFO HP : PB8=ON PA0=OFF PA1=ON
|
||||
*/
|
||||
package rfswitch
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/sx126x"
|
||||
)
|
||||
|
||||
type CustomSwitch struct {
|
||||
}
|
||||
|
||||
var (
|
||||
rfstate int
|
||||
)
|
||||
|
||||
func (s CustomSwitch) InitRFSwitch() {
|
||||
machine.RF_FE_CTRL1.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RF_FE_CTRL2.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RF_FE_CTRL3.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
rfstate = -1 //Unknown
|
||||
}
|
||||
|
||||
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
|
||||
if mode == rfstate {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
|
||||
case sx126x.RFSWITCH_TX_HP:
|
||||
machine.RF_FE_CTRL1.Set(false)
|
||||
machine.RF_FE_CTRL2.Set(true)
|
||||
machine.RF_FE_CTRL3.Set(true)
|
||||
|
||||
case sx126x.RFSWITCH_TX_LP:
|
||||
machine.RF_FE_CTRL1.Set(true)
|
||||
machine.RF_FE_CTRL2.Set(true)
|
||||
machine.RF_FE_CTRL3.Set(true)
|
||||
|
||||
case sx126x.RFSWITCH_RX:
|
||||
machine.RF_FE_CTRL1.Set(true)
|
||||
machine.RF_FE_CTRL2.Set(false)
|
||||
machine.RF_FE_CTRL3.Set(true)
|
||||
}
|
||||
|
||||
rfstate = mode
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//go:build lorae5
|
||||
// +build lorae5
|
||||
|
||||
package radio
|
||||
|
||||
/*
|
||||
/!\ LoRa-E5 module ONLY transmits through RFO_HP:
|
||||
|
||||
Receive: PA4=1, PA5=0
|
||||
Transmit(high output power, SMPS mode): PA4=0, PA5=1
|
||||
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/sx126x"
|
||||
)
|
||||
|
||||
type CustomSwitch struct {
|
||||
}
|
||||
|
||||
func (s CustomSwitch) InitRFSwitch() {
|
||||
machine.PA4.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.PB5.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
|
||||
switch mode {
|
||||
|
||||
case sx126x.RFSWITCH_RX:
|
||||
machine.PA4.Set(true)
|
||||
machine.PB5.Set(false)
|
||||
case sx126x.RFSWITCH_TX_LP:
|
||||
errors.New("RFSWITCH_TX_LP not supported ")
|
||||
case sx126x.RFSWITCH_TX_HP:
|
||||
machine.PA4.Set(false)
|
||||
machine.PB5.Set(true)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//go:build nucleowl55jc
|
||||
// +build nucleowl55jc
|
||||
|
||||
/*
|
||||
Nucleo WL55JC1
|
||||
RFSwitch
|
||||
|
||||
+-----------+---------+------------+------------+
|
||||
| | FE_CTRL1 | FE_CTRL2 | FE_CTRL3 |
|
||||
| | (PC4) | (PC5) | (PC3) |
|
||||
+-----------+----------+-----------+------------+
|
||||
| TX_HP | LOW | HIGH | HIGH |
|
||||
| TX_LP | HIGH | HIGH | HIGH |
|
||||
| RX | HIGH | LOW | HIGH |
|
||||
+-----------+----------+-----------+------------+
|
||||
*/
|
||||
package rfswitch
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/sx126x"
|
||||
)
|
||||
|
||||
type CustomSwitch struct {
|
||||
}
|
||||
|
||||
func (s CustomSwitch) InitRFSwitch() {
|
||||
machine.PC4.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.PC5.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.PC3.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
|
||||
switch mode {
|
||||
|
||||
case sx126x.RFSWITCH_TX_HP:
|
||||
machine.PC4.Set(false)
|
||||
machine.PC5.Set(true)
|
||||
machine.PC3.Set(true)
|
||||
|
||||
case sx126x.RFSWITCH_TX_LP:
|
||||
machine.PC4.Set(true)
|
||||
machine.PC5.Set(true)
|
||||
machine.PC3.Set(true)
|
||||
|
||||
case sx126x.RFSWITCH_RX:
|
||||
machine.PC4.Set(true)
|
||||
machine.PC5.Set(false)
|
||||
machine.PC3.Set(true)
|
||||
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
package sx126x
|
||||
|
||||
const (
|
||||
// SX126X physical layer properties
|
||||
SX126X_FREQUENCY_STEP_SIZE = 0.9536743164
|
||||
SX126X_MAX_PACKET_LENGTH = 255
|
||||
SX126X_CRYSTAL_FREQ = 32.0
|
||||
SX126X_DIV_EXPONENT = 25
|
||||
|
||||
// SX126X SPI commands
|
||||
// operational modes commands
|
||||
SX126X_CMD_NOP = 0x00
|
||||
SX126X_CMD_SET_SLEEP = 0x84
|
||||
SX126X_CMD_SET_STANDBY = 0x80
|
||||
SX126X_CMD_SET_FS = 0xC1
|
||||
SX126X_CMD_SET_TX = 0x83
|
||||
SX126X_CMD_SET_RX = 0x82
|
||||
SX126X_CMD_STOP_TIMER_ON_PREAMBLE = 0x9F
|
||||
SX126X_CMD_SET_RX_DUTY_CYCLE = 0x94
|
||||
SX126X_CMD_SET_CAD = 0xC5
|
||||
SX126X_CMD_SET_TX_CONTINUOUS_WAVE = 0xD1
|
||||
SX126X_CMD_SET_TX_INFINITE_PREAMBLE = 0xD2
|
||||
SX126X_CMD_SET_REGULATOR_MODE = 0x96
|
||||
SX126X_CMD_CALIBRATE = 0x89
|
||||
SX126X_CMD_CALIBRATE_IMAGE = 0x98
|
||||
SX126X_CMD_SET_PA_CONFIG = 0x95
|
||||
SX126X_CMD_SET_RX_TX_FALLBACK_MODE = 0x93
|
||||
|
||||
// register and buffer access commands
|
||||
SX126X_CMD_WRITE_REGISTER = 0x0D
|
||||
SX126X_CMD_READ_REGISTER = 0x1D
|
||||
SX126X_CMD_WRITE_BUFFER = 0x0E
|
||||
SX126X_CMD_READ_BUFFER = 0x1E
|
||||
|
||||
// DIO and IRQ control
|
||||
SX126X_CMD_SET_DIO_IRQ_PARAMS = 0x08
|
||||
SX126X_CMD_GET_IRQ_STATUS = 0x12
|
||||
SX126X_CMD_CLEAR_IRQ_STATUS = 0x02
|
||||
SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D
|
||||
SX126X_CMD_SET_DIO3_AS_TCXO_CTRL = 0x97
|
||||
|
||||
// RF, modulation and packet commands
|
||||
SX126X_CMD_SET_RF_FREQUENCY = 0x86
|
||||
SX126X_CMD_SET_PACKET_TYPE = 0x8A
|
||||
SX126X_CMD_GET_PACKET_TYPE = 0x11
|
||||
SX126X_CMD_SET_TX_PARAMS = 0x8E
|
||||
SX126X_CMD_SET_MODULATION_PARAMS = 0x8B
|
||||
SX126X_CMD_SET_PACKET_PARAMS = 0x8C
|
||||
SX126X_CMD_SET_CAD_PARAMS = 0x88
|
||||
SX126X_CMD_SET_BUFFER_BASE_ADDRESS = 0x8F
|
||||
SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT = 0x0A
|
||||
|
||||
// status commands
|
||||
SX126X_CMD_GET_STATUS = 0xC0
|
||||
SX126X_CMD_GET_RSSI_INST = 0x15
|
||||
SX126X_CMD_GET_RX_BUFFER_STATUS = 0x13
|
||||
SX126X_CMD_GET_PACKET_STATUS = 0x14
|
||||
SX126X_CMD_GET_DEVICE_ERRORS = 0x17
|
||||
SX126X_CMD_CLEAR_DEVICE_ERRORS = 0x07
|
||||
SX126X_CMD_GET_STATS = 0x10
|
||||
SX126X_CMD_RESET_STATS = 0x00
|
||||
|
||||
// SX126X register map
|
||||
SX126X_REG_WHITENING_INITIAL_MSB = 0x06B8
|
||||
SX126X_REG_WHITENING_INITIAL_LSB = 0x06B9
|
||||
SX126X_REG_CRC_INITIAL_MSB = 0x06BC
|
||||
SX126X_REG_CRC_INITIAL_LSB = 0x06BD
|
||||
SX126X_REG_CRC_POLYNOMIAL_MSB = 0x06BE
|
||||
SX126X_REG_CRC_POLYNOMIAL_LSB = 0x06BF
|
||||
SX126X_REG_SYNC_WORD_0 = 0x06C0
|
||||
SX126X_REG_SYNC_WORD_1 = 0x06C1
|
||||
SX126X_REG_SYNC_WORD_2 = 0x06C2
|
||||
SX126X_REG_SYNC_WORD_3 = 0x06C3
|
||||
SX126X_REG_SYNC_WORD_4 = 0x06C4
|
||||
SX126X_REG_SYNC_WORD_5 = 0x06C5
|
||||
SX126X_REG_SYNC_WORD_6 = 0x06C6
|
||||
SX126X_REG_SYNC_WORD_7 = 0x06C7
|
||||
SX126X_REG_NODE_ADDRESS = 0x06CD
|
||||
SX126X_REG_BROADCAST_ADDRESS = 0x06CE
|
||||
SX126X_REG_LORA_SYNC_WORD_MSB = 0x0740
|
||||
SX126X_REG_LORA_SYNC_WORD_LSB = 0x0741
|
||||
SX126X_REG_RANDOM_NUMBER_0 = 0x0819
|
||||
SX126X_REG_RANDOM_NUMBER_1 = 0x081A
|
||||
SX126X_REG_RANDOM_NUMBER_2 = 0x081B
|
||||
SX126X_REG_RANDOM_NUMBER_3 = 0x081C
|
||||
SX126X_REG_RX_GAIN = 0x08AC
|
||||
SX126X_REG_OCP_CONFIGURATION = 0x08E7
|
||||
SX126X_REG_XTA_TRIM = 0x0911
|
||||
SX126X_REG_XTB_TRIM = 0x0912
|
||||
|
||||
// undocumented registers
|
||||
SX126X_REG_SENSITIVITY_CONFIG = 0x0889 // SX1268 datasheet v1.1, section 15.1
|
||||
SX126X_REG_TX_CLAMP_CONFIG = 0x08D8 // SX1268 datasheet v1.1, section 15.2
|
||||
SX126X_REG_RTC_STOP = 0x0920 // SX1268 datasheet v1.1, section 15.3
|
||||
SX126X_REG_RTC_EVENT = 0x0944 // SX1268 datasheet v1.1, section 15.3
|
||||
SX126X_REG_IQ_CONFIG = 0x0736 // SX1268 datasheet v1.1, section 15.4
|
||||
SX126X_REG_RX_GAIN_RETENTION_0 = 0x029F // SX1268 datasheet v1.1, section 9.6
|
||||
SX126X_REG_RX_GAIN_RETENTION_1 = 0x02A0 // SX1268 datasheet v1.1, section 9.6
|
||||
SX126X_REG_RX_GAIN_RETENTION_2 = 0x02A1 // SX1268 datasheet v1.1, section 9.6
|
||||
|
||||
// SX126X SPI command variables
|
||||
//SX126X_CMD_SET_SLEEP MSB LSB DESCRIPTION
|
||||
SX126X_SLEEP_START_COLD = 0b00000000 // 2 2 sleep mode: cold start, configuration is lost (default)
|
||||
SX126X_SLEEP_START_WARM = 0b00000100 // 2 2 warm start, configuration is retained
|
||||
SX126X_SLEEP_RTC_OFF = 0b00000000 // 0 0 wake on RTC timeout: disabled
|
||||
SX126X_SLEEP_RTC_ON = 0b00000001 // 0 0 enabled
|
||||
|
||||
//SX126X_CMD_SET_STANDBY
|
||||
SX126X_STANDBY_RC = 0x00 // 7 0 standby mode: 13 MHz RC oscillator
|
||||
SX126X_STANDBY_XOSC = 0x01 // 7 0 32 MHz crystal oscillator
|
||||
|
||||
//SX126X_CMD_SET_RX
|
||||
SX126X_RX_TIMEOUT_NONE = 0x000000 // 23 0 Rx timeout duration: no timeout (Rx single mode)
|
||||
SX126X_RX_TIMEOUT_INF = 0xFFFFFF // 23 0 infinite (Rx continuous mode)
|
||||
|
||||
//SX126X_CMD_SET_TX
|
||||
SX126X_TX_TIMEOUT_NONE = 0x000000 // 23 0 Tx timeout duration: no timeout (Tx single mode)
|
||||
|
||||
//SX126X_CMD_STOP_TIMER_ON_PREAMBLE
|
||||
SX126X_STOP_ON_PREAMBLE_OFF = 0x00 // 7 0 stop timer on: sync word or header (default)
|
||||
SX126X_STOP_ON_PREAMBLE_ON = 0x01 // 7 0 preamble detection
|
||||
|
||||
//SX126X_CMD_SET_REGULATOR_MODE
|
||||
SX126X_REGULATOR_LDO = 0x00 // 7 0 set regulator mode: LDO (default)
|
||||
SX126X_REGULATOR_DC_DC = 0x01 // 7 0 DC-DC
|
||||
|
||||
//SX126X_CMD_CALIBRATE
|
||||
SX126X_CALIBRATE_IMAGE_OFF = 0b00000000 // 6 6 image calibration: disabled
|
||||
SX126X_CALIBRATE_IMAGE_ON = 0b01000000 // 6 6 enabled
|
||||
SX126X_CALIBRATE_ADC_BULK_P_OFF = 0b00000000 // 5 5 ADC bulk P calibration: disabled
|
||||
SX126X_CALIBRATE_ADC_BULK_P_ON = 0b00100000 // 5 5 enabled
|
||||
SX126X_CALIBRATE_ADC_BULK_N_OFF = 0b00000000 // 4 4 ADC bulk N calibration: disabled
|
||||
SX126X_CALIBRATE_ADC_BULK_N_ON = 0b00010000 // 4 4 enabled
|
||||
SX126X_CALIBRATE_ADC_PULSE_OFF = 0b00000000 // 3 3 ADC pulse calibration: disabled
|
||||
SX126X_CALIBRATE_ADC_PULSE_ON = 0b00001000 // 3 3 enabled
|
||||
SX126X_CALIBRATE_PLL_OFF = 0b00000000 // 2 2 PLL calibration: disabled
|
||||
SX126X_CALIBRATE_PLL_ON = 0b00000100 // 2 2 enabled
|
||||
SX126X_CALIBRATE_RC13M_OFF = 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled
|
||||
SX126X_CALIBRATE_RC13M_ON = 0b00000010 // 1 1 enabled
|
||||
SX126X_CALIBRATE_RC64K_OFF = 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled
|
||||
SX126X_CALIBRATE_RC64K_ON = 0b00000001 // 0 0 enabled
|
||||
SX126X_CALIBRATE_ALL = 0b01111111 // 6 0 calibrate all blocks
|
||||
|
||||
//SX126X_CMD_CALIBRATE_IMAGE
|
||||
SX126X_CAL_IMG_430_MHZ_1 = 0x6B
|
||||
SX126X_CAL_IMG_430_MHZ_2 = 0x6F
|
||||
SX126X_CAL_IMG_470_MHZ_1 = 0x75
|
||||
SX126X_CAL_IMG_470_MHZ_2 = 0x81
|
||||
SX126X_CAL_IMG_779_MHZ_1 = 0xC1
|
||||
SX126X_CAL_IMG_779_MHZ_2 = 0xC5
|
||||
SX126X_CAL_IMG_863_MHZ_1 = 0xD7
|
||||
SX126X_CAL_IMG_863_MHZ_2 = 0xDB
|
||||
SX126X_CAL_IMG_902_MHZ_1 = 0xE1
|
||||
SX126X_CAL_IMG_902_MHZ_2 = 0xE9
|
||||
|
||||
//SX126X_CMD_SET_PA_CONFIG
|
||||
SX126X_PA_CONFIG_HP_MAX = 0x07
|
||||
SX126X_PA_CONFIG_PA_LUT = 0x01
|
||||
SX126X_PA_CONFIG_SX1262_8 = 0x00
|
||||
|
||||
//SX126X_CMD_SET_RX_TX_FALLBACK_MODE
|
||||
SX126X_RX_TX_FALLBACK_MODE_FS = 0x40 // 7 0 after Rx/Tx go to: FS mode
|
||||
SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC = 0x30 // 7 0 standby with crystal oscillator
|
||||
SX126X_RX_TX_FALLBACK_MODE_STDBY_RC = 0x20 // 7 0 standby with RC oscillator (default)
|
||||
|
||||
//SX126X_CMD_SET_DIO_IRQ_PARAMS
|
||||
SX126X_IRQ_TIMEOUT = 0b1000000000 // 9 9 Rx or Tx timeout
|
||||
SX126X_IRQ_CAD_DETECTED = 0b0100000000 // 8 8 channel activity detected
|
||||
SX126X_IRQ_CAD_DONE = 0b0010000000 // 7 7 channel activity detection finished
|
||||
SX126X_IRQ_CRC_ERR = 0b0001000000 // 6 6 wrong CRC received
|
||||
SX126X_IRQ_HEADER_ERR = 0b0000100000 // 5 5 LoRa header CRC error
|
||||
SX126X_IRQ_HEADER_VALID = 0b0000010000 // 4 4 valid LoRa header received
|
||||
SX126X_IRQ_SYNC_WORD_VALID = 0b0000001000 // 3 3 valid sync word detected
|
||||
SX126X_IRQ_PREAMBLE_DETECTED = 0b0000000100 // 2 2 preamble detected
|
||||
SX126X_IRQ_RX_DONE = 0b0000000010 // 1 1 packet received
|
||||
SX126X_IRQ_TX_DONE = 0b0000000001 // 0 0 packet transmission completed
|
||||
SX126X_IRQ_ALL = 0b1111111111 // 9 0 all interrupts
|
||||
SX126X_IRQ_NONE = 0b0000000000 // 9 0 no interrupts
|
||||
|
||||
//SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL
|
||||
SX126X_DIO2_AS_IRQ = 0x00 // 7 0 DIO2 configuration: IRQ
|
||||
SX126X_DIO2_AS_RF_SWITCH = 0x01 // 7 0 RF switch control
|
||||
|
||||
//SX126X_CMD_SET_DIO3_AS_TCXO_CTRL
|
||||
SX126X_DIO3_OUTPUT_1_6 = 0x00 // 7 0 DIO3 voltage output for TCXO: 1.6 V
|
||||
SX126X_DIO3_OUTPUT_1_7 = 0x01 // 7 0 1.7 V
|
||||
SX126X_DIO3_OUTPUT_1_8 = 0x02 // 7 0 1.8 V
|
||||
SX126X_DIO3_OUTPUT_2_2 = 0x03 // 7 0 2.2 V
|
||||
SX126X_DIO3_OUTPUT_2_4 = 0x04 // 7 0 2.4 V
|
||||
SX126X_DIO3_OUTPUT_2_7 = 0x05 // 7 0 2.7 V
|
||||
SX126X_DIO3_OUTPUT_3_0 = 0x06 // 7 0 3.0 V
|
||||
SX126X_DIO3_OUTPUT_3_3 = 0x07 // 7 0 3.3 V
|
||||
|
||||
//SX126X_CMD_SET_PACKET_TYPE
|
||||
SX126X_PACKET_TYPE_GFSK = 0x00 // 7 0 packet type: GFSK
|
||||
SX126X_PACKET_TYPE_LORA = 0x01 // 7 0 LoRa
|
||||
|
||||
//SX126X_CMD_SET_TX_PARAMS
|
||||
SX126X_PA_RAMP_10U = 0x00 // 7 0 ramp time: 10 us
|
||||
SX126X_PA_RAMP_20U = 0x01 // 7 0 20 us
|
||||
SX126X_PA_RAMP_40U = 0x02 // 7 0 40 us
|
||||
SX126X_PA_RAMP_80U = 0x03 // 7 0 80 us
|
||||
SX126X_PA_RAMP_200U = 0x04 // 7 0 200 us
|
||||
SX126X_PA_RAMP_800U = 0x05 // 7 0 800 us
|
||||
SX126X_PA_RAMP_1700U = 0x06 // 7 0 1700 us
|
||||
SX126X_PA_RAMP_3400U = 0x07 // 7 0 3400 us
|
||||
|
||||
//SX126X_CMD_SET_MODULATION_PARAMS
|
||||
SX126X_GFSK_FILTER_NONE = 0x00 // 7 0 GFSK filter: none
|
||||
SX126X_GFSK_FILTER_GAUSS_0_3 = 0x08 // 7 0 Gaussian, BT = 0.3
|
||||
SX126X_GFSK_FILTER_GAUSS_0_5 = 0x09 // 7 0 Gaussian, BT = 0.5
|
||||
SX126X_GFSK_FILTER_GAUSS_0_7 = 0x0A // 7 0 Gaussian, BT = 0.7
|
||||
SX126X_GFSK_FILTER_GAUSS_1 = 0x0B // 7 0 Gaussian, BT = 1
|
||||
SX126X_GFSK_RX_BW_4_8 = 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz
|
||||
SX126X_GFSK_RX_BW_5_8 = 0x17 // 7 0 5.8 kHz
|
||||
SX126X_GFSK_RX_BW_7_3 = 0x0F // 7 0 7.3 kHz
|
||||
SX126X_GFSK_RX_BW_9_7 = 0x1E // 7 0 9.7 kHz
|
||||
SX126X_GFSK_RX_BW_11_7 = 0x16 // 7 0 11.7 kHz
|
||||
SX126X_GFSK_RX_BW_14_6 = 0x0E // 7 0 14.6 kHz
|
||||
SX126X_GFSK_RX_BW_19_5 = 0x1D // 7 0 19.5 kHz
|
||||
SX126X_GFSK_RX_BW_23_4 = 0x15 // 7 0 23.4 kHz
|
||||
SX126X_GFSK_RX_BW_29_3 = 0x0D // 7 0 29.3 kHz
|
||||
SX126X_GFSK_RX_BW_39_0 = 0x1C // 7 0 39.0 kHz
|
||||
SX126X_GFSK_RX_BW_46_9 = 0x14 // 7 0 46.9 kHz
|
||||
SX126X_GFSK_RX_BW_58_6 = 0x0C // 7 0 58.6 kHz
|
||||
SX126X_GFSK_RX_BW_78_2 = 0x1B // 7 0 78.2 kHz
|
||||
SX126X_GFSK_RX_BW_93_8 = 0x13 // 7 0 93.8 kHz
|
||||
SX126X_GFSK_RX_BW_117_3 = 0x0B // 7 0 117.3 kHz
|
||||
SX126X_GFSK_RX_BW_156_2 = 0x1A // 7 0 156.2 kHz
|
||||
SX126X_GFSK_RX_BW_187_2 = 0x12 // 7 0 187.2 kHz
|
||||
SX126X_GFSK_RX_BW_234_3 = 0x0A // 7 0 234.3 kHz
|
||||
SX126X_GFSK_RX_BW_312_0 = 0x19 // 7 0 312.0 kHz
|
||||
SX126X_GFSK_RX_BW_373_6 = 0x11 // 7 0 373.6 kHz
|
||||
SX126X_GFSK_RX_BW_467_0 = 0x09 // 7 0 467.0 kHz
|
||||
SX126X_LORA_BW_7_8 = 0x00 // 7 0 LoRa bandwidth: 7.8 kHz
|
||||
SX126X_LORA_BW_10_4 = 0x08 // 7 0 10.4 kHz
|
||||
SX126X_LORA_BW_15_6 = 0x01 // 7 0 15.6 kHz
|
||||
SX126X_LORA_BW_20_8 = 0x09 // 7 0 20.8 kHz
|
||||
SX126X_LORA_BW_31_25 = 0x02 // 7 0 31.25 kHz
|
||||
SX126X_LORA_BW_41_7 = 0x0A // 7 0 41.7 kHz
|
||||
SX126X_LORA_BW_62_5 = 0x03 // 7 0 62.5 kHz
|
||||
SX126X_LORA_BW_125_0 = 0x04 // 7 0 125.0 kHz
|
||||
SX126X_LORA_BW_250_0 = 0x05 // 7 0 250.0 kHz
|
||||
SX126X_LORA_BW_500_0 = 0x06 // 7 0 500.0 kHz
|
||||
SX126X_LORA_CR_4_5 = 0x01 // 7 0 LoRa coding rate: 4/5
|
||||
SX126X_LORA_CR_4_6 = 0x02 // 7 0 4/6
|
||||
SX126X_LORA_CR_4_7 = 0x03 // 7 0 4/7
|
||||
SX126X_LORA_CR_4_8 = 0x04 // 7 0 4/8
|
||||
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF = 0x00 // 7 0 LoRa low data rate optimization: disabled
|
||||
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON = 0x01 // 7 0 enabled
|
||||
|
||||
//SX126X_CMD_SET_PACKET_PARAMS
|
||||
SX126X_GFSK_PREAMBLE_DETECT_OFF = 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled
|
||||
SX126X_GFSK_PREAMBLE_DETECT_8 = 0x04 // 7 0 8 bits
|
||||
SX126X_GFSK_PREAMBLE_DETECT_16 = 0x05 // 7 0 16 bits
|
||||
SX126X_GFSK_PREAMBLE_DETECT_24 = 0x06 // 7 0 24 bits
|
||||
SX126X_GFSK_PREAMBLE_DETECT_32 = 0x07 // 7 0 32 bits
|
||||
SX126X_GFSK_ADDRESS_FILT_OFF = 0x00 // 7 0 GFSK address filtering: disabled
|
||||
SX126X_GFSK_ADDRESS_FILT_NODE = 0x01 // 7 0 node only
|
||||
SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST = 0x02 // 7 0 node and broadcast
|
||||
SX126X_GFSK_PACKET_FIXED = 0x00 // 7 0 GFSK packet type: fixed (payload length known in advance to both sides)
|
||||
SX126X_GFSK_PACKET_VARIABLE = 0x01 // 7 0 variable (payload length added to packet)
|
||||
SX126X_GFSK_CRC_OFF = 0x01 // 7 0 GFSK packet CRC: disabled
|
||||
SX126X_GFSK_CRC_1_BYTE = 0x00 // 7 0 1 byte
|
||||
SX126X_GFSK_CRC_2_BYTE = 0x02 // 7 0 2 byte
|
||||
SX126X_GFSK_CRC_1_BYTE_INV = 0x04 // 7 0 1 byte, inverted
|
||||
SX126X_GFSK_CRC_2_BYTE_INV = 0x06 // 7 0 2 byte, inverted
|
||||
SX126X_GFSK_WHITENING_OFF = 0x00 // 7 0 GFSK data whitening: disabled
|
||||
SX126X_GFSK_WHITENING_ON = 0x01 // 7 0 enabled
|
||||
SX126X_LORA_HEADER_EXPLICIT = 0x00 // 7 0 LoRa header mode: explicit
|
||||
SX126X_LORA_HEADER_IMPLICIT = 0x01 // 7 0 implicit
|
||||
SX126X_LORA_CRC_OFF = 0x00 // 7 0 LoRa CRC mode: disabled
|
||||
SX126X_LORA_CRC_ON = 0x01 // 7 0 enabled
|
||||
SX126X_LORA_IQ_STANDARD = 0x00 // 7 0 LoRa IQ setup: standard
|
||||
SX126X_LORA_IQ_INVERTED = 0x01 // 7 0 inverted
|
||||
|
||||
//SX126X_CMD_SET_CAD_PARAMS
|
||||
SX126X_CAD_ON_1_SYMB = 0x00 // 7 0 number of symbols used for CAD: 1
|
||||
SX126X_CAD_ON_2_SYMB = 0x01 // 7 0 2
|
||||
SX126X_CAD_ON_4_SYMB = 0x02 // 7 0 4
|
||||
SX126X_CAD_ON_8_SYMB = 0x03 // 7 0 8
|
||||
SX126X_CAD_ON_16_SYMB = 0x04 // 7 0 16
|
||||
SX126X_CAD_GOTO_STDBY = 0x00 // 7 0 after CAD is done, always go to STDBY_RC mode
|
||||
SX126X_CAD_GOTO_RX = 0x01 // 7 0 after CAD is done, go to Rx mode if activity is detected
|
||||
|
||||
//SX126X_CMD_GET_STATUS
|
||||
SX126X_STATUS_MODE_STDBY_RC = 0b00100000 // 6 4 current chip mode: STDBY_RC
|
||||
SX126X_STATUS_MODE_STDBY_XOSC = 0b00110000 // 6 4 STDBY_XOSC
|
||||
SX126X_STATUS_MODE_FS = 0b01000000 // 6 4 FS
|
||||
SX126X_STATUS_MODE_RX = 0b01010000 // 6 4 RX
|
||||
SX126X_STATUS_MODE_TX = 0b01100000 // 6 4 TX
|
||||
SX126X_STATUS_DATA_AVAILABLE = 0b00000100 // 3 1 command status: packet received and data can be retrieved
|
||||
SX126X_STATUS_CMD_TIMEOUT = 0b00000110 // 3 1 SPI command timed out
|
||||
SX126X_STATUS_CMD_INVALID = 0b00001000 // 3 1 invalid SPI command
|
||||
SX126X_STATUS_CMD_FAILED = 0b00001010 // 3 1 SPI command failed to execute
|
||||
SX126X_STATUS_TX_DONE = 0b00001100 // 3 1 packet transmission done
|
||||
SX126X_STATUS_SPI_FAILED = 0b11111111 // 7 0 SPI transaction failed
|
||||
|
||||
//SX126X_CMD_GET_PACKET_STATUS
|
||||
SX126X_GFSK_RX_STATUS_PREAMBLE_ERR = 0b10000000 // 7 7 GFSK Rx status: preamble error
|
||||
SX126X_GFSK_RX_STATUS_SYNC_ERR = 0b01000000 // 6 6 sync word error
|
||||
SX126X_GFSK_RX_STATUS_ADRS_ERR = 0b00100000 // 5 5 address error
|
||||
SX126X_GFSK_RX_STATUS_CRC_ERR = 0b00010000 // 4 4 CRC error
|
||||
SX126X_GFSK_RX_STATUS_LENGTH_ERR = 0b00001000 // 3 3 length error
|
||||
SX126X_GFSK_RX_STATUS_ABORT_ERR = 0b00000100 // 2 2 abort error
|
||||
SX126X_GFSK_RX_STATUS_PACKET_RECEIVED = 0b00000010 // 2 2 packet received
|
||||
SX126X_GFSK_RX_STATUS_PACKET_SENT = 0b00000001 // 2 2 packet sent
|
||||
|
||||
//SX126X_CMD_GET_DEVICE_ERRORS
|
||||
SX126X_PA_RAMP_ERR = 0b100000000 // 8 8 device errors: PA ramping failed
|
||||
SX126X_PLL_LOCK_ERR = 0b001000000 // 6 6 PLL failed to lock
|
||||
SX126X_XOSC_START_ERR = 0b000100000 // 5 5 crystal oscillator failed to start
|
||||
SX126X_IMG_CALIB_ERR = 0b000010000 // 4 4 image calibration failed
|
||||
SX126X_ADC_CALIB_ERR = 0b000001000 // 3 3 ADC calibration failed
|
||||
SX126X_PLL_CALIB_ERR = 0b000000100 // 2 2 PLL calibration failed
|
||||
SX126X_RC13M_CALIB_ERR = 0b000000010 // 1 1 RC13M calibration failed
|
||||
SX126X_RC64K_CALIB_ERR = 0b000000001 // 0 0 RC64K calibration failed
|
||||
|
||||
// SX126X SPI register variables
|
||||
//SX126X_REG_LORA_SYNC_WORD_MSB + LSB
|
||||
SX126X_SYNC_WORD_PUBLIC = 0x34 // actually 0x3444 NOTE: The low nibbles in each byte (0x_4_4) are masked out since apparently, they're reserved.
|
||||
SX126X_SYNC_WORD_PRIVATE = 0x12 // actually 0x1424 You couldn't make this up if you tried.
|
||||
|
||||
SX126X_LORA_MAC_PUBLIC_SYNCWORD = 0x3444
|
||||
SX126X_LORA_MAC_PRIVATE_SYNCWORD = 0x1424
|
||||
|
||||
SX126X_LORA_SF5 = 0x05
|
||||
SX126X_LORA_SF6 = 0x06
|
||||
SX126X_LORA_SF7 = 0x07
|
||||
SX126X_LORA_SF8 = 0x08
|
||||
SX126X_LORA_SF9 = 0x09
|
||||
SX126X_LORA_SF10 = 0x0A
|
||||
SX126X_LORA_SF11 = 0x0B
|
||||
SX126X_LORA_SF12 = 0x0C
|
||||
)
|
||||
@@ -0,0 +1,85 @@
|
||||
//go:build stm32wlx
|
||||
// +build stm32wlx
|
||||
|
||||
package sx126x
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"errors"
|
||||
"machine"
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// New creates a new SX126x connection.
|
||||
func New(spi drivers.SPI) *Device {
|
||||
c := make(chan RadioEvent, 10)
|
||||
d := Device{
|
||||
spi: spi,
|
||||
radioEventChan: c,
|
||||
}
|
||||
if d.spi == machine.SPI3 {
|
||||
d.SubGhzInit()
|
||||
d.SetDeviceType(DEVICE_TYPE_SX1262)
|
||||
} else {
|
||||
panic("Driver only support SUBGHZSPI (SPI3) on stm32wlx targets")
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
//SpiSetNss Sets the NSS line
|
||||
func (d *Device) SpiSetNss(state bool) {
|
||||
if state {
|
||||
stm32.PWR.SUBGHZSPICR.SetBits(stm32.PWR_SUBGHZSPICR_NSS)
|
||||
} else {
|
||||
stm32.PWR.SUBGHZSPICR.ClearBits(stm32.PWR_SUBGHZSPICR_NSS)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// WaitBusy sleep until all busy flags clears
|
||||
func (d *Device) WaitBusy() error {
|
||||
count := 100
|
||||
var rfbusyms, rfbusys bool
|
||||
for count > 0 {
|
||||
rfbusyms = stm32.PWR.SR2.HasBits(stm32.PWR_SR2_RFBUSYMS)
|
||||
rfbusys = stm32.PWR.SR2.HasBits(stm32.PWR_SR2_RFBUSYS)
|
||||
|
||||
if !(rfbusyms && rfbusys) {
|
||||
return nil
|
||||
}
|
||||
count--
|
||||
}
|
||||
return errors.New("WaitBusy Timeout")
|
||||
}
|
||||
|
||||
// SubGhzInit() configures internal SX1262's SPI bus.
|
||||
func (d *Device) SubGhzInit() {
|
||||
|
||||
// Enable APB3 Periph clock and delay
|
||||
stm32.RCC.APB3ENR.SetBits(stm32.RCC_APB3ENR_SUBGHZSPIEN)
|
||||
_ = stm32.RCC.APB3ENR.Get()
|
||||
|
||||
// Disable radio reset and wait it's ready
|
||||
stm32.RCC.CSR.ClearBits(stm32.RCC_CSR_RFRST)
|
||||
for stm32.RCC.CSR.HasBits(stm32.RCC_CSR_RFRSTF) {
|
||||
}
|
||||
|
||||
// Set NSS line low
|
||||
stm32.PWR.SUBGHZSPICR.SetBits(stm32.PWR_SUBGHZSPICR_NSS)
|
||||
|
||||
// Enable radio busy wakeup from Standby for CPU
|
||||
stm32.PWR.CR3.SetBits(stm32.PWR_CR3_EWRFBUSY)
|
||||
|
||||
// Clear busy flag
|
||||
stm32.PWR.SCR.Set(stm32.PWR_SCR_CWRFBUSYF)
|
||||
|
||||
// Enable SUBGHZ Spi
|
||||
// - /8 Prescaler
|
||||
// - Software Slave Management (NSS)
|
||||
// - FIFO Threshold and 8bit size
|
||||
stm32.SPI3.CR1.ClearBits(stm32.SPI_CR1_SPE)
|
||||
stm32.SPI3.CR1.Set(stm32.SPI_CR1_MSTR | stm32.SPI_CR1_SSI | (0b010 << 3) | stm32.SPI_CR1_SSM)
|
||||
stm32.SPI3.CR2.Set(stm32.SPI_CR2_FRXTH | (0b111 << 8))
|
||||
stm32.SPI3.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
// Package sx126x provides a driver for SX126x LoRa transceivers.
|
||||
// Inspired from https://github.com/Lora-net/sx126x_driver/
|
||||
|
||||
package sx126x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// SX126X radio transceiver RF_IN and RF_OUT may be connected
|
||||
// to RF Switch. This interface allows the creation of struct
|
||||
// that can drive the RF Switch (Used in Lora RX and Lora Tx)
|
||||
type RFSwitch interface {
|
||||
InitRFSwitch()
|
||||
SetRfSwitchMode(mode int) error
|
||||
}
|
||||
|
||||
const (
|
||||
DEVICE_TYPE_SX1261 = iota
|
||||
DEVICE_TYPE_SX1262 = iota
|
||||
DEVICE_TYPE_SX1268 = iota
|
||||
)
|
||||
|
||||
const (
|
||||
RFSWITCH_RX = iota
|
||||
RFSWITCH_TX_LP = iota
|
||||
RFSWITCH_TX_HP = iota
|
||||
)
|
||||
|
||||
const (
|
||||
RadioEventRxDone = iota
|
||||
RadioEventTxDone = iota
|
||||
RadioEventTimeout = iota
|
||||
RadioEventWatchdog = iota
|
||||
RadioEventCrcError = iota
|
||||
RadioEventUnhandled = iota
|
||||
)
|
||||
|
||||
// RadioEvent are used for communicating in the radio Event Channel
|
||||
type RadioEvent struct {
|
||||
EventType int
|
||||
IRQStatus uint16
|
||||
EventData []byte
|
||||
}
|
||||
|
||||
const (
|
||||
PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4
|
||||
SPI_BUFFER_SIZE = 256
|
||||
)
|
||||
|
||||
// Device wraps an SPI connection to a SX127x device.
|
||||
type Device struct {
|
||||
spi drivers.SPI // SPI bus for module communication
|
||||
radioEventChan chan RadioEvent // Channel for Receiving events
|
||||
loraConf LoraConfig // Current Lora configuration
|
||||
rfswitch RFSwitch // RF Switch, if any
|
||||
deepSleep bool // Internal Sleep state
|
||||
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
|
||||
spiBuffer [SPI_BUFFER_SIZE]uint8
|
||||
}
|
||||
|
||||
// Config holds the LoRa configuration parameters
|
||||
type LoraConfig struct {
|
||||
Freq uint32 // Frequency
|
||||
Cr uint8 // Coding Rate
|
||||
Sf uint8 // Spread Factor
|
||||
Bw uint8 // Bandwidth
|
||||
Ldr uint8 // Low Data Rate
|
||||
Preamble uint16 // PreambleLength
|
||||
SyncWord uint16 // Sync Word
|
||||
HeaderType uint8 // Header : Implicit/explicit
|
||||
Crc uint8 // CRC : Yes/No
|
||||
Iq uint8 // iq : Standard/inverted
|
||||
LoraTxPowerDBm int8 // Tx power in Dbm
|
||||
}
|
||||
|
||||
const (
|
||||
SX126X_RTC_FREQ_IN_HZ uint32 = 64000
|
||||
)
|
||||
|
||||
var (
|
||||
errUndefinedLoraConf = errors.New("Undefined Lora configuration")
|
||||
)
|
||||
|
||||
// --------------------------------------------------
|
||||
// Helper functions
|
||||
// --------------------------------------------------
|
||||
|
||||
// timeoutMsToRtcSteps converts Timeout (in ms) to RTC Steps
|
||||
func timeoutMsToRtcSteps(timeoutMs uint32) uint32 {
|
||||
r := uint32(timeoutMs * (SX126X_RTC_FREQ_IN_HZ / 1000))
|
||||
return r
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Channel and events
|
||||
// --------------------------------------------------
|
||||
//NewRadioEvent() returns a new RadioEvent that can be used in the RadioChannel
|
||||
func NewRadioEvent(eType int, irqStatus uint16, eData []byte) RadioEvent {
|
||||
r := RadioEvent{EventType: eType, IRQStatus: irqStatus, EventData: eData}
|
||||
return r
|
||||
}
|
||||
|
||||
// Get the RadioEvent channel of the device
|
||||
func (d *Device) GetRadioEventChan() chan RadioEvent {
|
||||
return d.radioEventChan
|
||||
}
|
||||
|
||||
// Specify device type (SX1261/2/8)
|
||||
func (d *Device) SetDeviceType(devType int) {
|
||||
d.deviceType = devType
|
||||
}
|
||||
|
||||
// SetRfSwitch let you define a custom RF Switch driver if needed
|
||||
func (d *Device) SetRfSwitch(rfswitch RFSwitch) {
|
||||
d.rfswitch = rfswitch
|
||||
d.rfswitch.InitRFSwitch()
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Operational modes functions
|
||||
// --------------------------------------------------
|
||||
|
||||
// DetectDevice() tries to detect the radio module by changing SyncWord value
|
||||
func (d *Device) DetectDevice() bool {
|
||||
bak := d.GetSyncWord()
|
||||
d.SetSyncWord(0xBEEF)
|
||||
tmp := d.GetSyncWord()
|
||||
if tmp != 0xBEEF {
|
||||
return false
|
||||
} else {
|
||||
d.SetSyncWord(bak)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// SetSleep sets the device in SLEEP mode with the lowest current consumption possible.
|
||||
func (d *Device) SetSleep() {
|
||||
d.ExecSetCommand(SX126X_CMD_SET_SLEEP, []uint8{SX126X_SLEEP_START_WARM | SX126X_SLEEP_RTC_OFF})
|
||||
}
|
||||
|
||||
// SetStandby sets the device in a configuration mode which is at an intermediate level of consumption
|
||||
func (d *Device) SetStandby() {
|
||||
d.ExecSetCommand(SX126X_CMD_SET_STANDBY, []uint8{SX126X_STANDBY_RC})
|
||||
}
|
||||
|
||||
// SetFs sets the device in frequency synthesis mode where the PLL is locked to the carrier frequency.
|
||||
func (d *Device) SetFs() {
|
||||
d.ExecSetCommand(SX126X_CMD_SET_FS, []uint8{})
|
||||
}
|
||||
|
||||
// SetTxContinuousWave set device in test mode to generate a continuous wave (RF tone)
|
||||
func (d *Device) SetTxContinuousWave() {
|
||||
if d.rfswitch != nil {
|
||||
d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
|
||||
}
|
||||
d.ExecSetCommand(SX126X_CMD_SET_TX_CONTINUOUS_WAVE, []uint8{})
|
||||
}
|
||||
|
||||
// SetTxContinuousPreamble set device in test mode to constantly modulate LoRa preamble symbols.
|
||||
// Take care to initialize all Lora settings like it's done in LoraTx before calling this function
|
||||
// If you don't init properly all the settings, it'll fail
|
||||
func (d *Device) SetTxContinuousPreamble() {
|
||||
if d.rfswitch != nil {
|
||||
d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
|
||||
}
|
||||
d.ExecSetCommand(SX126X_CMD_SET_TX_INFINITE_PREAMBLE, []uint8{})
|
||||
}
|
||||
|
||||
// SetTx() sets the device in TX mode
|
||||
// timeout is expressed in RTC Step unit (15uS)
|
||||
// The device will stay in Tx until countdown or packet transmitted
|
||||
// Value of 0x000000 will disable timer and device will stay TX
|
||||
func (d *Device) SetTx(timeoutRtcStep uint32) {
|
||||
var p [3]uint8
|
||||
p[0] = uint8((timeoutRtcStep >> 16) & 0xFF)
|
||||
p[1] = uint8((timeoutRtcStep >> 8) & 0xFF)
|
||||
p[2] = uint8((timeoutRtcStep >> 0) & 0xFF)
|
||||
d.ExecSetCommand(SX126X_CMD_SET_TX, p[:])
|
||||
}
|
||||
|
||||
// SetRx() sets the device in RX mode
|
||||
// timeout is expressed in RTC Step unit (15uS)
|
||||
// Value of 0x000000 => No timeout. Rx Single mode.
|
||||
// Value of 0xffffff => Rx Continuous mode
|
||||
// Other values => Timeout active. The device remains in RX until countdown or packet received
|
||||
func (d *Device) SetRx(timeoutRtcStep uint32) {
|
||||
var p [3]uint8
|
||||
p[0] = uint8(((timeoutRtcStep >> 16) & 0xFF))
|
||||
p[1] = uint8(((timeoutRtcStep >> 8) & 0xFF))
|
||||
p[2] = uint8(((timeoutRtcStep >> 0) & 0xFF))
|
||||
d.ExecSetCommand(SX126X_CMD_SET_RX, p[:])
|
||||
}
|
||||
|
||||
// StopTimerOnPreamble allows the user to select if the timer is stopped upon preamble detection of SyncWord / header detection.
|
||||
func (d *Device) StopTimerOnPreamble(enable bool) {
|
||||
var p [1]uint8
|
||||
if enable {
|
||||
p[0] = 1
|
||||
} else {
|
||||
p[0] = 0
|
||||
}
|
||||
d.ExecSetCommand(SX126X_CMD_STOP_TIMER_ON_PREAMBLE, p[:])
|
||||
}
|
||||
|
||||
// SetRegulatorMode sets the regulator more (depends on hardware implementation)
|
||||
func (d *Device) SetRegulatorMode(mode uint8) {
|
||||
p := []uint8{mode}
|
||||
d.ExecSetCommand(SX126X_CMD_SET_REGULATOR_MODE, p[:])
|
||||
}
|
||||
|
||||
// Calibrate starts the calibration of a block defined by calibParam
|
||||
func (d *Device) Calibrate(calibParam uint8) {
|
||||
p := []uint8{calibParam}
|
||||
d.ExecSetCommand(SX126X_CMD_CALIBRATE, p[:])
|
||||
}
|
||||
|
||||
// CalibrateImage calibrates the image rejection of the device for the device operating
|
||||
func (d *Device) CalibrateImage(freq uint32) {
|
||||
var calFreq [2]uint8
|
||||
if freq > 900000000 {
|
||||
calFreq[0] = 0xE1
|
||||
calFreq[1] = 0xE9
|
||||
} else if freq > 850000000 {
|
||||
calFreq[0] = 0xD7
|
||||
calFreq[1] = 0xD8
|
||||
} else if freq > 770000000 {
|
||||
calFreq[0] = 0xC1
|
||||
calFreq[1] = 0xC5
|
||||
} else if freq > 460000000 {
|
||||
calFreq[0] = 0x75
|
||||
calFreq[1] = 0x81
|
||||
} else if freq > 425000000 {
|
||||
calFreq[0] = 0x6B
|
||||
calFreq[1] = 0x6F
|
||||
}
|
||||
d.ExecSetCommand(SX126X_CMD_CALIBRATE_IMAGE, calFreq[:])
|
||||
}
|
||||
|
||||
// SetPaConfig sets the Power Amplifier configuration
|
||||
// deviceSel: 0 for SX1262, 1 for SX1261
|
||||
func (d *Device) SetPaConfig(paDutyCycle, hpMax, deviceSel, paLut uint8) {
|
||||
var p [4]uint8
|
||||
p[0] = paDutyCycle
|
||||
p[1] = hpMax
|
||||
p[2] = deviceSel
|
||||
p[3] = paLut
|
||||
d.ExecSetCommand(SX126X_CMD_SET_PA_CONFIG, p[:])
|
||||
}
|
||||
|
||||
// SetRxTxFallbackMode defines into which mode the chip goes after a successful transmission or after a packet reception.
|
||||
func (d *Device) SetRxTxFallbackMode(fallbackMode uint8) {
|
||||
d.ExecSetCommand(SX126X_CMD_SET_RX_TX_FALLBACK_MODE, []uint8{fallbackMode})
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Registers and Buffers
|
||||
// --------------------------------------------------
|
||||
|
||||
// ReadRegister reads register value
|
||||
func (d *Device) ReadRegister(addr, size uint16) ([]uint8, error) {
|
||||
d.CheckDeviceReady()
|
||||
d.SpiSetNss(false)
|
||||
// Send command
|
||||
cmd := []uint8{SX126X_CMD_READ_REGISTER, uint8((addr & 0xFF00) >> 8), uint8(addr & 0x00FF), 0x00}
|
||||
d.spi.Tx(cmd, nil)
|
||||
ret := d.spiBuffer[0:size]
|
||||
d.spi.Tx(nil, ret)
|
||||
d.SpiSetNss(true)
|
||||
d.WaitBusy()
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// WriteRegister writes value to register
|
||||
func (d *Device) WriteRegister(addr uint16, data []uint8) {
|
||||
d.CheckDeviceReady()
|
||||
d.SpiSetNss(false)
|
||||
cmd := []uint8{SX126X_CMD_WRITE_REGISTER, uint8((addr & 0xFF00) >> 8), uint8(addr & 0x00FF)}
|
||||
d.spi.Tx(append(cmd, data...), nil)
|
||||
d.SpiSetNss(true)
|
||||
d.WaitBusy()
|
||||
}
|
||||
|
||||
// WriteBuffer write data from current buffer position
|
||||
func (d *Device) WriteBuffer(data []uint8) {
|
||||
p := []uint8{0}
|
||||
p = append(p, data...)
|
||||
d.ExecSetCommand(SX126X_CMD_WRITE_BUFFER, p)
|
||||
}
|
||||
|
||||
// ReadBuffer Reads size bytes from current buffer position
|
||||
func (d *Device) ReadBuffer(size uint8) []uint8 {
|
||||
ret := d.ExecGetCommand(SX126X_CMD_READ_BUFFER, size)
|
||||
return ret
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// DIO and IRQ
|
||||
// --------------------------------------------------
|
||||
|
||||
// SetDioIrqParams configures DIO Irq
|
||||
func (d *Device) SetDioIrqParams(irqMask, dio1Mask, dio2Mask, dio3Mask uint16) {
|
||||
var p [8]uint8
|
||||
p[0] = uint8((irqMask >> 8) & 0xFF)
|
||||
p[1] = uint8(irqMask & 0xFF)
|
||||
p[2] = uint8((dio1Mask >> 8) & 0xFF)
|
||||
p[3] = uint8(dio1Mask & 0xFF)
|
||||
p[4] = uint8((dio2Mask >> 8) & 0xFF)
|
||||
p[5] = uint8(dio2Mask & 0xFF)
|
||||
p[6] = uint8((dio3Mask >> 8) & 0xFF)
|
||||
p[7] = uint8(dio3Mask & 0xFF)
|
||||
d.ExecSetCommand(SX126X_CMD_SET_DIO_IRQ_PARAMS, p[:])
|
||||
}
|
||||
|
||||
// GetIrqStatus returns IRQ status
|
||||
func (d *Device) GetIrqStatus() (irqStatus uint16) {
|
||||
r := d.ExecGetCommand(SX126X_CMD_GET_IRQ_STATUS, 2)
|
||||
ret := (uint16(r[0]) << 8) | uint16(r[1])
|
||||
return ret
|
||||
}
|
||||
|
||||
// ClearIrqStatus clears IRQ flags
|
||||
func (d *Device) ClearIrqStatus(clearIrqParams uint16) {
|
||||
var p [2]uint8
|
||||
p[0] = uint8((clearIrqParams >> 8) & 0xFF)
|
||||
p[1] = uint8(clearIrqParams & 0xFF)
|
||||
d.ExecSetCommand(SX126X_CMD_CLEAR_IRQ_STATUS, p[:])
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Communication Status Information
|
||||
// --------------------------------------------------
|
||||
|
||||
// GetStatus returns radio status(13.5.1)
|
||||
func (d *Device) GetStatus() (radioStatus uint8) {
|
||||
r := d.ExecGetCommand(SX126X_CMD_GET_STATUS, 1)
|
||||
return r[0]
|
||||
}
|
||||
|
||||
// GetRxBufferStatus returns the length of the last received packet (PayloadLengthRx)
|
||||
// and the address of the first byte received (RxStartBufferPointer). (13.5.2)
|
||||
func (d *Device) GetRxBufferStatus() (payloadLengthRx uint8, rxStartBufferPointer uint8) {
|
||||
r := d.ExecGetCommand(SX126X_CMD_GET_RX_BUFFER_STATUS, 2)
|
||||
return r[0], r[1]
|
||||
}
|
||||
|
||||
// GetPackeType returns current Packet Type (13.4.3)
|
||||
func (d *Device) GetPacketType() (packetType uint8) {
|
||||
r := d.ExecGetCommand(SX126X_CMD_GET_PACKET_TYPE, 1)
|
||||
return r[0]
|
||||
}
|
||||
|
||||
// GetDeviceErrors returns current Device Errors
|
||||
func (d *Device) GetDeviceErrors() uint16 {
|
||||
r := d.ExecGetCommand(SX126X_CMD_GET_DEVICE_ERRORS, 2)
|
||||
ret := uint16(r[0]<<8 + r[1])
|
||||
return ret
|
||||
}
|
||||
|
||||
// ClearDeviceErrors clears device Errors
|
||||
func (d *Device) ClearDeviceErrors() {
|
||||
p := [2]uint8{0x00, 0x00}
|
||||
d.ExecSetCommand(SX126X_CMD_CLEAR_DEVICE_ERRORS, p[:])
|
||||
}
|
||||
|
||||
// GetStats returns the number of informations received on a few last packets
|
||||
// Lora: NbPktReceived, NbPktCrcError, NbPktHeaderErr
|
||||
func (d *Device) GetLoraStats() (nbPktReceived, nbPktCrcError, nbPktHeaderErr uint16) {
|
||||
r := d.ExecGetCommand(SX126X_CMD_GET_STATS, 6)
|
||||
return uint16(r[0]<<8 | r[1]), uint16(r[2]<<8 | r[3]), uint16(r[4]<<8 | r[5])
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// PACKET / RADIO / PROTOCOL CONFIGURATION
|
||||
// ---------------------------------------
|
||||
|
||||
// SetPacketType sets the packet type
|
||||
func (d *Device) SetPacketType(packetType uint8) {
|
||||
var p [1]uint8
|
||||
p[0] = packetType
|
||||
d.ExecSetCommand(SX126X_CMD_SET_PACKET_TYPE, p[:])
|
||||
}
|
||||
|
||||
// SetSyncWord defines the Sync Word to yse
|
||||
func (d *Device) SetSyncWord(syncword uint16) {
|
||||
var p [2]uint8
|
||||
d.loraConf.SyncWord = syncword
|
||||
p[0] = uint8((syncword >> 8) & 0xFF)
|
||||
p[1] = uint8((syncword >> 0) & 0xFF)
|
||||
d.WriteRegister(SX126X_REG_LORA_SYNC_WORD_MSB, p[:])
|
||||
}
|
||||
|
||||
// GetSyncWord gets the Sync Word to use
|
||||
func (d *Device) GetSyncWord() uint16 {
|
||||
p, _ := d.ReadRegister(SX126X_REG_LORA_SYNC_WORD_MSB, 2)
|
||||
r := uint16(p[0])<<8 + uint16(p[1])
|
||||
return r
|
||||
}
|
||||
|
||||
// SetLoraPublicNetwork sets Sync Word to 0x3444 (Public) or 0x1424 (Private)
|
||||
func (d *Device) SetLoraPublicNetwork(enable bool) {
|
||||
if enable {
|
||||
d.SetSyncWord(SX126X_LORA_MAC_PUBLIC_SYNCWORD)
|
||||
} else {
|
||||
d.SetSyncWord(SX126X_LORA_MAC_PRIVATE_SYNCWORD)
|
||||
}
|
||||
}
|
||||
|
||||
// SetPacketParam sets various packet-related params
|
||||
func (d *Device) SetPacketParam(preambleLength uint16, headerType, crcType, payloadLength, invertIQ uint8) {
|
||||
var p [6]uint8
|
||||
p[0] = uint8((preambleLength >> 8) & 0xFF)
|
||||
p[1] = uint8(preambleLength & 0xFF)
|
||||
p[2] = headerType
|
||||
p[3] = payloadLength
|
||||
p[4] = crcType
|
||||
p[5] = invertIQ
|
||||
d.ExecSetCommand(SX126X_CMD_SET_PACKET_PARAMS, p[:])
|
||||
}
|
||||
|
||||
// SetBufferBaseAddress sets base address for buffer
|
||||
func (d *Device) SetBufferBaseAddress(txBaseAddress, rxBaseAddress uint8) {
|
||||
var p [2]uint8
|
||||
p[0] = txBaseAddress
|
||||
p[1] = rxBaseAddress
|
||||
d.ExecSetCommand(SX126X_CMD_SET_BUFFER_BASE_ADDRESS, p[:])
|
||||
}
|
||||
|
||||
// SetRfFrequency sets the radio frequency
|
||||
func (d *Device) SetRfFrequency(frequency uint32) {
|
||||
var p [4]uint8
|
||||
freq := uint32((uint64(frequency) << 25) / 32000000)
|
||||
p[0] = uint8((freq >> 24) & 0xFF)
|
||||
p[1] = uint8((freq >> 16) & 0xFF)
|
||||
p[2] = uint8((freq >> 8) & 0xFF)
|
||||
p[3] = uint8((freq >> 0) & 0xFF)
|
||||
d.ExecSetCommand(SX126X_CMD_SET_RF_FREQUENCY, p[:])
|
||||
}
|
||||
|
||||
// SetCurrentLimit sets max current in the module
|
||||
func (d *Device) SetCurrentLimit(limit uint8) {
|
||||
if limit > 140 {
|
||||
limit = 140
|
||||
}
|
||||
rawLimit := uint8(float32(limit) / 2.5)
|
||||
p := []uint8{rawLimit}
|
||||
d.WriteRegister(SX126X_REG_OCP_CONFIGURATION, p[:])
|
||||
}
|
||||
|
||||
// SetTxConfig sets power and rampup time
|
||||
func (d *Device) SetTxParams(power int8, rampTime uint8) {
|
||||
var p [2]uint8
|
||||
|
||||
if d.deviceType == DEVICE_TYPE_SX1261 {
|
||||
if power == 15 {
|
||||
d.SetPaConfig(0x06, 0x00, 0x01, 0x01)
|
||||
} else {
|
||||
d.SetPaConfig(0x04, 0x00, 0x01, 0x01)
|
||||
}
|
||||
if power > 14 {
|
||||
power = 14
|
||||
} else if power < -3 {
|
||||
power = -3
|
||||
}
|
||||
d.SetCurrentLimit(80) // Set max current limit to 80mA
|
||||
} else { // sx1262 and sx1268
|
||||
d.SetPaConfig(0x04, 0x07, 0x00, 0x01)
|
||||
if power > 22 {
|
||||
power = 22
|
||||
} else if power < -3 {
|
||||
power = -3
|
||||
}
|
||||
d.SetCurrentLimit(140) // Set max current limit to 140 mA
|
||||
}
|
||||
|
||||
p[0] = uint8(power)
|
||||
p[1] = rampTime
|
||||
d.ExecSetCommand(SX126X_CMD_SET_TX_PARAMS, p[:])
|
||||
}
|
||||
|
||||
// SetModulationParams sets the Lora modulation frequency
|
||||
func (d *Device) SetModulationParams(spreadingFactor, bandwidth, codingRate, lowDataRateOptimize uint8) {
|
||||
var p [4]uint8
|
||||
p[0] = spreadingFactor
|
||||
p[1] = bandwidth
|
||||
p[2] = codingRate
|
||||
p[3] = lowDataRateOptimize
|
||||
d.ExecSetCommand(SX126X_CMD_SET_MODULATION_PARAMS, p[:])
|
||||
}
|
||||
|
||||
// CheckDeviceReady sleep until all busy flags clears
|
||||
func (d *Device) CheckDeviceReady() error {
|
||||
if d.deepSleep == true {
|
||||
d.SpiSetNss(false)
|
||||
time.Sleep(time.Millisecond)
|
||||
d.SpiSetNss(true)
|
||||
d.deepSleep = false
|
||||
}
|
||||
return d.WaitBusy()
|
||||
}
|
||||
|
||||
// ExecSetCommand send a command to configure the peripheral
|
||||
func (d *Device) ExecSetCommand(cmd uint8, buf []uint8) {
|
||||
d.CheckDeviceReady()
|
||||
if cmd == SX126X_CMD_SET_SLEEP {
|
||||
d.deepSleep = true
|
||||
} else {
|
||||
d.deepSleep = false
|
||||
}
|
||||
d.SpiSetNss(false)
|
||||
// Send command and params
|
||||
d.spi.Tx(append([]uint8{cmd}, buf...), nil)
|
||||
d.SpiSetNss(true)
|
||||
if cmd != SX126X_CMD_SET_SLEEP {
|
||||
d.WaitBusy()
|
||||
}
|
||||
}
|
||||
|
||||
// ExecGetCommand queries the peripheral the peripheral
|
||||
func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 {
|
||||
d.CheckDeviceReady()
|
||||
d.SpiSetNss(false)
|
||||
// Send the command and flush first status byte (as not used)
|
||||
d.spi.Tx([]uint8{cmd, 0x00}, nil)
|
||||
d.spi.Tx(nil, d.spiBuffer[:size])
|
||||
d.SpiSetNss(true)
|
||||
d.WaitBusy()
|
||||
return d.spiBuffer[:size]
|
||||
}
|
||||
|
||||
//
|
||||
// Configuration
|
||||
//
|
||||
|
||||
// SetLoraFrequency() Sets current Lora Frequency
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetLoraFrequency(freq uint32) {
|
||||
d.loraConf.Freq = d.loraConf.Freq
|
||||
}
|
||||
|
||||
// SetLoraIqMode() defines the current IQ Mode (Standard/Inverted)
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetLoraIqMode(mode uint8) {
|
||||
if mode == 0 {
|
||||
d.loraConf.Iq = SX126X_LORA_IQ_STANDARD
|
||||
} else {
|
||||
d.loraConf.Iq = SX126X_LORA_IQ_INVERTED
|
||||
}
|
||||
}
|
||||
|
||||
// SetLoraCodingRate() sets current Lora Coding Rate
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetLoraCodingRate(cr uint8) {
|
||||
d.loraConf.Cr = cr
|
||||
}
|
||||
|
||||
// SetLoraBandwidth() sets current Lora Bandwidth
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetLoraBandwidth(bw uint8) {
|
||||
d.loraConf.Cr = bw
|
||||
}
|
||||
|
||||
// SetLoraCrc() sets current CRC mode (ON/OFF)
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetLoraCrc(enable bool) {
|
||||
if enable {
|
||||
d.loraConf.Crc = SX126X_LORA_CRC_ON
|
||||
} else {
|
||||
d.loraConf.Crc = SX126X_LORA_CRC_OFF
|
||||
}
|
||||
}
|
||||
|
||||
//SetLoraSpreadingFactor setc surrent Lora Spreading Factor
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetLoraSpreadingFactor(sf uint8) {
|
||||
d.loraConf.Sf = sf
|
||||
}
|
||||
|
||||
//
|
||||
// Lora functions
|
||||
//
|
||||
//
|
||||
|
||||
// LoraConfig() defines Lora configuration for next Lora operations
|
||||
func (d *Device) LoraConfig(cnf LoraConfig) {
|
||||
// Save given configuration
|
||||
d.loraConf = cnf
|
||||
// Switch to standby prior to configuration changes
|
||||
d.SetStandby()
|
||||
// Clear errors, disable radio interrupts for the moment
|
||||
d.ClearDeviceErrors()
|
||||
d.ClearIrqStatus(SX126X_IRQ_ALL)
|
||||
d.SetDioIrqParams(0x00, 0x00, 0x00, 0x00)
|
||||
// Define radio operation mode
|
||||
d.SetPacketType(SX126X_PACKET_TYPE_LORA)
|
||||
d.SetRfFrequency(d.loraConf.Freq)
|
||||
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
|
||||
d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U)
|
||||
d.SetSyncWord(d.loraConf.SyncWord)
|
||||
d.SetBufferBaseAddress(0, 0)
|
||||
}
|
||||
|
||||
// LoraTx sends a lora packet, (with timeout)
|
||||
func (d *Device) LoraTx(pkt []uint8, timeoutMs uint32) error {
|
||||
|
||||
if d.loraConf.Freq == 0 {
|
||||
return errUndefinedLoraConf
|
||||
}
|
||||
if d.rfswitch != nil {
|
||||
err := d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.ClearIrqStatus(SX126X_IRQ_ALL)
|
||||
irqVal := uint16(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
|
||||
d.SetStandby()
|
||||
d.SetPacketType(SX126X_PACKET_TYPE_LORA)
|
||||
d.SetRfFrequency(d.loraConf.Freq)
|
||||
d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U)
|
||||
d.SetBufferBaseAddress(0, 0)
|
||||
d.WriteBuffer(pkt)
|
||||
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
|
||||
d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, uint8(len(pkt)), d.loraConf.Iq)
|
||||
d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE)
|
||||
d.SetSyncWord(d.loraConf.SyncWord)
|
||||
d.SetTx(timeoutMsToRtcSteps(timeoutMs))
|
||||
|
||||
msg := <-d.GetRadioEventChan()
|
||||
if msg.EventType != RadioEventTxDone {
|
||||
return errors.New("Unexpected Radio Event while TX")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoraRx tries to receive a Lora packet (with timeout in milliseconds)
|
||||
func (d *Device) LoraRx(timeoutMs uint32) ([]uint8, error) {
|
||||
|
||||
if d.loraConf.Freq == 0 {
|
||||
return nil, errUndefinedLoraConf
|
||||
}
|
||||
if d.rfswitch != nil {
|
||||
err := d.rfswitch.SetRfSwitchMode(RFSWITCH_RX)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d.ClearIrqStatus(SX126X_IRQ_ALL)
|
||||
irqVal := uint16(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
|
||||
d.SetStandby()
|
||||
d.SetBufferBaseAddress(0, 0)
|
||||
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
|
||||
d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, 0xFF, d.loraConf.Iq)
|
||||
d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE)
|
||||
d.SetRx(timeoutMsToRtcSteps(timeoutMs))
|
||||
|
||||
msg := <-d.GetRadioEventChan()
|
||||
|
||||
if msg.EventType == RadioEventTimeout {
|
||||
return nil, nil
|
||||
} else if msg.EventType != RadioEventRxDone {
|
||||
return nil, errors.New("Unexpected Radio Event while RX")
|
||||
}
|
||||
|
||||
pLen, pStart := d.GetRxBufferStatus()
|
||||
d.SetBufferBaseAddress(0, pStart+1)
|
||||
pkt := d.ReadBuffer(pLen + 1)
|
||||
pkt = pkt[1:]
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
// HandleInterrupt must be called by main code on DIO state change.
|
||||
func (d *Device) HandleInterrupt() {
|
||||
st := d.GetIrqStatus()
|
||||
d.ClearIrqStatus(SX126X_IRQ_ALL)
|
||||
|
||||
rChan := d.GetRadioEventChan()
|
||||
|
||||
if (st & SX126X_IRQ_RX_DONE) > 0 {
|
||||
rChan <- NewRadioEvent(RadioEventRxDone, st, nil)
|
||||
}
|
||||
|
||||
if (st & SX126X_IRQ_TX_DONE) > 0 {
|
||||
rChan <- NewRadioEvent(RadioEventTxDone, st, nil)
|
||||
}
|
||||
|
||||
if (st & SX126X_IRQ_TIMEOUT) > 0 {
|
||||
rChan <- NewRadioEvent(RadioEventTimeout, st, nil)
|
||||
}
|
||||
|
||||
if (st & SX126X_IRQ_CRC_ERR) > 0 {
|
||||
rChan <- NewRadioEvent(RadioEventCrcError, st, nil)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user