diff --git a/boards/arduino-zero/include/periph_conf.h b/boards/arduino-zero/include/periph_conf.h index 177882619b..4b17f99498 100644 --- a/boards/arduino-zero/include/periph_conf.h +++ b/boards/arduino-zero/include/periph_conf.h @@ -183,23 +183,21 @@ static const pwm_conf_t pwm_config[] = { * @name SPI configuration * @{ */ -#define SPI_NUMOF (1) -#define SPI_0_EN 1 - -/* SPI0 */ -#define SPI_0_DEV SERCOM4->SPI -#define SPI_IRQ_0 SERCOM4_IRQn -#define SPI_0_GCLK_ID SERCOM4_GCLK_ID_CORE -/* SPI 0 pin configuration */ -#define SPI_0_SCLK GPIO_PIN(PB, 11) -#define SPI_0_SCLK_MUX GPIO_MUX_D -#define SPI_0_MISO GPIO_PIN(PA, 12) -#define SPI_0_MISO_MUX GPIO_MUX_D -#define SPI_0_MISO_PAD SPI_PAD_MISO_0 -#define SPI_0_MOSI GPIO_PIN(PB, 10) -#define SPI_0_MOSI_MUX GPIO_MUX_D -#define SPI_0_MOSI_PAD SPI_PAD_MOSI_2_SCK_3 +static const spi_conf_t spi_config[] = { + { + .dev = &SERCOM4->SPI, + .miso_pin = GPIO_PIN(PA, 12), + .mosi_pin = GPIO_PIN(PB, 10), + .clk_pin = GPIO_PIN(PB, 11), + .miso_mux = GPIO_MUX_D, + .mosi_mux = GPIO_MUX_D, + .clk_mux = GPIO_MUX_D, + .miso_pad = SPI_PAD_MISO_0, + .mosi_pad = SPI_PAD_MOSI_2_SCK_3 + } +}; +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ /** diff --git a/boards/saml21-xpro/include/periph_conf.h b/boards/saml21-xpro/include/periph_conf.h index aedcae7c99..b4d2d2d044 100644 --- a/boards/saml21-xpro/include/periph_conf.h +++ b/boards/saml21-xpro/include/periph_conf.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Kaspar Schleiser * 2015 FreshTemp, LLC. - * 2014 Freie Universität Berlin + * 2014-2016 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -16,12 +16,15 @@ * @brief Peripheral MCU configuration for the Atmel SAM L21 Xplained Pro board * * @author Thomas Eichinger - * @autor Kaspar Schleiser + * @author Kaspar Schleiser + * @author Hauke Petersen */ #ifndef PERIPH_CONF_H #define PERIPH_CONF_H +#include "periph_cpu.h" + #ifdef __cplusplus extern "C" { #endif @@ -29,7 +32,7 @@ extern "C" { /** * @brief GCLK reference speed */ -#define GCLK_REF (16000000U) +#define CLOCK_CORECLOCK (16000000U) /** * @name Timer peripheral configuration @@ -71,8 +74,22 @@ extern "C" { * @name SPI configuration * @{ */ -#define SPI_NUMOF (1) -#define SPI_0_EN 1 +static const spi_conf_t spi_config[] = { + { + .dev = &(SERCOM0->SPI), + .miso_pin = GPIO_PIN(PA, 4), + .mosi_pin = GPIO_PIN(PA, 6), + .clk_pin = GPIO_PIN(PA, 7), + .miso_mux = GPIO_MUX_D, + .mosi_mux = GPIO_MUX_D, + .clk_mux = GPIO_MUX_D, + .miso_pad = SPI_PAD_MISO_0, + .mosi_pad = SPI_PAD_MOSI_2_SCK_3 + + } +}; + +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ /** diff --git a/boards/samr21-xpro/include/board.h b/boards/samr21-xpro/include/board.h index 418a4f726d..373cb35e34 100644 --- a/boards/samr21-xpro/include/board.h +++ b/boards/samr21-xpro/include/board.h @@ -43,8 +43,8 @@ extern "C" { * * {spi bus, spi speed, cs pin, int pin, reset pin, sleep pin} */ -#define AT86RF2XX_PARAMS_BOARD {.spi = SPI_0, \ - .spi_speed = SPI_SPEED_5MHZ, \ +#define AT86RF2XX_PARAMS_BOARD {.spi = SPI_DEV(0), \ + .spi_clk = SPI_CLK_5MHZ, \ .cs_pin = GPIO_PIN(PB, 31), \ .int_pin = GPIO_PIN(PB, 0), \ .sleep_pin = GPIO_PIN(PA, 20), \ diff --git a/boards/samr21-xpro/include/periph_conf.h b/boards/samr21-xpro/include/periph_conf.h index 9834707849..29f47cd2cd 100644 --- a/boards/samr21-xpro/include/periph_conf.h +++ b/boards/samr21-xpro/include/periph_conf.h @@ -168,37 +168,32 @@ static const pwm_conf_t pwm_config[] = { * @name SPI configuration * @{ */ -#define SPI_NUMOF (2) -#define SPI_0_EN 1 -#define SPI_1_EN 1 +static const spi_conf_t spi_config[] = { + { + .dev = &SERCOM4->SPI, + .miso_pin = GPIO_PIN(PC, 19), + .mosi_pin = GPIO_PIN(PB, 30), + .clk_pin = GPIO_PIN(PC, 18), + .miso_mux = GPIO_MUX_F, + .mosi_mux = GPIO_MUX_F, + .clk_mux = GPIO_MUX_F, + .miso_pad = SPI_PAD_MISO_0, + .mosi_pad = SPI_PAD_MOSI_2_SCK_3 + }, + { + .dev = &SERCOM5->SPI, + .miso_pin = GPIO_PIN(PB, 2), + .mosi_pin = GPIO_PIN(PB, 22), + .clk_pin = GPIO_PIN(PB, 23), + .miso_mux = GPIO_MUX_D, + .mosi_mux = GPIO_MUX_D, + .clk_mux = GPIO_MUX_D, + .miso_pad = SPI_PAD_MISO_0, + .mosi_pad = SPI_PAD_MOSI_2_SCK_3 + } +}; -/* SPI0 */ -#define SPI_0_DEV SERCOM4->SPI -#define SPI_IRQ_0 SERCOM4_IRQn -#define SPI_0_GCLK_ID SERCOM4_GCLK_ID_CORE -/* SPI 0 pin configuration */ -#define SPI_0_SCLK GPIO_PIN(PC, 18) -#define SPI_0_SCLK_MUX GPIO_MUX_F -#define SPI_0_MISO GPIO_PIN(PC, 19) -#define SPI_0_MISO_MUX GPIO_MUX_F -#define SPI_0_MISO_PAD SPI_PAD_MISO_0 -#define SPI_0_MOSI GPIO_PIN(PB, 30) -#define SPI_0_MOSI_MUX GPIO_MUX_F -#define SPI_0_MOSI_PAD SPI_PAD_MOSI_2_SCK_3 - -/* SPI1 */ -#define SPI_1_DEV SERCOM5->SPI -#define SPI_IRQ_1 SERCOM5_IRQn -#define SPI_1_GCLK_ID SERCOM5_GCLK_ID_CORE -/* SPI 1 pin configuration */ -#define SPI_1_SCLK GPIO_PIN(PB, 23) -#define SPI_1_SCLK_MUX GPIO_MUX_D -#define SPI_1_MISO GPIO_PIN(PB, 02) -#define SPI_1_MISO_MUX GPIO_MUX_D -#define SPI_1_MISO_PAD SPI_PAD_MISO_0 -#define SPI_1_MOSI GPIO_PIN(PB, 22) -#define SPI_1_MOSI_MUX GPIO_MUX_D -#define SPI_1_MOSI_PAD SPI_PAD_MOSI_2_SCK_3 +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ /** diff --git a/boards/sodaq-autonomo/include/periph_conf.h b/boards/sodaq-autonomo/include/periph_conf.h index a5793881e1..456196ef0d 100644 --- a/boards/sodaq-autonomo/include/periph_conf.h +++ b/boards/sodaq-autonomo/include/periph_conf.h @@ -133,7 +133,7 @@ static const uart_conf_t uart_config[] = { .mux = GPIO_MUX_C, .rx_pad = UART_PAD_RX_1, .tx_pad = UART_PAD_TX_2, - }, + } }; /* interrupt function name mapping */ @@ -184,27 +184,21 @@ static const pwm_conf_t pwm_config[] = { * @name SPI configuration * @{ */ -#define SPI_NUMOF (1) -#define SPI_0_EN 1 -#define SPI_1_EN 0 - -/* SPI0 */ -#define SPI_0_DEV SERCOM3->SPI -#define SPI_IRQ_0 SERCOM3_IRQn -#define SPI_0_GCLK_ID SERCOM3_GCLK_ID_CORE -/* SPI 0 pin configuration */ -#define SPI_0_SCLK GPIO_PIN(PA, 21) -#define SPI_0_SCLK_MUX GPIO_MUX_D -#define SPI_0_MISO GPIO_PIN(PA, 22) -#define SPI_0_MISO_MUX GPIO_MUX_C -#define SPI_0_MISO_PAD SPI_PAD_MISO_0 -#define SPI_0_MOSI GPIO_PIN(PA, 20) -#define SPI_0_MOSI_MUX GPIO_MUX_D -#define SPI_0_MOSI_PAD SPI_PAD_MOSI_2_SCK_3 - -// How/where do we define SS? -#define SPI_0_SS GPIO_PIN(PA, 23) +static const spi_conf_t spi_config[] = { + { + .dev = &SERCOM3->SPI, + .miso_pin = GPIO_PIN(PA, 22), + .mosi_pin = GPIO_PIN(PA, 20), + .clk_pin = GPIO_PIN(PA, 21), + .miso_mux = GPIO_MUX_C, + .mosi_mux = GPIO_MUX_D, + .clk_mux = GPIO_MUX_D, + .miso_pad = SPI_PAD_MISO_0, + .mosi_pad = SPI_PAD_MOSI_2_SCK_3, + }, +}; +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ /** diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 721ca0da1f..954877d0b9 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -34,7 +34,8 @@ extern "C" { * @brief Use shared SPI functions * @{ */ -#define PERIPH_SPI_NEEDS_TRANSFER_BYTES +#define PERIPH_SPI_NEEDS_INIT_CS +#define PERIPH_SPI_NEEDS_TRANSFER_BYTE #define PERIPH_SPI_NEEDS_TRANSFER_REG #define PERIPH_SPI_NEEDS_TRANSFER_REGS /** @} */ @@ -127,15 +128,46 @@ typedef enum { } spi_mosipad_t; /** - * @brief Possible selections for SERCOM SPI clock mode (inspired by Arduino) + * @brief Override SPI modes + * @{ */ -typedef enum -{ - SERCOM_SPI_MODE_0 = 0, // CPOL : 0 | CPHA : 0 - SERCOM_SPI_MODE_1 = 1, // CPOL : 0 | CPHA : 1 - SERCOM_SPI_MODE_2 = 2, // CPOL : 1 | CPHA : 0 - SERCOM_SPI_MODE_3 = 3, // CPOL : 1 | CPHA : 1 -} sercom_spi_clockmode_t; +#define HAVE_SPI_MODE_T +typedef enum { + SPI_MODE_0 = 0x0, /**< CPOL=0, CPHA=0 */ + SPI_MODE_1 = 0x1, /**< CPOL=0, CPHA=1 */ + SPI_MODE_2 = 0x2, /**< CPOL=1, CPHA=0 */ + SPI_MODE_3 = 0x3 /**< CPOL=1, CPHA=1 */ +} spi_mode_t; +/** @} */ + +/** + * @brief Override SPI clock speed values + * @{ + */ +#define HAVE_SPI_CLK_T +typedef enum { + SPI_CLK_100KHZ = 100000U, /**< drive the SPI bus with 100KHz */ + SPI_CLK_400KHZ = 400000U, /**< drive the SPI bus with 400KHz */ + SPI_CLK_1MHZ = 1000000U, /**< drive the SPI bus with 1MHz */ + SPI_CLK_5MHZ = 5000000U, /**< drive the SPI bus with 5MHz */ + SPI_CLK_10MHZ = 10000000U /**< drive the SPI bus with 10MHz */ +} spi_clk_t; +/** @} */ + +/** + * @brief SPI device configuration + */ +typedef struct { + SercomSpi *dev; /**< pointer to the used SPI device */ + gpio_t miso_pin; /**< used MISO pin */ + gpio_t mosi_pin; /**< used MOSI pin */ + gpio_t clk_pin; /**< used CLK pin */ + gpio_mux_t miso_mux; /**< alternate function for MISO pin (mux) */ + gpio_mux_t mosi_mux; /**< alternate function for MOSI pin (mux) */ + gpio_mux_t clk_mux; /**< alternate function for CLK pin (mux) */ + spi_misopad_t miso_pad; /**< pad to use for MISO line */ + spi_mosipad_t mosi_pad; /**< pad to use for MOSI and CLK line */ +} spi_conf_t; /** * @brief Set up alternate function (PMUX setting) for a PORT pin @@ -145,6 +177,18 @@ typedef enum */ void gpio_init_mux(gpio_t pin, gpio_mux_t mux); +/** + * @brief Return the numeric id of a SERCOM device derived from its address + * + * @param[in] sercom SERCOM device + * + * @return numeric id of the given SERCOM device + */ +static inline int sercom_id(void *sercom) +{ + return ((((uint32_t)sercom) >> 10) & 0x7) - 2; +} + #ifdef __cplusplus } #endif diff --git a/cpu/sam0_common/periph/spi.c b/cpu/sam0_common/periph/spi.c new file mode 100644 index 0000000000..65b78e78c3 --- /dev/null +++ b/cpu/sam0_common/periph/spi.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2014-2016 Freie Universität Berlin + * 2015 Kaspar Schleiser + * 2015 FreshTemp, LLC. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_samd21 + * @{ + * + * @file + * @brief Low-level SPI driver implementation + * + * @author Thomas Eichinger + * @author Troels Hoffmeyer + * @author Hauke Petersen + * @author Joakim Nohlgård + * @author Kaspar Schleiser + * + * @} + */ + +#include "cpu.h" +#include "mutex.h" +#include "assert.h" +#include "periph/spi.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Array holding one pre-initialized mutex for each SPI device + */ +static mutex_t locks[SPI_NUMOF]; + +/** + * @brief Shortcut for accessing the used SPI SERCOM device + */ +static inline SercomSpi *dev(spi_t bus) +{ + return spi_config[bus].dev; +} + +static inline void poweron(spi_t bus) +{ +#if defined(CPU_FAM_SAMD21) + PM->APBCMASK.reg |= (PM_APBCMASK_SERCOM0 << sercom_id(dev(bus))); +#elif defined(CPU_FAM_SAML21) + MCLK->APBCMASK.reg |= (MCLK_APBCMASK_SERCOM0 << sercom_id(dev(bus))); +#endif +} + +static inline void poweroff(spi_t bus) +{ +#if defined(CPU_FAM_SAMD21) + PM->APBCMASK.reg &= ~(PM_APBCMASK_SERCOM0 << sercom_id(dev(bus))); +#elif defined(CPU_FAM_SAML21) + MCLK->APBCMASK.reg &= ~(MCLK_APBCMASK_SERCOM0 << sercom_id(dev(bus))); +#endif +} + +void spi_init(spi_t bus) +{ + /* make sure given bus is good */ + assert(bus < SPI_NUMOF); + + /* initialize the device lock */ + mutex_init(&locks[bus]); + + /* configure pins and their muxes */ + spi_init_pins(bus); + + /* wake up device */ + poweron(bus); + + /* reset all device configuration */ + dev(bus)->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST; + while ((dev(bus)->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) || + (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_SWRST)); + + /* configure base clock: using GLK GEN 0 */ +#if defined(CPU_FAM_SAMD21) + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | + (SERCOM0_GCLK_ID_CORE + sercom_id(dev(bus)))); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} +#elif defined(CPU_FAM_SAML21) + GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE + sercom_id(dev(bus))].reg = + (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0); +#endif + + /* enable receiver and configure character size to 8-bit + * no synchronization needed, as SERCOM device is not enabled */ + dev(bus)->CTRLB.reg = (SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN); + + /* put device back to sleep */ + poweroff(bus); +} + +void spi_init_pins(spi_t bus) +{ + gpio_init(spi_config[bus].miso_pin, GPIO_IN); + gpio_init(spi_config[bus].mosi_pin, GPIO_OUT); + gpio_init(spi_config[bus].clk_pin, GPIO_OUT); + gpio_init_mux(spi_config[bus].miso_pin, spi_config[bus].miso_mux); + gpio_init_mux(spi_config[bus].mosi_pin, spi_config[bus].mosi_mux); + gpio_init_mux(spi_config[bus].clk_pin, spi_config[bus].clk_mux); +} + +int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) +{ + /* get exclusive access to the device */ + mutex_lock(&locks[bus]); + /* power on the device */ + poweron(bus); + + /* configure bus clock, in synchronous mode its calculated from + * BAUD.reg = (f_ref / (2 * f_bus) - 1) + * with f_ref := CLOCK_CORECLOCK as defined by the board */ + dev(bus)->BAUD.reg = (uint8_t)(((uint32_t)CLOCK_CORECLOCK) / (2 * clk) - 1); + + /* configure device to be master and set mode and pads, + * + * NOTE: we could configure the pads already during spi_init, but for + * efficiency reason we do that here, so we can do all in one single write + * to the CTRLA register */ + dev(bus)->CTRLA.reg = (SERCOM_SPI_CTRLA_MODE(0x3) | /* 0x3 -> master */ + SERCOM_SPI_CTRLA_DOPO(spi_config[bus].mosi_pad) | + SERCOM_SPI_CTRLA_DIPO(spi_config[bus].miso_pad) | + (mode << SERCOM_SPI_CTRLA_CPOL_Pos)); + /* also no synchronization needed here, as CTRLA is write-synchronized */ + + /* finally enable the device */ + dev(bus)->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; + while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {} + + return SPI_OK; +} + +void spi_release(spi_t bus) +{ + /* disable device and put it back to sleep */ + dev(bus)->CTRLA.reg &= ~(SERCOM_SPI_CTRLA_ENABLE); + while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {} + poweroff(bus); + /* release access to the device */ + mutex_unlock(&locks[bus]); +} + +void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, + const void *out, void *in, size_t len) +{ + uint8_t *out_buf = (uint8_t *)out; + uint8_t *in_buf = (uint8_t *)in; + + assert(out || in); + + if (cs != SPI_CS_UNDEF) { + gpio_clear((gpio_t)cs); + } + + for (int i = 0; i < (int)len; i++) { + uint8_t tmp = (out_buf) ? out_buf[i] : 0; + while (!(dev(bus)->INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE)) {} + dev(bus)->DATA.reg = tmp; + while (!(dev(bus)->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) {} + tmp = (uint8_t)dev(bus)->DATA.reg; + if (in_buf) { + in_buf[i] = tmp; + } + } + + if ((!cont) && (cs != SPI_CS_UNDEF)) { + gpio_set((gpio_t)cs); + } +} diff --git a/cpu/samd21/cpu.c b/cpu/samd21/cpu.c index cef8383a01..8f0ebff4fc 100644 --- a/cpu/samd21/cpu.c +++ b/cpu/samd21/cpu.c @@ -107,4 +107,6 @@ void cpu_init(void) cortexm_init(); /* Initialise clock sources and generic clocks */ clk_init(); + /* trigger static peripheral initialization */ + periph_init(); } diff --git a/cpu/samd21/include/periph_cpu.h b/cpu/samd21/include/periph_cpu.h index 14ad374ebe..97dc3a2140 100644 --- a/cpu/samd21/include/periph_cpu.h +++ b/cpu/samd21/include/periph_cpu.h @@ -19,6 +19,8 @@ #ifndef CPU_PERIPH_H #define CPU_PERIPH_H +#include + #include "periph_cpu_common.h" #ifdef __cplusplus @@ -44,6 +46,13 @@ enum { */ #define GPIO_MODE(pr, ie, pe) (pr | (ie << 1) | (pe << 2)) +/** + * @brief Override SPI hardware chip select macro + * + * As of now, we do not support HW CS, so we always set it to a fixed value + */ +#define SPI_HWCS(x) (UINT_MAX - 1) + #ifndef DOXYGEN /** * @brief Override GPIO modes diff --git a/cpu/samd21/periph/spi.c b/cpu/samd21/periph/spi.c deleted file mode 100644 index f7eef079f6..0000000000 --- a/cpu/samd21/periph/spi.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License v2.1. See the file LICENSE in the top level directory for more - * details. - */ - -/** - * @ingroup cpu_samd21 - * @{ - * - * @file - * @brief Low-level SPI driver implementation - * - * @author Thomas Eichinger - * @author Troels Hoffmeyer - * @author Hauke Petersen - * @author Joakim Nohlgård - * - * @} - */ - -#include "cpu.h" -#include "mutex.h" -#include "periph/gpio.h" -#include "periph/spi.h" -#include "periph_conf.h" -#include "board.h" -#define ENABLE_DEBUG (0) -#include "debug.h" - -#if SPI_0_EN || SPI_1_EN - -/** - * @brief Internal helper function to do the actual work for spi_poweroff - */ -static void _spi_poweroff(SercomSpi* spi_dev); - -/** - * @brief Internal helper function to do the actual work for spi_poweron - */ -static void _spi_poweron(SercomSpi* spi_dev); - -/** - * @brief Array holding one pre-initialized mutex for each SPI device - */ -static mutex_t locks[] = { -#if SPI_0_EN - [SPI_0] = MUTEX_INIT, -#endif -#if SPI_1_EN - [SPI_1] = MUTEX_INIT, -#endif -#if SPI_2_EN - [SPI_2] = MUTEX_INIT -#endif -}; - -int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) -{ - SercomSpi* spi_dev = 0; - uint8_t sercom_gclk_id = 0; - gpio_t pin_sclk = 0; - gpio_t pin_miso = 0; - gpio_t pin_mosi = 0; - gpio_mux_t mux_sclk = 0; - gpio_mux_t mux_miso = 0; - gpio_mux_t mux_mosi = 0; - spi_mosipad_t mosi_pad = 0; - spi_misopad_t miso_pad = 0; - uint32_t cpha = 0; - uint32_t cpol = 0; - uint32_t f_baud = 0; - switch (speed) - { - case SPI_SPEED_100KHZ: - f_baud = 100000; - break; - case SPI_SPEED_400KHZ: - f_baud = 400000; - break; - case SPI_SPEED_1MHZ: - f_baud = 1000000; - break; - case SPI_SPEED_5MHZ: -#if CLOCK_CORECLOCK >= 5000000 - f_baud = 5000000; - break; -#else - return -1; -#endif - case SPI_SPEED_10MHZ: -#if CLOCK_CORECLOCK >= 10000000 - f_baud = 10000000; - break; -#else - return -1; -#endif - } - switch (conf) - { - case SPI_CONF_FIRST_RISING: /**< first data bit is transacted on the first rising SCK edge */ - cpha = 0; - cpol = 0; - break; - case SPI_CONF_SECOND_RISING: /**< first data bit is transacted on the second rising SCK edge */ - cpha = SERCOM_SPI_CTRLA_CPHA; - cpol = 0; - break; - case SPI_CONF_FIRST_FALLING: /**< first data bit is transacted on the first falling SCK edge */ - cpha = 0; - cpol = SERCOM_SPI_CTRLA_CPOL; - break; - case SPI_CONF_SECOND_FALLING: /**< first data bit is transacted on the second falling SCK edge */ - cpha = SERCOM_SPI_CTRLA_CPHA; - cpol = SERCOM_SPI_CTRLA_CPOL; - break; - } - switch (dev) - { -#if SPI_0_EN - case SPI_0: - spi_dev = &SPI_0_DEV; - sercom_gclk_id = SPI_0_GCLK_ID; - pin_sclk = SPI_0_SCLK; - mux_sclk = SPI_0_SCLK_MUX; - pin_miso = SPI_0_MISO; - mux_miso = SPI_0_MISO_MUX; - pin_mosi = SPI_0_MOSI; - mux_mosi = SPI_0_MOSI_MUX; - mosi_pad = SPI_0_MOSI_PAD; - miso_pad = SPI_0_MISO_PAD; - break; -#endif -#if SPI_1_EN - case SPI_1: - spi_dev = &SPI_1_DEV; - sercom_gclk_id = SPI_1_GCLK_ID; - pin_sclk = SPI_1_SCLK; - mux_sclk = SPI_1_SCLK_MUX; - pin_miso = SPI_1_MISO; - mux_miso = SPI_1_MISO_MUX; - pin_mosi = SPI_1_MOSI; - mux_mosi = SPI_1_MOSI_MUX; - mosi_pad = SPI_1_MOSI_PAD; - miso_pad = SPI_1_MISO_PAD; - break; -#endif - default: - return -1; - } - - /* Use the same sequence as ArduinoCore - * - setup pins - * - disable SPI - * - init SPI (reset, init clock NVIC, CTRLA, CTRLB) - * - init cpha/cpol, BAUD.reg - * - enable SPI - */ - gpio_init(pin_miso, GPIO_IN_PD); - gpio_init_mux(pin_sclk, mux_sclk); - gpio_init_mux(pin_miso, mux_miso); - gpio_init_mux(pin_mosi, mux_mosi); - - /* Disable spi to write confs */ - _spi_poweroff(spi_dev); - - /* reset */ - // Setting the Software Reset bit to 1 - spi_dev->CTRLA.bit.SWRST = 1; - - // Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 - while (spi_dev->CTRLA.bit.SWRST || spi_dev->SYNCBUSY.bit.SWRST) {} - - /* Turn on power manager for sercom */ - PM->APBCMASK.reg |= (PM_APBCMASK_SERCOM0 << (sercom_gclk_id - GCLK_CLKCTRL_ID_SERCOM0_CORE_Val)); - - /* Setup clock */ - /* SPI using CLK GEN 0 */ - GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | - GCLK_CLKCTRL_GEN_GCLK0 | - GCLK_CLKCTRL_ID(sercom_gclk_id)); - while (GCLK->STATUS.bit.SYNCBUSY) {} - - /* ???? init NVIC. Maybe not needed in master mode. */ - - /* Master mode */ - spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE_SPI_MASTER; - while (spi_dev->SYNCBUSY.reg) {}// ???? not needed - - spi_dev->BAUD.bit.BAUD = (uint8_t) (((uint32_t)CLOCK_CORECLOCK) / (2 * f_baud) - 1); /* Synchronous mode*/ - - spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_DOPO(mosi_pad) - | SERCOM_SPI_CTRLA_DIPO(miso_pad) - | cpha - | cpol; - while (spi_dev->SYNCBUSY.reg) {} // ???? not needed - - /* datasize 0 => 8 bits */ - spi_dev->CTRLB.reg = (SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN); - while (spi_dev->SYNCBUSY.reg) {} // ???? Only wait for clear of spi_dev->SYNCBUSY.bit.CTRLB - - /* enable */ - _spi_poweron(spi_dev); - return 0; -} - -int spi_init_slave(spi_t dev, spi_conf_t conf, char (*cb)(char)) -{ - (void)dev; - (void)conf; - (void)cb; - /* TODO */ - assert(false); - return -1; -} - -void spi_transmission_begin(spi_t dev, char reset_val) -{ - (void)dev; - (void)reset_val; - /* TODO */ - assert(false); -} - -int spi_acquire(spi_t dev) -{ - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - mutex_lock(&locks[dev]); - return 0; -} - -int spi_release(spi_t dev) -{ - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - mutex_unlock(&locks[dev]); - return 0; -} - -int spi_transfer_byte(spi_t dev, char out, char *in) -{ - SercomSpi* spi_dev = 0; - char tmp; - - switch(dev) - { -#if SPI_0_EN - case SPI_0: - spi_dev = &(SPI_0_DEV); - break; -#endif -#if SPI_1_EN - case SPI_1: - spi_dev = &(SPI_1_DEV); - break; -#endif - } - - while (!spi_dev->INTFLAG.bit.DRE) {} /* while data register is not empty*/ - spi_dev->DATA.bit.DATA = out; - - while (!spi_dev->INTFLAG.bit.DRE || !spi_dev->INTFLAG.bit.RXC) {} /* while receive is not complete*/ - tmp = (char)spi_dev->DATA.bit.DATA; - - if (in != NULL) - { - in[0] = tmp; - } - return 1; -} - -static void _spi_poweron(SercomSpi* spi_dev) -{ - if (spi_dev == NULL) { - return; - } - spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; - while (spi_dev->SYNCBUSY.bit.ENABLE) {} -} - -void spi_poweron(spi_t dev) -{ - switch(dev) { -#if SPI_0_EN - case SPI_0: - _spi_poweron(&SPI_0_DEV); - break; -#endif -#if SPI_1_EN - case SPI_1: - _spi_poweron(&SPI_1_DEV); - break; -#endif - } -} - -static void _spi_poweroff(SercomSpi* spi_dev) -{ - if (spi_dev == NULL) { - return; - } - spi_dev->CTRLA.bit.ENABLE = 0; /* Disable spi */ - while (spi_dev->SYNCBUSY.bit.ENABLE) {} -} - -void spi_poweroff(spi_t dev) -{ - switch(dev) { -#if SPI_0_EN - case SPI_0: - _spi_poweroff(&SPI_0_DEV); - break; -#endif -#if SPI_1_EN - case SPI_1: - _spi_poweroff(&SPI_1_DEV); - break; -#endif - } -} - -#endif /* SPI_0_EN || SPI_1_EN */ diff --git a/cpu/samd21/periph/uart.c b/cpu/samd21/periph/uart.c index a325b2e9e7..2d17cde605 100644 --- a/cpu/samd21/periph/uart.c +++ b/cpu/samd21/periph/uart.c @@ -57,7 +57,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) uart_ctx[uart].arg = arg; /* configure interrupts and enable RX interrupt */ _uart(uart)->INTENSET.reg = SERCOM_USART_INTENSET_RXC; - NVIC_EnableIRQ(SERCOM0_IRQn + _sercom_id(_uart(uart))); + NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(_uart(uart))); return UART_OK; } @@ -112,18 +112,18 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) void uart_poweron(uart_t uart) { - PM->APBCMASK.reg |= (PM_APBCMASK_SERCOM0 << _sercom_id(_uart(uart))); + PM->APBCMASK.reg |= (PM_APBCMASK_SERCOM0 << sercom_id(_uart(uart))); GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | - (SERCOM0_GCLK_ID_CORE + _sercom_id(_uart(uart))) << + (SERCOM0_GCLK_ID_CORE + sercom_id(_uart(uart))) << GCLK_CLKCTRL_ID_Pos); while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} } void uart_poweroff(uart_t uart) { - PM->APBCMASK.reg &= ~(PM_APBCMASK_SERCOM0 << _sercom_id(_uart(uart))); - GCLK->CLKCTRL.reg = ((SERCOM0_GCLK_ID_CORE + _sercom_id(_uart(uart))) << + PM->APBCMASK.reg &= ~(PM_APBCMASK_SERCOM0 << sercom_id(_uart(uart))); + GCLK->CLKCTRL.reg = ((SERCOM0_GCLK_ID_CORE + sercom_id(_uart(uart))) << GCLK_CLKCTRL_ID_Pos); while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} } diff --git a/cpu/saml21/periph/spi.c b/cpu/saml21/periph/spi.c deleted file mode 100644 index 56ed843df8..0000000000 --- a/cpu/saml21/periph/spi.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * 2015 Kaspar Schleiser - * FreshTemp, LLC. - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License v2.1. See the file LICENSE in the top level directory for more - * details. - */ - -/** - * @ingroup cpu_samd21 - * @{ - * - * @file spi.c - * @brief Low-level SPI driver implementation - * - * @author Thomas Eichinger - * @author Troels Hoffmeyer - * @author Hauke Petersen - * @author Joakim Nohlgård - * @author Kaspar Schleiser - * - * @} - */ - -#include "cpu.h" -#include "mutex.h" -#include "periph/gpio.h" -#include "periph/spi.h" - -#define ENABLE_DEBUG (0) -#include "debug.h" - -#if SPI_0_EN || SPI_1_EN - -/** - * @brief Array holding one pre-initialized mutex for each SPI device - */ -static mutex_t locks[] = { -#if SPI_0_EN - [SPI_0] = MUTEX_INIT, -#endif -#if SPI_1_EN - [SPI_1] = MUTEX_INIT, -#endif -#if SPI_2_EN - [SPI_2] = MUTEX_INIT -#endif -}; - -typedef struct spi_saml21_pin { - uint32_t pin; - uint32_t pmux; -} spi_saml21_pin_t; - -typedef struct spi_saml21 { - SercomSpi* dev; - uint32_t mclk; - uint32_t gclk_id; - - spi_saml21_pin_t sclk; - spi_saml21_pin_t miso; - spi_saml21_pin_t mosi; - - int dipo; - int dopo; -} spi_saml21_t; - -static const spi_saml21_t spi[] = { -#if SPI_0_EN - /* SPI device */ /* MCLK flag */ /* GLCK id */ /* SCLK */ /* MISO */ /* MOSI */ /* dipo+dopo */ - { &(SERCOM0->SPI), MCLK_APBCMASK_SERCOM0, SERCOM0_GCLK_ID_CORE, { GPIO_PIN(PA,7), 3 }, { GPIO_PIN(PA,4), 3 }, { GPIO_PIN(PA,6), 3 }, 0, 1 } -#endif -}; - -int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) -{ - SercomSpi* spi_dev = spi[dev].dev; - - uint8_t dopo = 0; - uint8_t dipo = 0; - uint8_t cpha = 0; - uint8_t cpol = 0; - uint32_t f_baud = 0; - - switch(speed) - { - case SPI_SPEED_100KHZ: - f_baud = 100000; - break; - case SPI_SPEED_400KHZ: - f_baud = 400000; - break; - case SPI_SPEED_1MHZ: - f_baud = 1000000; - break; - case SPI_SPEED_5MHZ: - return -1; - case SPI_SPEED_10MHZ: - return -1; - } - - switch(conf) - { - case SPI_CONF_FIRST_RISING: /**< first data bit is transacted on the first rising SCK edge */ - cpha = 0; - cpol = 0; - break; - case SPI_CONF_SECOND_RISING:/**< first data bit is transacted on the second rising SCK edge */ - cpha = 1; - cpol = 0; - break; - case SPI_CONF_FIRST_FALLING:/**< first data bit is transacted on the first falling SCK edge */ - cpha = 0; - cpol = 1; - break; - case SPI_CONF_SECOND_FALLING:/**< first data bit is transacted on the second falling SCK edge */ - cpha = 1; - cpol = 1; - break; - } - - /* Enable sercom4 in power manager */ - MCLK->APBCMASK.reg |= spi[dev].mclk; - - /* Setup clock */ - GCLK->PCHCTRL[ spi[dev].gclk_id ].reg = - GCLK_PCHCTRL_CHEN | - GCLK_PCHCTRL_GEN_GCLK0; - - while (!(GCLK->PCHCTRL[spi[dev].gclk_id].reg & GCLK_PCHCTRL_CHEN)) {} - - /* SCLK+MOSI = output */ - gpio_init(spi[dev].sclk.pin, GPIO_OUT); - gpio_init(spi[dev].mosi.pin, GPIO_OUT); - /* MISO = input */ - gpio_init(spi[dev].miso.pin, GPIO_IN); - - /* - * Set alternate funcion (PMUX) for our ports. - */ - gpio_init_mux(spi[dev].sclk.pin, spi[dev].sclk.pmux); - gpio_init_mux(spi[dev].miso.pin, spi[dev].miso.pmux); - gpio_init_mux(spi[dev].mosi.pin, spi[dev].mosi.pmux); - - /* pin pad mapping */ - dipo = spi[dev].dipo; - dopo = spi[dev].dopo; - - /* Disable spi to write config */ - spi_dev->CTRLA.bit.ENABLE = 0; - while (spi_dev->SYNCBUSY.reg) {} - - /* setup baud */ - spi_dev->BAUD.bit.BAUD = (uint8_t) (((uint32_t) GCLK_REF) / (2 * f_baud) - 1); /* Syncronous mode*/ - - spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3) /* 0x2 = slave 0x3 = master */ - | (SERCOM_SPI_CTRLA_DOPO(dopo)) - | (SERCOM_SPI_CTRLA_DIPO(dipo)) - | (cpha << SERCOM_SPI_CTRLA_CPHA_Pos) - | (cpol << SERCOM_SPI_CTRLA_CPOL_Pos); - - while (spi_dev->SYNCBUSY.reg) {} - spi_dev->CTRLB.reg = (SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN); - while(spi_dev->SYNCBUSY.reg) {} - spi_poweron(dev); - return 0; -} - -int spi_init_slave(spi_t dev, spi_conf_t conf, char (*cb)(char)) -{ - /* TODO */ - return -1; -} - -void spi_transmission_begin(spi_t dev, char reset_val) -{ - /* TODO*/ -} - -int spi_acquire(spi_t dev) -{ - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - mutex_lock(&locks[dev]); - return 0; -} - -int spi_release(spi_t dev) -{ - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - mutex_unlock(&locks[dev]); - return 0; -} - -int spi_transfer_byte(spi_t dev, char out, char *in) -{ - SercomSpi* spi_dev = spi[dev].dev; - - while (!spi_dev->INTFLAG.bit.DRE) {} /* while data register is not empty */ - spi_dev->DATA.bit.DATA = out; - - if (in) - { - while (!spi_dev->INTFLAG.bit.RXC) {} /* while receive is not complete */ - *in = spi_dev->DATA.bit.DATA; - } - else - { - /* clear input byte even if we're not interested */ - spi_dev->DATA.bit.DATA; - } - return 1; -} - -void spi_poweron(spi_t dev) -{ - SercomSpi* spi_dev = spi[dev].dev; - spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; - while(spi_dev->SYNCBUSY.bit.ENABLE) {} -} - -void spi_poweroff(spi_t dev) -{ - SercomSpi* spi_dev = spi[dev].dev; - spi_dev->CTRLA.bit.ENABLE = 0; - while(spi_dev->SYNCBUSY.bit.ENABLE) {} -} - -#endif /* SPI_0_EN || SPI_1_EN */