mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
sx127x/sx126x: Remove heap alloc in interrupt, add non blocking channel send/receive, and other cleanups
This commit is contained in:
committed by
Ron Evans
parent
0adc7e78aa
commit
56208a28d5
+41
-21
@@ -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
@@ -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:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user