mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
machine/atsamd51: refactor SPI pin configuration to only look at pin numbers
This commit does the same thing as https://github.com/tinygo-org/tinygo/pull/597 but for samd51 series chips. Pin mode and pad numbers are automatically calculated from pin numbers, returning an error if no valid pinout is possible.
This commit is contained in:
committed by
Ron Evans
parent
7ace991a2b
commit
31d3ac725f
@@ -82,14 +82,8 @@ const (
|
||||
|
||||
// SPI on the Feather M4.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
DIpad: sercomRXPad2,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -84,12 +84,8 @@ const (
|
||||
|
||||
// SPI on the ItsyBitsy M4.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad3,
|
||||
MISOPinMode: PinSERCOM,
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -97,15 +97,8 @@ const (
|
||||
// SPI on the Metro M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad0SCK1,
|
||||
DIpad: sercomRXPad2,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
@@ -119,14 +112,7 @@ const (
|
||||
// SPI1 on the Metro M4 on pins 11,12,13
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI1_SCK_PIN,
|
||||
MOSI: SPI1_MOSI_PIN,
|
||||
MISO: SPI1_MISO_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
DIpad: sercomRXPad0,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -120,15 +120,9 @@ const (
|
||||
|
||||
// SPI on the PyBadge.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
DIpad: sercomRXPad2,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -136,15 +130,13 @@ var (
|
||||
const (
|
||||
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
|
||||
SPI1_MOSI_PIN = PB15 // MOSI: SERCOM4/PAD[3]
|
||||
SPI1_MISO_PIN = NoPin
|
||||
)
|
||||
|
||||
// TFT SPI on the PyBadge.
|
||||
var (
|
||||
SPI1 = SPI{Bus: sam.SERCOM4_SPIM,
|
||||
SCK: SPI1_SCK_PIN,
|
||||
MOSI: SPI1_MOSI_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
+207
-34
@@ -112,6 +112,162 @@ const (
|
||||
PB31 Pin = 63
|
||||
)
|
||||
|
||||
const (
|
||||
pinPadMapSERCOM0Pad0 uint16 = 0x1000
|
||||
pinPadMapSERCOM1Pad0 uint16 = 0x2000
|
||||
pinPadMapSERCOM2Pad0 uint16 = 0x3000
|
||||
pinPadMapSERCOM3Pad0 uint16 = 0x4000
|
||||
pinPadMapSERCOM4Pad0 uint16 = 0x5000
|
||||
pinPadMapSERCOM5Pad0 uint16 = 0x6000
|
||||
pinPadMapSERCOM6Pad0 uint16 = 0x7000
|
||||
pinPadMapSERCOM7Pad0 uint16 = 0x8000
|
||||
pinPadMapSERCOM0Pad2 uint16 = 0x1200
|
||||
pinPadMapSERCOM1Pad2 uint16 = 0x2200
|
||||
pinPadMapSERCOM2Pad2 uint16 = 0x3200
|
||||
pinPadMapSERCOM3Pad2 uint16 = 0x4200
|
||||
pinPadMapSERCOM4Pad2 uint16 = 0x5200
|
||||
pinPadMapSERCOM5Pad2 uint16 = 0x6200
|
||||
pinPadMapSERCOM6Pad2 uint16 = 0x7200
|
||||
pinPadMapSERCOM7Pad2 uint16 = 0x8200
|
||||
|
||||
pinPadMapSERCOM0AltPad0 uint16 = 0x0010
|
||||
pinPadMapSERCOM1AltPad0 uint16 = 0x0020
|
||||
pinPadMapSERCOM2AltPad0 uint16 = 0x0030
|
||||
pinPadMapSERCOM3AltPad0 uint16 = 0x0040
|
||||
pinPadMapSERCOM4AltPad0 uint16 = 0x0050
|
||||
pinPadMapSERCOM5AltPad0 uint16 = 0x0060
|
||||
pinPadMapSERCOM6AltPad0 uint16 = 0x0070
|
||||
pinPadMapSERCOM7AltPad0 uint16 = 0x0080
|
||||
pinPadMapSERCOM0AltPad1 uint16 = 0x0011
|
||||
pinPadMapSERCOM1AltPad1 uint16 = 0x0021
|
||||
pinPadMapSERCOM2AltPad1 uint16 = 0x0031
|
||||
pinPadMapSERCOM3AltPad1 uint16 = 0x0041
|
||||
pinPadMapSERCOM4AltPad1 uint16 = 0x0051
|
||||
pinPadMapSERCOM5AltPad1 uint16 = 0x0061
|
||||
pinPadMapSERCOM6AltPad1 uint16 = 0x0071
|
||||
pinPadMapSERCOM7AltPad1 uint16 = 0x0081
|
||||
pinPadMapSERCOM0AltPad2 uint16 = 0x0012
|
||||
pinPadMapSERCOM1AltPad2 uint16 = 0x0022
|
||||
pinPadMapSERCOM2AltPad2 uint16 = 0x0032
|
||||
pinPadMapSERCOM3AltPad2 uint16 = 0x0042
|
||||
pinPadMapSERCOM4AltPad2 uint16 = 0x0052
|
||||
pinPadMapSERCOM5AltPad2 uint16 = 0x0062
|
||||
pinPadMapSERCOM6AltPad2 uint16 = 0x0072
|
||||
pinPadMapSERCOM7AltPad2 uint16 = 0x0082
|
||||
)
|
||||
|
||||
// pinPadMapping lists which pins have which SERCOMs attached to them.
|
||||
// The encoding is rather dense, with each uint16 encoding two pins and both
|
||||
// SERCOM and SERCOM-ALT.
|
||||
//
|
||||
// Observations:
|
||||
// * There are eight SERCOMs. Those SERCOM numbers can be encoded in 4 bits.
|
||||
// * Even pad numbers are usually on even pins, and odd pad numbers are usually
|
||||
// on odd pins. The exception is SERCOM-ALT, which sometimes swaps pad 0 and 1.
|
||||
// With that, there is still an invariant that the pad number for an odd pin is
|
||||
// the pad number for the corresponding even pin with the low bit toggled.
|
||||
// * Pin pads come in pairs. If PA00 has pad 0, then PA01 has pad 1.
|
||||
// With this information, we can encode SERCOM pin/pad numbers much more
|
||||
// efficiently. Due to pads coming in pairs, we can ignore half the pins: the
|
||||
// information for an odd pin can be calculated easily from the preceding even
|
||||
// pin.
|
||||
//
|
||||
// Each word below is split in two bytes. The 8 high bytes are for SERCOM and
|
||||
// the 8 low bits are for SERCOM-ALT. Of each byte, the 4 high bits encode the
|
||||
// SERCOM + 1 while the two low bits encodes the pad number (the pad number for
|
||||
// the odd pin can be trivially calculated by toggling the low bit of the pad
|
||||
// number). It encodes SERCOM + 1 instead of just the SERCOM number, to make it
|
||||
// easy to check whether a nibble is set at all.
|
||||
//
|
||||
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
|
||||
var pinPadMapping = [32]uint16{
|
||||
// page 32
|
||||
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
|
||||
|
||||
// page 33
|
||||
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
|
||||
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
|
||||
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
|
||||
//PC04 / 2: pinPadMapSERCOM6Pad0 | 0,
|
||||
//PC06 / 2: pinPadMapSERCOM6Pad2 | 0,
|
||||
PA08 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
|
||||
PA10 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
PB10 / 2: 0 | pinPadMapSERCOM4AltPad2,
|
||||
PB12 / 2: pinPadMapSERCOM4Pad0 | 0,
|
||||
PB14 / 2: pinPadMapSERCOM4Pad2 | 0,
|
||||
//PD08 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
//PD10 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
//PC10 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
|
||||
// page 34
|
||||
//PC12 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
//PC14 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
PA12 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
|
||||
PA14 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
PA16 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
PA18 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
//PC16 / 2: pinPadMapSERCOM6Pad0 | pinPadMapSERCOM0AltPad1,
|
||||
//PC18 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM0AltPad2,
|
||||
//PC22 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
//PD20 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
|
||||
PB18 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
|
||||
// page 35
|
||||
PB20 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM7AltPad1,
|
||||
PA20 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PA22 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM5AltPad1,
|
||||
PA24 / 2: pinPadMapSERCOM3Pad2 | pinPadMapSERCOM5AltPad2,
|
||||
PB22 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM5AltPad2,
|
||||
PB24 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
|
||||
PB26 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
|
||||
PB28 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
//PC24 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
//PC26 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC26 doesn't support SERCOM, but PC27 does
|
||||
//PC28 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC29 doesn't exist in the datasheet?
|
||||
PA30 / 2: 0 | pinPadMapSERCOM1AltPad2,
|
||||
|
||||
// page 36
|
||||
PB30 / 2: 0 | pinPadMapSERCOM5AltPad1,
|
||||
PB00 / 2: 0 | pinPadMapSERCOM5AltPad2,
|
||||
PB02 / 2: 0 | pinPadMapSERCOM5AltPad0,
|
||||
}
|
||||
|
||||
// findPinPadMapping looks up the pad number and the pinmode for a given pin and
|
||||
// SERCOM number. The result can either be SERCOM, SERCOM-ALT, or "not found"
|
||||
// (indicated by returning ok=false). The pad number is returned to calculate
|
||||
// the DOPO/DIPO bitfields of the various serial peripherals.
|
||||
func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok bool) {
|
||||
bytes := pinPadMapping[pin/2]
|
||||
upper := byte(bytes >> 8)
|
||||
lower := byte(bytes & 0xff)
|
||||
|
||||
if upper != 0 {
|
||||
// SERCOM
|
||||
if (upper>>4)-1 == sercom {
|
||||
pinMode = PinSERCOM
|
||||
pad |= uint32(upper % 4)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if lower != 0 {
|
||||
// SERCOM-ALT
|
||||
if (lower>>4)-1 == sercom {
|
||||
pinMode = PinSERCOMAlt
|
||||
pad |= uint32(lower % 4)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// If the pin is uneven, toggle the lowest bit of the pad number.
|
||||
if pin&1 != 0 {
|
||||
pad ^= 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
@@ -501,11 +657,6 @@ const (
|
||||
sercomTXPad0 = 0 // Only for UART
|
||||
sercomTXPad2 = 1 // Only for UART
|
||||
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
|
||||
|
||||
spiTXPad0SCK1 = 0
|
||||
spiTXPad2SCK3 = 1
|
||||
spiTXPad3SCK1 = 2
|
||||
spiTXPad0SCK3 = 3
|
||||
)
|
||||
|
||||
// Configure the UART.
|
||||
@@ -938,15 +1089,8 @@ func (i2c I2C) readByte() byte {
|
||||
|
||||
// SPI
|
||||
type SPI struct {
|
||||
Bus *sam.SERCOM_SPIM_Type
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
DOpad int
|
||||
DIpad int
|
||||
SCKPinMode PinMode
|
||||
MOSIPinMode PinMode
|
||||
MISOPinMode PinMode
|
||||
Bus *sam.SERCOM_SPIM_Type
|
||||
SERCOM uint8
|
||||
}
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
@@ -960,34 +1104,61 @@ type SPIConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
doPad := spi.DOpad
|
||||
diPad := spi.DIpad
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
// Use default pins if not set.
|
||||
if config.SCK == 0 && config.MOSI == 0 && config.MISO == 0 {
|
||||
config.SCK = SPI0_SCK_PIN
|
||||
config.MOSI = SPI0_MOSI_PIN
|
||||
config.MISO = SPI0_MISO_PIN
|
||||
}
|
||||
|
||||
// set default frequency
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 4000000
|
||||
}
|
||||
|
||||
// Determine the input pinout (for MISO).
|
||||
var dataInPinout uint32
|
||||
misoPinMode, misoPad, ok := findPinPadMapping(spi.SERCOM, config.MISO)
|
||||
if config.MISO != NoPin {
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
dataInPinout = misoPad // mapped directly
|
||||
}
|
||||
|
||||
// Determine the output pinout (for MOSI/SCK).
|
||||
// See DOPO field in the CTRLA register on page 986 of the datasheet.
|
||||
var dataOutPinout uint32
|
||||
sckPinMode, sckPad, ok := findPinPadMapping(spi.SERCOM, config.SCK)
|
||||
if !ok || sckPad != 1 {
|
||||
// SCK pad must always be 1
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
mosiPinMode, mosiPad, ok := findPinPadMapping(spi.SERCOM, config.MOSI)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
switch mosiPad {
|
||||
case 0:
|
||||
dataOutPinout = 0x0
|
||||
case 3:
|
||||
dataOutPinout = 0x2
|
||||
default:
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// Disable SPI port.
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
if spi.SCKPinMode == 0 {
|
||||
spi.SCKPinMode = PinSERCOMAlt
|
||||
config.SCK.Configure(PinConfig{Mode: sckPinMode})
|
||||
config.MOSI.Configure(PinConfig{Mode: mosiPinMode})
|
||||
if config.MISO != NoPin {
|
||||
config.MISO.Configure(PinConfig{Mode: misoPinMode})
|
||||
}
|
||||
if spi.MOSIPinMode == 0 {
|
||||
spi.MOSIPinMode = PinSERCOMAlt
|
||||
}
|
||||
if spi.MISOPinMode == 0 {
|
||||
spi.MISOPinMode = PinSERCOMAlt
|
||||
}
|
||||
|
||||
spi.SCK.Configure(PinConfig{Mode: spi.SCKPinMode})
|
||||
spi.MOSI.Configure(PinConfig{Mode: spi.MOSIPinMode})
|
||||
spi.MISO.Configure(PinConfig{Mode: spi.MISOPinMode})
|
||||
|
||||
// reset SERCOM
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_SWRST)
|
||||
@@ -996,17 +1167,17 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
}
|
||||
|
||||
// set bit transfer order
|
||||
dataOrder := 0
|
||||
dataOrder := uint32(0)
|
||||
if config.LSBFirst {
|
||||
dataOrder = 1
|
||||
}
|
||||
|
||||
// Set SPI master
|
||||
// SERCOM_SPIM_CTRLA_MODE_SPI_MASTER = 3
|
||||
spi.Bus.CTRLA.Set(uint32((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
||||
(doPad << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
||||
(diPad << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos)))
|
||||
spi.Bus.CTRLA.Set((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
||||
(dataOutPinout << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
||||
(dataInPinout << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos))
|
||||
|
||||
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPIM_CTRLB_CHSIZE_Pos) | // 8bit char size
|
||||
sam.SERCOM_SPIM_CTRLB_RXEN) // receive enable
|
||||
@@ -1040,6 +1211,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
|
||||
Reference in New Issue
Block a user