Skip to content

Commit 62663c1

Browse files
jwetzellCopilot
andauthored
add SPI driver for semtech sx128x chips (#864)
* add SPI driver for semtech sx128x chips Co-authored-by: Copilot <copilot@github.com> * handle busy loop better * switch to time based busy timeout Co-authored-by: Copilot <copilot@github.com> * comment functions * start on using types for function inputs * use types where applicable and align with datasheet more Co-authored-by: Copilot <copilot@github.com> * work on exporting less constants * only export "actionable" errors * combine identical constants * add crude lora rx and tx examples * change from type aliases to local types --------- Co-authored-by: Copilot <copilot@github.com>
1 parent 8f37293 commit 62663c1

8 files changed

Lines changed: 1473 additions & 0 deletions

File tree

examples/sx128x/lora_rx/main.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"machine"
6+
"runtime"
7+
"time"
8+
9+
"tinygo.org/x/drivers/sx128x"
10+
)
11+
12+
var (
13+
// pin mapping specific to the lilygo t3s3, change as needed for your board
14+
sdoPin = machine.GPIO6
15+
sdiPin = machine.GPIO3
16+
sckPin = machine.GPIO5
17+
nssPin = machine.GPIO7
18+
busyPin = machine.GPIO36
19+
resetPin = machine.GPIO8
20+
dio1Pin = machine.GPIO9
21+
)
22+
23+
func setupPins() {
24+
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
25+
nssPin.Set(true)
26+
27+
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
28+
resetPin.Set(true)
29+
30+
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
31+
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
32+
}
33+
34+
func main() {
35+
setupPins()
36+
37+
spi := machine.SPI0
38+
spi.Configure(machine.SPIConfig{
39+
Mode: 0,
40+
Frequency: 8 * 1e6,
41+
SDO: sdoPin,
42+
SDI: sdiPin,
43+
SCK: sckPin,
44+
})
45+
46+
radio := sx128x.New(
47+
spi,
48+
nssPin,
49+
resetPin,
50+
busyPin,
51+
)
52+
53+
radio.WaitWhileBusy(time.Second)
54+
SetupLora(radio)
55+
56+
for {
57+
data, err := Rx(radio)
58+
if err != nil {
59+
println("failed to receive:", err)
60+
} else {
61+
println("received:", string(data))
62+
}
63+
}
64+
}
65+
66+
func SetupLora(radio *sx128x.Device) {
67+
radio.SetStandby(sx128x.STANDBY_RC)
68+
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
69+
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)
70+
71+
radio.SetRfFrequency(2400000000) // 2.4Ghz
72+
radio.SetModulationParamsLoRa(sx128x.LORA_SF_9, sx128x.LORA_BW_1600, sx128x.LORA_CR_4_7)
73+
74+
// section 14.4.1 shows required register setting for setting up LoRa operations. These depend on the chosen spreading factor.
75+
radio.WriteRegister(0x925, []byte{0x32})
76+
radio.WriteRegister(0x93C, []byte{0x01})
77+
78+
radio.SetTxParams(13, sx128x.RADIO_RAMP_02_US)
79+
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, 0xFF, sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
80+
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, []byte{0x14, 0x24}) // full sync word is 0x1424
81+
82+
}
83+
84+
func Rx(radio *sx128x.Device) ([]byte, error) {
85+
radio.SetStandby(sx128x.STANDBY_RC)
86+
radio.SetDioIrqParams(sx128x.IRQ_RX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, sx128x.IRQ_RX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, 0x00, 0x00)
87+
radio.SetBufferBaseAddress(0, 0)
88+
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
89+
radio.SetRx(sx128x.PERIOD_BASE_4_MS, 250) // 4ms * 250 = 1s
90+
// busy wait for IRQ indication
91+
for dio1Pin.Get() == false {
92+
runtime.Gosched()
93+
}
94+
irqStatus, _ := radio.GetIrqStatus()
95+
if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 {
96+
payloadLength, bufferOffset, err := radio.GetRxBufferStatus()
97+
if err != nil {
98+
return nil, err
99+
}
100+
data, err := radio.ReadBuffer(bufferOffset, payloadLength)
101+
return data, nil
102+
} else if irqStatus&sx128x.IRQ_RX_TX_TIMEOUT_MASK != 0 {
103+
return nil, errors.New("rx timeout")
104+
}
105+
return nil, errors.New("unexpected IRQ status")
106+
}

