Skip to content

Commit d90f194

Browse files
deadprogramaykevl
authored andcommitted
machine/samd21: Initial implementation of I2S hardware interface using Circuit Playground Express
Signed-off-by: Ron Evans <[email protected]>
1 parent 11567c6 commit d90f194

File tree

6 files changed

+333
-0
lines changed

6 files changed

+333
-0
lines changed

.circleci/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ commands:
8282
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
8383
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
8484
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
85+
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
8586
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
8687
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
8788
test-linux:

src/examples/i2s/i2s.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Example using the i2s hardware interface on the Adafruit Circuit Playground Express
2+
// to read data from the onboard MEMS microphone.
3+
//
4+
package main
5+
6+
import (
7+
"machine"
8+
)
9+
10+
func main() {
11+
machine.I2S0.Configure(machine.I2SConfig{
12+
Mode: machine.I2SModePDM,
13+
ClockSource: machine.I2SClockSourceExternal,
14+
Stereo: true,
15+
})
16+
17+
data := make([]uint32, 64)
18+
19+
for {
20+
// get the next group of samples
21+
machine.I2S0.Read(data)
22+
23+
println("data", data[0], data[1], data[2], data[4], "...")
24+
}
25+
}

src/machine/board_circuitplay_express.go

+12
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,15 @@ const (
9999
var (
100100
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
101101
)
102+
103+
// I2S pins
104+
const (
105+
I2S_SCK_PIN = PA10
106+
I2S_SD_PIN = PA08
107+
I2S_WS_PIN = 0xff // no WS, instead uses SCK to sync
108+
)
109+
110+
// I2S on the Circuit Playground Express.
111+
var (
112+
I2S0 = I2S{Bus: sam.I2S}
113+
)

src/machine/board_itsybitsy-m0.go

+12
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,15 @@ const (
7373
var (
7474
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
7575
)
76+
77+
// I2S pins
78+
const (
79+
I2S_SCK_PIN = PA10
80+
I2S_SD_PIN = PA08
81+
I2S_WS_PIN = 0xff // TODO: figure out what this is on ItsyBitsy M0.
82+
)
83+
84+
// I2S on the ItsyBitsy M0.
85+
var (
86+
I2S0 = I2S{Bus: sam.I2S}
87+
)

src/machine/i2s.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// +build sam
2+
3+
// This is the definition for I2S bus functions.
4+
// Actual implementations if available for any given hardware
5+
// are to be found in its the board definition.
6+
//
7+
// For more info about I2S, see: https://en.wikipedia.org/wiki/I%C2%B2S
8+
//
9+
10+
package machine
11+
12+
type I2SMode uint8
13+
type I2SStandard uint8
14+
type I2SClockSource uint8
15+
type I2SDataFormat uint8
16+
17+
const (
18+
I2SModeMaster I2SMode = iota
19+
I2SModeSlave
20+
I2SModePDM
21+
)
22+
23+
const (
24+
I2StandardPhilips I2SStandard = iota
25+
I2SStandardMSB
26+
I2SStandardLSB
27+
)
28+
29+
const (
30+
I2SClockSourceInternal I2SClockSource = iota
31+
I2SClockSourceExternal
32+
)
33+
34+
const (
35+
I2SDataFormatDefault I2SDataFormat = 0
36+
I2SDataFormat8bit = 8
37+
I2SDataFormat16bit = 16
38+
I2SDataFormat24bit = 24
39+
I2SDataFormat32bit = 32
40+
)
41+
42+
// All fields are optional and may not be required or used on a particular platform.
43+
type I2SConfig struct {
44+
SCK uint8
45+
WS uint8
46+
SD uint8
47+
Mode I2SMode
48+
Standard I2SStandard
49+
ClockSource I2SClockSource
50+
DataFormat I2SDataFormat
51+
AudioFrequency uint32
52+
MasterClockOutput bool
53+
Stereo bool
54+
}

src/machine/machine_atsamd21.go

+229
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,235 @@ func (i2c I2C) readByte() byte {
808808
return byte(i2c.Bus.DATA)
809809
}
810810

811+
// I2S on the SAMD21.
812+
813+
// I2S
814+
type I2S struct {
815+
Bus *sam.I2S_Type
816+
}
817+
818+
// Configure is used to configure the I2S interface. You must call this
819+
// before you can use the I2S bus.
820+
func (i2s I2S) Configure(config I2SConfig) {
821+
// handle defaults
822+
if config.SCK == 0 {
823+
config.SCK = I2S_SCK_PIN
824+
config.WS = I2S_WS_PIN
825+
config.SD = I2S_SD_PIN
826+
}
827+
828+
if config.AudioFrequency == 0 {
829+
config.AudioFrequency = 48000
830+
}
831+
832+
if config.DataFormat == I2SDataFormatDefault {
833+
if config.Stereo {
834+
config.DataFormat = I2SDataFormat16bit
835+
} else {
836+
config.DataFormat = I2SDataFormat32bit
837+
}
838+
}
839+
840+
// Turn on clock for I2S
841+
sam.PM.APBCMASK |= sam.PM_APBCMASK_I2S_
842+
843+
// setting clock rate for sample.
844+
division_factor := CPU_FREQUENCY / (config.AudioFrequency * uint32(config.DataFormat))
845+
846+
// Switch Generic Clock Generator 3 to DFLL48M.
847+
sam.GCLK.GENDIV = sam.RegValue((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
848+
(division_factor << sam.GCLK_GENDIV_DIV_Pos))
849+
waitForSync()
850+
851+
sam.GCLK.GENCTRL = sam.RegValue((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENCTRL_ID_Pos) |
852+
(sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) |
853+
sam.GCLK_GENCTRL_IDC |
854+
sam.GCLK_GENCTRL_GENEN)
855+
waitForSync()
856+
857+
// Use Generic Clock Generator 3 as source for I2S.
858+
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_I2S_0 << sam.GCLK_CLKCTRL_ID_Pos) |
859+
(sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_CLKCTRL_GEN_Pos) |
860+
sam.GCLK_CLKCTRL_CLKEN)
861+
waitForSync()
862+
863+
// reset the device
864+
i2s.Bus.CTRLA |= sam.I2S_CTRLA_SWRST
865+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_SWRST) > 0 {
866+
}
867+
868+
// disable device before continuing
869+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
870+
}
871+
i2s.Bus.CTRLA &^= sam.I2S_CTRLA_ENABLE
872+
873+
// setup clock
874+
if config.ClockSource == I2SClockSourceInternal {
875+
// TODO: make sure correct for I2S output
876+
877+
// set serial clock select pin
878+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SCKSEL
879+
880+
// set frame select pin
881+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_FSSEL
882+
} else {
883+
// Configure FS generation from SCK clock.
884+
i2s.Bus.CLKCTRL0 &^= sam.I2S_CLKCTRL_FSSEL
885+
}
886+
887+
if config.Standard == I2StandardPhilips {
888+
// set 1-bit delay
889+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_BITDELAY
890+
} else {
891+
// set 0-bit delay
892+
i2s.Bus.CLKCTRL0 &^= sam.I2S_CLKCTRL_BITDELAY
893+
}
894+
895+
// set number of slots.
896+
if config.Stereo {
897+
i2s.Bus.CLKCTRL0 |= (1 << sam.I2S_CLKCTRL_NBSLOTS_Pos)
898+
} else {
899+
i2s.Bus.CLKCTRL0 &^= (1 << sam.I2S_CLKCTRL_NBSLOTS_Pos)
900+
}
901+
902+
// set slot size
903+
switch config.DataFormat {
904+
case I2SDataFormat8bit:
905+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_8
906+
907+
case I2SDataFormat16bit:
908+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_16
909+
910+
case I2SDataFormat24bit:
911+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_24
912+
913+
case I2SDataFormat32bit:
914+
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_32
915+
}
916+
917+
// configure pin for clock
918+
GPIO{config.SCK}.Configure(GPIOConfig{Mode: GPIO_COM})
919+
920+
// configure pin for WS, if needed
921+
if config.WS != 0xff {
922+
GPIO{config.WS}.Configure(GPIOConfig{Mode: GPIO_COM})
923+
}
924+
925+
// now set serializer data size.
926+
switch config.DataFormat {
927+
case I2SDataFormat8bit:
928+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_8
929+
930+
case I2SDataFormat16bit:
931+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_16
932+
933+
case I2SDataFormat24bit:
934+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_24
935+
936+
case I2SDataFormat32bit:
937+
case I2SDataFormatDefault:
938+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_32
939+
}
940+
941+
// set serializer slot adjustment
942+
if config.Standard == I2SStandardLSB {
943+
// adjust right
944+
i2s.Bus.SERCTRL1 &^= sam.I2S_SERCTRL_SLOTADJ
945+
} else {
946+
// adjust left
947+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SLOTADJ
948+
949+
// reverse bit order?
950+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_BITREV
951+
}
952+
953+
// set serializer mode.
954+
if config.Mode == I2SModePDM {
955+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SERMODE_PDM2
956+
} else {
957+
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SERMODE_RX
958+
}
959+
960+
// configure data pin
961+
GPIO{config.SD}.Configure(GPIOConfig{Mode: GPIO_COM})
962+
963+
// re-enable
964+
i2s.Bus.CTRLA |= sam.I2S_CTRLA_ENABLE
965+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
966+
}
967+
968+
// enable i2s clock
969+
i2s.Bus.CTRLA |= sam.I2S_CTRLA_CKEN0
970+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_CKEN0) > 0 {
971+
}
972+
973+
// enable i2s serializer
974+
i2s.Bus.CTRLA |= sam.I2S_CTRLA_SEREN1
975+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_SEREN1) > 0 {
976+
}
977+
}
978+
979+
// Read data from the I2S bus into the provided slice.
980+
// The I2S bus must already have been configured correctly.
981+
func (i2s I2S) Read(p []uint32) (n int, err error) {
982+
i := 0
983+
for i = 0; i < len(p); i++ {
984+
// Wait until ready
985+
for (i2s.Bus.INTFLAG & sam.I2S_INTFLAG_RXRDY1) == 0 {
986+
}
987+
988+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_DATA1) > 0 {
989+
}
990+
991+
// read data
992+
p[i] = uint32(i2s.Bus.DATA1)
993+
994+
// indicate read complete
995+
i2s.Bus.INTFLAG = sam.I2S_INTFLAG_RXRDY1
996+
}
997+
998+
return i, nil
999+
}
1000+
1001+
// Write data to the I2S bus from the provided slice.
1002+
// The I2S bus must already have been configured correctly.
1003+
func (i2s I2S) Write(p []uint32) (n int, err error) {
1004+
i := 0
1005+
for i = 0; i < len(p); i++ {
1006+
// Wait until ready
1007+
for (i2s.Bus.INTFLAG & sam.I2S_INTFLAG_TXRDY1) == 0 {
1008+
}
1009+
1010+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_DATA1) > 0 {
1011+
}
1012+
1013+
// write data
1014+
i2s.Bus.DATA1 = sam.RegValue(p[i])
1015+
1016+
// indicate write complete
1017+
i2s.Bus.INTFLAG = sam.I2S_INTFLAG_TXRDY1
1018+
}
1019+
1020+
return i, nil
1021+
}
1022+
1023+
// Close the I2S bus.
1024+
func (i2s I2S) Close() error {
1025+
// Sync wait
1026+
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
1027+
}
1028+
1029+
// disable I2S
1030+
i2s.Bus.CTRLA &^= sam.I2S_CTRLA_ENABLE
1031+
1032+
return nil
1033+
}
1034+
1035+
func waitForSync() {
1036+
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
1037+
}
1038+
}
1039+
8111040
// SPI
8121041
type SPI struct {
8131042
Bus *sam.SERCOM_SPI_Type

0 commit comments

Comments
 (0)