1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

cpu/sam0+boards: adapted to new SPI API

- adapted the SPI driver
- merged SPI driver for samr21 and saml21
- adapted all boards using the CPU
This commit is contained in:
Hauke Petersen 2016-11-08 18:26:58 +01:00
parent 10b0013315
commit ea07a6817c
12 changed files with 326 additions and 649 deletions

View File

@ -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]))
/** @} */
/**

View File

@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <thomas.eichinger@fu-berlin.de>
* @autor Kaspar Schleiser <kaspar@schleiser.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#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]))
/** @} */
/**

View File

@ -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), \

View File

@ -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]))
/** @} */
/**

View File

@ -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]))
/** @} */
/**

View File

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

View File

@ -0,0 +1,179 @@
/*
* Copyright (C) 2014-2016 Freie Universität Berlin
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <thomas.eichinger@fu-berlin.de>
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#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);
}
}

View File

@ -107,4 +107,6 @@ void cpu_init(void)
cortexm_init();
/* Initialise clock sources and generic clocks */
clk_init();
/* trigger static peripheral initialization */
periph_init();
}

View File

@ -19,6 +19,8 @@
#ifndef CPU_PERIPH_H
#define CPU_PERIPH_H
#include <limits.h>
#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

View File

@ -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 <thomas.eichinger@fu-berlin.de>
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/
#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 */

View File

@ -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) {}
}

View File

@ -1,234 +0,0 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <thomas.eichinger@fu-berlin.de>
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#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 */