Browse Source

machine/samd21: Initial implementation of I2S hardware interface using Circuit Playground Express

Signed-off-by: Ron Evans <ron@hybridgroup.com>
pull/346/head
Ron Evans 6 years ago
committed by Ayke
parent
commit
d90f1947d9
  1. 1
      .circleci/config.yml
  2. 25
      src/examples/i2s/i2s.go
  3. 12
      src/machine/board_circuitplay_express.go
  4. 12
      src/machine/board_itsybitsy-m0.go
  5. 54
      src/machine/i2s.go
  6. 229
      src/machine/machine_atsamd21.go

1
.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:

25
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], "...")
}
}

12
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}
)

12
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}
)

54
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
}

229
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

Loading…
Cancel
Save