mirror of
https://github.com/soypat/lora.git
synced 2026-08-11 06:03:38 +00:00
add lots of functionality and fix bugs
This commit is contained in:
@@ -21,8 +21,9 @@ type Config struct {
|
||||
CRC bool
|
||||
// Low data rate optimisation flag. The use of this flag is mandated when
|
||||
// the symbol duration exceeds 16ms. Increases reliability at high spreading factors.
|
||||
LDRO bool
|
||||
IQMode uint8
|
||||
LDRO bool
|
||||
// IQInversion configures I and Q signal inversion.
|
||||
IQInversion bool
|
||||
}
|
||||
|
||||
func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
||||
@@ -48,10 +49,14 @@ func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
||||
Npayload++
|
||||
Npayload *= (cr + 4)
|
||||
}
|
||||
Npayload += 8 + int64(cfg.PreambleLength) + 5 // Says 4.25 in manual but we round up.
|
||||
// Says 4.25 in manual but we round up to 5. This means we'll overestimate the
|
||||
// time calculated.
|
||||
Npayload += 8 + int64(cfg.PreambleLength) + 5
|
||||
// base units for time calculation. A higher number means more resolution.
|
||||
const baseUnitOfTime = time.Microsecond
|
||||
// Calculate LoRa Transmission Parameter Relationship page 28.
|
||||
Ts_us := 1000_000 * cfg.SpreadFactor.ChipsPerSymbol() / cfg.Bandwidth.Hertz()
|
||||
return time.Microsecond * (time.Duration(Npayload * Ts_us))
|
||||
Ts_us := int64(baseUnitOfTime) * cfg.SpreadFactor.ChipsPerSymbol() / cfg.Bandwidth.Hertz()
|
||||
return baseUnitOfTime * time.Duration(Npayload*Ts_us)
|
||||
}
|
||||
|
||||
type HeaderType uint8
|
||||
@@ -64,9 +69,13 @@ const (
|
||||
type CodingRate uint8
|
||||
|
||||
const (
|
||||
// 4/5 coding rate.
|
||||
CR4_5 CodingRate = 1
|
||||
// 4/6 coding rate.
|
||||
CR4_6 CodingRate = 2
|
||||
// 4/7 coding rate.
|
||||
CR4_7 CodingRate = 3
|
||||
// 4/8 coding rate.
|
||||
CR4_8 CodingRate = 4
|
||||
)
|
||||
|
||||
|
||||
+29
-24
@@ -77,9 +77,9 @@ const (
|
||||
// SX127X_PAYLOAD_LENGTH uint8 = 0x40
|
||||
|
||||
// Low Noise Amp
|
||||
lnaMAX_GAIN uint8 = 0x23
|
||||
lnaOFF_GAIN uint8 = 0x00
|
||||
lnaLOW_GAIN uint8 = 0x20
|
||||
lnaMAX_GAIN uint8 = 0b001
|
||||
lnaOFF_GAIN uint8 = 0b111
|
||||
lnaLOW_GAIN uint8 = 0b110
|
||||
|
||||
// Bandwidth
|
||||
bw7_8 uint8 = 0x00
|
||||
@@ -100,32 +100,37 @@ const (
|
||||
privateSyncword = 0x14
|
||||
)
|
||||
|
||||
var bandwidths = [bw500_0 + 1]lora.Frequency{
|
||||
bw7_8: 7.8e3 * lora.Hertz,
|
||||
bw10_4: 10.4e3 * lora.Hertz,
|
||||
bw15_6: 15.6e3 * lora.Hertz,
|
||||
bw20_8: 20.8e3 * lora.Hertz,
|
||||
bw31_25: 31.25e3 * lora.Hertz,
|
||||
bw41_7: 41.7e3 * lora.Hertz,
|
||||
bw62_5: 62.5e3 * lora.Hertz,
|
||||
bw125_0: 125e3 * lora.Hertz,
|
||||
bw250_0: 250e3 * lora.Hertz,
|
||||
bw500_0: 500e3 * lora.Hertz,
|
||||
}
|
||||
|
||||
func bwReg(bandwidth lora.Frequency) (value byte) {
|
||||
switch {
|
||||
case bandwidth <= 7.8e3*lora.Hertz:
|
||||
value = bw7_8
|
||||
case bandwidth <= 10.4e3*lora.Hertz:
|
||||
value = bw10_4
|
||||
case bandwidth <= 15.6e3*lora.Hertz:
|
||||
value = bw15_6
|
||||
case bandwidth <= 20.8e3*lora.Hertz:
|
||||
value = bw20_8
|
||||
case bandwidth <= 31.25e3*lora.Hertz:
|
||||
value = bw31_25
|
||||
case bandwidth <= 41.7e3*lora.Hertz:
|
||||
value = bw41_7
|
||||
case bandwidth <= 62.5e3*lora.Hertz:
|
||||
value = bw62_5
|
||||
case bandwidth <= 125*lora.KiloHertz:
|
||||
value = bw125_0
|
||||
case bandwidth <= 250e3*lora.KiloHertz:
|
||||
value = bw250_0
|
||||
default:
|
||||
value = bw500_0
|
||||
value = bw7_8
|
||||
for i := byte(len(bandwidths) - 1); i >= 0; i-- {
|
||||
if bandwidth >= bandwidths[i] {
|
||||
value = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func reg2Bw(bwByte byte) (value lora.Frequency) {
|
||||
if bwByte > byte(len(bandwidths)-1) {
|
||||
return 0
|
||||
}
|
||||
return bandwidths[bwByte]
|
||||
}
|
||||
|
||||
// Operation modes.
|
||||
const (
|
||||
opmMASK uint8 = 0x07
|
||||
|
||||
+293
-13
@@ -1,8 +1,51 @@
|
||||
/*
|
||||
package sx127x implements a driver for the SX127x LoRa tranceiver family.
|
||||
It is a low-level driver that exposes the device's registers and their
|
||||
|
||||
The SX127x family includes the SX1276, SX1277, SX1278 and SX1279. The
|
||||
differences between them are the supported frequency bands and the
|
||||
maximum output power.
|
||||
|
||||
# FIFO and packet handling
|
||||
|
||||
The SX127x has a 256-byte FIFO buffer that is accesible via the SPI interface.
|
||||
The FIFO is shared by the transmitter and the receiver but can be split via
|
||||
the FifoTxBaseAddr and FifoRxBaseAddr registers.
|
||||
|
||||
What follows is a diagram of the FIFO buffer and the data pointers:
|
||||
|
||||
+-------------------+
|
||||
| |
|
||||
| Unused |
|
||||
| |
|
||||
+-------------------+ <-- RxByteAddr (address of last byte received)
|
||||
| | ^
|
||||
| Packet N | | FifoRxBytesUp (Only for )
|
||||
| | |
|
||||
+-------------------+ <-- RegFifoRxCurrentAddr (address of start of last packet received)
|
||||
| Packet N-1 |
|
||||
+-------------------+
|
||||
| ... | <-- FifoAddrPtr (SPI read/write pointer)
|
||||
+-------------------+
|
||||
| Packet 0 |
|
||||
+-------------------+ <-- FifoRxBaseAddr
|
||||
| Unused |
|
||||
+-------------------+
|
||||
| | ^
|
||||
| Packet to | | PayloadLength
|
||||
| Transmit | |
|
||||
+-------------------+ <-- RegFifoTxCurrentAddr
|
||||
| Unused |
|
||||
+-------------------+
|
||||
|
||||
The FIFO is accessible through the SPI
|
||||
*/
|
||||
package sx127x
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lora"
|
||||
@@ -10,9 +53,12 @@ import (
|
||||
|
||||
// TODO: page 82: To minimize the current consumption of the SX1276/77/78/79, please ensure that the CLKOUT signal is disabled when not required.
|
||||
|
||||
// PinOutput is a function that sets the logic-level of a pin to high (true)
|
||||
// or low (false). It is used to abstract a GPIO pin interface.
|
||||
type PinOutput func(level bool)
|
||||
|
||||
type SPI interface {
|
||||
Transfer(w byte) (byte, error)
|
||||
Tx(writeBuffer, readBuffer []byte) error
|
||||
}
|
||||
|
||||
@@ -27,22 +73,59 @@ func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) Configure(cfg lora.Config) error {
|
||||
var (
|
||||
errBadSpread = errors.New("bad spread factor")
|
||||
errSF6Implicit = errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode.
|
||||
errPreambleTooShort = errors.New("preamble length too short")
|
||||
ErrNotDetected = errors.New("sx127x not detected")
|
||||
errBadMode = errors.New("bad mode: sx127x in FSK/OOK mode, not LoRa or viceversa")
|
||||
errBadCodingRate = errors.New("bad coding rate")
|
||||
errUnsupportedBandwidth = errors.New("bandwidth too high for frequency around 169MHz")
|
||||
)
|
||||
|
||||
func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||
switch {
|
||||
case cfg.SpreadFactor < lora.SF6 || cfg.SpreadFactor > lora.SF12:
|
||||
return errors.New("bad spread factor")
|
||||
err = errBadSpread
|
||||
case cfg.SpreadFactor == lora.SF6 && cfg.HeaderType != lora.HeaderImplicit:
|
||||
return errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode.
|
||||
err = errSF6Implicit
|
||||
case cfg.PreambleLength < 6:
|
||||
return errors.New("preamble length too short")
|
||||
err = errPreambleTooShort
|
||||
case cfg.CodingRate < lora.CR4_5 || cfg.CodingRate > lora.CR4_8:
|
||||
err = errBadCodingRate
|
||||
case cfg.Frequency < 175*lora.MegaHertz && cfg.Bandwidth > 125*lora.KiloHertz:
|
||||
err = errUnsupportedBandwidth
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Reset()
|
||||
// We need to be in sleep mode to set LoRa mode if in FSK/OOK.
|
||||
d.write8(regOP_MODE, opmSLEEP) // No need to check error, do it in SetOpmode.
|
||||
if !d.IsConnected() {
|
||||
return errors.New("sx127x not detected")
|
||||
return ErrNotDetected
|
||||
}
|
||||
err := d.SetOpMode(OpSleep)
|
||||
err = d.SetOpMode(OpSleep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.setFrequency(cfg.Frequency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.setBandwidth(cfg.Bandwidth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.enableCRC(cfg.CRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.EnableAutoAGC(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.setPreambleLength(cfg.PreambleLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,6 +133,31 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.setSyncWord(cfg.SyncWord)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.setCodingRate(cfg.CodingRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.setSpreadFactorConsistent(cfg.SpreadFactor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.enableImplicitHeaderMode(cfg.HeaderType == lora.HeaderImplicit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.enableIQInversion(cfg.IQInversion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.enableTxContinuousMode(false) // TODO: enable or disable?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.setHopPeriod(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -78,14 +186,82 @@ func (d *DeviceLoRa) GetOpMode() (OpMode, error) {
|
||||
if err != nil {
|
||||
return invalidOpMode, err
|
||||
}
|
||||
if got&byte(opLoRaBit) == 0 {
|
||||
if got&byte(opLoRaBit) == 0 || // LongRangeMode bit influences operation.
|
||||
got&(1<<6) != 0 { // AccessSharedReg bit allows access to FSK registers in LoRa mode, should not be set.
|
||||
// if the LoRa mode bit is not set, which would mean the device is in
|
||||
// FSK/OOK mode or disconnected.
|
||||
return invalidOpMode, errors.New("device in FSK/OOK mode")
|
||||
return invalidOpMode, errBadMode
|
||||
}
|
||||
return OpMode(got & opmMASK), nil
|
||||
}
|
||||
|
||||
// SetLNAGain sets the Low Noise amplifier gain with a value between 0 and 6
|
||||
// where 0 is Off and 6 is the maximum gain.
|
||||
func (d *DeviceLoRa) SetLNAGain(gain uint8) error {
|
||||
if gain > 6 {
|
||||
return errors.New("gain must be between 0 and 6")
|
||||
}
|
||||
const lnaMask = 0b111 << 5
|
||||
gain = 0b111 - gain // invert gain value to reflect the fact that 0 is max gain.
|
||||
return d.writeMasked8(regLNA, lnaMask, gain<<5)
|
||||
}
|
||||
|
||||
// setBandwidth sets the bandwidth of the LoRa modulation.
|
||||
func (d *DeviceLoRa) setBandwidth(bw lora.Frequency) error {
|
||||
const bwMask = 0b1111 << 4
|
||||
bwByte := bwReg(bw)
|
||||
return d.writeMasked8(regMODEM_CONFIG_1, bwMask, bwByte<<4)
|
||||
}
|
||||
|
||||
// enableImplicitHeaderMode enables implicit header mode (instead of explicit).
|
||||
func (d *DeviceLoRa) enableImplicitHeaderMode(enable bool) error {
|
||||
return d.writeMasked8(regMODEM_CONFIG_1, 1, b2u8(enable))
|
||||
}
|
||||
|
||||
// enableIQInversion inverts LoRa I and Q signals when set to true.
|
||||
func (d *DeviceLoRa) enableIQInversion(enable bool) error {
|
||||
const iqMask = 1 << 6
|
||||
return d.writeMasked8(regINVERTIQ, iqMask, b2u8(enable)<<6)
|
||||
}
|
||||
|
||||
// ReadConfig reads the configuration parameters from the device and returns
|
||||
// the corresponding lora.Config for the current device configuration.
|
||||
// Some lora.Config parameters are not set such as IQ, LDR, and Tx power.
|
||||
func (d *DeviceLoRa) ReadConfig() (cfg lora.Config, err error) {
|
||||
var buf [5]byte
|
||||
err = d.read(regMODEM_CONFIG_1, buf[:5])
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
cfg1 := buf[0]
|
||||
cfg.Bandwidth = reg2Bw(cfg1 >> 4)
|
||||
cfg.CodingRate = lora.CodingRate(cfg1>>1) & 0b111
|
||||
cfg.HeaderType = lora.HeaderType(cfg1 & 1)
|
||||
cfg2 := buf[1]
|
||||
cfg.SpreadFactor = lora.SpreadFactor(cfg1 >> 4)
|
||||
cfg.CRC = cfg2&0x4 != 0
|
||||
// continuousMode = cfg2&0x8 != 0
|
||||
cfg.PreambleLength = binary.BigEndian.Uint16(buf[3:])
|
||||
// Read sync word.
|
||||
sync, err := d.read8(regSYNC_WORD)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
cfg.SyncWord = sync
|
||||
// Read Frequency.
|
||||
err = d.read(regFRF_MSB, buf[:3])
|
||||
freq := uint64(buf[0])<<16 | uint64(buf[1])<<8 | uint64(buf[2])
|
||||
cfg.Frequency = lora.Frequency((freq * 15625) >> 8)
|
||||
// Read IQ inversion.
|
||||
iq, err := d.read8(regINVERTIQ)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
cfg.IQInversion = iq&(1<<6) != 0
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// setPreambleLength defines number of preamble
|
||||
func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
|
||||
var buf [2]byte
|
||||
@@ -94,9 +270,8 @@ func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
|
||||
return d.write8(regPREAMBLE_LSB, buf[1])
|
||||
}
|
||||
|
||||
// SetCrc Enable CRC generation and check on payload.
|
||||
func (d *DeviceLoRa) enableCRC(enable bool) error {
|
||||
return d.writeMasked8(regMODEM_CONFIG_2, 1<<2, b2u8(enable)<<2)
|
||||
func (d *DeviceLoRa) enableTxContinuousMode(enable bool) error {
|
||||
return d.writeMasked8(regMODEM_CONFIG_2, 1<<3, b2u8(enable)<<3)
|
||||
}
|
||||
|
||||
// setOCP defines Overload Current Protection configuration. It receives
|
||||
@@ -139,8 +314,8 @@ func (d *DeviceLoRa) setTxPower(txPow int8) error {
|
||||
return d.write8(regOCP, 0) // TODO: Disable OCP?
|
||||
}
|
||||
|
||||
// setFrequency sets the radio frequency.
|
||||
func (d *DeviceLoRa) setFrequency(freq uint32) error {
|
||||
// setFrequency sets the center radio frequency.
|
||||
func (d *DeviceLoRa) setFrequency(freq lora.Frequency) error {
|
||||
var freqReg [3]byte
|
||||
frf := freq / fSTEP // Page 82, 5.3.3 PLL.
|
||||
freqReg[0] = byte(frf >> 16)
|
||||
@@ -153,8 +328,98 @@ func (d *DeviceLoRa) setFrequency(freq uint32) error {
|
||||
return d.write8(regFRF_LSB, freqReg[2]) // Write LSB last!
|
||||
}
|
||||
|
||||
// setSpreadFactorConsistent sets the spreading factor and closely related parameters
|
||||
// including Low Data Rate Optimization, DetectionOptimize, and DetectionThreshold.
|
||||
func (d *DeviceLoRa) setSpreadFactorConsistent(sf lora.SpreadFactor) (err error) {
|
||||
err = d.setSpreadingFactor(sf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isSF6 := sf == lora.SF6
|
||||
err = d.enableLowDataRateOptimization(isSF6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set DetectionOptimize to 0x05 for SF6 and to 0x03 otherwise (SF7 to SF12).
|
||||
err = d.writeMasked8(regDETECTION_OPTIMIZE, 0b111, 1|(0b10<<b2u8(isSF6)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set DetectionThreshold to 0x0C for SF6 and to 0x0A otherwise (SF7 to SF12).
|
||||
return d.write8(regDETECTION_THRESHOLD, 0b1000|(0b10<<b2u8(isSF6)))
|
||||
}
|
||||
|
||||
// setSpreadingFactor sets the spreading factor. The value must be between 6 and 12.
|
||||
// It does not set parameters closely associated with the spreading factor such as
|
||||
// the Low Data Optimization, the Detection threshold, Detection Optimize and the
|
||||
// Symbol Timeout.
|
||||
func (d *DeviceLoRa) setSpreadingFactor(sf lora.SpreadFactor) error {
|
||||
if sf < 6 || sf > 12 {
|
||||
return errBadSpread
|
||||
}
|
||||
const sfMask = 0b111 << 4
|
||||
return d.writeMasked8(regMODEM_CONFIG_2, sfMask, uint8(sf)<<4)
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) setSyncWord(sync byte) error {
|
||||
return d.write8(regSYNC_WORD, sync)
|
||||
}
|
||||
|
||||
// EnableAutoAGC enables/disables Automatic Gain Control. This means the value set
|
||||
// by SetLNAGain will be ignored. Set to false to use the value set by SetLNAGain.
|
||||
func (d *DeviceLoRa) EnableAutoAGC(b bool) error {
|
||||
const agcMask = 1 << 2
|
||||
return d.writeMasked8(regMODEM_CONFIG_3, agcMask, b2u8(b)<<2)
|
||||
}
|
||||
|
||||
// enableLowDataRateOptimization enables/disables Low Data Rate Optimization, a
|
||||
// feature which is mandated when symbol length exceeds 16ms.
|
||||
func (d *DeviceLoRa) enableLowDataRateOptimization(b bool) error {
|
||||
const ldoMask = 1 << 3
|
||||
return d.writeMasked8(regMODEM_CONFIG_3, ldoMask, b2u8(b)<<3)
|
||||
}
|
||||
|
||||
// enableLowFrequencyMode enables/disables access to LowFrequencyMode registers.
|
||||
func (d *DeviceLoRa) enableLowFrequencyMode(b bool) error {
|
||||
const lowFreqMask = 1 << 3
|
||||
return d.writeMasked8(regOP_MODE, lowFreqMask, b2u8(b)<<3)
|
||||
}
|
||||
|
||||
// enableCRC enables/disables CRC generation and checking.
|
||||
func (d *DeviceLoRa) enableCRC(b bool) error {
|
||||
const crcMask = 1 << 2
|
||||
return d.writeMasked8(regMODEM_CONFIG_2, crcMask, b2u8(b)<<2)
|
||||
}
|
||||
|
||||
// setTimeoutInSymbols sets the timeout in symbols. The value must be between 0 and 1023.
|
||||
// The timeout is used to stop reception automatically. The equation is:
|
||||
//
|
||||
// Timeout = timeoutSymbols * Ts (where Ts is the symbol period)
|
||||
func (d *DeviceLoRa) setTimeoutInSymbols(symbTimeout uint16) (err error) {
|
||||
if symbTimeout > 0x3FF {
|
||||
return errors.New("timeout value too large")
|
||||
}
|
||||
err = d.writeMasked8(regMODEM_CONFIG_2, 0b11, byte(symbTimeout>>8))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.write8(regSYMB_TIMEOUT_LSB, byte(symbTimeout))
|
||||
}
|
||||
|
||||
// setCodingRate sets the error coding rate. The value must be between 4/5 and 4/8.
|
||||
func (d *DeviceLoRa) setCodingRate(cr lora.CodingRate) error {
|
||||
if cr < lora.CR4_5 || cr > lora.CR4_8 {
|
||||
return errBadCodingRate
|
||||
}
|
||||
return d.writeMasked8(regMODEM_CONFIG_1, 0b111<<1, uint8(cr)<<1)
|
||||
}
|
||||
|
||||
// setHopPeriod sets number of symbol periods between frequency hops. (0 = disabled).
|
||||
func (d *DeviceLoRa) setHopPeriod(val uint8) error { return d.write8(regHOP_PERIOD, val) }
|
||||
|
||||
func (d *DeviceLoRa) writeMasked8(addr uint8, mask, value byte) error {
|
||||
if value != 0 && value&^mask != 0 {
|
||||
println("value", value, "mask", mask)
|
||||
panic("misuse of writeMasked8") // Bug in this package if hit.
|
||||
}
|
||||
existing, err := d.read8(addr)
|
||||
@@ -211,3 +476,18 @@ func b2u8(b bool) uint8 {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) read(addr uint8, buf []byte) error {
|
||||
if len(buf) <= 1 {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
d.csEnable(true)
|
||||
_, err := d.bus.Transfer(addr)
|
||||
if err != nil {
|
||||
d.csEnable(false)
|
||||
return err
|
||||
}
|
||||
err = d.bus.Tx(nil, buf)
|
||||
d.csEnable(false)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user