Skip to content

Commit 48f084b

Browse files
committed
SPI3w seems to work fine
1 parent d4fd245 commit 48f084b

File tree

6 files changed

+206
-107
lines changed

6 files changed

+206
-107
lines changed

rp2-pio/examples/ws2812/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
func main() {
1313
const ws2812Pin = machine.GP16
1414
sm, _ := pio.PIO0.ClaimStateMachine()
15-
ws, err := piolib.NewWS2812(sm, ws2812Pin)
15+
ws, err := piolib.NewWS2812(sm, ws2812Pin, 400_000)
1616
if err != nil {
1717
panic(err.Error())
1818
}

rp2-pio/piolib/all_generate.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import (
1010
const timeoutRetries = math.MaxUint16 * 8
1111

1212
var (
13-
errTimeout = errors.New("piolib:timeout")
14-
errBusy = errors.New("piolib:busy")
13+
errTimeout = errors.New("piolib:timeout")
14+
errContentionTimeout = errors.New("piolib:contention timeout")
15+
errBusy = errors.New("piolib:busy")
1516

1617
errDMAUnavail = errors.New("piolib:DMA channel unavailable")
1718
)
@@ -26,11 +27,6 @@ func gosched() {
2627
runtime.Gosched()
2728
}
2829

29-
type deadliner struct {
30-
// timeout is a bitshift value for the timeout.
31-
timeout uint8
32-
}
33-
3430
type deadline struct {
3531
t time.Time
3632
}
@@ -42,16 +38,23 @@ func (dl deadline) expired() bool {
4238
return time.Since(dl.t) > 0
4339
}
4440

41+
type deadliner struct {
42+
// timeout is a bitshift value for the timeout.
43+
timeout uint8
44+
}
45+
4546
func (ch deadliner) newDeadline() deadline {
4647
var t time.Time
4748
if ch.timeout != 0 {
48-
t = time.Now().Add(time.Duration(1 << ch.timeout))
49+
calc := time.Duration(1 << ch.timeout)
50+
t = time.Now().Add(calc)
4951
}
5052
return deadline{t: t}
5153
}
5254

5355
func (ch *deadliner) setTimeout(timeout time.Duration) {
5456
if timeout <= 0 {
57+
ch.timeout = 0
5558
return // No timeout.
5659
}
5760
for i := uint8(0); i < 64; i++ {

rp2-pio/piolib/dma.go

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ func (ch dmaChannel) Init(cfg dmaChannelConfig) {
8686
ch.HW().CTRL_TRIG.Set(cfg.CTRL)
8787
}
8888

89+
// CurrentConfig copies the actual configuration of the DMA channel.
90+
func (ch dmaChannel) CurrentConfig() dmaChannelConfig {
91+
ch.mustValid()
92+
return dmaChannelConfig{CTRL: ch.HW().CTRL_TRIG.Get()}
93+
}
94+
8995
func (ch dmaChannel) mustValid() {
9096
if !ch.IsValid() {
9197
panic("use of uninitialized DMA channel")
@@ -165,64 +171,135 @@ const (
165171

166172
// Push32 writes each element of src slice into the memory location at dst.
167173
func (ch dmaChannel) Push32(dst *uint32, src []uint32, dreq uint32) error {
174+
return dmaPush(ch, dst, src, dreq)
175+
}
176+
177+
// Push16 writes each element of src slice into the memory location at dst.
178+
func (ch dmaChannel) Push16(dst *uint16, src []uint16, dreq uint32) error {
179+
return dmaPush(ch, dst, src, dreq)
180+
}
181+
182+
// Push8 writes each element of src slice into the memory location at dst.
183+
func (ch dmaChannel) Push8(dst *byte, src []byte, dreq uint32) error {
184+
return dmaPush(ch, dst, src, dreq)
185+
}
186+
187+
// Push32 writes each element of src slice into the memory location at dst.
188+
func dmaPush[T uint8 | uint16 | uint32](ch dmaChannel, dst *T, src []T, dreq uint32) error {
168189
hw := ch.HW()
190+
hw.CTRL_TRIG.ClearBits(rp.DMA_CH0_CTRL_TRIG_EN_Msk)
169191
srcPtr := uint32(uintptr(unsafe.Pointer(&src[0])))
170192
dstPtr := uint32(uintptr(unsafe.Pointer(dst)))
171193
hw.READ_ADDR.Set(srcPtr)
172194
hw.WRITE_ADDR.Set(dstPtr)
173195
hw.TRANS_COUNT.Set(uint32(len(src)))
196+
174197
// memfence
175-
var cc dmaChannelConfig
176-
cc.CTRL = hw.CTRL_TRIG.Get()
198+
199+
cc := ch.CurrentConfig()
177200
cc.setTREQ_SEL(dreq)
178-
cc.setTransferDataSize(dmaTxSize32)
201+
cc.setTransferDataSize(dmaSize[T]())
179202
cc.setChainTo(ch.idx)
180203
cc.setReadIncrement(true)
181204
cc.setWriteIncrement(false)
182205
cc.setEnable(true)
183206

207+
deadline := ch.dl.newDeadline()
208+
// If currently busy we wait.
209+
for ch.busy() {
210+
if deadline.expired() {
211+
return errContentionTimeout
212+
}
213+
gosched()
214+
}
215+
216+
// We begin our DMA transfer here!
184217
hw.CTRL_TRIG.Set(cc.CTRL)
185218

186-
deadline := ch.dl.newDeadline()
219+
deadline = ch.dl.newDeadline()
187220
for ch.busy() {
188221
if deadline.expired() {
222+
ch.abort()
189223
return errTimeout
190224
}
191225
gosched()
192226
}
227+
hw.CTRL_TRIG.ClearBits(rp.DMA_CH0_CTRL_TRIG_EN_Msk)
193228
return nil
194229
}
195230

196231
// Pull32 reads the memory location at src into dst slice, incrementing dst pointer but not src.
197232
func (ch dmaChannel) Pull32(dst []uint32, src *uint32, dreq uint32) error {
233+
return dmaPull(ch, dst, src, dreq)
234+
}
235+
236+
// Pull16 reads the memory location at src into dst slice, incrementing dst pointer but not src.
237+
func (ch dmaChannel) Pull16(dst []uint16, src *uint16, dreq uint32) error {
238+
return dmaPull(ch, dst, src, dreq)
239+
}
240+
241+
// Pull8 reads the memory location at src into dst slice, incrementing dst pointer but not src.
242+
func (ch dmaChannel) Pull8(dst []byte, src *byte, dreq uint32) error {
243+
return dmaPull(ch, dst, src, dreq)
244+
}
245+
246+
// Pull32 reads the memory location at src into dst slice, incrementing dst pointer but not src.
247+
func dmaPull[T uint8 | uint16 | uint32](ch dmaChannel, dst []T, src *T, dreq uint32) error {
198248
hw := ch.HW()
249+
hw.CTRL_TRIG.ClearBits(rp.DMA_CH0_CTRL_TRIG_EN_Msk)
199250
srcPtr := uint32(uintptr(unsafe.Pointer(src)))
200251
dstPtr := uint32(uintptr(unsafe.Pointer(&dst[0])))
201252
hw.READ_ADDR.Set(srcPtr)
202253
hw.WRITE_ADDR.Set(dstPtr)
203254
hw.TRANS_COUNT.Set(uint32(len(dst)))
255+
204256
// memfence
205-
var cc dmaChannelConfig
206-
cc.CTRL = hw.CTRL_TRIG.Get()
257+
258+
cc := ch.CurrentConfig()
207259
cc.setTREQ_SEL(dreq)
208-
cc.setTransferDataSize(dmaTxSize32)
260+
cc.setTransferDataSize(dmaSize[T]())
209261
cc.setChainTo(ch.idx)
210262
cc.setReadIncrement(false)
211263
cc.setWriteIncrement(true)
212264
cc.setEnable(true)
213265

266+
// If currently busy we wait.
267+
deadline := ch.dl.newDeadline()
268+
for ch.busy() {
269+
if deadline.expired() {
270+
return errContentionTimeout
271+
}
272+
gosched()
273+
}
274+
275+
// We begin our DMA transfer here!
214276
hw.CTRL_TRIG.Set(cc.CTRL)
215277

216-
deadline := ch.dl.newDeadline()
278+
deadline = ch.dl.newDeadline()
217279
for ch.busy() {
218280
if deadline.expired() {
281+
ch.abort()
219282
return errTimeout
220283
}
221284
gosched()
222285
}
223286
return nil
224287
}
225288

289+
func dmaSize[T uint8 | uint16 | uint32]() dmaTxSize {
290+
var a T
291+
switch unsafe.Sizeof(a) {
292+
case 1:
293+
return dmaTxSize8
294+
case 2:
295+
return dmaTxSize16
296+
case 4:
297+
return dmaTxSize32
298+
default:
299+
panic("invalid DMA transfer size")
300+
}
301+
}
302+
226303
// abort aborts the current transfer sequence on the channel and blocks until
227304
// all in-flight transfers have been flushed through the address and data FIFOs.
228305
// After this, it is safe to restart the channel.
@@ -234,13 +311,14 @@ func (ch dmaChannel) abort() {
234311
// Until this point, it is unsafe to restart the channel.
235312
chMask := uint32(1 << ch.idx)
236313
rp.DMA.CHAN_ABORT.Set(chMask)
237-
retries := timeoutRetries
238-
for rp.DMA.CHAN_ABORT.Get()&chMask != 0 && retries > 0 {
314+
315+
deadline := ch.dl.newDeadline()
316+
for rp.DMA.CHAN_ABORT.Get()&chMask != 0 {
317+
if deadline.expired() {
318+
println("DMA abort timeout")
319+
break
320+
}
239321
gosched()
240-
retries--
241-
}
242-
if retries == 0 {
243-
println("DMA abort timeout")
244322
}
245323
}
246324

rp2-pio/piolib/parallel8.go

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ package piolib
55
import (
66
"errors"
77
"machine"
8+
"unsafe"
89

910
pio "github.com/tinygo-org/pio/rp2-pio"
1011
)
1112

1213
// Parallel8Tx is a 8-wire, only send Parallel implementation.
1314
type Parallel8Tx struct {
14-
sm pio.StateMachine
15-
offset uint8
16-
dmaChannel uint32
15+
sm pio.StateMachine
16+
offset uint8
17+
dma dmaChannel
1718
}
1819

1920
// unused for now.
@@ -57,13 +58,12 @@ func NewParallel8Tx(sm pio.StateMachine, wr, dStart machine.Pin, baud uint32) (*
5758
sm.Init(offset, cfg)
5859
sm.SetEnabled(true)
5960

60-
return &Parallel8Tx{sm: sm, dmaChannel: noDMA, offset: offset}, nil
61+
return &Parallel8Tx{sm: sm, offset: offset}, nil
6162
}
6263

6364
func (pl *Parallel8Tx) Write(data []uint8) error {
64-
if pl.dmaChannel != noDMA {
65-
// pl.dmaWrite(data)
66-
return nil
65+
if pl.IsDMAEnabled() {
66+
return pl.dmaWrite(data)
6767
}
6868
retries := int8(127)
6969
for _, char := range data {
@@ -79,54 +79,48 @@ func (pl *Parallel8Tx) Write(data []uint8) error {
7979
return nil
8080
}
8181

82-
/*
83-
func (pl *Parallel8Tx) EnableDMA(dmaChan uint32) error {
82+
func (pl *Parallel8Tx) IsDMAEnabled() bool {
83+
return pl.dma.IsValid()
84+
}
85+
86+
func (pl *Parallel8Tx) EnableDMA(enabled bool) error {
8487
if !pl.sm.IsValid() {
8588
return errors.New("PIO Statemachine needs initializing") //Not initialized
8689
}
87-
pl.dmaChannel = dmaChan // DMA enabled
88-
if dmaChan == noDMA {
90+
dmaAlreadyEnabled := pl.IsDMAEnabled()
91+
if !enabled || dmaAlreadyEnabled {
92+
if !enabled && dmaAlreadyEnabled {
93+
pl.dma.Unclaim()
94+
pl.dma = dmaChannel{} // Invalidate DMA channel.
95+
}
8996
return nil
9097
}
91-
Pio := pl.sm.PIO()
92-
dmaConfig := getDefaultDMAConfig(dmaChan)
93-
setTransferDataSize(dmaConfig, DMA_SIZE_8)
94-
setBSwap(dmaConfig, false)
95-
setDREQ(dmaConfig, uint32(Pio.GetIRQ()))
96-
dmaChannelConfigure(dmaChan, dmaConfig, pl.sm.TxReg(), nil, 0, false)
97-
return nil
98-
}
9998

100-
func (pl *Parallel8Tx) dmaWrite(data []byte) {
101-
dmaChan := &dmaChannels[pl.dmaChannel]
102-
for dmaChan.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0 {
103-
runtime.Gosched()
99+
channel, ok := _DMA.ClaimChannel()
100+
if !ok {
101+
return errDMAUnavail
104102
}
105103

106-
readAddr := uint32(uintptr(unsafe.Pointer(&data[0])))
107-
dmaChan.TRANS_COUNT.Set(uint32(len(data)))
108-
dmaChan.READ_ADDR.Set(uint32(readAddr))
109-
dmaChan.CTRL_TRIG.Set(dmaChan.CTRL_TRIG.Get() | rp.DMA_CH0_CTRL_TRIG_EN)
104+
channel.dl = pl.dma.dl // Copy deadline.
105+
pl.dma = channel
106+
cc := pl.dma.CurrentConfig()
107+
cc.setBSwap(false)
108+
cc.setTransferDataSize(dmaTxSize8)
109+
pl.dma.Init(cc)
110+
return nil
111+
}
110112

111-
for dmaChan.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0 {
112-
runtime.Gosched()
113-
}
114-
for !pl.sm.IsTxFIFOEmpty() {
115-
runtime.Gosched()
113+
func (pl *Parallel8Tx) dmaWrite(data []byte) error {
114+
dreq := dmaPIO_TxDREQ(pl.sm)
115+
err := pl.dma.Push8((*byte)(unsafe.Pointer(&pl.sm.TxReg().Reg)), data, dreq)
116+
if err != nil {
117+
return err
116118
}
117-
}
118119

119-
func dmaChannelConfigure(channel, config uint32, writeAddr, readAddr *volatile.Register32, transferCount uint32, trigger bool) {
120-
regAddr := func(reg *volatile.Register32) uint32 {
121-
if reg == nil {
122-
return 0
123-
}
124-
return uint32(uintptr(unsafe.Pointer(reg)))
120+
// DMA is done after this point but we still have to wait for
121+
// the FIFO to be empty
122+
for !pl.sm.IsTxFIFOEmpty() {
123+
gosched()
125124
}
126-
dmaChan := dmaChannels[channel]
127-
dmaChan.READ_ADDR.Set(regAddr(readAddr))
128-
dmaChan.WRITE_ADDR.Set(regAddr(writeAddr))
129-
dmaChan.TRANS_COUNT.Set(transferCount)
130-
dmaChan.CTRL_TRIG.Set(config | rp.DMA_CH0_CTRL_TRIG_EN)
125+
return nil
131126
}
132-
*/

0 commit comments

Comments
 (0)