From ee2bfdb5036d0b4544841a5e4ef3931717844c18 Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Fri, 31 Jul 2026 07:55:58 +0100 Subject: [PATCH 1/2] i2s: Merge FIFOs while reading isn't supported --- rp2-pio/piolib/i2s.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rp2-pio/piolib/i2s.go b/rp2-pio/piolib/i2s.go index bf95e33..c8d63d6 100644 --- a/rp2-pio/piolib/i2s.go +++ b/rp2-pio/piolib/i2s.go @@ -65,6 +65,11 @@ func NewI2S(sm pio.StateMachine, data, clockAndNext machine.Pin) (*I2S, error) { cfg.SetSidesetPins(clockAndNext) cfg.SetOutShift(false, true, 32) + // Join the RX and TX FIFOs into a single TX FIFO of depth 8. + // This will likely need removing if reads get implemented, + // but it's just an optimization, it shouldn't break anything. + cfg.SetFIFOJoin(pio.FifoJoinTx) + sm.Init(offset, cfg) pinMask := uint32(1< Date: Fri, 31 Jul 2026 08:24:42 +0100 Subject: [PATCH 2/2] i2s: time.Sleep instead of runtime.Gosched if TX FIFO full --- rp2-pio/piolib/i2s.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/rp2-pio/piolib/i2s.go b/rp2-pio/piolib/i2s.go index c8d63d6..55714d3 100644 --- a/rp2-pio/piolib/i2s.go +++ b/rp2-pio/piolib/i2s.go @@ -5,6 +5,7 @@ package piolib import ( "errors" "machine" + "time" pio "github.com/tinygo-org/pio/rp2-pio" ) @@ -12,8 +13,9 @@ import ( // I2S is a wrapper around a PIO state machine that implements I2S. // Currently only supports writing to the I2S peripheral. type I2S struct { - sm pio.StateMachine - offset uint8 + sm pio.StateMachine + offset uint8 + fifoSleep uint32 } // NewI2S creates a new I2S peripheral using the given PIO state machine. @@ -96,6 +98,20 @@ func (i2s *I2S) SetSampleFrequency(freq uint32) error { return err } i2s.sm.SetClkDiv(whole, frac) + + // Each FIFO entry is one 32-bit word. Each audio frame is also + // one 32-bit word. Calculate how long that takes to play so we + // can sleep for long enough to clear a FIFO entry to write into + // whenever we have frames to write but the FIFO is full. + frameTime := uint32(time.Second) / freq + // N.B. time.Second < math.MaxUint32 => this never overflows + // (frameTime == 22675ns @ 44.1kHz, 20833ns @ 48kHz) + + // Sleep for at least 1 bit of audio output longer, so we never + // end our sleep exactly as the final bit is output and so end up + // sleeping a whole other frame. + i2s.fifoSleep = frameTime + frameTime/32 + return nil } @@ -132,7 +148,7 @@ func i2sWrite[T uint16 | uint32](i2s *I2S, b []T) (int, error) { i := 0 for i < len(b) { if i2s.sm.IsTxFIFOFull() { - gosched() + time.Sleep(time.Duration(i2s.fifoSleep)) continue } i2s.sm.TxPut(uint32(b[i]))