Skip to content

Commit 3fa926c

Browse files
aykevldeadprogram
authored andcommitted
machine/atsamd21: refactor SPI pin handling to only look at pin numbers
Pin mode and pad numbers are automatically calculated from the pin numbers, returning an error if no pinout could be found.
1 parent 01e5869 commit 3fa926c

File tree

7 files changed

+202
-71
lines changed

7 files changed

+202
-71
lines changed

src/machine/board_arduino_nano33.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,10 @@ const (
125125

126126
// SPI on the Arduino Nano 33.
127127
var (
128-
SPI0 = SPI{Bus: sam.SERCOM0_SPI,
129-
SCK: SPI0_SCK_PIN,
130-
MOSI: SPI0_MOSI_PIN,
131-
MISO: SPI0_MISO_PIN,
132-
DOpad: spiTXPad2SCK3,
133-
DIpad: sercomRXPad0,
134-
PinMode: PinSERCOM}
128+
SPI0 = SPI{
129+
Bus: sam.SERCOM0_SPI,
130+
SERCOM: 0,
131+
}
135132
)
136133

137134
// I2S pins

src/machine/board_circuitplay_express.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,10 @@ const (
114114

115115
// SPI on the Circuit Playground Express.
116116
var (
117-
SPI0 = SPI{Bus: sam.SERCOM3_SPI,
118-
SCK: SPI0_SCK_PIN,
119-
MOSI: SPI0_MOSI_PIN,
120-
MISO: SPI0_MISO_PIN,
121-
DOpad: spiTXPad2SCK3,
122-
DIpad: sercomRXPad0,
123-
PinMode: PinSERCOMAlt}
117+
SPI0 = SPI{
118+
Bus: sam.SERCOM3_SPI,
119+
SERCOM: 3,
120+
}
124121
)
125122

126123
// I2S pins

src/machine/board_feather-m0.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,10 @@ const (
8888

8989
// SPI on the Feather M0.
9090
var (
91-
SPI0 = SPI{Bus: sam.SERCOM4_SPI,
92-
SCK: SPI0_SCK_PIN,
93-
MOSI: SPI0_MOSI_PIN,
94-
MISO: SPI0_MISO_PIN,
95-
DOpad: spiTXPad2SCK3,
96-
DIpad: sercomRXPad0,
97-
PinMode: PinSERCOMAlt}
91+
SPI0 = SPI{
92+
Bus: sam.SERCOM4_SPI,
93+
SERCOM: 4,
94+
}
9895
)
9996

10097
// I2S pins

src/machine/board_itsybitsy-m0.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,10 @@ const (
9090

9191
// SPI on the ItsyBitsy M0.
9292
var (
93-
SPI0 = SPI{Bus: sam.SERCOM4_SPI,
94-
SCK: SPI0_SCK_PIN,
95-
MOSI: SPI0_MOSI_PIN,
96-
MISO: SPI0_MISO_PIN,
97-
DOpad: spiTXPad2SCK3,
98-
DIpad: sercomRXPad0,
99-
PinMode: PinSERCOMAlt}
93+
SPI0 = SPI{
94+
Bus: sam.SERCOM4_SPI,
95+
SERCOM: 4,
96+
}
10097
)
10198

10299
// "Internal" SPI pins; SPI flash is attached to these on ItsyBitsy M0
@@ -109,13 +106,10 @@ const (
109106

110107
// "Internal" SPI on Sercom 5
111108
var (
112-
SPI1 = SPI{Bus: sam.SERCOM5_SPI,
113-
SCK: SPI1_SCK_PIN,
114-
MOSI: SPI1_MOSI_PIN,
115-
MISO: SPI1_MISO_PIN,
116-
DOpad: spiTXPad2SCK3,
117-
DIpad: sercomRXPad1,
118-
PinMode: PinSERCOMAlt}
109+
SPI1 = SPI{
110+
Bus: sam.SERCOM5_SPI,
111+
SERCOM: 5,
112+
}
119113
)
120114

121115
// I2S pins

src/machine/board_trinket.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,10 @@ const (
6565

6666
// SPI on the Trinket M0.
6767
var (
68-
SPI0 = SPI{Bus: sam.SERCOM0_SPI,
69-
SCK: SPI0_SCK_PIN,
70-
MOSI: SPI0_MOSI_PIN,
71-
MISO: SPI0_MISO_PIN,
72-
DOpad: spiTXPad2SCK3,
73-
DIpad: sercomRXPad0,
74-
PinMode: PinSERCOMAlt}
68+
SPI0 = SPI{
69+
Bus: sam.SERCOM0_SPI,
70+
SERCOM: 0,
71+
}
7572
)
7673

7774
// I2C pins

src/machine/machine.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package machine
22

3+
import "errors"
4+
5+
var (
6+
ErrInvalidInputPin = errors.New("machine: invalid input pin")
7+
ErrInvalidOutputPin = errors.New("machine: invalid output pin")
8+
)
9+
310
type PinConfig struct {
411
Mode PinMode
512
}

src/machine/machine_atsamd21.go

Lines changed: 171 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,117 @@ const (
103103
PB31 Pin = 63
104104
)
105105

106+
const (
107+
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
108+
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
109+
pinPadMapSERCOM2Pad0 byte = (0x30 << 1) | 0x00
110+
pinPadMapSERCOM3Pad0 byte = (0x40 << 1) | 0x00
111+
pinPadMapSERCOM4Pad0 byte = (0x50 << 1) | 0x00
112+
pinPadMapSERCOM5Pad0 byte = (0x60 << 1) | 0x00
113+
pinPadMapSERCOM0Pad2 byte = (0x10 << 1) | 0x10
114+
pinPadMapSERCOM1Pad2 byte = (0x20 << 1) | 0x10
115+
pinPadMapSERCOM2Pad2 byte = (0x30 << 1) | 0x10
116+
pinPadMapSERCOM3Pad2 byte = (0x40 << 1) | 0x10
117+
pinPadMapSERCOM4Pad2 byte = (0x50 << 1) | 0x10
118+
pinPadMapSERCOM5Pad2 byte = (0x60 << 1) | 0x10
119+
120+
pinPadMapSERCOM0AltPad0 byte = (0x01 << 1) | 0x00
121+
pinPadMapSERCOM1AltPad0 byte = (0x02 << 1) | 0x00
122+
pinPadMapSERCOM2AltPad0 byte = (0x03 << 1) | 0x00
123+
pinPadMapSERCOM3AltPad0 byte = (0x04 << 1) | 0x00
124+
pinPadMapSERCOM4AltPad0 byte = (0x05 << 1) | 0x00
125+
pinPadMapSERCOM5AltPad0 byte = (0x06 << 1) | 0x00
126+
pinPadMapSERCOM0AltPad2 byte = (0x01 << 1) | 0x01
127+
pinPadMapSERCOM1AltPad2 byte = (0x02 << 1) | 0x01
128+
pinPadMapSERCOM2AltPad2 byte = (0x03 << 1) | 0x01
129+
pinPadMapSERCOM3AltPad2 byte = (0x04 << 1) | 0x01
130+
pinPadMapSERCOM4AltPad2 byte = (0x05 << 1) | 0x01
131+
pinPadMapSERCOM5AltPad2 byte = (0x06 << 1) | 0x01
132+
)
133+
134+
// pinPadMapping lists which pins have which SERCOMs attached to them.
135+
// The encoding is rather dense, with each byte encoding two pins and both
136+
// SERCOM and SERCOM-ALT.
137+
//
138+
// Observations:
139+
// * There are six SERCOMs. Those SERCOM numbers can be encoded in 3 bits.
140+
// * Even pad numbers are always on even pins, and odd pad numbers are always on
141+
// odd pins.
142+
// * Pin pads come in pairs. If PA00 has pad 0, then PA01 has pad 1.
143+
// With this information, we can encode SERCOM pin/pad numbers much more
144+
// efficiently. First of all, due to pads coming in pairs, we can ignore half
145+
// the pins: the information for an odd pin can be calculated easily from the
146+
// preceding even pin. And second, if odd pads are always on odd pins and even
147+
// pads on even pins, we can drop a single bit from the pad number.
148+
//
149+
// Each byte below is split in two nibbles. The 4 high bits are for SERCOM and
150+
// the 4 low bits are for SERCOM-ALT. Of each nibble, the 3 high bits encode the
151+
// SERCOM + 1 while the low bit encodes whether this is PAD0 or PAD2 (0 means
152+
// PAD0, 1 means PAD2). It encodes SERCOM + 1 instead of just the SERCOM number,
153+
// to make it easy to check whether a nibble is set at all.
154+
var pinPadMapping = [32]byte{
155+
// page 21
156+
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
157+
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
158+
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
159+
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
160+
PA08 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad0,
161+
PA10 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
162+
163+
// page 22
164+
PB10 / 2: 0 | pinPadMapSERCOM4AltPad2,
165+
PB12 / 2: pinPadMapSERCOM4Pad0 | 0,
166+
PB14 / 2: pinPadMapSERCOM4Pad2 | 0,
167+
PA12 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad0,
168+
PA14 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
169+
PA16 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad0,
170+
PA18 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
171+
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
172+
PA20 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM3AltPad2,
173+
PA22 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM5AltPad0,
174+
PA24 / 2: pinPadMapSERCOM3Pad2 | pinPadMapSERCOM5AltPad2,
175+
176+
// page 23
177+
PB22 / 2: 0 | pinPadMapSERCOM5AltPad2,
178+
PA30 / 2: 0 | pinPadMapSERCOM1AltPad2,
179+
PB30 / 2: 0 | pinPadMapSERCOM5AltPad0,
180+
PB00 / 2: 0 | pinPadMapSERCOM5AltPad2,
181+
PB02 / 2: 0 | pinPadMapSERCOM5AltPad0,
182+
}
183+
184+
// findPinPadMapping looks up the pad number and the pinmode for a given pin,
185+
// given a SERCOM number. The result can either be SERCOM, SERCOM-ALT, or "not
186+
// found" (indicated by returning ok=false). The pad number is returned to
187+
// calculate the DOPO/DIPO bitfields of the various serial peripherals.
188+
func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok bool) {
189+
nibbles := pinPadMapping[pin/2]
190+
upper := nibbles >> 4
191+
lower := nibbles & 0xf
192+
193+
if upper != 0 {
194+
// SERCOM
195+
if (upper>>1)-1 == sercom {
196+
pinMode = PinSERCOM
197+
pad |= uint32((upper & 1) << 1)
198+
ok = true
199+
}
200+
}
201+
if lower != 0 {
202+
// SERCOM-ALT
203+
if (lower>>1)-1 == sercom {
204+
pinMode = PinSERCOMAlt
205+
pad |= uint32((lower & 1) << 1)
206+
ok = true
207+
}
208+
}
209+
210+
if ok {
211+
// The lower bit of the pad is the same as the lower bit of the pin number.
212+
pad |= uint32(pin & 1)
213+
}
214+
return
215+
}
216+
106217
// InitADC initializes the ADC.
107218
func InitADC() {
108219
// ADC Bias Calibration
@@ -272,11 +383,6 @@ const (
272383
sercomTXPad0 = 0 // Only for UART
273384
sercomTXPad2 = 1 // Only for UART
274385
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
275-
276-
spiTXPad0SCK1 = 0
277-
spiTXPad2SCK3 = 1
278-
spiTXPad3SCK1 = 2
279-
spiTXPad0SCK3 = 3
280386
)
281387

282388
// Configure the UART.
@@ -874,13 +980,8 @@ func waitForSync() {
874980

875981
// SPI
876982
type SPI struct {
877-
Bus *sam.SERCOM_SPI_Type
878-
SCK Pin
879-
MOSI Pin
880-
MISO Pin
881-
DOpad int
882-
DIpad int
883-
PinMode PinMode
983+
Bus *sam.SERCOM_SPI_Type
984+
SERCOM uint8
884985
}
885986

886987
// SPIConfig is used to store config info for SPI.
@@ -894,30 +995,69 @@ type SPIConfig struct {
894995
}
895996

896997
// Configure is intended to setup the SPI interface.
897-
func (spi SPI) Configure(config SPIConfig) {
898-
config.SCK = spi.SCK
899-
config.MOSI = spi.MOSI
900-
config.MISO = spi.MISO
901-
902-
doPad := spi.DOpad
903-
diPad := spi.DIpad
904-
905-
pinMode := spi.PinMode
998+
func (spi SPI) Configure(config SPIConfig) error {
999+
// Use default pins if not set.
1000+
if config.SCK == 0 && config.MOSI == 0 && config.MISO == 0 {
1001+
config.SCK = SPI0_SCK_PIN
1002+
config.MOSI = SPI0_MOSI_PIN
1003+
config.MISO = SPI0_MISO_PIN
1004+
}
9061005

9071006
// set default frequency
9081007
if config.Frequency == 0 {
9091008
config.Frequency = 4000000
9101009
}
9111010

1011+
// Determine the input pinout (for MISO).
1012+
misoPinMode, misoPad, ok := findPinPadMapping(spi.SERCOM, config.MISO)
1013+
if !ok {
1014+
return ErrInvalidInputPin
1015+
}
1016+
dataInPinout := misoPad // mapped directly
1017+
1018+
// Determine the output pinout (for MOSI/SCK).
1019+
// See table 26-7 on page 494 of the datasheet.
1020+
var dataOutPinout uint32
1021+
sckPinMode, sckPad, ok := findPinPadMapping(spi.SERCOM, config.SCK)
1022+
if !ok {
1023+
return ErrInvalidOutputPin
1024+
}
1025+
mosiPinMode, mosiPad, ok := findPinPadMapping(spi.SERCOM, config.MOSI)
1026+
if !ok {
1027+
return ErrInvalidOutputPin
1028+
}
1029+
switch sckPad {
1030+
case 1:
1031+
switch mosiPad {
1032+
case 0:
1033+
dataOutPinout = 0x0
1034+
case 3:
1035+
dataOutPinout = 0x2
1036+
default:
1037+
return ErrInvalidOutputPin
1038+
}
1039+
case 3:
1040+
switch mosiPad {
1041+
case 2:
1042+
dataOutPinout = 0x1
1043+
case 0:
1044+
dataOutPinout = 0x3
1045+
default:
1046+
return ErrInvalidOutputPin
1047+
}
1048+
default:
1049+
return ErrInvalidOutputPin
1050+
}
1051+
9121052
// Disable SPI port.
9131053
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_ENABLE)
9141054
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_ENABLE) {
9151055
}
9161056

9171057
// enable pins
918-
config.SCK.Configure(PinConfig{Mode: pinMode})
919-
config.MOSI.Configure(PinConfig{Mode: pinMode})
920-
config.MISO.Configure(PinConfig{Mode: pinMode})
1058+
config.SCK.Configure(PinConfig{Mode: sckPinMode})
1059+
config.MOSI.Configure(PinConfig{Mode: mosiPinMode})
1060+
config.MISO.Configure(PinConfig{Mode: misoPinMode})
9211061

9221062
// reset SERCOM
9231063
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_SWRST)
@@ -926,16 +1066,16 @@ func (spi SPI) Configure(config SPIConfig) {
9261066
}
9271067

9281068
// set bit transfer order
929-
dataOrder := 0
1069+
dataOrder := uint32(0)
9301070
if config.LSBFirst {
9311071
dataOrder = 1
9321072
}
9331073

9341074
// Set SPI master
935-
spi.Bus.CTRLA.Set(uint32((sam.SERCOM_SPI_CTRLA_MODE_SPI_MASTER << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
936-
(doPad << sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
937-
(diPad << sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
938-
(dataOrder << sam.SERCOM_SPI_CTRLA_DORD_Pos)))
1075+
spi.Bus.CTRLA.Set((sam.SERCOM_SPI_CTRLA_MODE_SPI_MASTER << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
1076+
(dataOutPinout << sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
1077+
(dataInPinout << sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
1078+
(dataOrder << sam.SERCOM_SPI_CTRLA_DORD_Pos))
9391079

9401080
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPI_CTRLB_CHSIZE_Pos) | // 8bit char size
9411081
sam.SERCOM_SPI_CTRLB_RXEN) // receive enable
@@ -969,6 +1109,8 @@ func (spi SPI) Configure(config SPIConfig) {
9691109
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_ENABLE)
9701110
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_ENABLE) {
9711111
}
1112+
1113+
return nil
9721114
}
9731115

9741116
// Transfer writes/reads a single byte using the SPI interface.

0 commit comments

Comments
 (0)