sx127x/sx126x: Remove heap alloc in interrupt, add non blocking channel send/receive, and other cleanups

This commit is contained in:
Olivier Fauchon
2023-02-23 14:15:07 +01:00
committed by Ron Evans
parent 0adc7e78aa
commit 56208a28d5
2 changed files with 91 additions and 45 deletions
+41 -21
View File
@@ -34,8 +34,9 @@ const (
)
const (
PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4
SPI_BUFFER_SIZE = 256
PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4
SPI_BUFFER_SIZE = 256
RADIOEVENTCHAN_SIZE = 1
)
// Device wraps an SPI connection to a SX126x device.
@@ -47,14 +48,18 @@ type Device struct {
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
spiTxBuf []byte // global Tx buffer to avoid heap allocations in interrupt
spiRxBuf []byte // global Rx buffer to avoid heap allocations in interrupt
}
// New creates a new SX126x connection.
func New(spi drivers.SPI) *Device {
return &Device{
spi: spi,
radioEventChan: make(chan lora.RadioEvent, 10),
radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
spiTxBuf: make([]byte, SPI_BUFFER_SIZE),
spiRxBuf: make([]byte, SPI_BUFFER_SIZE),
}
}
@@ -250,21 +255,25 @@ func (d *Device) ReadRegister(addr, size uint16) ([]uint8, error) {
d.CheckDeviceReady()
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.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, SX126X_CMD_READ_REGISTER, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF), 0x00)
d.spi.Tx(d.spiTxBuf, nil)
// Read registers
d.spiRxBuf = d.spiRxBuf[0:size]
d.spi.Tx(nil, d.spiRxBuf)
d.controller.SetNss(true)
d.controller.WaitWhileBusy()
return ret, nil
return d.spiRxBuf, nil
}
// WriteRegister writes value to register
func (d *Device) WriteRegister(addr uint16, data []uint8) {
d.CheckDeviceReady()
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.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, SX126X_CMD_WRITE_REGISTER, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF))
d.spiTxBuf = append(d.spiTxBuf, data...)
d.spi.Tx(d.spiTxBuf, nil)
d.controller.SetNss(true)
d.controller.WaitWhileBusy()
}
@@ -497,7 +506,10 @@ func (d *Device) ExecSetCommand(cmd uint8, buf []uint8) {
}
d.controller.SetNss(false)
// Send command and params
d.spi.Tx(append([]uint8{cmd}, buf...), nil)
d.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, cmd)
d.spiTxBuf = append(d.spiTxBuf, buf...)
d.spi.Tx(d.spiTxBuf, nil)
d.controller.SetNss(true)
if cmd != SX126X_CMD_SET_SLEEP {
d.controller.WaitWhileBusy()
@@ -509,11 +521,15 @@ func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 {
d.CheckDeviceReady()
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.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, cmd, 0x00)
d.spi.Tx(d.spiTxBuf, nil)
// Read resp
d.spiRxBuf = d.spiRxBuf[:size]
d.spi.Tx(nil, d.spiRxBuf)
d.controller.SetNss(true)
d.controller.WaitWhileBusy()
return d.spiBuffer[:size]
return d.spiRxBuf
}
//
@@ -685,22 +701,26 @@ func (d *Device) HandleInterrupt() {
st := d.GetIrqStatus()
d.ClearIrqStatus(SX126X_IRQ_ALL)
rChan := d.GetRadioEventChan()
if (st & SX126X_IRQ_RX_DONE) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventRxDone, st, nil)
e := lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil}
d.radioEventChan <- e
}
if (st & SX126X_IRQ_TX_DONE) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventTxDone, st, nil)
e := lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil}
d.radioEventChan <- e
}
if (st & SX126X_IRQ_TIMEOUT) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventTimeout, st, nil)
e := lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil}
d.radioEventChan <- e
}
if (st & SX126X_IRQ_CRC_ERR) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventCrcError, st, nil)
e := lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil}
d.radioEventChan <- e
}
}
+50 -24
View File
@@ -15,7 +15,8 @@ import (
// So we can keep track of the origin of interruption
const (
SPI_BUFFER_SIZE = 256
RADIOEVENTCHAN_SIZE = 1
SPI_BUFFER_SIZE = 256
)
// Device wraps an SPI connection to a SX127x device.
@@ -27,8 +28,8 @@ type Device struct {
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
packetIndex uint8 // FIXME ... useless ?
spiTxBuf []byte // global Tx buffer to avoid heap allocations in interrupt
spiRxBuf []byte // global Rx buffer to avoid heap allocations in interrupt
}
// --------------------------------------------------
@@ -46,7 +47,9 @@ func New(spi machine.SPI, rstPin machine.Pin) *Device {
k := Device{
spi: spi,
rstPin: rstPin,
radioEventChan: make(chan lora.RadioEvent, 10),
radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
spiTxBuf: make([]byte, SPI_BUFFER_SIZE),
spiRxBuf: make([]byte, SPI_BUFFER_SIZE),
}
return &k
}
@@ -79,21 +82,37 @@ func (d *Device) DetectDevice() bool {
// ReadRegister reads register value
func (d *Device) ReadRegister(reg uint8) uint8 {
d.controller.SetNss(false)
d.spi.Tx([]byte{reg & 0x7f}, nil)
var value [1]byte
d.spi.Tx(nil, value[:])
// Send register
//d.spiTxBuf = []byte{reg & 0x7f}
d.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, byte(reg&0x7f))
//println("R1 : ", len(d.spiTxBuf))
d.spi.Tx(d.spiTxBuf, nil)
// Read value
//d.spiRxBuf = []byte{reg & 0x00}
d.spiRxBuf = d.spiRxBuf[:0]
d.spiRxBuf = append(d.spiRxBuf, 0)
//println("R2 : ", len(d.spiTxBuf))
d.spi.Tx(nil, d.spiRxBuf)
d.controller.SetNss(true)
return value[0]
return d.spiRxBuf[0]
}
// WriteRegister writes value to register
func (d *Device) WriteRegister(reg uint8, value uint8) uint8 {
var response [1]byte
d.controller.SetNss(false)
d.spi.Tx([]byte{reg | 0x80}, nil)
d.spi.Tx([]byte{value}, response[:])
// Send register
d.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, byte(reg|0x80))
d.spi.Tx(d.spiTxBuf, nil)
// Send value
d.spiTxBuf = d.spiTxBuf[:0]
d.spiTxBuf = append(d.spiTxBuf, byte(value))
d.spiRxBuf = d.spiRxBuf[:0]
d.spiRxBuf = append(d.spiRxBuf, 0)
d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
d.controller.SetNss(true)
return response[0]
return d.spiRxBuf[0]
}
// SetOpMode changes the sx1276 mode
@@ -434,16 +453,13 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
// Mask all but RxDone
d.WriteRegister(SX127X_REG_IRQ_FLAGS_MASK, ^(SX127X_IRQ_LORA_RXDONE_MASK | SX127X_IRQ_LORA_RXTOUT_MASK))
// Get Radio Event Channel
radioCh := d.GetRadioEventChan()
// Single RX mode don't properly handle Timeouts on sx127x, so we use Continuous RX
// Go routine is a workaround to stop the Continuous RX and fire a timeout Event
d.SetOpMode(SX127X_OPMODE_RX)
var msg lora.RadioEvent
select {
case msg = <-radioCh:
case msg = <-d.radioEventChan:
if msg.EventType != lora.RadioEventRxDone {
return nil, errors.New("Unexpected Radio Event while RX " + string(0x30+msg.EventType))
}
@@ -459,10 +475,11 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
pLen := d.ReadRegister(SX127X_REG_RX_NB_BYTES)
d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, d.ReadRegister(SX127X_REG_FIFO_RX_CURRENT_ADDR))
rxData := []uint8{}
for i := uint8(0); i < pLen; i++ {
d.spiBuffer[i] = d.ReadRegister(SX127X_REG_FIFO)
rxData = append(rxData, d.ReadRegister(SX127X_REG_FIFO))
}
return d.spiBuffer[:pLen], nil
return rxData, nil
}
// SetTxContinuousMode enable Continuous Tx mode
@@ -506,26 +523,35 @@ func (d *Device) RandomU32() uint32 {
// HandleInterrupt must be called by main code on DIO state change.
func (d *Device) HandleInterrupt() {
// Get IRQ and clear
st := d.ReadRegister(SX127X_REG_IRQ_FLAGS)
d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
rChan := d.GetRadioEventChan()
if (st & SX127X_IRQ_LORA_RXDONE_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventRxDone, uint16(st), nil)
e := lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil}
d.radioEventChan <- e
}
if (st & SX127X_IRQ_LORA_TXDONE_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventTxDone, uint16(st), nil)
e := lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil}
d.radioEventChan <- e
}
if (st & SX127X_IRQ_LORA_RXTOUT_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventTimeout, uint16(st), nil)
e := lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil}
select {
case d.radioEventChan <- e:
default:
}
}
if (st & SX127X_IRQ_LORA_CRCERR_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventCrcError, uint16(st), nil)
e := lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil}
select {
case d.radioEventChan <- e:
default:
}
}
}