diff --git a/src/sam3x8e/Kconfig b/src/sam3x8e/Kconfig index 92da8e58..b2dcb254 100644 --- a/src/sam3x8e/Kconfig +++ b/src/sam3x8e/Kconfig @@ -7,6 +7,7 @@ config SAM_SELECT default y select HAVE_GPIO select HAVE_GPIO_ADC + select HAVE_GPIO_SPI select HAVE_USER_INTERFACE config BOARD_DIRECTORY diff --git a/src/sam3x8e/Makefile b/src/sam3x8e/Makefile index 43aef609..289ffb2e 100644 --- a/src/sam3x8e/Makefile +++ b/src/sam3x8e/Makefile @@ -15,7 +15,7 @@ CFLAGS_klipper.elf += -T lib/cmsis-sam3x8e/source/gcc/sam3x8e_flash.ld CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs # Add source files -src-y += sam3x8e/main.c sam3x8e/timer.c sam3x8e/gpio.c +src-y += sam3x8e/main.c sam3x8e/timer.c sam3x8e/gpio.c sam3x8e/spi.c src-y += generic/crc16_ccitt.c generic/alloc.c src-y += generic/armcm_irq.c generic/timer_irq.c src-y += ../lib/cmsis-sam3x8e/source/system_sam3xa.c diff --git a/src/sam3x8e/gpio.h b/src/sam3x8e/gpio.h index 0c75f642..1cab4614 100644 --- a/src/sam3x8e/gpio.h +++ b/src/sam3x8e/gpio.h @@ -28,4 +28,11 @@ uint32_t gpio_adc_sample(struct gpio_adc g); uint16_t gpio_adc_read(struct gpio_adc g); void gpio_adc_cancel_sample(struct gpio_adc g); +struct spi_config { + uint32_t cfg; +}; +struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate); +void spi_transfer(struct spi_config config, uint8_t receive_data + , uint8_t len, uint8_t *data); + #endif // gpio.h diff --git a/src/sam3x8e/spi.c b/src/sam3x8e/spi.c new file mode 100644 index 00000000..791d71b3 --- /dev/null +++ b/src/sam3x8e/spi.c @@ -0,0 +1,123 @@ +// SPI transmissions on sam3x8e +// +// Copyright (C) 2018 Petri Honkala +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // NULL +#include "autoconf.h" +#include "command.h" // shutdown +#include "gpio.h" +#include "sched.h" +#include +#include + +#define REGPTR SPI0 +#define PERIPH_ID ID_SPI0 + +#define CHANNEL 0 // Use same channel for all + +static void +spi_init(void) +{ + /* Configure SCK, MISO and MOSI */ + gpio_peripheral('A', PIO_PA25A_SPI0_MISO, 'A', 0); // Arduino 74 + gpio_peripheral('A', PIO_PA26A_SPI0_MOSI, 'A', 0); // Arduino 75 + gpio_peripheral('A', PIO_PA27A_SPI0_SPCK, 'A', 0); // Arduino 76 + + // Enable SPI clocks + if (!(PMC->PMC_PCSR0 & (1u << PERIPH_ID))) { + PMC->PMC_PCER0 = (1 << PERIPH_ID); + } + + /* Disable SPI */ + REGPTR->SPI_CR = SPI_CR_SPIDIS; + /* Execute a software reset of the SPI twice */ + REGPTR->SPI_CR = SPI_CR_SWRST; + REGPTR->SPI_CR = SPI_CR_SWRST; + + REGPTR->SPI_MR = ( SPI_MR_MSTR | // Set master mode + SPI_MR_MODFDIS | // Disable fault detection + SPI_MR_PCS(CHANNEL) // Fixes peripheral select + ); + REGPTR->SPI_IDR = 0xffffffff; // Disable ISRs + + /* Clear Chip Select Registers */ + REGPTR->SPI_CSR[0] = 0; + REGPTR->SPI_CSR[1] = 0; + REGPTR->SPI_CSR[2] = 0; + REGPTR->SPI_CSR[3] = 0; + + /* Set basic channel config */ + REGPTR->SPI_CSR[CHANNEL] = 0; + /* Enable SPI */ + REGPTR->SPI_CR = SPI_CR_SPIEN; +} + +struct spi_config +spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) +{ + if (bus != CHANNEL || mode > 3) + shutdown("Invalid spi_setup parameters"); + + // Make sure bus is enabled + spi_init(); + + uint32_t config = 0; + uint32_t clockDiv; + if (rate < (CHIP_FREQ_CPU_MAX / 255)) { + clockDiv = 255; + } else if (rate >= (CHIP_FREQ_CPU_MAX / 2)) { + clockDiv = 2; + } else { + clockDiv = (CHIP_FREQ_CPU_MAX / (rate + 1)) + 1; + } + + /****** Will be written to SPI_CSRx register ******/ + // CSAAT : Chip Select Active After Transfer + config = SPI_CSR_CSAAT; + config |= SPI_CSR_BITS_8_BIT; // TODO: support for SPI_CSR_BITS_16_BIT + // NOTE: NCPHA is inverted, CPHA normal!! + switch(mode) { + case 0: + config |= SPI_CSR_NCPHA; + break; + case 1: + config |= 0; + break; + case 2: + config |= SPI_CSR_NCPHA; + config |= SPI_CSR_CPOL; + break; + case 3: + config |= SPI_CSR_CPOL; + break; + }; + + config |= ((clockDiv & 0xffu) << SPI_CSR_SCBR_Pos); + return (struct spi_config){.cfg = config}; +} + +void +spi_transfer(struct spi_config config, uint8_t receive_data + , uint8_t len, uint8_t *data) +{ + REGPTR->SPI_CSR[CHANNEL] = config.cfg; + + Spi* const pSpi = REGPTR; + if (receive_data) { + while (len--) { + pSpi->SPI_TDR = *data; + // wait for receive register + while (!(pSpi->SPI_SR & SPI_SR_RDRF)) { asm volatile("nop"); }; + // get data + *data++ = pSpi->SPI_RDR; + } + } else { + while (len--) { + pSpi->SPI_TDR = *data++; + // wait for receive register + while (!(pSpi->SPI_SR & SPI_SR_RDRF)) { asm volatile("nop"); }; + } + } +}