diff options
author | deadprogram <[email protected]> | 2024-12-08 10:37:30 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2024-12-18 15:23:00 +0100 |
commit | 5701bf81f30d06daa4bc093f716f496c16147e10 (patch) | |
tree | adeee6085ded92d0226cac3601fcc1d546ee495f | |
parent | 6110f0bc1b5cb807fa2ac01b12b5aeffcf3308c6 (diff) | |
download | tinygo-5701bf81f30d06daa4bc093f716f496c16147e10.tar.gz tinygo-5701bf81f30d06daa4bc093f716f496c16147e10.zip |
feature: modify i2s interface/implementation to better match specification
Signed-off-by: deadprogram <[email protected]>
-rw-r--r-- | src/examples/i2s/i2s.go | 4 | ||||
-rw-r--r-- | src/machine/board_arduino_mkr1000.go | 5 | ||||
-rw-r--r-- | src/machine/board_arduino_mkrwifi1010.go | 3 | ||||
-rw-r--r-- | src/machine/board_arduino_nano33.go | 3 | ||||
-rw-r--r-- | src/machine/board_arduino_zero.go | 3 | ||||
-rw-r--r-- | src/machine/board_circuitplay_express.go | 3 | ||||
-rw-r--r-- | src/machine/board_feather-m0-express.go | 3 | ||||
-rw-r--r-- | src/machine/board_feather-m0.go | 3 | ||||
-rw-r--r-- | src/machine/board_gemma-m0.go | 3 | ||||
-rw-r--r-- | src/machine/board_grandcentral-m4.go | 3 | ||||
-rw-r--r-- | src/machine/board_itsybitsy-m0.go | 3 | ||||
-rw-r--r-- | src/machine/board_p1am-100.go | 3 | ||||
-rw-r--r-- | src/machine/board_qtpy.go | 3 | ||||
-rw-r--r-- | src/machine/board_trinket.go | 3 | ||||
-rw-r--r-- | src/machine/board_wioterminal.go | 8 | ||||
-rw-r--r-- | src/machine/board_xiao.go | 3 | ||||
-rw-r--r-- | src/machine/i2s.go | 34 | ||||
-rw-r--r-- | src/machine/machine_atsamd21.go | 153 |
18 files changed, 173 insertions, 70 deletions
diff --git a/src/examples/i2s/i2s.go b/src/examples/i2s/i2s.go index 4936d92ff..06857c488 100644 --- a/src/examples/i2s/i2s.go +++ b/src/examples/i2s/i2s.go @@ -13,11 +13,11 @@ func main() { Stereo: true, }) - data := make([]uint32, 64) + data := make([]uint16, 64) for { // get the next group of samples - machine.I2S0.Read(data) + machine.I2S0.ReadMono(data) println("data", data[0], data[1], data[2], data[4], "...") } diff --git a/src/machine/board_arduino_mkr1000.go b/src/machine/board_arduino_mkr1000.go index 2c9ae603f..f5130120e 100644 --- a/src/machine/board_arduino_mkr1000.go +++ b/src/machine/board_arduino_mkr1000.go @@ -74,8 +74,9 @@ const ( // I2S pins const ( I2S_SCK_PIN Pin = PA10 - I2S_SD_PIN Pin = PA07 - I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33. + I2S_SDO_PIN Pin = PA07 + I2S_SDI_PIN = NoPin + I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino MKR1000 ) // USB CDC identifiers diff --git a/src/machine/board_arduino_mkrwifi1010.go b/src/machine/board_arduino_mkrwifi1010.go index 18330f37f..c68da9b62 100644 --- a/src/machine/board_arduino_mkrwifi1010.go +++ b/src/machine/board_arduino_mkrwifi1010.go @@ -74,7 +74,8 @@ const ( // I2S pins const ( I2S_SCK_PIN Pin = PA10 - I2S_SD_PIN Pin = PA07 + I2S_SDO_PIN Pin = PA07 + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino MKR WiFi 1010. ) diff --git a/src/machine/board_arduino_nano33.go b/src/machine/board_arduino_nano33.go index 17f255443..9232d3819 100644 --- a/src/machine/board_arduino_nano33.go +++ b/src/machine/board_arduino_nano33.go @@ -118,7 +118,8 @@ const ( // I2S pins const ( I2S_SCK_PIN Pin = PA10 - I2S_SD_PIN Pin = PA08 + I2S_SDO_PIN Pin = PA08 + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33. ) diff --git a/src/machine/board_arduino_zero.go b/src/machine/board_arduino_zero.go index f09fb47c5..758fcb16e 100644 --- a/src/machine/board_arduino_zero.go +++ b/src/machine/board_arduino_zero.go @@ -69,7 +69,8 @@ const ( // I2S pins - might not be exposed const ( I2S_SCK_PIN Pin = PA10 - I2S_SD_PIN Pin = PA07 + I2S_SDO_PIN Pin = PA07 + I2S_SDI_PIN = NoPin I2S_WS_PIN Pin = PA11 ) diff --git a/src/machine/board_circuitplay_express.go b/src/machine/board_circuitplay_express.go index 1601fcab3..ce1f29c75 100644 --- a/src/machine/board_circuitplay_express.go +++ b/src/machine/board_circuitplay_express.go @@ -102,7 +102,8 @@ var SPI0 = sercomSPIM3 // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA08 + I2S_SDO_PIN = PA08 + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync ) diff --git a/src/machine/board_feather-m0-express.go b/src/machine/board_feather-m0-express.go index a0f7c2305..226369ffc 100644 --- a/src/machine/board_feather-m0-express.go +++ b/src/machine/board_feather-m0-express.go @@ -81,7 +81,8 @@ var SPI0 = sercomSPIM4 // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA07 + I2S_SDO_PIN = PA07 + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin // TODO: figure out what this is on Feather M0 Express. ) diff --git a/src/machine/board_feather-m0.go b/src/machine/board_feather-m0.go index 5cd339340..f38d8ec88 100644 --- a/src/machine/board_feather-m0.go +++ b/src/machine/board_feather-m0.go @@ -76,7 +76,8 @@ var SPI0 = sercomSPIM4 // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA08 + I2S_SDO_PIN = PA08 + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin // TODO: figure out what this is on Feather M0. ) diff --git a/src/machine/board_gemma-m0.go b/src/machine/board_gemma-m0.go index 3702c74c3..af1caaad6 100644 --- a/src/machine/board_gemma-m0.go +++ b/src/machine/board_gemma-m0.go @@ -76,7 +76,8 @@ var ( // I2S (not connected, needed for atsamd21). const ( I2S_SCK_PIN = NoPin - I2S_SD_PIN = NoPin + I2S_SDO_PIN = NoPin + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin ) diff --git a/src/machine/board_grandcentral-m4.go b/src/machine/board_grandcentral-m4.go index 46fb95697..61ef6a89b 100644 --- a/src/machine/board_grandcentral-m4.go +++ b/src/machine/board_grandcentral-m4.go @@ -224,7 +224,8 @@ const ( I2S_SCK_PIN = I2S0_SCK_PIN // default pins I2S_WS_PIN = I2S0_FS_PIN // - I2S_SD_PIN = I2S0_SDO_PIN // + I2S_SDO_PIN = I2S0_SDO_PIN + I2S_SDI_PIN = NoPin ) // SD card pins diff --git a/src/machine/board_itsybitsy-m0.go b/src/machine/board_itsybitsy-m0.go index 67ebdee90..0cc6cad31 100644 --- a/src/machine/board_itsybitsy-m0.go +++ b/src/machine/board_itsybitsy-m0.go @@ -89,7 +89,8 @@ var SPI1 = sercomSPIM5 // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA08 + I2S_SDO_PIN = PA08 + I2S_SDI_PIN = NoPin I2S_WS_PIN = NoPin // TODO: figure out what this is on ItsyBitsy M0. ) diff --git a/src/machine/board_p1am-100.go b/src/machine/board_p1am-100.go index d6fbdcb36..f2a7d13f9 100644 --- a/src/machine/board_p1am-100.go +++ b/src/machine/board_p1am-100.go @@ -123,7 +123,8 @@ var ( // I2S pins const ( I2S_SCK_PIN Pin = D2 - I2S_SD_PIN Pin = A6 + I2S_SDO_PIN Pin = A6 + I2S_SDI_PIN = NoPin I2S_WS_PIN = D3 ) diff --git a/src/machine/board_qtpy.go b/src/machine/board_qtpy.go index 49bb9c97b..e8a93e38d 100644 --- a/src/machine/board_qtpy.go +++ b/src/machine/board_qtpy.go @@ -81,7 +81,8 @@ var ( // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA08 + I2S_SDO_PIN = PA08 + I2S_SDI_PIN = NoPin // TODO: figure out what this is on QT Py M0. I2S_WS_PIN = NoPin // TODO: figure out what this is on QT Py M0. ) diff --git a/src/machine/board_trinket.go b/src/machine/board_trinket.go index 2ce419a4a..089eadbf0 100644 --- a/src/machine/board_trinket.go +++ b/src/machine/board_trinket.go @@ -67,7 +67,8 @@ var ( // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA08 + I2S_SDO_PIN = PA08 + I2S_SDI_PIN = NoPin // TODO: figure out what this is on Trinket M0. I2S_WS_PIN = NoPin // TODO: figure out what this is on Trinket M0. ) diff --git a/src/machine/board_wioterminal.go b/src/machine/board_wioterminal.go index 99a04d6ec..6997120b9 100644 --- a/src/machine/board_wioterminal.go +++ b/src/machine/board_wioterminal.go @@ -376,6 +376,14 @@ var ( I2C1 = sercomI2CM3 ) +// I2S pins +const ( + I2S_SCK_PIN = BCM18 + I2S_SDO_PIN = BCM21 + I2S_SDI_PIN = BCM20 + I2S_WS_PIN = BCM19 +) + // SPI pins const ( SPI0_SCK_PIN = SCK // SCK: SERCOM5/PAD[1] diff --git a/src/machine/board_xiao.go b/src/machine/board_xiao.go index f0ecf068e..5bbb34d68 100644 --- a/src/machine/board_xiao.go +++ b/src/machine/board_xiao.go @@ -82,7 +82,8 @@ var SPI0 = sercomSPIM0 // I2S pins const ( I2S_SCK_PIN = PA10 - I2S_SD_PIN = PA08 + I2S_SDO_PIN = PA08 + I2S_SDI_PIN = NoPin // TODO: figure out what this is on Xiao I2S_WS_PIN = NoPin // TODO: figure out what this is on Xiao ) diff --git a/src/machine/i2s.go b/src/machine/i2s.go index 8f5e30953..13dc80f61 100644 --- a/src/machine/i2s.go +++ b/src/machine/i2s.go @@ -1,4 +1,4 @@ -//go:build sam +//go:build sam && atsamd21 // This is the definition for I2S bus functions. // Actual implementations if available for any given hardware @@ -9,6 +9,22 @@ package machine +import "errors" + +// If you are getting a compile error on this line please check to see you've +// correctly implemented the methods on the I2S type. They must match +// the interface method signatures type to type perfectly. +// If not implementing the I2S type please remove your target from the build tags +// at the top of this file. +var _ interface { + SetSampleFrequency(freq uint32) error + ReadMono(b []uint16) (int, error) + ReadStereo(b []uint32) (int, error) + WriteMono(b []uint16) (int, error) + WriteStereo(b []uint32) (int, error) + Enable(enabled bool) +} = (*I2S)(nil) + type I2SMode uint8 type I2SStandard uint8 type I2SClockSource uint8 @@ -18,6 +34,7 @@ const ( I2SModeSource I2SMode = iota I2SModeReceiver I2SModePDM + I2SModeSourceReceiver ) const ( @@ -39,11 +56,20 @@ const ( I2SDataFormat32bit = 32 ) +var ( + ErrInvalidSampleFrequency = errors.New("i2s: invalid sample frequency") +) + // All fields are optional and may not be required or used on a particular platform. type I2SConfig struct { - SCK Pin - WS Pin - SD Pin + // clock + SCK Pin + // word select + WS Pin + // data out + SDO Pin + // data in + SDI Pin Mode I2SMode Standard I2SStandard ClockSource I2SClockSource diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go index fe67f45a3..3d0abc0fa 100644 --- a/src/machine/machine_atsamd21.go +++ b/src/machine/machine_atsamd21.go @@ -926,23 +926,26 @@ func (i2c *I2C) readByte() byte { // I2S type I2S struct { - Bus *sam.I2S_Type + Bus *sam.I2S_Type + Frequency uint32 + DataFormat I2SDataFormat } var I2S0 = I2S{Bus: sam.I2S} // Configure is used to configure the I2S interface. You must call this // before you can use the I2S bus. -func (i2s I2S) Configure(config I2SConfig) { +func (i2s *I2S) Configure(config I2SConfig) error { // handle defaults if config.SCK == 0 { config.SCK = I2S_SCK_PIN config.WS = I2S_WS_PIN - config.SD = I2S_SD_PIN + config.SDO = I2S_SDO_PIN + config.SDI = I2S_SDI_PIN } if config.AudioFrequency == 0 { - config.AudioFrequency = 48000 + config.AudioFrequency = 44100 } if config.DataFormat == I2SDataFormatDefault { @@ -952,39 +955,17 @@ func (i2s I2S) Configure(config I2SConfig) { config.DataFormat = I2SDataFormat32bit } } + i2s.DataFormat = config.DataFormat // Turn on clock for I2S sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_) - // setting clock rate for sample. - division_factor := CPUFrequency() / (config.AudioFrequency * uint32(config.DataFormat)) - - // Switch Generic Clock Generator 3 to DFLL48M. - sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) | - (division_factor << sam.GCLK_GENDIV_DIV_Pos)) - waitForSync() - - sam.GCLK.GENCTRL.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENCTRL_ID_Pos) | - (sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) | - sam.GCLK_GENCTRL_IDC | - sam.GCLK_GENCTRL_GENEN) - waitForSync() - - // Use Generic Clock Generator 3 as source for I2S. - sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_I2S_0 << sam.GCLK_CLKCTRL_ID_Pos) | - (sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_CLKCTRL_GEN_Pos) | - sam.GCLK_CLKCTRL_CLKEN) - waitForSync() - - // reset the device - i2s.Bus.CTRLA.SetBits(sam.I2S_CTRLA_SWRST) - for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_SWRST) { + if err := i2s.SetSampleFrequency(config.AudioFrequency); err != nil { + return err } // disable device before continuing - for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_ENABLE) { - } - i2s.Bus.CTRLA.ClearBits(sam.I2S_CTRLA_ENABLE) + i2s.Enable(false) // setup clock if config.ClockSource == I2SClockSourceInternal { @@ -1067,19 +1048,25 @@ func (i2s I2S) Configure(config I2SConfig) { } // set serializer mode. - if config.Mode == I2SModePDM { + switch config.Mode { + case I2SModePDM: i2s.Bus.SERCTRL1.SetBits(sam.I2S_SERCTRL_SERMODE_PDM2) - } else { + case I2SModeSource: + i2s.Bus.SERCTRL1.SetBits(sam.I2S_SERCTRL_SERMODE_TX) + case I2SModeReceiver: i2s.Bus.SERCTRL1.SetBits(sam.I2S_SERCTRL_SERMODE_RX) } - // configure data pin - config.SD.Configure(PinConfig{Mode: PinCom}) + // configure data pins + if config.SDO != NoPin { + config.SDO.Configure(PinConfig{Mode: PinCom}) + } + if config.SDI != NoPin { + config.SDI.Configure(PinConfig{Mode: PinCom}) + } // re-enable - i2s.Bus.CTRLA.SetBits(sam.I2S_CTRLA_ENABLE) - for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_ENABLE) { - } + i2s.Enable(true) // enable i2s clock i2s.Bus.CTRLA.SetBits(sam.I2S_CTRLA_CKEN0) @@ -1090,11 +1077,23 @@ func (i2s I2S) Configure(config I2SConfig) { i2s.Bus.CTRLA.SetBits(sam.I2S_CTRLA_SEREN1) for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_SEREN1) { } + + return nil } -// Read data from the I2S bus into the provided slice. +// Read mono data from the I2S bus into the provided slice. // The I2S bus must already have been configured correctly. -func (i2s I2S) Read(p []uint32) (n int, err error) { +func (i2s *I2S) ReadMono(p []uint16) (n int, err error) { + return i2sRead(i2s, p) +} + +// Read stereo data from the I2S bus into the provided slice. +// The I2S bus must already have been configured correctly. +func (i2s *I2S) ReadStereo(p []uint32) (n int, err error) { + return i2sRead(i2s, p) +} + +func i2sRead[T uint16 | uint32](i2s *I2S, p []T) (int, error) { i := 0 for i = 0; i < len(p); i++ { // Wait until ready @@ -1105,7 +1104,7 @@ func (i2s I2S) Read(p []uint32) (n int, err error) { } // read data - p[i] = i2s.Bus.DATA1.Get() + p[i] = T(i2s.Bus.DATA1.Get()) // indicate read complete i2s.Bus.INTFLAG.Set(sam.I2S_INTFLAG_RXRDY1) @@ -1114,9 +1113,19 @@ func (i2s I2S) Read(p []uint32) (n int, err error) { return i, nil } -// Write data to the I2S bus from the provided slice. +// Write mono data to the I2S bus from the provided slice. // The I2S bus must already have been configured correctly. -func (i2s I2S) Write(p []uint32) (n int, err error) { +func (i2s *I2S) WriteMono(p []uint16) (n int, err error) { + return i2sWrite(i2s, p) +} + +// Write stereo data to the I2S bus from the provided slice. +// The I2S bus must already have been configured correctly. +func (i2s *I2S) WriteStereo(p []uint32) (n int, err error) { + return i2sWrite(i2s, p) +} + +func i2sWrite[T uint16 | uint32](i2s *I2S, p []T) (int, error) { i := 0 for i = 0; i < len(p); i++ { // Wait until ready @@ -1127,7 +1136,7 @@ func (i2s I2S) Write(p []uint32) (n int, err error) { } // write data - i2s.Bus.DATA1.Set(p[i]) + i2s.Bus.DATA1.Set(uint32(p[i])) // indicate write complete i2s.Bus.INTFLAG.Set(sam.I2S_INTFLAG_TXRDY1) @@ -1136,18 +1145,64 @@ func (i2s I2S) Write(p []uint32) (n int, err error) { return i, nil } -// Close the I2S bus. -func (i2s I2S) Close() error { - // Sync wait - for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_ENABLE) { +// SetSampleFrequency is used to set the sample frequency for the I2S bus. +func (i2s *I2S) SetSampleFrequency(freq uint32) error { + if freq == 0 { + return ErrInvalidSampleFrequency } - // disable I2S - i2s.Bus.CTRLA.ClearBits(sam.I2S_CTRLA_ENABLE) + if i2s.Frequency == freq { + return nil + } + + i2s.Frequency = freq + + // setting clock rate for sample. + division_factor := CPUFrequency() / (i2s.Frequency * uint32(i2s.DataFormat)) + + // Switch Generic Clock Generator 3 to DFLL48M. + sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) | + (division_factor << sam.GCLK_GENDIV_DIV_Pos)) + waitForSync() + + sam.GCLK.GENCTRL.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENCTRL_ID_Pos) | + (sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) | + sam.GCLK_GENCTRL_IDC | + sam.GCLK_GENCTRL_GENEN) + waitForSync() + + // Use Generic Clock Generator 3 as source for I2S. + sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_I2S_0 << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + waitForSync() + + // reset the device + i2s.Bus.CTRLA.SetBits(sam.I2S_CTRLA_SWRST) + for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_SWRST) { + } return nil } +// Enabled is used to enable or disable the I2S bus. +func (i2s *I2S) Enable(enabled bool) { + if enabled { + i2s.Bus.CTRLA.SetBits(sam.I2S_CTRLA_ENABLE) + for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_ENABLE) { + } + + return + } + + // disable + for i2s.Bus.SYNCBUSY.HasBits(sam.I2S_SYNCBUSY_ENABLE) { + } + i2s.Bus.CTRLA.ClearBits(sam.I2S_CTRLA_ENABLE) + + return +} + func waitForSync() { for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { } |