aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml1
-rw-r--r--src/examples/i2s/i2s.go25
-rw-r--r--src/machine/board_circuitplay_express.go12
-rw-r--r--src/machine/board_itsybitsy-m0.go12
-rw-r--r--src/machine/i2s.go54
-rw-r--r--src/machine/machine_atsamd21.go229
6 files changed, 333 insertions, 0 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9ca20a993..a1bfead64 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -82,6 +82,7 @@ commands:
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
+ - run: tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
test-linux:
diff --git a/src/examples/i2s/i2s.go b/src/examples/i2s/i2s.go
new file mode 100644
index 000000000..2f2c2f5b2
--- /dev/null
+++ b/src/examples/i2s/i2s.go
@@ -0,0 +1,25 @@
+// Example using the i2s hardware interface on the Adafruit Circuit Playground Express
+// to read data from the onboard MEMS microphone.
+//
+package main
+
+import (
+ "machine"
+)
+
+func main() {
+ machine.I2S0.Configure(machine.I2SConfig{
+ Mode: machine.I2SModePDM,
+ ClockSource: machine.I2SClockSourceExternal,
+ Stereo: true,
+ })
+
+ data := make([]uint32, 64)
+
+ for {
+ // get the next group of samples
+ machine.I2S0.Read(data)
+
+ println("data", data[0], data[1], data[2], data[4], "...")
+ }
+}
diff --git a/src/machine/board_circuitplay_express.go b/src/machine/board_circuitplay_express.go
index 8da4276ab..311eeaae7 100644
--- a/src/machine/board_circuitplay_express.go
+++ b/src/machine/board_circuitplay_express.go
@@ -99,3 +99,15 @@ const (
var (
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
)
+
+// I2S pins
+const (
+ I2S_SCK_PIN = PA10
+ I2S_SD_PIN = PA08
+ I2S_WS_PIN = 0xff // no WS, instead uses SCK to sync
+)
+
+// I2S on the Circuit Playground Express.
+var (
+ I2S0 = I2S{Bus: sam.I2S}
+)
diff --git a/src/machine/board_itsybitsy-m0.go b/src/machine/board_itsybitsy-m0.go
index 93966f282..f0ff15efc 100644
--- a/src/machine/board_itsybitsy-m0.go
+++ b/src/machine/board_itsybitsy-m0.go
@@ -73,3 +73,15 @@ const (
var (
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
)
+
+// I2S pins
+const (
+ I2S_SCK_PIN = PA10
+ I2S_SD_PIN = PA08
+ I2S_WS_PIN = 0xff // TODO: figure out what this is on ItsyBitsy M0.
+)
+
+// I2S on the ItsyBitsy M0.
+var (
+ I2S0 = I2S{Bus: sam.I2S}
+)
diff --git a/src/machine/i2s.go b/src/machine/i2s.go
new file mode 100644
index 000000000..ed7589579
--- /dev/null
+++ b/src/machine/i2s.go
@@ -0,0 +1,54 @@
+// +build sam
+
+// This is the definition for I2S bus functions.
+// Actual implementations if available for any given hardware
+// are to be found in its the board definition.
+//
+// For more info about I2S, see: https://en.wikipedia.org/wiki/I%C2%B2S
+//
+
+package machine
+
+type I2SMode uint8
+type I2SStandard uint8
+type I2SClockSource uint8
+type I2SDataFormat uint8
+
+const (
+ I2SModeMaster I2SMode = iota
+ I2SModeSlave
+ I2SModePDM
+)
+
+const (
+ I2StandardPhilips I2SStandard = iota
+ I2SStandardMSB
+ I2SStandardLSB
+)
+
+const (
+ I2SClockSourceInternal I2SClockSource = iota
+ I2SClockSourceExternal
+)
+
+const (
+ I2SDataFormatDefault I2SDataFormat = 0
+ I2SDataFormat8bit = 8
+ I2SDataFormat16bit = 16
+ I2SDataFormat24bit = 24
+ I2SDataFormat32bit = 32
+)
+
+// All fields are optional and may not be required or used on a particular platform.
+type I2SConfig struct {
+ SCK uint8
+ WS uint8
+ SD uint8
+ Mode I2SMode
+ Standard I2SStandard
+ ClockSource I2SClockSource
+ DataFormat I2SDataFormat
+ AudioFrequency uint32
+ MasterClockOutput bool
+ Stereo bool
+}
diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go
index 278fa3c0b..89a62292c 100644
--- a/src/machine/machine_atsamd21.go
+++ b/src/machine/machine_atsamd21.go
@@ -808,6 +808,235 @@ func (i2c I2C) readByte() byte {
return byte(i2c.Bus.DATA)
}
+// I2S on the SAMD21.
+
+// I2S
+type I2S struct {
+ Bus *sam.I2S_Type
+}
+
+// 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) {
+ // handle defaults
+ if config.SCK == 0 {
+ config.SCK = I2S_SCK_PIN
+ config.WS = I2S_WS_PIN
+ config.SD = I2S_SD_PIN
+ }
+
+ if config.AudioFrequency == 0 {
+ config.AudioFrequency = 48000
+ }
+
+ if config.DataFormat == I2SDataFormatDefault {
+ if config.Stereo {
+ config.DataFormat = I2SDataFormat16bit
+ } else {
+ config.DataFormat = I2SDataFormat32bit
+ }
+ }
+
+ // Turn on clock for I2S
+ sam.PM.APBCMASK |= sam.PM_APBCMASK_I2S_
+
+ // setting clock rate for sample.
+ division_factor := CPU_FREQUENCY / (config.AudioFrequency * uint32(config.DataFormat))
+
+ // Switch Generic Clock Generator 3 to DFLL48M.
+ sam.GCLK.GENDIV = sam.RegValue((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
+ (division_factor << sam.GCLK_GENDIV_DIV_Pos))
+ waitForSync()
+
+ sam.GCLK.GENCTRL = sam.RegValue((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 = sam.RegValue16((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 |= sam.I2S_CTRLA_SWRST
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_SWRST) > 0 {
+ }
+
+ // disable device before continuing
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
+ }
+ i2s.Bus.CTRLA &^= sam.I2S_CTRLA_ENABLE
+
+ // setup clock
+ if config.ClockSource == I2SClockSourceInternal {
+ // TODO: make sure correct for I2S output
+
+ // set serial clock select pin
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SCKSEL
+
+ // set frame select pin
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_FSSEL
+ } else {
+ // Configure FS generation from SCK clock.
+ i2s.Bus.CLKCTRL0 &^= sam.I2S_CLKCTRL_FSSEL
+ }
+
+ if config.Standard == I2StandardPhilips {
+ // set 1-bit delay
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_BITDELAY
+ } else {
+ // set 0-bit delay
+ i2s.Bus.CLKCTRL0 &^= sam.I2S_CLKCTRL_BITDELAY
+ }
+
+ // set number of slots.
+ if config.Stereo {
+ i2s.Bus.CLKCTRL0 |= (1 << sam.I2S_CLKCTRL_NBSLOTS_Pos)
+ } else {
+ i2s.Bus.CLKCTRL0 &^= (1 << sam.I2S_CLKCTRL_NBSLOTS_Pos)
+ }
+
+ // set slot size
+ switch config.DataFormat {
+ case I2SDataFormat8bit:
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_8
+
+ case I2SDataFormat16bit:
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_16
+
+ case I2SDataFormat24bit:
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_24
+
+ case I2SDataFormat32bit:
+ i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_32
+ }
+
+ // configure pin for clock
+ GPIO{config.SCK}.Configure(GPIOConfig{Mode: GPIO_COM})
+
+ // configure pin for WS, if needed
+ if config.WS != 0xff {
+ GPIO{config.WS}.Configure(GPIOConfig{Mode: GPIO_COM})
+ }
+
+ // now set serializer data size.
+ switch config.DataFormat {
+ case I2SDataFormat8bit:
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_8
+
+ case I2SDataFormat16bit:
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_16
+
+ case I2SDataFormat24bit:
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_24
+
+ case I2SDataFormat32bit:
+ case I2SDataFormatDefault:
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_32
+ }
+
+ // set serializer slot adjustment
+ if config.Standard == I2SStandardLSB {
+ // adjust right
+ i2s.Bus.SERCTRL1 &^= sam.I2S_SERCTRL_SLOTADJ
+ } else {
+ // adjust left
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SLOTADJ
+
+ // reverse bit order?
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_BITREV
+ }
+
+ // set serializer mode.
+ if config.Mode == I2SModePDM {
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SERMODE_PDM2
+ } else {
+ i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SERMODE_RX
+ }
+
+ // configure data pin
+ GPIO{config.SD}.Configure(GPIOConfig{Mode: GPIO_COM})
+
+ // re-enable
+ i2s.Bus.CTRLA |= sam.I2S_CTRLA_ENABLE
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
+ }
+
+ // enable i2s clock
+ i2s.Bus.CTRLA |= sam.I2S_CTRLA_CKEN0
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_CKEN0) > 0 {
+ }
+
+ // enable i2s serializer
+ i2s.Bus.CTRLA |= sam.I2S_CTRLA_SEREN1
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_SEREN1) > 0 {
+ }
+}
+
+// Read 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) {
+ i := 0
+ for i = 0; i < len(p); i++ {
+ // Wait until ready
+ for (i2s.Bus.INTFLAG & sam.I2S_INTFLAG_RXRDY1) == 0 {
+ }
+
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_DATA1) > 0 {
+ }
+
+ // read data
+ p[i] = uint32(i2s.Bus.DATA1)
+
+ // indicate read complete
+ i2s.Bus.INTFLAG = sam.I2S_INTFLAG_RXRDY1
+ }
+
+ return i, nil
+}
+
+// Write 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) {
+ i := 0
+ for i = 0; i < len(p); i++ {
+ // Wait until ready
+ for (i2s.Bus.INTFLAG & sam.I2S_INTFLAG_TXRDY1) == 0 {
+ }
+
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_DATA1) > 0 {
+ }
+
+ // write data
+ i2s.Bus.DATA1 = sam.RegValue(p[i])
+
+ // indicate write complete
+ i2s.Bus.INTFLAG = sam.I2S_INTFLAG_TXRDY1
+ }
+
+ return i, nil
+}
+
+// Close the I2S bus.
+func (i2s I2S) Close() error {
+ // Sync wait
+ for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
+ }
+
+ // disable I2S
+ i2s.Bus.CTRLA &^= sam.I2S_CTRLA_ENABLE
+
+ return nil
+}
+
+func waitForSync() {
+ for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
+ }
+}
+
// SPI
type SPI struct {
Bus *sam.SERCOM_SPI_Type