From d6d06396b4df3e82bf334a66d08496283719cb5d Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 14 Jan 2023 21:13:03 +0100 Subject: [PATCH] sx126x: refactor to RadioController interface to more easily handle non-STM32WL boards and remove duplicated code Signed-off-by: deadprogram --- examples/lora/lorawan/common/sim.go | 2 +- examples/lora/lorawan/common/stm32wlx.go | 65 +++++++++++++ examples/lora/lorawan/common/sx126x.go | 32 +++---- .../sx126x/lora_continuous/lora_continuous.go | 10 +- examples/sx126x/lora_continuous/radio.go | 19 ++++ .../sx126x/lora_continuous/radio_stm32wl.go | 15 +++ examples/sx126x/lora_rxtx/lora_rxtx.go | 24 +---- examples/sx126x/lora_rxtx/radio.go | 19 ++++ examples/sx126x/lora_rxtx/radio_stm32wl.go | 15 +++ examples/sx126x/rfswitch/lorae5.go | 42 -------- examples/sx126x/rfswitch/wl55jc.go | 54 ----------- .../gnse.go => sx126x/radiocontrol_gnse.go | 34 +++---- sx126x/radiocontrol_lorae5.go | 47 +++++++++ sx126x/radiocontrol_pins.go | 96 +++++++++++++++++++ ...spi_stm32wl.go => radiocontrol_stm32wl.go} | 56 +++++------ .../radiocontrol_wl55jc.go | 32 ++++--- sx126x/sx126x.go | 93 +++++++++++------- 17 files changed, 420 insertions(+), 235 deletions(-) create mode 100644 examples/lora/lorawan/common/stm32wlx.go create mode 100644 examples/sx126x/lora_continuous/radio.go create mode 100644 examples/sx126x/lora_continuous/radio_stm32wl.go create mode 100644 examples/sx126x/lora_rxtx/radio.go create mode 100644 examples/sx126x/lora_rxtx/radio_stm32wl.go delete mode 100644 examples/sx126x/rfswitch/lorae5.go delete mode 100644 examples/sx126x/rfswitch/wl55jc.go rename examples/sx126x/rfswitch/gnse.go => sx126x/radiocontrol_gnse.go (67%) create mode 100644 sx126x/radiocontrol_lorae5.go create mode 100644 sx126x/radiocontrol_pins.go rename sx126x/{spi_stm32wl.go => radiocontrol_stm32wl.go} (63%) rename examples/lora/lorawan/common/wl55jc.go => sx126x/radiocontrol_wl55jc.go (68%) diff --git a/examples/lora/lorawan/common/sim.go b/examples/lora/lorawan/common/sim.go index 43db446..c11a047 100644 --- a/examples/lora/lorawan/common/sim.go +++ b/examples/lora/lorawan/common/sim.go @@ -1,4 +1,4 @@ -//go:build !featherwing && !gnse && !lorae5 && !nucleowl55jc +//go:build !featherwing && !stm32wlx && !sx126x package common diff --git a/examples/lora/lorawan/common/stm32wlx.go b/examples/lora/lorawan/common/stm32wlx.go new file mode 100644 index 0000000..8b8461b --- /dev/null +++ b/examples/lora/lorawan/common/stm32wlx.go @@ -0,0 +1,65 @@ +//go:build stm32wlx + +package common + +import ( + "machine" + + "tinygo.org/x/drivers/lora" + "tinygo.org/x/drivers/sx126x" +) + +const ( + FREQ = 868100000 + LORA_DEFAULT_RXTIMEOUT_MS = 1000 + LORA_DEFAULT_TXTIMEOUT_MS = 5000 +) + +var ( + loraRadio *sx126x.Device +) + +var spi = machine.SPI3 + +func newRadioControl() sx126x.RadioController { + return sx126x.NewRadioControl() +} + +// do sx126x setup here +func SetupLora() (lora.Radio, error) { + loraRadio = sx126x.New(spi) + loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) + + // Create radio controller for target + loraRadio.SetRadioController(newRadioControl()) + + if state := loraRadio.DetectDevice(); !state { + return nil, errRadioNotFound + } + + loraConf := lora.Config{ + Freq: FREQ, + Bw: lora.Bandwidth_500_0, + Sf: lora.SpreadingFactor9, + Cr: lora.CodingRate4_7, + HeaderType: lora.HeaderExplicit, + Preamble: 12, + Ldr: lora.LowDataRateOptimizeOff, + Iq: lora.IQStandard, + Crc: lora.CRCOn, + SyncWord: lora.SyncPrivate, + LoraTxPowerDBm: 20, + } + + loraRadio.LoraConfig(loraConf) + + return loraRadio, nil +} + +func FirmwareVersion() string { + return "sx126x" +} + +func Lorarx() ([]byte, error) { + return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS) +} diff --git a/examples/lora/lorawan/common/sx126x.go b/examples/lora/lorawan/common/sx126x.go index 01c78c5..7d13d34 100644 --- a/examples/lora/lorawan/common/sx126x.go +++ b/examples/lora/lorawan/common/sx126x.go @@ -1,13 +1,9 @@ -//go:build gnse || lorae5 || nucleowl55jc +//go:build !stm32wlx && sx126x package common import ( - "device/stm32" "machine" - "runtime/interrupt" - - rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch" "tinygo.org/x/drivers/lora" "tinygo.org/x/drivers/sx126x" @@ -23,23 +19,28 @@ var ( loraRadio *sx126x.Device ) +var ( + spi = machine.SPI0 + nssPin, busyPin, dio0Pin, dio1Pin = machine.GP17, machine.GP20, machine.GP11, machine.GP10 + rxPin, txLowPin, txHighPin = machine.GP13, machine.GP12, machine.GP12 +) + +func newRadioControl() sx126x.RadioController { + return sx126x.NewRadioControl(nssPin, busyPin, dio0Pin, dio1Pin, rxPin, txLowPin, txHighPin) +} + // do sx126x setup here func SetupLora() (lora.Radio, error) { - loraRadio = sx126x.New(machine.SPI3) + loraRadio = sx126x.New(spi) loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) - // Create RF Switch - var radioSwitch rfswitch.CustomSwitch - loraRadio.SetRfSwitch(radioSwitch) + // Create radio controller for target + loraRadio.SetRadioController(newRadioControl()) if state := loraRadio.DetectDevice(); !state { return nil, errRadioNotFound } - // Add interrupt handler for Radio IRQs - intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler) - intr.Enable() - loraConf := lora.Config{ Freq: FREQ, Bw: lora.Bandwidth_500_0, @@ -59,11 +60,6 @@ func SetupLora() (lora.Radio, error) { return loraRadio, nil } -// radioIntHandler will take care of radio interrupts -func radioIntHandler(intr interrupt.Interrupt) { - loraRadio.HandleInterrupt() -} - func FirmwareVersion() string { return "sx126x" } diff --git a/examples/sx126x/lora_continuous/lora_continuous.go b/examples/sx126x/lora_continuous/lora_continuous.go index 4000dac..42fa3b9 100644 --- a/examples/sx126x/lora_continuous/lora_continuous.go +++ b/examples/sx126x/lora_continuous/lora_continuous.go @@ -5,8 +5,6 @@ import ( "machine" "time" - rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch" - "tinygo.org/x/drivers/lora" "tinygo.org/x/drivers/sx126x" ) @@ -24,12 +22,11 @@ func main() { machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput}) // Create the driver - loraRadio = sx126x.New(machine.SPI3) + loraRadio = sx126x.New(spi) loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) - // Create RF Switch - var radioSwitch rfswitch.CustomSwitch - loraRadio.SetRfSwitch(radioSwitch) + // Create radio controller for target + loraRadio.SetRadioController(newRadioControl()) state := loraRadio.DetectDevice() if !state { @@ -76,6 +73,5 @@ func main() { loraRadio.SetStandby() time.Sleep(60 * time.Second) - } } diff --git a/examples/sx126x/lora_continuous/radio.go b/examples/sx126x/lora_continuous/radio.go new file mode 100644 index 0000000..2778164 --- /dev/null +++ b/examples/sx126x/lora_continuous/radio.go @@ -0,0 +1,19 @@ +//go:build !stm32wlx + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/sx126x" +) + +var ( + spi = machine.SPI0 + nssPin, busyPin, dio0Pin, dio1Pin = machine.GP17, machine.GP20, machine.GP11, machine.GP10 + rxPin, txLowPin, txHighPin = machine.GP13, machine.GP12, machine.GP12 +) + +func newRadioControl() sx126x.RadioController { + return sx126x.NewRadioControl(nssPin, busyPin, dio0Pin, dio1Pin, rxPin, txLowPin, txHighPin) +} diff --git a/examples/sx126x/lora_continuous/radio_stm32wl.go b/examples/sx126x/lora_continuous/radio_stm32wl.go new file mode 100644 index 0000000..9da9de5 --- /dev/null +++ b/examples/sx126x/lora_continuous/radio_stm32wl.go @@ -0,0 +1,15 @@ +//go:build stm32wlx + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/sx126x" +) + +var spi = machine.SPI3 + +func newRadioControl() sx126x.RadioController { + return sx126x.NewRadioControl() +} diff --git a/examples/sx126x/lora_rxtx/lora_rxtx.go b/examples/sx126x/lora_rxtx/lora_rxtx.go index 1a2295a..524d6c6 100644 --- a/examples/sx126x/lora_rxtx/lora_rxtx.go +++ b/examples/sx126x/lora_rxtx/lora_rxtx.go @@ -4,13 +4,9 @@ package main // 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/lora" "tinygo.org/x/drivers/sx126x" ) @@ -27,23 +23,17 @@ var ( 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 = sx126x.New(spi) loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) - // Create RF Switch - var radioSwitch rfswitch.CustomSwitch - loraRadio.SetRfSwitch(radioSwitch) + // Create radio controller for target + loraRadio.SetRadioController(newRadioControl()) // Detect the device state := loraRadio.DetectDevice() @@ -51,10 +41,6 @@ func main() { panic("sx126x not detected.") } - // Add interrupt handler for Radio IRQs - intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler) - intr.Enable() - loraConf := lora.Config{ Freq: FREQ, Bw: lora.Bandwidth_500_0, @@ -73,10 +59,10 @@ func main() { var count uint for { - tStart := time.Now() + start := time.Now() println("main: Receiving Lora for 10 seconds") - for int(time.Now().Sub(tStart).Seconds()) < 10 { + for time.Since(start) < 10*time.Second { buf, err := loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS) if err != nil { println("RX Error: ", err) diff --git a/examples/sx126x/lora_rxtx/radio.go b/examples/sx126x/lora_rxtx/radio.go new file mode 100644 index 0000000..2778164 --- /dev/null +++ b/examples/sx126x/lora_rxtx/radio.go @@ -0,0 +1,19 @@ +//go:build !stm32wlx + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/sx126x" +) + +var ( + spi = machine.SPI0 + nssPin, busyPin, dio0Pin, dio1Pin = machine.GP17, machine.GP20, machine.GP11, machine.GP10 + rxPin, txLowPin, txHighPin = machine.GP13, machine.GP12, machine.GP12 +) + +func newRadioControl() sx126x.RadioController { + return sx126x.NewRadioControl(nssPin, busyPin, dio0Pin, dio1Pin, rxPin, txLowPin, txHighPin) +} diff --git a/examples/sx126x/lora_rxtx/radio_stm32wl.go b/examples/sx126x/lora_rxtx/radio_stm32wl.go new file mode 100644 index 0000000..9da9de5 --- /dev/null +++ b/examples/sx126x/lora_rxtx/radio_stm32wl.go @@ -0,0 +1,15 @@ +//go:build stm32wlx + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/sx126x" +) + +var spi = machine.SPI3 + +func newRadioControl() sx126x.RadioController { + return sx126x.NewRadioControl() +} diff --git a/examples/sx126x/rfswitch/lorae5.go b/examples/sx126x/rfswitch/lorae5.go deleted file mode 100644 index c385ad1..0000000 --- a/examples/sx126x/rfswitch/lorae5.go +++ /dev/null @@ -1,42 +0,0 @@ -//go: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 -} diff --git a/examples/sx126x/rfswitch/wl55jc.go b/examples/sx126x/rfswitch/wl55jc.go deleted file mode 100644 index 814d4ca..0000000 --- a/examples/sx126x/rfswitch/wl55jc.go +++ /dev/null @@ -1,54 +0,0 @@ -//go: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 - -} diff --git a/examples/sx126x/rfswitch/gnse.go b/sx126x/radiocontrol_gnse.go similarity index 67% rename from examples/sx126x/rfswitch/gnse.go rename to sx126x/radiocontrol_gnse.go index f1fc619..e2a72ee 100644 --- a/examples/sx126x/rfswitch/gnse.go +++ b/sx126x/radiocontrol_gnse.go @@ -9,52 +9,48 @@ 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 +package sx126x import ( "machine" - - "tinygo.org/x/drivers/sx126x" ) -type CustomSwitch struct { +// RadioControl for GNSE board. +type RadioControl struct { + STM32RadioControl } -var ( - rfstate int -) +func NewRadioControl() *RadioControl { + return &RadioControl{STM32RadioControl{}} +} -func (s CustomSwitch) InitRFSwitch() { +// Init pins needed for controlling rx/tx +func (rc *RadioControl) Init() error { 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 + + return nil } -func (s CustomSwitch) SetRfSwitchMode(mode int) error { - if mode == rfstate { - return nil - } - +func (rc *RadioControl) SetRfSwitchMode(mode int) error { switch mode { - case sx126x.RFSWITCH_TX_HP: + case 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: + case 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: + case RFSWITCH_RX: machine.RF_FE_CTRL1.Set(true) machine.RF_FE_CTRL2.Set(false) machine.RF_FE_CTRL3.Set(true) } - rfstate = mode - return nil } diff --git a/sx126x/radiocontrol_lorae5.go b/sx126x/radiocontrol_lorae5.go new file mode 100644 index 0000000..4adbf23 --- /dev/null +++ b/sx126x/radiocontrol_lorae5.go @@ -0,0 +1,47 @@ +//go:build lorae5 + +/* +LoRa-E5 module ONLY transmits through RFO_HP: + +Receive: PA4=1, PA5=0 +Transmit(high output power, SMPS mode): PA4=0, PA5=1 +*/ + +package sx126x + +import ( + "machine" +) + +// RadioControl for LoRa-E5 board. +type RadioControl struct { + STM32RadioControl +} + +func NewRadioControl() *RadioControl { + return &RadioControl{STM32RadioControl: STM32RadioControl{}} +} + +// Init pins needed for controlling rx/tx +func (rc *RadioControl) Init() error { + machine.PA4.Configure(machine.PinConfig{Mode: machine.PinOutput}) + machine.PB5.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + return nil +} + +func (rc *RadioControl) SetRfSwitchMode(mode int) error { + switch mode { + + case RFSWITCH_RX: + machine.PA4.Set(true) + machine.PB5.Set(false) + case RFSWITCH_TX_LP: + return errLowPowerTxNotSupported + case RFSWITCH_TX_HP: + machine.PA4.Set(false) + machine.PB5.Set(true) + + } + return nil +} diff --git a/sx126x/radiocontrol_pins.go b/sx126x/radiocontrol_pins.go new file mode 100644 index 0000000..9c15ec5 --- /dev/null +++ b/sx126x/radiocontrol_pins.go @@ -0,0 +1,96 @@ +//go:build !stm32wlx + +package sx126x + +import ( + "machine" + + "time" +) + +type RadioControl struct { + nssPin, busyPin, dio0Pin, dio1Pin machine.Pin + rxPin, txLowPin, txHighPin machine.Pin +} + +func NewRadioControl(nssPin, busyPin, dio0Pin, dio1Pin, + rxPin, txLowPin, txHighPin machine.Pin) *RadioControl { + return &RadioControl{ + nssPin: nssPin, + busyPin: busyPin, + dio0Pin: dio0Pin, + dio1Pin: dio1Pin, + rxPin: rxPin, + txLowPin: txLowPin, + txHighPin: txHighPin, + } +} + +// SetNss sets the NSS line aka chip select for SPI. +func (rc *RadioControl) SetNss(state bool) error { + rc.nssPin.Set(state) + return nil +} + +// WaitWhileBusy wait until the radio is no longer busy +func (rc *RadioControl) WaitWhileBusy() error { + count := 100 + for count > 0 { + if !rc.busyPin.Get() { + return nil + } + count-- + time.Sleep(time.Millisecond) + } + return errWaitWhileBusyTimeout +} + +// Init() configures whatever needed for sx126x radio control +func (rc *RadioControl) Init() error { + rc.nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + rc.busyPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + return nil +} + +// add interrupt handler for Radio IRQs for pins +func (rc *RadioControl) SetupInterrupts(handler func()) error { + irqHandler = handler + + rc.dio0Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + if err := rc.dio0Pin.SetInterrupt(machine.PinRising, handleInterrupt); err != nil { + return errRadioNotFound + } + + rc.dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + if err := rc.dio1Pin.SetInterrupt(machine.PinRising, handleInterrupt); err != nil { + return errRadioNotFound + } + + return nil +} + +var irqHandler func() + +func handleInterrupt(machine.Pin) { + irqHandler() +} + +func (rc *RadioControl) SetRfSwitchMode(mode int) error { + switch mode { + + case RFSWITCH_RX: + rc.rxPin.Set(true) + rc.txLowPin.Set(false) + rc.txHighPin.Set(false) + case RFSWITCH_TX_LP: + rc.rxPin.Set(false) + rc.txLowPin.Set(true) + rc.txHighPin.Set(false) + case RFSWITCH_TX_HP: + rc.rxPin.Set(false) + rc.txLowPin.Set(false) + rc.txHighPin.Set(true) + } + + return nil +} diff --git a/sx126x/spi_stm32wl.go b/sx126x/radiocontrol_stm32wl.go similarity index 63% rename from sx126x/spi_stm32wl.go rename to sx126x/radiocontrol_stm32wl.go index 586b0a4..89942ae 100644 --- a/sx126x/spi_stm32wl.go +++ b/sx126x/radiocontrol_stm32wl.go @@ -4,40 +4,28 @@ package sx126x import ( "device/stm32" - "errors" - "machine" - "tinygo.org/x/drivers" - "tinygo.org/x/drivers/lora" + + "runtime/interrupt" ) -// New creates a new SX126x connection. -func New(spi drivers.SPI) *Device { - c := make(chan lora.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 +// STM32RadioControl helps implement the RadioController interface +type STM32RadioControl struct { + irqHandler func() } -// SpiSetNss Sets the NSS line -func (d *Device) SpiSetNss(state bool) { +// SetNss sets the NSS line aka chip select for SPI. +func (rc *STM32RadioControl) SetNss(state bool) error { if state { stm32.PWR.SUBGHZSPICR.SetBits(stm32.PWR_SUBGHZSPICR_NSS) } else { stm32.PWR.SUBGHZSPICR.ClearBits(stm32.PWR_SUBGHZSPICR_NSS) - } + + return nil } -// WaitBusy sleep until all busy flags clears -func (d *Device) WaitBusy() error { +// WaitWhileBusy wait until the radio is no longer busy +func (rc *STM32RadioControl) WaitWhileBusy() error { count := 100 var rfbusyms, rfbusys bool for count > 0 { @@ -49,12 +37,11 @@ func (d *Device) WaitBusy() error { } count-- } - return errors.New("WaitBusy Timeout") + return errWaitWhileBusyTimeout } -// SubGhzInit() configures internal SX1262's SPI bus. -func (d *Device) SubGhzInit() { - +// init() configures whatever needed for sx126x radio control +func init() { // Enable APB3 Periph clock and delay stm32.RCC.APB3ENR.SetBits(stm32.RCC_APB3ENR_SUBGHZSPIEN) _ = stm32.RCC.APB3ENR.Get() @@ -81,5 +68,18 @@ func (d *Device) SubGhzInit() { 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) - +} + +func (rc *STM32RadioControl) SetupInterrupts(handler func()) error { + irqHandler = handler + intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, handleInterrupt) + intr.Enable() + + return nil +} + +var irqHandler func() + +func handleInterrupt(interrupt.Interrupt) { + irqHandler() } diff --git a/examples/lora/lorawan/common/wl55jc.go b/sx126x/radiocontrol_wl55jc.go similarity index 68% rename from examples/lora/lorawan/common/wl55jc.go rename to sx126x/radiocontrol_wl55jc.go index c4b32cd..d542ace 100644 --- a/examples/lora/lorawan/common/wl55jc.go +++ b/sx126x/radiocontrol_wl55jc.go @@ -1,8 +1,8 @@ //go:build nucleowl55jc /* - Nucleo WL55JC1 - RFSwitch +Nucleo WL55JC1 +RFSwitch +-----------+---------+------------+------------+ | | FE_CTRL1 | FE_CTRL2 | FE_CTRL3 | @@ -13,42 +13,48 @@ | RX | HIGH | LOW | HIGH | +-----------+----------+-----------+------------+ */ -package common +package sx126x import ( "machine" - - "tinygo.org/x/drivers/sx126x" ) -type CustomSwitch struct { +// RadioControl for Nucleo WL55JC1 board. +type RadioControl struct { + STM32RadioControl } -func (s CustomSwitch) InitRFSwitch() { +func NewRadioControl() *RadioControl { + return &RadioControl{STM32RadioControl{}} +} + +// Init pins needed for controlling rx/tx +func (rc *RadioControl) Init() error { machine.PC4.Configure(machine.PinConfig{Mode: machine.PinOutput}) machine.PC5.Configure(machine.PinConfig{Mode: machine.PinOutput}) machine.PC3.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + return nil } -func (s CustomSwitch) SetRfSwitchMode(mode int) error { +func (rc *RadioControl) SetRfSwitchMode(mode int) error { switch mode { - case sx126x.RFSWITCH_TX_HP: + case RFSWITCH_TX_HP: machine.PC4.Set(false) machine.PC5.Set(true) machine.PC3.Set(true) - case sx126x.RFSWITCH_TX_LP: + case RFSWITCH_TX_LP: machine.PC4.Set(true) machine.PC5.Set(true) machine.PC3.Set(true) - case sx126x.RFSWITCH_RX: + case RFSWITCH_RX: machine.PC4.Set(true) machine.PC5.Set(false) machine.PC3.Set(true) - } - return nil + return nil } diff --git a/sx126x/sx126x.go b/sx126x/sx126x.go index 39f0839..04b7591 100644 --- a/sx126x/sx126x.go +++ b/sx126x/sx126x.go @@ -13,12 +13,22 @@ import ( "tinygo.org/x/drivers/lora" ) -// SX126X radio transceiver RF_IN and RF_OUT may be connected -// to RF Switch. This interface allows the creation of struct +var ( + errWaitWhileBusyTimeout = errors.New("WaitBusy Timeout") + errLowPowerTxNotSupported = errors.New("RFSWITCH_TX_LP not supported") + errRadioNotFound = errors.New("LoRa radio not found") +) + +// SX126X radio transceiver has several pins that control +// RF_IN, RF_OUT, NSS, and BUSY. +// This interface allows the creation of struct // that can drive the RF Switch (Used in Lora RX and Lora Tx) -type RFSwitch interface { - InitRFSwitch() +type RadioController interface { + Init() error SetRfSwitchMode(mode int) error + SetNss(state bool) error + WaitWhileBusy() error + SetupInterrupts(handler func()) error } const ( @@ -38,18 +48,28 @@ const ( SPI_BUFFER_SIZE = 256 ) -// Device wraps an SPI connection to a SX127x device. +// Device wraps an SPI connection to a SX126x device. type Device struct { spi drivers.SPI // SPI bus for module communication - rstPin, csPin machine.Pin // GPIOs for reset and chip select + rstPin machine.Pin // GPIOs for reset, chip select, and busy pin radioEventChan chan lora.RadioEvent // Channel for Receiving events loraConf lora.Config // Current Lora configuration - rfswitch RFSwitch // RF Switch, if any + controller RadioController // to manage interactions with the radio deepSleep bool // Internal Sleep state deviceType int // sx1261,sx1262,sx1268 (defaults sx1261) spiBuffer [SPI_BUFFER_SIZE]uint8 } +// New creates a new SX126x connection. +func New(spi drivers.SPI) *Device { + c := make(chan lora.RadioEvent, 10) + d := Device{ + spi: spi, + radioEventChan: c, + } + return &d +} + const ( SX126X_RTC_FREQ_IN_HZ uint32 = 64000 ) @@ -79,10 +99,15 @@ 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() +// SetRadioControl let you define the RadioController +func (d *Device) SetRadioController(rc RadioController) error { + d.controller = rc + if err := d.controller.Init(); err != nil { + return err + } + d.controller.SetupInterrupts(d.HandleInterrupt) + + return nil } // -------------------------------------------------- @@ -126,8 +151,8 @@ func (d *Device) SetFs() { // 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) + if d.controller != nil { + d.controller.SetRfSwitchMode(RFSWITCH_TX_HP) } d.ExecSetCommand(SX126X_CMD_SET_TX_CONTINUOUS_WAVE, []uint8{}) } @@ -136,8 +161,8 @@ func (d *Device) SetTxContinuousWave() { // Take care to initialize all Lora settings like it's done in Tx 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) + if d.controller != nil { + d.controller.SetRfSwitchMode(RFSWITCH_TX_HP) } d.ExecSetCommand(SX126X_CMD_SET_TX_INFINITE_PREAMBLE, []uint8{}) } @@ -235,25 +260,25 @@ func (d *Device) SetRxTxFallbackMode(fallbackMode uint8) { // ReadRegister reads register value func (d *Device) ReadRegister(addr, size uint16) ([]uint8, error) { d.CheckDeviceReady() - d.SpiSetNss(false) + d.controller.SetNss(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() + d.controller.SetNss(true) + d.controller.WaitWhileBusy() return ret, nil } // WriteRegister writes value to register func (d *Device) WriteRegister(addr uint16, data []uint8) { d.CheckDeviceReady() - d.SpiSetNss(false) + d.controller.SetNss(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() + d.controller.SetNss(true) + d.controller.WaitWhileBusy() } // WriteBuffer write data from current buffer position @@ -466,12 +491,12 @@ func (d *Device) SetModulationParams(spreadingFactor, bandwidth, codingRate, low // CheckDeviceReady sleep until all busy flags clears func (d *Device) CheckDeviceReady() error { if d.deepSleep == true { - d.SpiSetNss(false) + d.controller.SetNss(false) time.Sleep(time.Millisecond) - d.SpiSetNss(true) + d.controller.SetNss(true) d.deepSleep = false } - return d.WaitBusy() + return d.controller.WaitWhileBusy() } // ExecSetCommand send a command to configure the peripheral @@ -482,24 +507,24 @@ func (d *Device) ExecSetCommand(cmd uint8, buf []uint8) { } else { d.deepSleep = false } - d.SpiSetNss(false) + d.controller.SetNss(false) // Send command and params d.spi.Tx(append([]uint8{cmd}, buf...), nil) - d.SpiSetNss(true) + d.controller.SetNss(true) if cmd != SX126X_CMD_SET_SLEEP { - d.WaitBusy() + d.controller.WaitWhileBusy() } } // ExecGetCommand queries the peripheral the peripheral func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 { d.CheckDeviceReady() - d.SpiSetNss(false) + d.controller.SetNss(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() + d.controller.SetNss(true) + d.controller.WaitWhileBusy() return d.spiBuffer[:size] } @@ -582,8 +607,8 @@ func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error { return lora.ErrUndefinedLoraConf } - if d.rfswitch != nil { - err := d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP) + if d.controller != nil { + err := d.controller.SetRfSwitchMode(RFSWITCH_TX_HP) if err != nil { return err } @@ -616,8 +641,8 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) { return nil, lora.ErrUndefinedLoraConf } - if d.rfswitch != nil { - err := d.rfswitch.SetRfSwitchMode(RFSWITCH_RX) + if d.controller != nil { + err := d.controller.SetRfSwitchMode(RFSWITCH_RX) if err != nil { return nil, err }