Skip to content

Commit

Permalink
machine/samd21: implement SPI interface for currently supported SAMD2…
Browse files Browse the repository at this point in the history
…1 boards

Signed-off-by: Ron Evans <ron@hybridgroup.com>
  • Loading branch information
deadprogram authored and aykevl committed Mar 4, 2019
1 parent ea3d232 commit 665c3bd
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 15 deletions.
23 changes: 19 additions & 4 deletions src/machine/board_circuitplay_express.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,41 @@ const (
PROXIMITY = A10
)

// USBCDC pins
// USBCDC pins (logical UART0)
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)

// UART0 pins
// UART0 pins (logical UART1)
const (
UART_TX_PIN = PB08 // PORTB
UART_RX_PIN = PB09 // PORTB
)

// I2C pins
const (
SDA_PIN = PA00 // SDA: SERCOM3/PAD[0]
SCL_PIN = PA01 // SCL: SERCOM3/PAD[1]
SDA_PIN = PB02 // I2C0 external
SCL_PIN = PB03 // I2C0 external

SDA1_PIN = PA00 // I2C1 internal
SCL1_PIN = PA01 // I2C1 internal
)

// I2C on the Circuit Playground Express.
var (
I2C0 = I2C{Bus: sam.SERCOM5_I2CM} // external device
I2C1 = I2C{Bus: sam.SERCOM1_I2CM} // internal device
)

// SPI pins (internal flash)
const (
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
SPI0_MOSI_PIN = PA20 // MOSI: SERCOM3/PAD[2]
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
)

// SPI on the Circuit Playground Express.
var (
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
)
12 changes: 12 additions & 0 deletions src/machine/board_itsybitsy-m0.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,15 @@ const (
var (
I2C0 = I2C{Bus: sam.SERCOM3_I2CM}
)

// SPI pins
const (
SPI0_SCK_PIN = PB11 // SCK: SERCOM4/PAD[3]
SPI0_MOSI_PIN = PB10 // MOSI: SERCOM4/PAD[2]
SPI0_MISO_PIN = PA12 // MISO: SERCOM4/PAD[0]
)

// SPI on the ItsyBitsy M0.
var (
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
)
132 changes: 122 additions & 10 deletions src/machine/machine_atsamd21.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ func (p GPIO) Configure(config GPIOConfig) {
// enable port config
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)

case GPIO_SERCOM_ALT:
if p.Pin&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
p.setPMux(val | (GPIO_SERCOM_ALT << sam.PORT_PMUX0_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
p.setPMux(val | (GPIO_SERCOM_ALT << sam.PORT_PMUX0_PMUXE_Pos))
}
// enable port config
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)

case GPIO_COM:
if p.Pin&1 > 0 {
// odd pin, so save the even pins
Expand Down Expand Up @@ -257,9 +270,6 @@ var (

// The first hardware serial port on the SAMD21. Uses the SERCOM0 interface.
UART1 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()}

// The second hardware serial port on the SAMD21. Uses the SERCOM1 interface.
UART2 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()}
)

const (
Expand All @@ -272,6 +282,11 @@ 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.
Expand Down Expand Up @@ -408,13 +423,6 @@ func handleUART1() {
UART1.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
}

//go:export SERCOM1_IRQHandler
func handleUART2() {
// should reset IRQ
UART2.Receive(byte((UART2.Bus.DATA & 0xFF)))
UART2.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
}

// I2C on the SAMD21.
type I2C struct {
Bus *sam.SERCOM_I2CM_Type
Expand Down Expand Up @@ -644,6 +652,110 @@ func (i2c I2C) readByte() byte {
return byte(i2c.Bus.DATA)
}

// SPI
type SPI struct {
Bus *sam.SERCOM_SPI_Type
}

// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
SCK uint8
MOSI uint8
MISO uint8
LSBFirst bool
Mode uint8
}

// Configure is intended to setup the SPI interface.
func (spi SPI) Configure(config SPIConfig) {
config.SCK = SPI0_SCK_PIN
config.MOSI = SPI0_MOSI_PIN
config.MISO = SPI0_MISO_PIN

doPad := spiTXPad2SCK3
diPad := sercomRXPad0

// set default frequency
if config.Frequency == 0 {
config.Frequency = 4000000
}

// Disable SPI port.
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_ENABLE
for (spi.Bus.SYNCBUSY & sam.SERCOM_SPI_SYNCBUSY_ENABLE) > 0 {
}

// enable pins
GPIO{config.SCK}.Configure(GPIOConfig{Mode: GPIO_SERCOM_ALT})
GPIO{config.MOSI}.Configure(GPIOConfig{Mode: GPIO_SERCOM_ALT})
GPIO{config.MISO}.Configure(GPIOConfig{Mode: GPIO_SERCOM_ALT})

// reset SERCOM
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_SWRST
for (spi.Bus.CTRLA&sam.SERCOM_SPI_CTRLA_SWRST) > 0 ||
(spi.Bus.SYNCBUSY&sam.SERCOM_SPI_SYNCBUSY_SWRST) > 0 {
}

// set bit transfer order
dataOrder := 0
if config.LSBFirst {
dataOrder = 1
}

// Set SPI master
spi.Bus.CTRLA = (sam.SERCOM_SPI_CTRLA_MODE_SPI_MASTER << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
sam.RegValue(doPad<<sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
sam.RegValue(diPad<<sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
sam.RegValue(dataOrder<<sam.SERCOM_SPI_CTRLA_DORD_Pos)

spi.Bus.CTRLB |= (0 << sam.SERCOM_SPI_CTRLB_CHSIZE_Pos) | // 8bit char size
sam.SERCOM_SPI_CTRLB_RXEN // receive enable

for (spi.Bus.SYNCBUSY & sam.SERCOM_SPI_SYNCBUSY_CTRLB) > 0 {
}

// set mode
switch config.Mode {
case 0:
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPOL
case 1:
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPOL
case 2:
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_CPOL
case 3:
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_CPHA | sam.SERCOM_SPI_CTRLA_CPOL
default: // to mode 0
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPOL
}

// Set synch speed for SPI
baudRate := (CPU_FREQUENCY / (2 * config.Frequency)) - 1
spi.Bus.BAUD = sam.RegValue8(baudRate)

// Enable SPI port.
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_ENABLE
for (spi.Bus.SYNCBUSY & sam.SERCOM_SPI_SYNCBUSY_ENABLE) > 0 {
}
}

// Transfer writes/reads a single byte using the SPI interface.
func (spi SPI) Transfer(w byte) (byte, error) {
// write data
spi.Bus.DATA = sam.RegValue(w)

// wait for receive
for (spi.Bus.INTFLAG & sam.SERCOM_SPI_INTFLAG_RXC) == 0 {
}

// return data
return byte(spi.Bus.DATA), nil
}

// PWM
const period = 0xFFFF

Expand Down
2 changes: 1 addition & 1 deletion src/machine/spi.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build nrf stm32f103xx
// +build nrf stm32f103xx atsamd21g18a

package machine

Expand Down

0 comments on commit 665c3bd

Please sign in to comment.