examples/sx128x/lora_tx/main.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"machine"
6+
"runtime"
7+
"time"
8+
9+
"tinygo.org/x/drivers/sx128x"
10+
)
11+
12+
var (
13+
// pin mapping specific to the lilygo t3s3, change as needed for your board
14+
sdoPin = machine.GPIO6
15+
sdiPin = machine.GPIO3
16+
sckPin = machine.GPIO5
17+
nssPin = machine.GPIO7
18+
busyPin = machine.GPIO36
19+
resetPin = machine.GPIO8
20+
dio1Pin = machine.GPIO9
21+
)
22+
23+
func setupPins() {
24+
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
25+
nssPin.Set(true)
26+
27+
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
28+
resetPin.Set(true)
29+
30+
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
31+
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
32+
}
33+
34+
func main() {
35+
setupPins()
36+
37+
spi := machine.SPI0
38+
spi.Configure(machine.SPIConfig{
39+
Mode: 0,
40+
Frequency: 8 * 1e6,
41+
SDO: sdoPin,
42+
SDI: sdiPin,
43+
SCK: sckPin,
44+
})
45+
46+
radio := sx128x.New(
47+
spi,
48+
nssPin,
49+
resetPin,
50+
busyPin,
51+
)
52+
53+
radio.WaitWhileBusy(time.Second)
54+
SetupLora(radio)
55+
56+
for {
57+
Tx(radio, []byte("Hello, world!"))
58+
time.Sleep(1 * time.Second)
59+
}
60+
}
61+
62+
func SetupLora(radio *sx128x.Device) {
63+
radio.SetStandby(sx128x.STANDBY_RC)
64+
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
65+
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)
66+
67+
radio.SetRfFrequency(2400000000) // 2.4Ghz
68+
radio.SetModulationParamsLoRa(sx128x.LORA_SF_9, sx128x.LORA_BW_1600, sx128x.LORA_CR_4_7)
69+
70+
// section 14.4.1 shows required register setting for setting up LoRa operations. These depend on the chosen spreading factor.
71+
radio.WriteRegister(0x925, []byte{0x32})
72+
radio.WriteRegister(0x93C, []byte{0x01})
73+
74+
radio.SetTxParams(13, sx128x.RADIO_RAMP_02_US)
75+
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, 0xFF, sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
76+
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, []byte{0x14, 0x24}) // full sync word is 0x1424
77+
78+
}
79+
80+
func Tx(radio *sx128x.Device, data []byte) error {
81+
if len(data) > 255 {
82+
return errors.New("data length exceeds maximum of 255 bytes")
83+
}
84+
radio.SetStandby(sx128x.STANDBY_RC)
85+
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, uint8(len(data)&0xFF), sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
86+
radio.SetBufferBaseAddress(0, 0)
87+
radio.WriteBuffer(0, data)
88+
radio.SetDioIrqParams(sx128x.IRQ_TX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, sx128x.IRQ_TX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, 0x00, 0x00)
89+
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
90+
radio.SetTx(sx128x.PERIOD_BASE_4_MS, 250) // 4ms * 250 = 1s
91+
// busy wait for IRQ indication
92+
for dio1Pin.Get() == false {
93+
runtime.Gosched()
94+
}
95+
return nil
96+
}

sx128x/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SX128x Radio
2+
Radio from Semtech in the 2.4 GHz band. This driver uses SPI to communicate with the radio instead of the alternative UART interface.
3+
4+
## Supported Chips
5+
- [SX1280](https://www.semtech.com/products/wireless-rf/lora-connect/sx1280)
6+
- [SX1281](https://www.semtech.com/products/wireless-rf/lora-connect/sx1281)
7+

sx128x/commands.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package sx128x
2+
3+
const (
4+
// SX128X SPI commands
5+
cmdGetStatus = uint8(0xC0)
6+
7+
// Register Access Operations
8+
cmdWriteRegister = uint8(0x18)
9+
cmdReadRegister = uint8(0x19)
10+
11+
// Data Buffer Operations
12+
cmdWriteBuffer = uint8(0x1A)
13+
cmdReadBuffer = uint8(0x1B)
14+
15+
// Radio Operation Modes
16+
cmdSetSleep = uint8(0x84)
17+
cmdSetStandby = uint8(0x80)
18+
cmdSetFS = uint8(0xC1)
19+
cmdSetTx = uint8(0x83)
20+
cmdSetRx = uint8(0x82)
21+
cmdSetRxDutyCycle = uint8(0x94)
22+
cmdSetLongPreamble = uint8(0x9B)
23+
cmdSetCAD = uint8(0xC5)
24+
cmdSetTxContinuousWave = uint8(0xD1)
25+
cmdSetContinuousPreamble = uint8(0xD2)
26+
cmdSetAutoTx = uint8(0x98)
27+
cmdSetAutoFS = uint8(0x9E)
28+
29+
// Radio Configuration
30+
cmdSetPacketType = uint8(0x8A)
31+
cmdGetPacketType = uint8(0x03)
32+
cmdSetRFFrequency = uint8(0x86)
33+
cmdSetTxParams = uint8(0x8E)
34+
cmdSetCADParams = uint8(0x88)
35+
cmdSetBufferBaseAddress = uint8(0x8F)
36+
cmdSetModulationParams = uint8(0x8B)
37+
cmdSetPacketParams = uint8(0x8C)
38+
39+
// Communication Status Information
40+
cmdGetRxBufferStatus = uint8(0x17)
41+
cmdGetPacketStatus = uint8(0x1D)
42+
cmdGetRSSIInst = uint8(0x1F)
43+
44+
// IRQ Handling
45+
cmdSetDIOIRQParams = uint8(0x8D)
46+
cmdGetIRQStatus = uint8(0x15)
47+
cmdClearIRQStatus = uint8(0x97)
48+
49+
// Miscellaneous
50+
cmdSetRegulatorMode = uint8(0x96)
51+
cmdSetSaveContext = uint8(0xD5)
52+
)

0 commit comments

Comments
 (0)