From a3656198547c524253c25fcf9e29835b01fd7105 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Mon, 8 Dec 2014 15:26:30 +0100 Subject: [PATCH 1/2] cpu/stm32f3: added SPI driver --- cpu/stm32f3/periph/spi.c | 431 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 cpu/stm32f3/periph/spi.c diff --git a/cpu/stm32f3/periph/spi.c b/cpu/stm32f3/periph/spi.c new file mode 100644 index 0000000000..6203ec6dee --- /dev/null +++ b/cpu/stm32f3/periph/spi.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences + * 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_stm32f3 + * @{ + * + * @file + * @brief Low-level SPI driver implementation + * + * @author Peter Kietzmann + * @author Fabian Nack + * @author Hauke Petersen + * + * @} + */ +#include + +#include "board.h" +#include "cpu.h" +#include "periph/spi.h" +#include "periph_conf.h" +#include "thread.h" +#include "sched.h" +#include "vtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* guard this file in case no SPI device is defined */ +#if SPI_NUMOF + +typedef struct { + char(*cb)(char data); +} spi_state_t; + +/* static device mapping */ +static SPI_TypeDef *const spi[] = { +#if SPI_0_EN + SPI_0_DEV, +#endif +#if SPI_1_EN + SPI_1_DEV, +#endif +#if SPI_2_EN + SPI_2_DEV +#endif +}; + +static inline void irq_handler_transfer(SPI_TypeDef *spi, spi_t dev); + +static spi_state_t spi_config[SPI_NUMOF]; + +int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) +{ + uint8_t speed_divider; + + switch (speed) { + case SPI_SPEED_100KHZ: + return -2; /* not possible for stm32f3 */ + break; + case SPI_SPEED_400KHZ: + speed_divider = 7; /* makes 656 kHz */ + break; + case SPI_SPEED_1MHZ: + speed_divider = 6; /* makes 1.3 MHz */ + break; + case SPI_SPEED_5MHZ: + speed_divider = 4; /* makes 5.3 MHz */ + break; + case SPI_SPEED_10MHZ: + speed_divider = 3; /* makes 10.5 MHz */ + break; + default: + return -1; + } + + switch (dev) { +#if SPI_0_EN + case SPI_0: + /* enable clocks */ + SPI_0_CLKEN(); + SPI_0_SCK_PORT_CLKEN(); + SPI_0_MISO_PORT_CLKEN(); + SPI_0_MOSI_PORT_CLKEN(); + break; +#endif /* SPI_0_EN */ +#if SPI_1_EN + case SPI_1: + /* enable clocks */ + SPI_1_CLKEN(); + SPI_1_SCK_PORT_CLKEN(); + SPI_1_MISO_PORT_CLKEN(); + SPI_1_MOSI_PORT_CLKEN(); + break; +#endif /* SPI_1_EN */ +#if SPI_2_EN + case SPI_2: + /* enable clocks */ + SPI_2_CLKEN(); + SPI_2_SCK_PORT_CLKEN(); + SPI_2_MISO_PORT_CLKEN(); + SPI_2_MOSI_PORT_CLKEN(); + break; +#endif /* SPI_2_EN */ + default: + return -2; + } + + /* configure SCK, MISO and MOSI pin */ + spi_conf_pins(dev); + + /**************** SPI-Init *****************/ + spi[dev]->I2SCFGR &= ~(SPI_I2SCFGR_I2SMOD);/* Activate the SPI mode (Reset I2SMOD bit in I2SCFGR register) */ + spi[dev]->CR1 = 0; + spi[dev]->CR2 = 0; + /* the NSS (chip select) is managed purely by software */ + spi[dev]->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI; + spi[dev]->CR1 |= (speed_divider << 3); /* Define serial clock baud rate. 001 leads to f_PCLK/4 */ + spi[dev]->CR1 |= (SPI_CR1_MSTR); /* 1: master configuration */ + spi[dev]->CR1 |= (conf); + /* enable SPI */ + spi[dev]->CR1 |= (SPI_CR1_SPE); + return 0; +} + +int spi_init_slave(spi_t dev, spi_conf_t conf, char(*cb)(char data)) +{ + switch (dev) { +#if SPI_0_EN + case SPI_0: + /* enable clocks */ + SPI_0_CLKEN(); + SPI_0_SCK_PORT_CLKEN(); + SPI_0_MISO_PORT_CLKEN(); + SPI_0_MOSI_PORT_CLKEN(); + /* configure interrupt channel */ + NVIC_SetPriority(SPI_0_IRQ, SPI_IRQ_PRIO); /* set SPI interrupt priority */ + NVIC_EnableIRQ(SPI_0_IRQ); /* set SPI interrupt priority */ + break; +#endif /* SPI_0_EN */ +#if SPI_1_EN + case SPI_1: + /* enable clocks */ + SPI_1_CLKEN(); + SPI_1_SCK_PORT_CLKEN(); + SPI_1_MISO_PORT_CLKEN(); + SPI_1_MOSI_PORT_CLKEN(); + /* configure interrupt channel */ + NVIC_SetPriority(SPI_1_IRQ, SPI_IRQ_PRIO); + NVIC_EnableIRQ(SPI_1_IRQ); + break; +#endif /* SPI_1_EN */ +#if SPI_2_EN + case SPI_2: + /* enable clocks */ + SPI_2_CLKEN(); + SPI_2_SCK_PORT_CLKEN(); + SPI_2_MISO_PORT_CLKEN(); + SPI_2_MOSI_PORT_CLKEN(); + /* configure interrupt channel */ + NVIC_SetPriority(SPI_2_IRQ, SPI_IRQ_PRIO); + NVIC_EnableIRQ(SPI_2_IRQ); + break; +#endif /* SPI_2_EN */ + default: + return -1; + } + + /* configure sck, miso and mosi pin */ + spi_conf_pins(dev); + + /***************** SPI-Init *****************/ + spi[dev]->I2SCFGR &= ~(SPI_I2SCFGR_I2SMOD); + spi[dev]->CR1 = 0; + spi[dev]->CR2 = 0; + /* enable RXNEIE flag to enable rx buffer not empty interrupt */ + spi[dev]->CR2 |= (SPI_CR2_RXNEIE); /*1:not masked */ + spi[dev]->CR1 |= (conf); + /* the NSS (chip select) is managed by software and NSS is low (slave enabled) */ + spi[dev]->CR1 |= SPI_CR1_SSM; + /* set callback */ + spi_config[dev].cb = cb; + /* enable SPI device */ + spi[dev]->CR1 |= SPI_CR1_SPE; + return 0; +} + +int spi_conf_pins(spi_t dev) +{ + GPIO_TypeDef *port[3]; + int pin[3], af[3]; + + switch (dev) { +#if SPI_0_EN + case SPI_0: + port[0] = SPI_0_SCK_PORT; + pin[0] = SPI_0_SCK_PIN; + af[0] = SPI_0_SCK_AF; + port[1] = SPI_0_MOSI_PORT; + pin[1] = SPI_0_MOSI_PIN; + af[1] = SPI_0_MOSI_AF; + port[2] = SPI_0_MISO_PORT; + pin[2] = SPI_0_MISO_PIN; + af[2] = SPI_0_MISO_AF; + break; +#endif /* SPI_0_EN */ +#if SPI_1_EN + case SPI_1: + port[0] = SPI_1_SCK_PORT; + pin[0] = SPI_1_SCK_PIN; + af[0] = SPI_1_SCK_AF; + port[1] = SPI_1_MOSI_PORT; + pin[1] = SPI_1_MOSI_PIN; + af[1] = SPI_1_MOSI_AF; + port[2] = SPI_1_MISO_PORT; + pin[2] = SPI_1_MISO_PIN; + af[2] = SPI_1_MISO_AF; + break; +#endif /* SPI_1_EN */ +#if SPI_2_EN + case SPI_2: + port[0] = SPI_2_SCK_PORT; + pin[0] = SPI_2_SCK_PIN; + af[0] = SPI_2_SCK_AF; + port[1] = SPI_2_MOSI_PORT; + pin[1] = SPI_2_MOSI_PIN; + af[1] = SPI_2_MOSI_AF; + port[2] = SPI_2_MISO_PORT; + pin[2] = SPI_2_MISO_PIN; + af[2] = SPI_2_MISO_AF; + break; +#endif /* SPI_2_EN */ + default: + return -1; + } + + for (int i = 0; i < 3; i++) { + /* Set GPIOs to AF mode */ + port[i]->MODER &= ~(3 << (2 * pin[i])); + port[i]->MODER |= (2 << (2 * pin[i])); + /* Set speed */ + port[i]->OSPEEDR &= ~(3 << (2 * pin[i])); + port[i]->OSPEEDR |= (3 << (2 * pin[i])); + /* Set to push-pull configuration */ + port[i]->OTYPER &= ~(1 << pin[i]); + /* Configure push-pull resistors */ + port[i]->PUPDR &= ~(3 << (2 * pin[i])); + port[i]->PUPDR |= (2 << (2 * pin[i])); + /* Configure GPIOs for the SPI alternate function */ + int hl = (pin[i] < 8) ? 0 : 1; + port[i]->AFR[hl] &= ~(0xf << ((pin[i] - (hl * 8)) * 4)); + port[i]->AFR[hl] |= (af[i] << ((pin[i] - (hl * 8)) * 4)); + } + + return 0; +} + +int spi_transfer_byte(spi_t dev, char out, char *in) +{ + if (dev >= SPI_NUMOF) { + return -1; + } + + while (!(spi[dev]->SR & SPI_SR_TXE)); + spi[dev]->DR = out; + + while (!(spi[dev]->SR & SPI_SR_RXNE)); + + if (in != NULL) { + *in = spi[dev]->DR; + } + else { + spi[dev]->DR; + } + + return 1; +} + +int spi_transfer_bytes(spi_t dev, char *out, char *in, unsigned int length) +{ + + int i, trans_ret, trans_bytes = 0; + char in_temp; + + for (i = 0; i < length; i++) { + if (out != NULL) { + trans_ret = spi_transfer_byte(dev, out[i], &in_temp); + } + else { + trans_ret = spi_transfer_byte(dev, 0, &in_temp); + } + if (trans_ret < 0) { + return -1; + } + if (in != NULL) { + in[i] = in_temp; + } + trans_bytes++; + } + + return trans_bytes++; +} + +int spi_transfer_reg(spi_t dev, uint8_t reg, char out, char *in) +{ + int trans_ret; + + trans_ret = spi_transfer_byte(dev, reg, in); + if (trans_ret < 0) { + return -1; + } + trans_ret = spi_transfer_byte(dev, out, in); + if (trans_ret < 0) { + return -1; + } + + return 1; +} + +int spi_transfer_regs(spi_t dev, uint8_t reg, char *out, char *in, unsigned int length) +{ + int trans_ret; + + trans_ret = spi_transfer_byte(dev, reg, in); + if (trans_ret < 0) { + return -1; + } + trans_ret = spi_transfer_bytes(dev, out, in, length); + if (trans_ret < 0) { + return -1; + } + + return trans_ret; +} + +void spi_transmission_begin(spi_t dev, char reset_val) +{ + if (dev < SPI_NUMOF) { + spi[dev]->DR = reset_val; + } +} + +void spi_poweron(spi_t dev) +{ + switch (dev) { +#if SPI_0_EN + case SPI_0: + SPI_0_CLKEN(); + break; +#endif +#if SPI_1_EN + case SPI_1: + SPI_1_CLKEN(); + break; +#endif +#if SPI_2_EN + case SPI_2: + SPI_2_CLKEN(); + break; +#endif + } +} + +void spi_poweroff(spi_t dev) +{ + switch (dev) { +#if SPI_0_EN + case SPI_0: + while (SPI_0_DEV->SR & SPI_SR_BSY); + SPI_0_CLKDIS(); + break; +#endif +#if SPI_1_EN + case SPI_1: + while (SPI_1_DEV->SR & SPI_SR_BSY); + SPI_1_CLKDIS(); + break; +#endif +#if SPI_2_EN + case SPI_2: + while (SPI_2_DEV->SR & SPI_SR_BSY); + SPI_2_CLKDIS(); + break; +#endif + } +} + +static inline void irq_handler_transfer(SPI_TypeDef *spi, spi_t dev) +{ + + if (spi->SR & SPI_SR_RXNE) { + char data; + data = spi->DR; + data = spi_config[dev].cb(data); + spi->DR = data; + } + /* see if a thread with higher priority wants to run now */ + if (sched_context_switch_request) { + thread_yield(); + } +} + +#if SPI_0_EN +void SPI_0_IRQ_HANDLER(void) +{ + irq_handler_transfer(SPI_0_DEV, SPI_0); +} +#endif + +#if SPI_1_EN +void SPI_1_IRQ_HANDLER(void) +{ + irq_handler_transfer(SPI_1_DEV, SPI_1); +} +#endif + +#if SPI_2_EN +void SPI_2_IRQ_HANDLER(void) +{ + irq_handler_transfer(SPI_2_DEV, SPI_2); +} +#endif + +#endif /* SPI_NUMOF */ From 7f0ffcf615d65e4c9afd6e4a8ef2cd1b17babaec Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Mon, 8 Dec 2014 15:26:41 +0100 Subject: [PATCH 2/2] board/stm32f3discovery: added SPI configuration --- boards/stm32f3discovery/Makefile.features | 2 +- boards/stm32f3discovery/include/periph_conf.h | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/boards/stm32f3discovery/Makefile.features b/boards/stm32f3discovery/Makefile.features index 38847eeda9..4dfa4fad14 100644 --- a/boards/stm32f3discovery/Makefile.features +++ b/boards/stm32f3discovery/Makefile.features @@ -1,2 +1,2 @@ FEATURES_PROVIDED += cpp -FEATURES_PROVIDED += periph_uart periph_gpio periph_pwm +FEATURES_PROVIDED += periph_uart periph_gpio periph_pwm periph_spi diff --git a/boards/stm32f3discovery/include/periph_conf.h b/boards/stm32f3discovery/include/periph_conf.h index 916db6d14f..9cdc8804ad 100644 --- a/boards/stm32f3discovery/include/periph_conf.h +++ b/boards/stm32f3discovery/include/periph_conf.h @@ -147,6 +147,56 @@ extern "C" { #define PWM_1_PIN_AF 2 /** @} */ +/** + * @name SPI configuration + * @{ + */ +#define SPI_NUMOF (2U) +#define SPI_0_EN 1 +#define SPI_1_EN 1 +#define SPI_IRQ_PRIO 1 + +/* SPI 0 device config */ +#define SPI_0_DEV SPI1 +#define SPI_0_CLKEN() (RCC->APB2ENR |= RCC_APB2ENR_SPI1EN) +#define SPI_0_CLKDIS() (RCC->APB2ENR &= ~RCC_APB2ENR_SPI1EN) +#define SPI_0_IRQ SPI1_IRQn +#define SPI_0_IRQ_HANDLER isr_spi1 +/* SPI 0 pin configuration */ +#define SPI_0_SCK_PORT GPIOA +#define SPI_0_SCK_PIN 5 +#define SPI_0_SCK_AF 5 +#define SPI_0_SCK_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOAEN) +#define SPI_0_MISO_PORT GPIOA +#define SPI_0_MISO_PIN 6 +#define SPI_0_MISO_AF 5 +#define SPI_0_MISO_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOAEN) +#define SPI_0_MOSI_PORT GPIOA +#define SPI_0_MOSI_PIN 7 +#define SPI_0_MOSI_AF 5 +#define SPI_0_MOSI_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOAEN) + +/* SPI 1 device config */ +#define SPI_1_DEV SPI3 +#define SPI_1_CLKEN() (RCC->APB1ENR |= RCC_APB1ENR_SPI3EN) +#define SPI_1_CLKDIS() (RCC->APB1ENR &= ~RCC_APB1ENR_SPI3EN) +#define SPI_1_IRQ SPI3_IRQn +#define SPI_1_IRQ_HANDLER isr_spi3 +/* SPI 1 pin configuration */ +#define SPI_1_SCK_PORT GPIOC +#define SPI_1_SCK_PIN 10 +#define SPI_1_SCK_AF 6 +#define SPI_1_SCK_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOCEN) +#define SPI_1_MISO_PORT GPIOC +#define SPI_1_MISO_PIN 11 +#define SPI_1_MISO_AF 6 +#define SPI_1_MISO_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOCEN) +#define SPI_1_MOSI_PORT GPIOC +#define SPI_1_MOSI_PIN 12 +#define SPI_1_MOSI_AF 6 +#define SPI_1_MOSI_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOCEN) +/** @} */ + /** * @brief GPIO configuration * @{