mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-08 17:03:39 +00:00
b6c750ccd1
This first version of the driver has been tested with STM32WL SoC, which embeddeds SX1262 radio on the same die.
700 lines
20 KiB
Go
700 lines
20 KiB
Go
// 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)
|
|
}
|
|
|
|
}
|