sam3x8e: Add SPI support

Signed-off-by: Petri Honkala <cruwaller@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2018-05-04 12:47:26 -04:00
parent 22487d95e9
commit 31ae74c56c
4 changed files with 132 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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

123
src/sam3x8e/spi.c Normal file
View File

@ -0,0 +1,123 @@
// SPI transmissions on sam3x8e
//
// Copyright (C) 2018 Petri Honkala <cruwaller@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <stddef.h> // NULL
#include "autoconf.h"
#include "command.h" // shutdown
#include "gpio.h"
#include "sched.h"
#include <sam3x8e.h>
#include <string.h>
#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"); };
}
}
}