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.
Этот коммит содержится в:
родитель
7ace991a2b
коммит
31d3ac725f
5 изменённых файлов: 224 добавлений и 83 удалений
|
@ -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,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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.
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче