From 665c3bdaa65678e1905c16e63ee6c71aa4d6172c Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Mon, 4 Mar 2019 21:04:25 +0100 Subject: [PATCH] machine/samd21: implement SPI interface for currently supported SAMD21 boards Signed-off-by: Ron Evans --- src/machine/board_circuitplay_express.go | 23 +++- src/machine/board_itsybitsy-m0.go | 12 +++ src/machine/machine_atsamd21.go | 132 +++++++++++++++++++++-- src/machine/spi.go | 2 +- 4 files changed, 154 insertions(+), 15 deletions(-) diff --git a/src/machine/board_circuitplay_express.go b/src/machine/board_circuitplay_express.go index d1e02b3f..702ad90b 100644 --- a/src/machine/board_circuitplay_express.go +++ b/src/machine/board_circuitplay_express.go @@ -53,13 +53,13 @@ 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 @@ -67,8 +67,11 @@ const ( // 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. @@ -76,3 +79,15 @@ 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} +) diff --git a/src/machine/board_itsybitsy-m0.go b/src/machine/board_itsybitsy-m0.go index d823a8d1..29293dfc 100644 --- a/src/machine/board_itsybitsy-m0.go +++ b/src/machine/board_itsybitsy-m0.go @@ -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} +) diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go index b71be37d..b3dc1b6f 100644 --- a/src/machine/machine_atsamd21.go +++ b/src/machine/machine_atsamd21.go @@ -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 @@ -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 ( @@ -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. @@ -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 @@ -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< 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 diff --git a/src/machine/spi.go b/src/machine/spi.go index beaa2e46..83f16d5e 100644 --- a/src/machine/spi.go +++ b/src/machine/spi.go @@ -1,4 +1,4 @@ -// +build nrf stm32f103xx +// +build nrf stm32f103xx atsamd21g18a package machine