mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 02:28:41 +00:00
add SPI driver for semtech sx128x chips (#864)
* add SPI driver for semtech sx128x chips Co-authored-by: Copilot <copilot@github.com> * handle busy loop better * switch to time based busy timeout Co-authored-by: Copilot <copilot@github.com> * comment functions * start on using types for function inputs * use types where applicable and align with datasheet more Co-authored-by: Copilot <copilot@github.com> * work on exporting less constants * only export "actionable" errors * combine identical constants * add crude lora rx and tx examples * change from type aliases to local types --------- Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sx128x"
|
||||
)
|
||||
|
||||
var (
|
||||
// pin mapping specific to the lilygo t3s3, change as needed for your board
|
||||
sdoPin = machine.GPIO6
|
||||
sdiPin = machine.GPIO3
|
||||
sckPin = machine.GPIO5
|
||||
nssPin = machine.GPIO7
|
||||
busyPin = machine.GPIO36
|
||||
resetPin = machine.GPIO8
|
||||
dio1Pin = machine.GPIO9
|
||||
)
|
||||
|
||||
func setupPins() {
|
||||
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
nssPin.Set(true)
|
||||
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Set(true)
|
||||
|
||||
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
}
|
||||
|
||||
func main() {
|
||||
setupPins()
|
||||
|
||||
spi := machine.SPI0
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Mode: 0,
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: sdoPin,
|
||||
SDI: sdiPin,
|
||||
SCK: sckPin,
|
||||
})
|
||||
|
||||
radio := sx128x.New(
|
||||
spi,
|
||||
nssPin,
|
||||
resetPin,
|
||||
busyPin,
|
||||
)
|
||||
|
||||
radio.WaitWhileBusy(time.Second)
|
||||
SetupLora(radio)
|
||||
|
||||
for {
|
||||
data, err := Rx(radio)
|
||||
if err != nil {
|
||||
println("failed to receive:", err)
|
||||
} else {
|
||||
println("received:", string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetupLora(radio *sx128x.Device) {
|
||||
radio.SetStandby(sx128x.STANDBY_RC)
|
||||
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
|
||||
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)
|
||||
|
||||
radio.SetRfFrequency(2400000000) // 2.4Ghz
|
||||
radio.SetModulationParamsLoRa(sx128x.LORA_SF_9, sx128x.LORA_BW_1600, sx128x.LORA_CR_4_7)
|
||||
|
||||
// section 14.4.1 shows required register setting for setting up LoRa operations. These depend on the chosen spreading factor.
|
||||
radio.WriteRegister(0x925, []byte{0x32})
|
||||
radio.WriteRegister(0x93C, []byte{0x01})
|
||||
|
||||
radio.SetTxParams(13, sx128x.RADIO_RAMP_02_US)
|
||||
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, 0xFF, sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
|
||||
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, []byte{0x14, 0x24}) // full sync word is 0x1424
|
||||
|
||||
}
|
||||
|
||||
func Rx(radio *sx128x.Device) ([]byte, error) {
|
||||
radio.SetStandby(sx128x.STANDBY_RC)
|
||||
radio.SetDioIrqParams(sx128x.IRQ_RX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, sx128x.IRQ_RX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, 0x00, 0x00)
|
||||
radio.SetBufferBaseAddress(0, 0)
|
||||
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
|
||||
radio.SetRx(sx128x.PERIOD_BASE_4_MS, 250) // 4ms * 250 = 1s
|
||||
// busy wait for IRQ indication
|
||||
for dio1Pin.Get() == false {
|
||||
runtime.Gosched()
|
||||
}
|
||||
irqStatus, _ := radio.GetIrqStatus()
|
||||
if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 {
|
||||
payloadLength, bufferOffset, err := radio.GetRxBufferStatus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := radio.ReadBuffer(bufferOffset, payloadLength)
|
||||
return data, nil
|
||||
} else if irqStatus&sx128x.IRQ_RX_TX_TIMEOUT_MASK != 0 {
|
||||
return nil, errors.New("rx timeout")
|
||||
}
|
||||
return nil, errors.New("unexpected IRQ status")
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sx128x"
|
||||
)
|
||||
|
||||
var (
|
||||
// pin mapping specific to the lilygo t3s3, change as needed for your board
|
||||
sdoPin = machine.GPIO6
|
||||
sdiPin = machine.GPIO3
|
||||
sckPin = machine.GPIO5
|
||||
nssPin = machine.GPIO7
|
||||
busyPin = machine.GPIO36
|
||||
resetPin = machine.GPIO8
|
||||
dio1Pin = machine.GPIO9
|
||||
)
|
||||
|
||||
func setupPins() {
|
||||
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
nssPin.Set(true)
|
||||
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Set(true)
|
||||
|
||||
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
}
|
||||
|
||||
func main() {
|
||||
setupPins()
|
||||
|
||||
spi := machine.SPI0
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Mode: 0,
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: sdoPin,
|
||||
SDI: sdiPin,
|
||||
SCK: sckPin,
|
||||
})
|
||||
|
||||
radio := sx128x.New(
|
||||
spi,
|
||||
nssPin,
|
||||
resetPin,
|
||||
busyPin,
|
||||
)
|
||||
|
||||
radio.WaitWhileBusy(time.Second)
|
||||
SetupLora(radio)
|
||||
|
||||
for {
|
||||
Tx(radio, []byte("Hello, world!"))
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func SetupLora(radio *sx128x.Device) {
|
||||
radio.SetStandby(sx128x.STANDBY_RC)
|
||||
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
|
||||
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)
|
||||
|
||||
radio.SetRfFrequency(2400000000) // 2.4Ghz
|
||||
radio.SetModulationParamsLoRa(sx128x.LORA_SF_9, sx128x.LORA_BW_1600, sx128x.LORA_CR_4_7)
|
||||
|
||||
// section 14.4.1 shows required register setting for setting up LoRa operations. These depend on the chosen spreading factor.
|
||||
radio.WriteRegister(0x925, []byte{0x32})
|
||||
radio.WriteRegister(0x93C, []byte{0x01})
|
||||
|
||||
radio.SetTxParams(13, sx128x.RADIO_RAMP_02_US)
|
||||
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, 0xFF, sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
|
||||
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, []byte{0x14, 0x24}) // full sync word is 0x1424
|
||||
|
||||
}
|
||||
|
||||
func Tx(radio *sx128x.Device, data []byte) error {
|
||||
if len(data) > 255 {
|
||||
return errors.New("data length exceeds maximum of 255 bytes")
|
||||
}
|
||||
radio.SetStandby(sx128x.STANDBY_RC)
|
||||
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, uint8(len(data)&0xFF), sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
|
||||
radio.SetBufferBaseAddress(0, 0)
|
||||
radio.WriteBuffer(0, data)
|
||||
radio.SetDioIrqParams(sx128x.IRQ_TX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, sx128x.IRQ_TX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, 0x00, 0x00)
|
||||
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
|
||||
radio.SetTx(sx128x.PERIOD_BASE_4_MS, 250) // 4ms * 250 = 1s
|
||||
// busy wait for IRQ indication
|
||||
for dio1Pin.Get() == false {
|
||||
runtime.Gosched()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# SX128x Radio
|
||||
Radio from Semtech in the 2.4 GHz band. This driver uses SPI to communicate with the radio instead of the alternative UART interface.
|
||||
|
||||
## Supported Chips
|
||||
- [SX1280](https://www.semtech.com/products/wireless-rf/lora-connect/sx1280)
|
||||
- [SX1281](https://www.semtech.com/products/wireless-rf/lora-connect/sx1281)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package sx128x
|
||||
|
||||
const (
|
||||
// SX128X SPI commands
|
||||
cmdGetStatus = uint8(0xC0)
|
||||
|
||||
// Register Access Operations
|
||||
cmdWriteRegister = uint8(0x18)
|
||||
cmdReadRegister = uint8(0x19)
|
||||
|
||||
// Data Buffer Operations
|
||||
cmdWriteBuffer = uint8(0x1A)
|
||||
cmdReadBuffer = uint8(0x1B)
|
||||
|
||||
// Radio Operation Modes
|
||||
cmdSetSleep = uint8(0x84)
|
||||
cmdSetStandby = uint8(0x80)
|
||||
cmdSetFS = uint8(0xC1)
|
||||
cmdSetTx = uint8(0x83)
|
||||
cmdSetRx = uint8(0x82)
|
||||
cmdSetRxDutyCycle = uint8(0x94)
|
||||
cmdSetLongPreamble = uint8(0x9B)
|
||||
cmdSetCAD = uint8(0xC5)
|
||||
cmdSetTxContinuousWave = uint8(0xD1)
|
||||
cmdSetContinuousPreamble = uint8(0xD2)
|
||||
cmdSetAutoTx = uint8(0x98)
|
||||
cmdSetAutoFS = uint8(0x9E)
|
||||
|
||||
// Radio Configuration
|
||||
cmdSetPacketType = uint8(0x8A)
|
||||
cmdGetPacketType = uint8(0x03)
|
||||
cmdSetRFFrequency = uint8(0x86)
|
||||
cmdSetTxParams = uint8(0x8E)
|
||||
cmdSetCADParams = uint8(0x88)
|
||||
cmdSetBufferBaseAddress = uint8(0x8F)
|
||||
cmdSetModulationParams = uint8(0x8B)
|
||||
cmdSetPacketParams = uint8(0x8C)
|
||||
|
||||
// Communication Status Information
|
||||
cmdGetRxBufferStatus = uint8(0x17)
|
||||
cmdGetPacketStatus = uint8(0x1D)
|
||||
cmdGetRSSIInst = uint8(0x1F)
|
||||
|
||||
// IRQ Handling
|
||||
cmdSetDIOIRQParams = uint8(0x8D)
|
||||
cmdGetIRQStatus = uint8(0x15)
|
||||
cmdClearIRQStatus = uint8(0x97)
|
||||
|
||||
// Miscellaneous
|
||||
cmdSetRegulatorMode = uint8(0x96)
|
||||
cmdSetSaveContext = uint8(0xD5)
|
||||
)
|
||||
@@ -0,0 +1,356 @@
|
||||
package sx128x
|
||||
|
||||
type SleepConfig uint8
|
||||
type StandbyConfig uint8
|
||||
type PeriodBase uint8
|
||||
type PacketType uint8
|
||||
type RadioRampTime uint8
|
||||
type CadSymbolNum uint8
|
||||
|
||||
// GFSK Modulation Params
|
||||
type GFSKBLEBitrateBandwidth uint8
|
||||
type ModulationIndex uint8
|
||||
type ModulationShaping uint8
|
||||
|
||||
// GFSK Packet Params
|
||||
type GFSKPreambleLength uint8
|
||||
type GFSKSyncWordLength uint8
|
||||
type GFSKSyncWordMatch uint8
|
||||
type GFSKHeaderType uint8
|
||||
type GFSKCrcType uint8
|
||||
|
||||
// BLE Packet Params
|
||||
type BLEConnectionState uint8
|
||||
type BLECrcType uint8
|
||||
type BLETestPayload uint8
|
||||
|
||||
// FLRC Modulation Params
|
||||
type FLRCBitrateBandwidth uint8
|
||||
type FLRCCodingRate uint8
|
||||
|
||||
// FLRC Packet Params
|
||||
type FLRCPreambleLength uint8
|
||||
type FLRCSyncWordLength uint8
|
||||
type FLRCSyncWordMatch uint8
|
||||
type FLRCHeaderType uint8
|
||||
type FLRCCrcType uint8
|
||||
|
||||
// LoRa Modulation Params
|
||||
type LoRaSpreadingFactor uint8
|
||||
type LoRaBandwidth uint8
|
||||
type LoRaCodingRate uint8
|
||||
|
||||
// LoRa Packet Params
|
||||
type LoRaHeaderType uint8
|
||||
type LoRaCrcType uint8
|
||||
type LoRaIqType uint8
|
||||
|
||||
// Misc
|
||||
type RegulatorMode uint8
|
||||
type IRQMask = uint16
|
||||
type CircuitMode uint8
|
||||
type CommandStatus uint8
|
||||
|
||||
// Packet Status
|
||||
type GFSKPacketInfo uint8
|
||||
type BLEPacketInfo uint8
|
||||
type FLRCPacketInfo uint8
|
||||
|
||||
const (
|
||||
whiteningDisable = 0x00
|
||||
whiteningEnable = 0x08
|
||||
|
||||
// Circuit Mode
|
||||
circuitModeMask = uint8(0b11100000)
|
||||
CIRCUIT_MODE_STDBY_RC = CircuitMode(0x2)
|
||||
CIRCUIT_MODE_STDBY_XOSC = CircuitMode(0x3)
|
||||
CIRCUIT_MODE_FS = CircuitMode(0x4)
|
||||
CIRCUIT_MODE_RX = CircuitMode(0x5)
|
||||
CIRCUIT_MODE_TX = CircuitMode(0x6)
|
||||
|
||||
// Command Status
|
||||
commandStatusMask = uint8(0b00011100)
|
||||
COMMAND_STATUS_SUCCESS = CommandStatus(0x1)
|
||||
COMMAND_STATUS_DATA_AVAILABLE = CommandStatus(0x2)
|
||||
COMMAND_STATUS_TIMEOUT = CommandStatus(0x3)
|
||||
COMMAND_STATUS_PROCESSING_ERROR = CommandStatus(0x4)
|
||||
COMMAND_STATUS_EXECUTION_ERROR = CommandStatus(0x5)
|
||||
COMMAND_STATUS_TX_DONE = CommandStatus(0x6)
|
||||
|
||||
// SleepConfig
|
||||
SLEEP_DATA_BUFFER_RETAIN = SleepConfig(2)
|
||||
SLEEP_DATA_RAM_RETAIN = SleepConfig(1)
|
||||
|
||||
// StandbyConfig
|
||||
STANDBY_RC = StandbyConfig(0)
|
||||
STANDBY_XOSC = StandbyConfig(1)
|
||||
|
||||
// PeriodBase
|
||||
PERIOD_BASE_15_625_US = PeriodBase(0)
|
||||
PERIOD_BASE_62_5_US = PeriodBase(1)
|
||||
PERIOD_BASE_1_MS = PeriodBase(2)
|
||||
PERIOD_BASE_4_MS = PeriodBase(3)
|
||||
|
||||
// PacketType
|
||||
PACKET_TYPE_GFSK = PacketType(0x00) // default
|
||||
PACKET_TYPE_LORA = PacketType(0x01)
|
||||
PACKET_TYPE_RANGING = PacketType(0x02)
|
||||
PACKET_TYPE_FLRC = PacketType(0x03)
|
||||
PACKET_TYPE_BLE = PacketType(0x04)
|
||||
|
||||
// RampTime
|
||||
RADIO_RAMP_02_US = RadioRampTime(0x00)
|
||||
RADIO_RAMP_04_US = RadioRampTime(0x20)
|
||||
RADIO_RAMP_06_US = RadioRampTime(0x40)
|
||||
RADIO_RAMP_08_US = RadioRampTime(0x60)
|
||||
RADIO_RAMP_10_US = RadioRampTime(0x80)
|
||||
RADIO_RAMP_12_US = RadioRampTime(0xA0)
|
||||
RADIO_RAMP_16_US = RadioRampTime(0xC0)
|
||||
RADIO_RAMP_20_US = RadioRampTime(0xE0)
|
||||
|
||||
// CadSymbolNum
|
||||
LORA_CAD_01_SYMBOL = CadSymbolNum(0x00)
|
||||
LORA_CAD_02_SYMBOLS = CadSymbolNum(0x20)
|
||||
LORA_CAD_04_SYMBOLS = CadSymbolNum(0x40)
|
||||
LORA_CAD_08_SYMBOLS = CadSymbolNum(0x60)
|
||||
LORA_CAD_16_SYMBOLS = CadSymbolNum(0x80)
|
||||
|
||||
// GFSK Modulation Params
|
||||
// Bitrate + Bandwidth - same for BLE
|
||||
GFSK_BLE_BR_2_000_BW_2_4 = GFSKBLEBitrateBandwidth(0x04)
|
||||
GFSK_BLE_BR_1_600_BW_2_4 = GFSKBLEBitrateBandwidth(0x28)
|
||||
GFSK_BLE_BR_1_000_BW_2_4 = GFSKBLEBitrateBandwidth(0x4C)
|
||||
GFSK_BLE_BR_1_000_BW_1_2 = GFSKBLEBitrateBandwidth(0x45)
|
||||
GFSK_BLE_BR_0_800_BW_2_4 = GFSKBLEBitrateBandwidth(0x70)
|
||||
GFSK_BLE_BR_0_800_BW_1_2 = GFSKBLEBitrateBandwidth(0x69)
|
||||
GFSK_BLE_BR_0_500_BW_1_2 = GFSKBLEBitrateBandwidth(0x8D)
|
||||
GFSK_BLE_BR_0_500_BW_0_6 = GFSKBLEBitrateBandwidth(0x86)
|
||||
GFSK_BLE_BR_0_400_BW_1_2 = GFSKBLEBitrateBandwidth(0xB1)
|
||||
GFSK_BLE_BR_0_400_BW_0_6 = GFSKBLEBitrateBandwidth(0xAA)
|
||||
GFSK_BLE_BR_0_250_BW_0_6 = GFSKBLEBitrateBandwidth(0xCE)
|
||||
GFSK_BLE_BR_0_250_BW_0_3 = GFSKBLEBitrateBandwidth(0xC7)
|
||||
GFSK_BLE_BR_0_125_BW_0_3 = GFSKBLEBitrateBandwidth(0xEF)
|
||||
|
||||
// Modulation Index - same for BLE
|
||||
MOD_IND_0_35 = ModulationIndex(0x00)
|
||||
MOD_IND_0_5 = ModulationIndex(0x01)
|
||||
MOD_IND_0_75 = ModulationIndex(0x02)
|
||||
MOD_IND_1_00 = ModulationIndex(0x03)
|
||||
MOD_IND_1_25 = ModulationIndex(0x04)
|
||||
MOD_IND_1_50 = ModulationIndex(0x05)
|
||||
MOD_IND_1_75 = ModulationIndex(0x06)
|
||||
MOD_IND_2_00 = ModulationIndex(0x07)
|
||||
MOD_IND_2_25 = ModulationIndex(0x08)
|
||||
MOD_IND_2_50 = ModulationIndex(0x09)
|
||||
MOD_IND_2_75 = ModulationIndex(0x0A)
|
||||
MOD_IND_3_00 = ModulationIndex(0x0B)
|
||||
MOD_IND_3_25 = ModulationIndex(0x0C)
|
||||
MOD_IND_3_50 = ModulationIndex(0x0D)
|
||||
MOD_IND_3_75 = ModulationIndex(0x0E)
|
||||
MOD_IND_4_00 = ModulationIndex(0x0F)
|
||||
|
||||
// Modulation Shaping - same for BLE and FLRC
|
||||
MOD_SHAPING_OFF = ModulationShaping(0x00)
|
||||
MOD_SHAPING_1_0 = ModulationShaping(0x10)
|
||||
MOD_SHAPING_0_5 = ModulationShaping(0x20)
|
||||
|
||||
// GFSK Packet Params
|
||||
// Preamble Length
|
||||
GFSK_PREAMBLE_LENGTH_04_BITS = GFSKPreambleLength(0x00)
|
||||
GFSK_PREAMBLE_LENGTH_08_BITS = GFSKPreambleLength(0x10)
|
||||
GFSK_PREAMBLE_LENGTH_12_BITS = GFSKPreambleLength(0x20)
|
||||
GFSK_PREAMBLE_LENGTH_16_BITS = GFSKPreambleLength(0x30)
|
||||
GFSK_PREAMBLE_LENGTH_20_BITS = GFSKPreambleLength(0x40)
|
||||
GFSK_PREAMBLE_LENGTH_24_BITS = GFSKPreambleLength(0x50)
|
||||
GFSK_PREAMBLE_LENGTH_28_BITS = GFSKPreambleLength(0x60)
|
||||
GFSK_PREAMBLE_LENGTH_32_BITS = GFSKPreambleLength(0x70)
|
||||
|
||||
// Sync Word Length
|
||||
GFSK_SYNC_WORD_LEN_1_B = GFSKSyncWordLength(0x00)
|
||||
GFSK_SYNC_WORD_LEN_2_B = GFSKSyncWordLength(0x02)
|
||||
GFSK_SYNC_WORD_LEN_3_B = GFSKSyncWordLength(0x04)
|
||||
GFSK_SYNC_WORD_LEN_4_B = GFSKSyncWordLength(0x06)
|
||||
GFSK_SYNC_WORD_LEN_5_B = GFSKSyncWordLength(0x08)
|
||||
|
||||
// Sync Word Match
|
||||
GFSK_SYNCWORD_MATCH_OFF = GFSKSyncWordMatch(0x00)
|
||||
GFSK_SYNCWORD_MATCH_1 = GFSKSyncWordMatch(0x10)
|
||||
GFSK_SYNCWORD_MATCH_2 = GFSKSyncWordMatch(0x20)
|
||||
GFSK_SYNCWORD_MATCH_1_2 = GFSKSyncWordMatch(0x30)
|
||||
GFSK_SYNCWORD_MATCH_3 = GFSKSyncWordMatch(0x40)
|
||||
GFSK_SYNCWORD_MATCH_1_3 = GFSKSyncWordMatch(0x50)
|
||||
GFSK_SYNCWORD_MATCH_2_3 = GFSKSyncWordMatch(0x60)
|
||||
GFSK_SYNCWORD_MATCH_1_2_3 = GFSKSyncWordMatch(0x70)
|
||||
|
||||
// GFSK Header Type
|
||||
GFSK_HEADER_FIXED_LENGTH = GFSKHeaderType(0x00)
|
||||
GFSK_HEADER_VARIABLE_LENGTH = GFSKHeaderType(0x20)
|
||||
|
||||
// GFSK CRC Type
|
||||
GFSK_CRC_OFF = GFSKCrcType(0x00)
|
||||
GFSK_CRC_1_BYTE = GFSKCrcType(0x10)
|
||||
GFSK_CRC_2_BYTES = GFSKCrcType(0x20)
|
||||
|
||||
// BLE Packet Params
|
||||
// Connection State
|
||||
BLE_MASTER_SLAVE = BLEConnectionState(0x00)
|
||||
BLE_ADVERTISER = BLEConnectionState(0x02)
|
||||
BLE_TX_TEST_MODE = BLEConnectionState(0x04)
|
||||
BLE_RX_TEST_MODE = BLEConnectionState(0x06)
|
||||
BLE_RXTX_TEST_MODE = BLEConnectionState(0x08)
|
||||
|
||||
// CRC Type
|
||||
BLE_CRC_OFF = BLECrcType(0x00)
|
||||
BLE_CRC_3_BYTES = BLECrcType(0x10)
|
||||
|
||||
// BLE Test Payload
|
||||
BLE_PAYLOAD_PRBS_9 = BLETestPayload(0x00)
|
||||
BLE_PAYLOAD_EYELONG_1_0 = BLETestPayload(0x04)
|
||||
BLE_PAYLOAD_EYESHORT_1_0 = BLETestPayload(0x08)
|
||||
BLE_PAYLOAD_PRBS_15 = BLETestPayload(0x0C)
|
||||
BLE_PAYLOAD_ALL_1 = BLETestPayload(0x10)
|
||||
BLE_PAYLOAD_ALL_0 = BLETestPayload(0x14)
|
||||
BLE_PAYLOAD_EYELONG_0_1 = BLETestPayload(0x18)
|
||||
BLE_PAYLOAD_EYESHORT_0_1 = BLETestPayload(0x1C)
|
||||
|
||||
// FLRC Modulation Params
|
||||
// Bitrate + Bandwidth
|
||||
FLRC_BR_1_300_BW_1_2 = FLRCBitrateBandwidth(0x45)
|
||||
FLRC_BR_1_000_BW_1_2 = FLRCBitrateBandwidth(0x69)
|
||||
FLRC_BR_0_650_BW_0_6 = FLRCBitrateBandwidth(0x86)
|
||||
FLRC_BR_0_520_BW_0_6 = FLRCBitrateBandwidth(0xAA)
|
||||
FLRC_BR_0_325_BW_0_3 = FLRCBitrateBandwidth(0xC7)
|
||||
FLRC_BR_0_260_BW_0_3 = FLRCBitrateBandwidth(0xEB)
|
||||
|
||||
// Coding Rate
|
||||
FLRC_CR_1_2 = FLRCCodingRate(0x00) // 1/2
|
||||
FLRC_CR_3_4 = FLRCCodingRate(0x02) // 3/4
|
||||
FLRC_CR_1_0 = FLRCCodingRate(0x04) // 1
|
||||
|
||||
// FLRC Packet Params
|
||||
// Preamble Length
|
||||
FLRC_PREAMBLE_LENGTH_4_BITS = FLRCPreambleLength(0x00)
|
||||
FLRC_PREAMBLE_LENGTH_8_BITS = FLRCPreambleLength(0x10)
|
||||
FLRC_PREAMBLE_LENGTH_12_BITS = FLRCPreambleLength(0x20)
|
||||
FLRC_PREAMBLE_LENGTH_16_BITS = FLRCPreambleLength(0x30)
|
||||
FLRC_PREAMBLE_LENGTH_20_BITS = FLRCPreambleLength(0x40)
|
||||
FLRC_PREAMBLE_LENGTH_24_BITS = FLRCPreambleLength(0x50)
|
||||
FLRC_PREAMBLE_LENGTH_28_BITS = FLRCPreambleLength(0x60)
|
||||
FLRC_PREAMBLE_LENGTH_32_BITS = FLRCPreambleLength(0x70)
|
||||
|
||||
// Sync Word Length
|
||||
FLRC_SYNC_WORD_LEN_0 = FLRCSyncWordLength(0x00)
|
||||
FLRC_SYNC_WORD_LEN_32_BITS = FLRCSyncWordLength(0x04)
|
||||
|
||||
// Sync Word Match
|
||||
FLRC_SYNC_WORD_MATCH_DISABLE = FLRCSyncWordMatch(0x00) // Disable Sync Word
|
||||
FLRC_SYNC_WORD_MATCH_1 = FLRCSyncWordMatch(0x10) // Sync Word 1
|
||||
FLRC_SYNC_WORD_MATCH_2 = FLRCSyncWordMatch(0x20) // Sync Word 2
|
||||
FLRC_SYNC_WORD_MATCH_1_2 = FLRCSyncWordMatch(0x30) // Sync Word 1 or Sync Word 2
|
||||
FLRC_SYNC_WORD_MATCH_3 = FLRCSyncWordMatch(0x40) // Sync Word 3
|
||||
FLRC_SYNC_WORD_MATCH_1_3 = FLRCSyncWordMatch(0x50) // Sync Word 1 or Sync Word 3
|
||||
FLRC_SYNC_WORD_MATCH_2_3 = FLRCSyncWordMatch(0x60) // Sync Word 2 or Sync Word 3
|
||||
FLRC_SYNC_WORD_MATCH_1_2_3 = FLRCSyncWordMatch(0x70) // Sync Word 1 or Sync Word 2 or Sync Word 3
|
||||
|
||||
// Header Type
|
||||
FLRC_HEADER_FIXED_LENGTH = FLRCHeaderType(0x00)
|
||||
FLRC_HEADER_VARIABLE_LENGTH = FLRCHeaderType(0x20)
|
||||
|
||||
// CRC Type
|
||||
FLRC_CRC_OFF = FLRCCrcType(0x00)
|
||||
FLRC_CRC_1_BYTE = FLRCCrcType(0x10)
|
||||
FLRC_CRC_2_BYTES = FLRCCrcType(0x20)
|
||||
FLRC_CRC_3_BYTES = FLRCCrcType(0x30)
|
||||
|
||||
// LoRa Modulation Params
|
||||
|
||||
// SpreadingFactor
|
||||
LORA_SF_5 = LoRaSpreadingFactor(0x50)
|
||||
LORA_SF_6 = LoRaSpreadingFactor(0x60)
|
||||
LORA_SF_7 = LoRaSpreadingFactor(0x70)
|
||||
LORA_SF_8 = LoRaSpreadingFactor(0x80)
|
||||
LORA_SF_9 = LoRaSpreadingFactor(0x90)
|
||||
LORA_SF_10 = LoRaSpreadingFactor(0xA0)
|
||||
LORA_SF_11 = LoRaSpreadingFactor(0xB0)
|
||||
LORA_SF_12 = LoRaSpreadingFactor(0xC0)
|
||||
|
||||
// Bandwidth
|
||||
LORA_BW_1600 = LoRaBandwidth(0x0A)
|
||||
LORA_BW_800 = LoRaBandwidth(0x18)
|
||||
LORA_BW_400 = LoRaBandwidth(0x26)
|
||||
LORA_BW_200 = LoRaBandwidth(0x34)
|
||||
|
||||
// CodingRate
|
||||
LORA_CR_4_5 = LoRaCodingRate(0x01)
|
||||
LORA_CR_4_6 = LoRaCodingRate(0x02)
|
||||
LORA_CR_4_7 = LoRaCodingRate(0x03)
|
||||
LORA_CR_4_8 = LoRaCodingRate(0x04)
|
||||
LORA_CR_LI_4_5 = LoRaCodingRate(0x05)
|
||||
LORA_CR_LI_4_6 = LoRaCodingRate(0x06)
|
||||
LORA_CR_LI_4_8 = LoRaCodingRate(0x07)
|
||||
|
||||
// LoraPacketParams
|
||||
// HeaderType
|
||||
LORA_HEADER_EXPLICIT = LoRaHeaderType(0x00)
|
||||
LORA_HEADER_IMPLICIT = LoRaHeaderType(0x80)
|
||||
|
||||
// CRC Type
|
||||
LORA_CRC_ENABLE = LoRaCrcType(0x20)
|
||||
LORA_CRC_DISABLE = LoRaCrcType(0x00)
|
||||
|
||||
// IQ Type
|
||||
LORA_IQ_INVERTED = LoRaIqType(0x00)
|
||||
LORA_IQ_STD = LoRaIqType(0x40)
|
||||
|
||||
// RegulatorMode
|
||||
REGULATOR_LDO = RegulatorMode(0)
|
||||
REGULATOR_DC_DC = RegulatorMode(1)
|
||||
|
||||
// IRQ masks
|
||||
IRQ_ALL_MASK = IRQMask(0xFFFF)
|
||||
IRQ_NONE_MASK = IRQMask(0x0000)
|
||||
IRQ_TX_DONE_MASK = IRQMask(0b0000000000000001)
|
||||
IRQ_RX_DONE_MASK = IRQMask(0b0000000000000010)
|
||||
IRQ_SYNC_WORD_VALID_MASK = IRQMask(0b0000000000000100)
|
||||
IRQ_SYNC_WORD_ERROR_MASK = IRQMask(0b0000000000001000)
|
||||
IRQ_HEADER_VALID_MASK = IRQMask(0b0000000000010000)
|
||||
IRQ_HEADER_ERROR_MASK = IRQMask(0b0000000000100000)
|
||||
IRQ_CRC_ERROR_MASK = IRQMask(0b0000000001000000)
|
||||
IRQ_RANGING_SLAVE_RESPONSE_DONE_MASK = IRQMask(0b0000000010000000)
|
||||
IRQ_RANGING_SLAVE_RESPONSE_DISCARD_MASK = IRQMask(0b0000000100000000)
|
||||
IRQ_RANGING_MASTER_RESULT_VALID_MASK = IRQMask(0b0000001000000000)
|
||||
IRQ_RANGING_MASTER_TIMEOUT_MASK = IRQMask(0b0000010000000000)
|
||||
IRQ_RANGING_SLAVE_REQUEST_VALID_MASK = IRQMask(0b0000100000000000)
|
||||
IRQ_CAD_DONE_MASK = IRQMask(0b0001000000000000)
|
||||
IRQ_CAD_DETECTED_MASK = IRQMask(0b0010000000000000)
|
||||
IRQ_RX_TX_TIMEOUT_MASK = IRQMask(0b0100000000000000)
|
||||
IRQ_PREAMBLE_DETECTED_MASK = IRQMask(0b1000000000000000)
|
||||
IRQ_ADVANCED_RANGING_DONE_MASK = IRQMask(0b1000000000000000)
|
||||
|
||||
// GFSK Packet Info
|
||||
GFSK_SYNC_ERROR = GFSKPacketInfo(0b1000000)
|
||||
GFSK_LENGTH_ERROR = GFSKPacketInfo(0b0100000)
|
||||
GFSK_CRC_ERROR = GFSKPacketInfo(0b0010000)
|
||||
GFSK_ABORT_ERROR = GFSKPacketInfo(0b0001000)
|
||||
GFSK_HEADER_RECEIVED = GFSKPacketInfo(0b0000100)
|
||||
GFSK_PACKET_RECEIVED = GFSKPacketInfo(0b0000010)
|
||||
GFSK_PACKET_CRTL_BUSY = GFSKPacketInfo(0b0000001)
|
||||
|
||||
// BLE Packet Info
|
||||
BLE_SYNC_ERROR = BLEPacketInfo(0b1000000)
|
||||
BLE_LENGTH_ERROR = BLEPacketInfo(0b0100000)
|
||||
BLE_CRC_ERROR = BLEPacketInfo(0b0010000)
|
||||
BLE_ABORT_ERROR = BLEPacketInfo(0b0001000)
|
||||
BLE_HEADER_RECEIVED = BLEPacketInfo(0b0000100)
|
||||
BLE_PACKET_RECEIVED = BLEPacketInfo(0b0000010)
|
||||
BLE_PACKET_CRTL_BUSY = BLEPacketInfo(0b0000001)
|
||||
|
||||
// FLRC Packet Info
|
||||
FLRC_SYNC_ERROR = FLRCPacketInfo(0b1000000)
|
||||
FLRC_LENGTH_ERROR = FLRCPacketInfo(0b0100000)
|
||||
FLRC_CRC_ERROR = FLRCPacketInfo(0b0010000)
|
||||
FLRC_ABORT_ERROR = FLRCPacketInfo(0b0001000)
|
||||
FLRC_HEADER_RECEIVED = FLRCPacketInfo(0b0000100)
|
||||
FLRC_PACKET_RECEIVED = FLRCPacketInfo(0b0000010)
|
||||
FLRC_PACKET_CRTL_BUSY = FLRCPacketInfo(0b0000001)
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
package sx128x
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrBusyPinTimeout = errors.New("busy pin timeout")
|
||||
errDataTooLong = errors.New("data over 256 bytes")
|
||||
errInvalidSleepConfig = errors.New("invalid sleep config")
|
||||
errInvalidStandbyConfig = errors.New("invalid standby config")
|
||||
errFrequencyTooLow = errors.New("frequency below 2.4Ghz")
|
||||
errFrequencyTooHigh = errors.New("frequency above 2.5Ghz")
|
||||
errPowerTooLow = errors.New("power level below -18dBm")
|
||||
errPowerTooHigh = errors.New("power level above 13dBm")
|
||||
errInvalidPeriodBase = errors.New("invalid period base")
|
||||
errInvalidPacketType = errors.New("invalid packet type")
|
||||
errInvalidRegulatorMode = errors.New("invalid regulator mode")
|
||||
errPayloadLengthTooShort = errors.New("payload length too short")
|
||||
errPayloadLengthTooLong = errors.New("payload length too long")
|
||||
)
|
||||
@@ -0,0 +1,69 @@
|
||||
package sx128x
|
||||
|
||||
const (
|
||||
|
||||
// SX128X register map
|
||||
REG_FIRMWARE_VERSIONS = uint16(0x153)
|
||||
REG_RX_GAIN = uint16(0x891)
|
||||
REG_MANUAL_GAIN_SETTING = uint16(0x895)
|
||||
REG_LNA_GAIN_VALUE = uint16(0x89E)
|
||||
REG_LNA_GAIN_CONTROL = uint16(0x89F)
|
||||
REG_SYNCH_PEAK_ATTENUATION = uint16(0x8C2)
|
||||
REG_PAYLOAD_LENGTH = uint16(0x901)
|
||||
REG_LORA_HEADER_MODE = uint16(0x903)
|
||||
REG_RANGING_REQUEST_ADDRESS_BYTE_3 = uint16(0x912)
|
||||
REG_RANGING_REQUEST_ADDRESS_BYTE_2 = uint16(0x913)
|
||||
REG_RANGING_REQUEST_ADDRESS_BYTE_1 = uint16(0x914)
|
||||
REG_RANGING_REQUEST_ADDRESS_BYTE_0 = uint16(0x915)
|
||||
REG_RANGING_DEVICE_ADDRESS_BYTE_3 = uint16(0x916)
|
||||
REG_RANGING_DEVICE_ADDRESS_BYTE_2 = uint16(0x917)
|
||||
REG_RANGING_DEVICE_ADDRESS_BYTE_1 = uint16(0x918)
|
||||
REG_RANGING_DEVICE_ADDRESS_BYTE_0 = uint16(0x919)
|
||||
REG_RANGING_FILTER_WINDOW_SIZE = uint16(0x91E)
|
||||
REG_RESET_RANGING_FILTER = uint16(0x923)
|
||||
REG_RANGING_RESULT_MUX = uint16(0x924)
|
||||
REG_SF_ADDITIONAL_CONFIGURATION = uint16(0x925)
|
||||
REG_RANGING_CALIBRATION_BYTE_2 = uint16(0x92B)
|
||||
REG_RANGING_CALIBRATION_BYTE_1 = uint16(0x92C)
|
||||
REG_RANGING_CALIBRATION_BYTE_0 = uint16(0x92D)
|
||||
REG_RANGING_ID_CHECK_LENGTH = uint16(0x931)
|
||||
REG_FREQUENCY_ERROR_CORRECTION = uint16(0x93C)
|
||||
REG_CAD_DETECT_PEAK = uint16(0x942)
|
||||
REG_LORA_SYNC_WORD_MSB = uint16(0x944)
|
||||
REG_LORA_SYNC_WORD_LSB = uint16(0x945)
|
||||
REG_HEADER_CRC = uint16(0x954)
|
||||
REG_CODING_RATE = uint16(0x950)
|
||||
REG_FEI_BYTE_2 = uint16(0x954)
|
||||
REG_FEI_BYTE_1 = uint16(0x955)
|
||||
REG_FEI_BYTE_0 = uint16(0x956)
|
||||
REG_RANGING_RESULT_BYTE_2 = uint16(0x961)
|
||||
REG_RANGING_RESULT_BYTE_1 = uint16(0x962)
|
||||
REG_RANGING_RESULT_BYTE_0 = uint16(0x963)
|
||||
REG_RANGING_RSSI = uint16(0x964)
|
||||
REG_FREEZE_RANGING_RESULT = uint16(0x97F)
|
||||
REG_PACKET_PREAMBLE_SETTINGS = uint16(0x9C1)
|
||||
REG_WHITENING_INITIAL_VALUE = uint16(0x9C5)
|
||||
REG_CRC_POLYNOMIAL_DEFINITION_MSB = uint16(0x9C6)
|
||||
REG_CRC_POLYNOMIAL_DEFINITION_LSB = uint16(0x9C7)
|
||||
REG_CRC_POLYNOMIAL_SEED_BYTE_2 = uint16(0x9C7)
|
||||
REG_CRC_POLYNOMIAL_SEED_BYTE_1 = uint16(0x9C8)
|
||||
REG_CRC_POLYNOMIAL_SEED_BYTE_0 = uint16(0x9C9)
|
||||
REG_CRC_MSB_INITIAL_VALUE = uint16(0x9C8)
|
||||
REG_CRC_LSB_INITIAL_VALUE = uint16(0x9C9)
|
||||
REG_SYNC_ADDRESS_CONTROL = uint16(0x9CD)
|
||||
REG_SYNC_ADDRESS_1_BYTE_4 = uint16(0x9CE)
|
||||
REG_SYNC_ADDRESS_1_BYTE_3 = uint16(0x9CF)
|
||||
REG_SYNC_ADDRESS_1_BYTE_2 = uint16(0x9D0)
|
||||
REG_SYNC_ADDRESS_1_BYTE_1 = uint16(0x9D1)
|
||||
REG_SYNC_ADDRESS_1_BYTE_0 = uint16(0x9D2)
|
||||
REG_SYNC_ADDRESS_2_BYTE_4 = uint16(0x9D3)
|
||||
REG_SYNC_ADDRESS_2_BYTE_3 = uint16(0x9D4)
|
||||
REG_SYNC_ADDRESS_2_BYTE_2 = uint16(0x9D5)
|
||||
REG_SYNC_ADDRESS_2_BYTE_1 = uint16(0x9D6)
|
||||
REG_SYNC_ADDRESS_2_BYTE_0 = uint16(0x9D7)
|
||||
REG_SYNC_ADDRESS_3_BYTE_4 = uint16(0x9D8)
|
||||
REG_SYNC_ADDRESS_3_BYTE_3 = uint16(0x9D9)
|
||||
REG_SYNC_ADDRESS_3_BYTE_2 = uint16(0x9DA)
|
||||
REG_SYNC_ADDRESS_3_BYTE_1 = uint16(0x9DB)
|
||||
REG_SYNC_ADDRESS_3_BYTE_0 = uint16(0x9DC)
|
||||
)
|
||||
@@ -0,0 +1,768 @@
|
||||
package sx128x
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
spi drivers.SPI
|
||||
nssPin pin.Output
|
||||
resetPin pin.Output
|
||||
busyPin pin.Input
|
||||
spiTxBuf []byte
|
||||
spiRxBuf []byte
|
||||
}
|
||||
|
||||
func New(spi drivers.SPI, nssPin pin.Output, resetPin pin.Output, busyPin pin.Input) *Device {
|
||||
return &Device{
|
||||
spi: spi,
|
||||
nssPin: nssPin,
|
||||
resetPin: resetPin,
|
||||
busyPin: busyPin,
|
||||
spiTxBuf: make([]byte, 256), // TODO: optimize buffer size
|
||||
spiRxBuf: make([]byte, 256),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) Reset() {
|
||||
d.resetPin.Set(false)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
d.resetPin.Set(true)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
func (d *Device) WaitWhileBusy(timeout time.Duration) error {
|
||||
// largest busy period is on boot with around ~400ish this should be more than enough
|
||||
now := time.Now()
|
||||
for d.busyPin.Get() {
|
||||
if time.Since(now) > timeout {
|
||||
return ErrBusyPinTimeout
|
||||
}
|
||||
runtime.Gosched()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get tranceiver status, returns circuit mode and command status
|
||||
func (d *Device) GetStatus() (CircuitMode, CommandStatus, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
status, err := d.spi.Transfer(cmdGetStatus)
|
||||
d.nssPin.Set(true)
|
||||
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
circuitMode := (status & circuitModeMask) >> 5
|
||||
commandStatus := (status & commandStatusMask) >> 2
|
||||
return CircuitMode(circuitMode), CommandStatus(commandStatus), nil
|
||||
}
|
||||
|
||||
func (d *Device) WriteRegister(addr uint16, data []byte) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdWriteRegister, uint8((addr>>8)&0xFF), uint8(addr&0xFF))
|
||||
d.spiTxBuf = append(d.spiTxBuf, data...)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) ReadRegister(addr uint16) (uint8, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdReadRegister, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF), 0x00, 0x00)
|
||||
d.spiRxBuf = d.spiRxBuf[:5]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return d.spiRxBuf[4], nil
|
||||
}
|
||||
|
||||
func (d *Device) WriteBuffer(offset uint8, data []byte) error {
|
||||
if len(data) > 256 {
|
||||
return errDataTooLong
|
||||
}
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdWriteBuffer, offset)
|
||||
d.spiTxBuf = append(d.spiTxBuf, data...)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Read data from the payload buffer starting at the given offset with the given length
|
||||
func (d *Device) ReadBuffer(offset uint8, length uint8) ([]byte, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdReadBuffer, offset, 0x00)
|
||||
for i := uint8(0); i < length; i++ {
|
||||
d.spiTxBuf = append(d.spiTxBuf, 0x00)
|
||||
}
|
||||
d.spiRxBuf = d.spiRxBuf[:len(d.spiTxBuf)]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.spiRxBuf[3:], nil
|
||||
}
|
||||
|
||||
// Set the device into sleep mode with the given configuration: 0 (no retention), 1 (ram retentation), 2 (buffer retention) or 3 (ram and buffer retention)
|
||||
func (d *Device) SetSleep(sleepConfig SleepConfig) error {
|
||||
if sleepConfig > (SLEEP_DATA_BUFFER_RETAIN | SLEEP_DATA_RAM_RETAIN) {
|
||||
return errInvalidSleepConfig
|
||||
}
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetSleep, uint8(sleepConfig))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Put device into standby mode, 0 (RC) or 1 (XOSC)
|
||||
func (d *Device) SetStandby(standbyConfig StandbyConfig) error {
|
||||
if standbyConfig > STANDBY_XOSC { // XOSC is the highest standby config anything higher is invalid
|
||||
return errInvalidStandbyConfig
|
||||
}
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetStandby, uint8(standbyConfig))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the device into Frequency Synthesizer mode
|
||||
func (d *Device) SetFs() error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetFS)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
func checkPeriodBase(periodBase PeriodBase) error {
|
||||
if periodBase > PERIOD_BASE_4_MS { // 4ms is the highest period base anything higher is invalid
|
||||
return errInvalidPeriodBase
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the device in transmit mode, the IRQ status should be cleared before using this command
|
||||
// timout is determined by periodBase * periodBaseCount
|
||||
func (d *Device) SetTx(periodBase PeriodBase, periodBaseCount uint16) error {
|
||||
err := checkPeriodBase(periodBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetTx, uint8(periodBase), uint8((periodBaseCount>>8)&0xFF), uint8(periodBaseCount&0xFF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Sets the device in receive mode, the IRQ status should be cleared before using this command
|
||||
// timeout is determined by periodBase * periodBaseCount
|
||||
func (d *Device) SetRx(periodBase PeriodBase, periodBaseCount uint16) error {
|
||||
err := checkPeriodBase(periodBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetRx, uint8(periodBase), uint8((periodBaseCount>>8)&0xFF), uint8(periodBaseCount&0xFF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Sets the device in a continuous receive mode, it enters receive mode with a timeout of periodBase * rxPeriodBaseCount.
|
||||
// If no packet is received it will enter sleep mode for periodBase * sleepPeriodBaseCount before re-entering receive mode.
|
||||
// The loop is exited when a packet is received or the device is put into standby mode.
|
||||
func (d *Device) SetRxDutyCycle(periodBase PeriodBase, rxPeriodBaseCount uint16, sleepPeriodBaseCount uint16) error {
|
||||
err := checkPeriodBase(periodBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetRxDutyCycle, uint8(periodBase), uint8((rxPeriodBaseCount&0xFF00)>>8), uint8(rxPeriodBaseCount&0x00FF))
|
||||
d.spiTxBuf = append(d.spiTxBuf, uint8((sleepPeriodBaseCount&0xFF00)>>8), uint8(sleepPeriodBaseCount&0x00FF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Sets the transceiver into Long Preamble mode, and can only be used with either the LoRa mode and GFSK mode
|
||||
func (d *Device) SetLongPreamble(enable bool) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetLongPreamble)
|
||||
if enable {
|
||||
d.spiTxBuf = append(d.spiTxBuf, 1)
|
||||
} else {
|
||||
d.spiTxBuf = append(d.spiTxBuf, 0)
|
||||
}
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Channel activity detection (CAD) is a LoRa specific mode of operation where the device searches for a LoRa signal.
|
||||
// After search has completed, the device returns to STDBY_RC mode. The length of the search is configured via the SetCadParams() command.
|
||||
func (d *Device) SetCAD() error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetCAD)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Test command to generate a Continuous Wave (RF tone) at a selected frequency and output power
|
||||
// The device remains in Tx Continuous Wave until the host sends a mode configuration command.
|
||||
func (d *Device) SetTxContinuousWave() error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetTxContinuousWave)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Test command to generate an infinite sequence of alternating ‘0’s and ‘1’s in
|
||||
// GFSK modulation and symbol 0 in LoRa. The device remains in transmit until the host sends a mode configuration command.
|
||||
func (d *Device) SetTxContinuousPreamble() error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetContinuousPreamble)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// This command allows the transceiver to send a packet at a user programmable time after the end of a packet reception.
|
||||
// This is useful for Bluetooth Low Energy (BLE) compatibility which requires the transceiver to be able to send back a response 150µs after a packet reception.
|
||||
func (d *Device) SetAutoTx(timeUs uint16) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetAutoTx, uint8((timeUs&0xFF00)>>8), uint8(timeUs&0x00FF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Modifies the chip behavior so that the state following a Rx or Tx operation is FS and not standby.
|
||||
// This allows for faster transitions between Rx and/or Tx.
|
||||
func (d *Device) SetAutoFs(enable bool) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetAutoFS)
|
||||
if enable {
|
||||
d.spiTxBuf = append(d.spiTxBuf, 1)
|
||||
} else {
|
||||
d.spiTxBuf = append(d.spiTxBuf, 0)
|
||||
}
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Choose between GFSK, LoRa, Ranging, FLRC or BLE packet types, this will affect the available configuration parameters and the structure of the packet
|
||||
func (d *Device) SetPacketType(packetType PacketType) error {
|
||||
if packetType > PACKET_TYPE_BLE { // BLE is the highest packet type anything higher is invalid.
|
||||
return errInvalidPacketType
|
||||
}
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetPacketType, uint8(packetType))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the currently configured packet type, this will be 0 (GFSK), 1 (LoRa), 2 (Ranging), 3 (FLRC) or 4 (BLE)
|
||||
func (d *Device) GetPacketType() (PacketType, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdGetPacketType, 0x00, 0x00)
|
||||
d.spiRxBuf = d.spiRxBuf[:3]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return PacketType(d.spiRxBuf[2]), nil
|
||||
}
|
||||
|
||||
// Set the RF frequency in Hz, must be between 2.4 GHz and 2.5 GHz
|
||||
func (d *Device) SetRfFrequency(frequencyHz uint32) error {
|
||||
if frequencyHz < 2400000000 {
|
||||
return errFrequencyTooLow
|
||||
}
|
||||
if frequencyHz > 2500000000 {
|
||||
return errFrequencyTooHigh
|
||||
}
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
rfFrequency := uint32((uint64(frequencyHz) << 18) / 52000000)
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetRFFrequency, uint8((rfFrequency>>16)&0xFF), uint8((rfFrequency>>8)&0xFF), uint8(rfFrequency&0xFF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the output power in dBm, must be between -18 and 13 dBm, and the ramp time
|
||||
func (d *Device) SetTxParams(powerdBm int8, rampTime RadioRampTime) error {
|
||||
if powerdBm < -18 {
|
||||
return errPowerTooLow
|
||||
}
|
||||
if powerdBm > 13 {
|
||||
return errPowerTooHigh
|
||||
}
|
||||
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
adjustedPower := uint8(powerdBm + 18)
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetTxParams, adjustedPower, uint8(rampTime))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the number of symbols used for channel activity detection which determines the sensitivity of the detection.
|
||||
// This is only applicable in LoRa mode.
|
||||
func (d *Device) SetCadParams(cadSymbolNum uint8) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetCADParams, cadSymbolNum)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the base address for the internal buffer for Tx and Rx operations.
|
||||
// When transmitting or receiving data is read from or written to the buffer starting at the given offset.
|
||||
func (d *Device) SetBufferBaseAddress(txBase uint8, rxBase uint8) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetBufferBaseAddress, txBase, rxBase)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// The arguments to this function depend on the packet type. It is recommended to use the mode specific functions for a better experience.
|
||||
// BLE & GFSK: BitrateBandwidth, ModulationIndex, ModulationShaping
|
||||
// FLRC: BitrateBandwidth, CodingRate, ModulationShaping
|
||||
// LoRa & Ranging: SpreadingFactor, Bandwidth, CodingRate
|
||||
func (d *Device) SetModulationParams(modParam1, modParam2, modParam3 uint8) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetModulationParams, modParam1, modParam2, modParam3)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) SetModulationParamsBLE(bitrateBandwidth GFSKBLEBitrateBandwidth, modulationIndex ModulationIndex, modulationShaping ModulationShaping) error {
|
||||
return d.SetModulationParams(uint8(bitrateBandwidth), uint8(modulationIndex), uint8(modulationShaping))
|
||||
}
|
||||
|
||||
func (d *Device) SetModulationParamsGFSK(bitrateBandwidth GFSKBLEBitrateBandwidth, modulationIndex ModulationIndex, modulationShaping ModulationShaping) error {
|
||||
return d.SetModulationParams(uint8(bitrateBandwidth), uint8(modulationIndex), uint8(modulationShaping))
|
||||
}
|
||||
|
||||
func (d *Device) SetModulationParamsFLRC(bitrateBandwidth FLRCBitrateBandwidth, codingRate FLRCCodingRate, modulationShaping ModulationShaping) error {
|
||||
return d.SetModulationParams(uint8(bitrateBandwidth), uint8(codingRate), uint8(modulationShaping))
|
||||
}
|
||||
|
||||
func (d *Device) SetModulationParamsLoRa(spreadingFactor LoRaSpreadingFactor, bandwidth LoRaBandwidth, codingRate LoRaCodingRate) error {
|
||||
return d.SetModulationParams(uint8(spreadingFactor), uint8(bandwidth), uint8(codingRate))
|
||||
}
|
||||
|
||||
// The arguments to this function depend on the packet type. It is recommended to use the mode specific functions for a better experience.
|
||||
// GFSK & FLRC: PreambleLength, SyncWordLength, SyncWordMatch, HeaderType, PayloadLength, CrcLength, Whitening
|
||||
// BLE: ConnectionState, CrcLength, BleTestPayload, Whitening
|
||||
// LoRa & Ranging: PreambleLength, HeaderType, PayloadLength, CRC, InvertIQ/chirp invert
|
||||
func (d *Device) SetPacketParams(param1, param2, param3, param4, param5, param6, param7 uint8) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetPacketParams, param1, param2, param3, param4, param5, param6, param7)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set GFSK related packet parameters, this assumes the packet type is already set to GFSK.
|
||||
// - payloadLength: range of 0-255
|
||||
func (d *Device) SetPacketParamsGFSK(preambleLength GFSKPreambleLength, syncWordLength GFSKSyncWordLength, syncWordMatch GFSKSyncWordMatch, headerType GFSKHeaderType, payloadLength uint8, crcLength GFSKCrcType, whitening bool) error {
|
||||
var whiteningVal uint8
|
||||
if whitening {
|
||||
whiteningVal = whiteningEnable
|
||||
} else {
|
||||
whiteningVal = whiteningDisable
|
||||
}
|
||||
return d.SetPacketParams(uint8(preambleLength), uint8(syncWordLength), uint8(syncWordMatch), uint8(headerType), payloadLength, uint8(crcLength), whiteningVal)
|
||||
}
|
||||
|
||||
// Set FLRC related packet parameters, this assumes the packet type is already set to FLRC.
|
||||
// - payloadLength: range of 6-127
|
||||
func (d *Device) SetPacketParamsFLRC(preambleLength FLRCPreambleLength, syncWordLength FLRCSyncWordLength, syncWordMatch FLRCSyncWordMatch, headerType FLRCHeaderType, payloadLength uint8, crcLength FLRCCrcType) error {
|
||||
if payloadLength < 6 {
|
||||
return errPayloadLengthTooShort
|
||||
}
|
||||
if payloadLength > 127 {
|
||||
return errPayloadLengthTooLong
|
||||
}
|
||||
return d.SetPacketParams(uint8(preambleLength), uint8(syncWordLength), uint8(syncWordMatch), uint8(headerType), payloadLength, uint8(crcLength), whiteningDisable)
|
||||
}
|
||||
|
||||
// Set BLE related packet parameters, this assumes the packet type is already set to BLE.
|
||||
func (d *Device) SetPacketParamsBLE(connectionState BLEConnectionState, crcLength BLECrcType, bleTestPayload BLETestPayload, whitening bool) error {
|
||||
var whiteningVal uint8
|
||||
if whitening {
|
||||
whiteningVal = whiteningEnable
|
||||
} else {
|
||||
whiteningVal = whiteningDisable
|
||||
}
|
||||
return d.SetPacketParams(uint8(connectionState), uint8(crcLength), uint8(bleTestPayload), whiteningVal, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Set LoRa related packet parameters, this assumes the packet type is already set to LoRa.
|
||||
// - payloadLength: range of 1-255
|
||||
func (d *Device) SetPacketParamsLoRa(preambleLength uint32, headerType LoRaHeaderType, payloadLength uint8, crcType LoRaCrcType, iqType LoRaIqType) error {
|
||||
if payloadLength == 0 {
|
||||
return errPayloadLengthTooShort
|
||||
}
|
||||
exponent, mantissa := getExponentAndMantissa(preambleLength)
|
||||
return d.SetPacketParams(uint8(exponent<<4)|mantissa, uint8(headerType), payloadLength, uint8(crcType), uint8(iqType), 0, 0)
|
||||
}
|
||||
|
||||
func getExponentAndMantissa(value uint32) (uint8, uint8) {
|
||||
// pulled from RadioLib https://github.com/jgromes/RadioLib/blob/master/src/modules/SX128x/SX128x.cpp
|
||||
e := uint8(1)
|
||||
m := uint8(1)
|
||||
len := uint32(0)
|
||||
for e = uint8(1); e <= 15; e++ {
|
||||
for m = uint8(1); m <= 15; m++ {
|
||||
len = uint32(m) * (uint32(1 << e))
|
||||
if len >= value {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len >= value {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return e, m
|
||||
}
|
||||
|
||||
// Get information about the most recent packet received.
|
||||
// Return the payload length, the offset in the buffer where the payload starts.
|
||||
func (d *Device) GetRxBufferStatus() (uint8, uint8, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdGetRxBufferStatus, 0x00, 0x00, 0x00)
|
||||
d.spiRxBuf = d.spiRxBuf[:4]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return d.spiRxBuf[2], d.spiRxBuf[3], nil
|
||||
}
|
||||
|
||||
// The return type of this function depends on the packet type. Use mode specific function for typed returns.
|
||||
// BLE, GFSK & FLRC: unused, rssiSync, errors, status, sync
|
||||
// LoRa & Ranging: rssiSync, SNR
|
||||
func (d *Device) GetPacketStatus() (uint8, uint8, uint8, uint8, uint8, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdGetPacketStatus, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||
d.spiRxBuf = d.spiRxBuf[:7]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, err
|
||||
}
|
||||
return d.spiRxBuf[2], d.spiRxBuf[3], d.spiRxBuf[4], d.spiRxBuf[5], d.spiRxBuf[6], nil
|
||||
}
|
||||
|
||||
// Get information about the most recent GFSK packet received or transmitted:
|
||||
// - RSSI of last received packet
|
||||
// - packet information (each bit represents a different error or status flag)
|
||||
// - whether the last packet transmission has ended
|
||||
// - the sync word that was used for the last packet reception (0-3)
|
||||
func (d *Device) GetPacketStatusGFSK() (float32, GFSKPacketInfo, bool, uint8, error) {
|
||||
_, rssiSync, packetInfo, status, sync, err := d.GetPacketStatus()
|
||||
if err != nil {
|
||||
return 0, 0, false, 0, err
|
||||
}
|
||||
return float32(int8(rssiSync)) / 2 * -1, GFSKPacketInfo(packetInfo), status != 0, sync, nil
|
||||
}
|
||||
|
||||
// Get information about the most recent BLE packet received or transmitted:
|
||||
// - RSSI of last received packet
|
||||
// - packet information (each bit represents a different error or status flag)
|
||||
// - whether the last packet transmission has ended
|
||||
// - the sync word that was used for the last packet reception (0-1)
|
||||
func (d *Device) GetPacketStatusBLE() (float32, BLEPacketInfo, bool, uint8, error) {
|
||||
_, rssiSync, packetInfo, status, sync, err := d.GetPacketStatus()
|
||||
if err != nil {
|
||||
return 0, 0, false, 0, err
|
||||
}
|
||||
return float32(int8(rssiSync)) / 2 * -1, BLEPacketInfo(packetInfo), status != 0, sync, nil
|
||||
}
|
||||
|
||||
// Get information about the most recent BLE packet received or transmitted:
|
||||
// - RSSI of last received packet
|
||||
// - packet information (each bit represents a different error or status flag)
|
||||
// - PID field of the received packet
|
||||
// - NO_ACK field of the received packet
|
||||
// - PID check status of the current packet
|
||||
// - whether the last packet transmission has ended
|
||||
// - the sync word that was used for the last packet reception (0-1)
|
||||
func (d *Device) GetPacketStatusFLRC() (float32, FLRCPacketInfo, uint8, bool, bool, bool, uint8, error) {
|
||||
_, rawRSSI, packetInfo, rxTxInfo, sync, err := d.GetPacketStatus()
|
||||
|
||||
rxPid := (rxTxInfo & 0b11000000) >> 6
|
||||
noAck := (rxTxInfo & 0b00100000) != 0
|
||||
pidCheck := (rxTxInfo & 0b00010000) != 0
|
||||
txDone := (rxTxInfo & 0b00000001) != 0
|
||||
|
||||
if err != nil {
|
||||
return 0, 0, 0, false, false, false, 0, err
|
||||
}
|
||||
return float32(int8(rawRSSI)) / 2 * -1, FLRCPacketInfo(packetInfo), rxPid, noAck, pidCheck, txDone, sync, nil
|
||||
}
|
||||
|
||||
// Get information about the most recent LoRa packet received:
|
||||
// - RSSI of last received packet
|
||||
// - signal-to-noise ratio (SNR) of last received packet
|
||||
func (d *Device) GetPacketStatusLoRa() (float32, float32, error) {
|
||||
rawRSSI, rawSnr, _, _, _, err := d.GetPacketStatus()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return float32(int8(rawRSSI)) / 2 * -1, float32(int8(rawSnr)) / 4, nil
|
||||
}
|
||||
|
||||
// Get the instantaneous RSSI value during reception of the packet
|
||||
func (d *Device) GetRssiInst() (float32, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdGetRSSIInst, 0x00, 0x00)
|
||||
d.spiRxBuf = d.spiRxBuf[:3]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return float32(int8(d.spiRxBuf[2])) / 2 * -1, nil
|
||||
}
|
||||
|
||||
// Configure the overall IRQ mask and the mapping of individual IRQs to the DIO1, DIO2 and DIO3 pins
|
||||
func (d *Device) SetDioIrqParams(irqMask IRQMask, dio1Mask IRQMask, dio2Mask IRQMask, dio3Mask IRQMask) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetDIOIRQParams, uint8((irqMask&0xFF00)>>8), uint8(irqMask&0x00FF))
|
||||
d.spiTxBuf = append(d.spiTxBuf, uint8((dio1Mask&0xFF00)>>8), uint8(dio1Mask&0x00FF))
|
||||
d.spiTxBuf = append(d.spiTxBuf, uint8((dio2Mask&0xFF00)>>8), uint8(dio2Mask&0x00FF))
|
||||
d.spiTxBuf = append(d.spiTxBuf, uint8((dio3Mask&0xFF00)>>8), uint8(dio3Mask&0x00FF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the current IRQ status.
|
||||
func (d *Device) GetIrqStatus() (IRQMask, error) {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdGetIRQStatus, 0x00, 0x00, 0x00)
|
||||
d.spiRxBuf = d.spiRxBuf[:4]
|
||||
err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
|
||||
d.nssPin.Set(true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(d.spiRxBuf[2])<<8 | uint16(d.spiRxBuf[3]), err
|
||||
}
|
||||
|
||||
// Clear the IRQ bits specified in the irqMask.
|
||||
func (d *Device) ClearIrqStatus(irqMask IRQMask) error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdClearIRQStatus, uint8((irqMask&0xFF00)>>8), uint8(irqMask&0x00FF))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Switch between the low-dropout regulator (LDO) and the DC-DC converter for internal power regulation.
|
||||
func (d *Device) SetRegulatorMode(mode RegulatorMode) error {
|
||||
if mode > REGULATOR_DC_DC { // DC-DC is the highest regulator mode anything higher is invalid
|
||||
return errInvalidRegulatorMode
|
||||
}
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetRegulatorMode, uint8(mode))
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Stores the present context of the radio register values to the Data RAM which will be restored when the device wakes up from sleep mode.
|
||||
func (d *Device) SetSaveContext() error {
|
||||
err := d.WaitWhileBusy(time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nssPin.Set(false)
|
||||
d.spiTxBuf = d.spiTxBuf[:0]
|
||||
d.spiTxBuf = append(d.spiTxBuf, cmdSetSaveContext)
|
||||
err = d.spi.Tx(d.spiTxBuf, nil)
|
||||
d.nssPin.Set(true)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user