aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2011-04-28 13:52:58 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2011-04-28 14:23:30 -0400
commit7cd5350622f5c7c84662beaee5b92a362be0f59b (patch)
tree24b17d60a6b9086b2f95a21c322c0a7d2381ec0d /libmaple
parente6d44c187435c53b262c5336929db4aafb812811 (diff)
downloadlibrambutan-7cd5350622f5c7c84662beaee5b92a362be0f59b.tar.gz
librambutan-7cd5350622f5c7c84662beaee5b92a362be0f59b.zip
SPI refactor.
Still a polling driver, but the libmaple proper interface exposes enough that users enable the various interrupts and define their own IRQ handlers if they feel like it. Wirish HardwareSPI interface was largely redone; it's more like the Arduino implementation now, although there are some differences when I didn't like their API. The old methods are still there, but are deprecated and slated for deletion in 0.1.0. New board-specific values: BOARD_NR_SPI, BOARD_SPIx_NSS_PIN, BOARD_SPIx_MOSI_PIN, BOARD_SPIx_MISO_PIN, and BOARD_SPIx_SCK_PIN, for x from 1 to BOARD_NR_SPI. Documentation was updated appropriately.
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/spi.c333
-rw-r--r--libmaple/spi.h495
2 files changed, 629 insertions, 199 deletions
diff --git a/libmaple/spi.c b/libmaple/spi.c
index 7bdc18a..2fbc2ac 100644
--- a/libmaple/spi.c
+++ b/libmaple/spi.c
@@ -3,161 +3,250 @@
*
* Copyright (c) 2010 Perry Hung.
*
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
*****************************************************************************/
/**
- * @brief libmaple serial peripheral interface (SPI) definitions
- *
- * Initial implementation for the SPI interface.
- *
- * This driver implements only the bare essentials of a polling driver at the
- * moment. Master mode only, 8-bit data frames, and polling.
- *
- * The caller is responsible for controlling the chip select line.
- *
- * TODO: interrupt based driver, DMA.
- *
+ * @file spi.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Serial Peripheral Interface (SPI) support.
+ * Currently, there is no Integrated Interchip Sound (I2S) support.
*/
-#include "libmaple.h"
-#include "gpio.h"
-#include "rcc.h"
#include "spi.h"
+#include "bitband.h"
-typedef struct spi_dev {
- SPI *base;
- gpio_dev *gpio_d;
- uint8 sck_pin;
- uint8 miso_pin;
- uint8 mosi_pin;
-} spi_dev;
-
-spi_dev spi_dev1 = {
- .base = (SPI*)SPI1_BASE,
- .gpio_d = NULL,
- .sck_pin = 5,
- .miso_pin = 6,
- .mosi_pin = 7
-};
+/*
+ * SPI devices
+ */
-spi_dev spi_dev2 = {
- .base = (SPI*)SPI2_BASE,
- .gpio_d = NULL,
- .sck_pin = 13,
- .miso_pin = 14,
- .mosi_pin = 15
+static spi_dev spi1 = {
+ .regs = SPI1_BASE,
+ .clk_id = RCC_SPI1,
+ .irq_num = NVIC_SPI1,
};
+spi_dev *SPI1 = &spi1;
-static void spi_gpio_cfg(const spi_dev *dev);
+static spi_dev spi2 = {
+ .regs = SPI2_BASE,
+ .clk_id = RCC_SPI2,
+ .irq_num = NVIC_SPI2,
+};
+spi_dev *SPI2 = &spi2;
-/**
- * @brief Initialize a spi peripheral
- * @param spi_num which spi to turn on, SPI1 or SPI2?
- * @param prescale prescale factor on the input clock.
- * @param endian data frame format (LSBFIRST?)
- * @param mode SPI mode number
- */
-void spi_init(uint32 spi_num,
- uint32 prescale,
- uint32 endian,
- uint32 mode) {
- ASSERT(spi_num == 1 || spi_num == 2);
- ASSERT(mode < 4);
-
- SPI *spi;
- uint32 cr1 = 0;
-
- switch (spi_num) {
- case 1:
- /* limit to 18 mhz max speed */
- ASSERT(prescale != CR1_BR_PRESCALE_2);
- spi = (SPI*)SPI1_BASE;
- rcc_clk_enable(RCC_SPI1);
- spi_dev1.gpio_d = GPIOA;
- spi_gpio_cfg(&spi_dev1);
- break;
- case 2:
- spi = (SPI*)SPI2_BASE;
- rcc_clk_enable(RCC_SPI2);
- spi_dev1.gpio_d = GPIOB,
- spi_gpio_cfg(&spi_dev2);
- break;
- }
+#ifdef STM32_HIGH_DENSITY
+static spi_dev spi3 = {
+ .regs = SPI3_BASE,
+ .clk_id = RCC_SPI3,
+ .irq_num = NVIC_SPI3,
+};
+spi_dev *SPI3 = &spi3;
+#endif
- cr1 = prescale | endian | mode | CR1_MSTR | CR1_SSI | CR1_SSM;
- spi->CR1 = cr1;
+/*
+ * SPI convenience routines
+ */
- /* Peripheral enable */
- spi->CR1 |= CR1_SPE;
+/**
+ * @brief Initialize and reset a SPI device.
+ * @param dev Device to initialize and reset.
+ */
+void spi_init(spi_dev *dev) {
+ rcc_clk_enable(dev->clk_id);
+ rcc_reset_dev(dev->clk_id);
}
/**
- * @brief SPI synchronous 8-bit write, blocking.
- * @param spi_num which spi to send on
- * @return data shifted back from the slave
+ * @brief Configure GPIO bit modes for use as a SPI bus master's pins.
+ * @param nss_dev NSS pin's GPIO device
+ * @param comm_dev SCK, MISO, MOSI pins' GPIO device
+ * @param nss_bit NSS pin's GPIO bit on nss_dev
+ * @param sck_bit SCK pin's GPIO bit on comm_dev
+ * @param miso_bit MISO pin's GPIO bit on comm_dev
+ * @param mosi_bit MOSI pin's GPIO bit on comm_dev
*/
-uint8 spi_tx_byte(uint32 spi_num, uint8 data) {
- SPI *spi;
+void spi_master_gpio_cfg(gpio_dev *nss_dev,
+ gpio_dev *comm_dev,
+ uint8 nss_bit,
+ uint8 sck_bit,
+ uint8 miso_bit,
+ uint8 mosi_bit) {
+ gpio_set_mode(nss_dev, nss_bit, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(comm_dev, sck_bit, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(comm_dev, miso_bit, GPIO_INPUT_FLOATING);
+ gpio_set_mode(comm_dev, mosi_bit, GPIO_AF_OUTPUT_PP);
+}
- spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE;
+/**
+ * @brief Configure GPIO bit modes for use as a SPI bus slave's pins.
+ * @param nss_dev NSS pin's GPIO device
+ * @param comm_dev SCK, MISO, MOSI pins' GPIO device
+ * @param nss_bit NSS pin's GPIO bit on nss_dev
+ * @param sck_bit SCK pin's GPIO bit on comm_dev
+ * @param miso_bit MISO pin's GPIO bit on comm_dev
+ * @param mosi_bit MOSI pin's GPIO bit on comm_dev
+ */
+void spi_slave_gpio_cfg(gpio_dev *nss_dev,
+ gpio_dev *comm_dev,
+ uint8 nss_bit,
+ uint8 sck_bit,
+ uint8 miso_bit,
+ uint8 mosi_bit) {
+ gpio_set_mode(nss_dev, nss_bit, GPIO_INPUT_FLOATING);
+ gpio_set_mode(comm_dev, sck_bit, GPIO_INPUT_FLOATING);
+ gpio_set_mode(comm_dev, miso_bit, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(comm_dev, mosi_bit, GPIO_INPUT_FLOATING);
+}
- while (!(spi->SR & SR_TXE))
- ;
+static void spi_reconfigure(spi_dev *dev, uint32 cr1_config);
- spi->DR = data;
+/**
+ * @brief Configure and enable a SPI device as bus master.
+ *
+ * The device's peripheral will be disabled before being reconfigured.
+ *
+ * @param dev Device to configure as bus master
+ * @param baud Bus baud rate
+ * @param mode SPI mode
+ * @param flags Logical OR of spi_cfg_flag values.
+ * @see spi_cfg_flag
+ */
+void spi_master_enable(spi_dev *dev,
+ spi_baud_rate baud,
+ spi_mode mode,
+ uint32 flags) {
+ spi_reconfigure(dev, baud | flags | SPI_CR1_MSTR | mode);
+}
- while (!(spi->SR & SR_RXNE))
- ;
+/**
+ * @brief Configure and enable a SPI device as a bus slave.
+ *
+ * The device's peripheral will be disabled before being reconfigured.
+ *
+ * @param dev Device to configure as a bus slave
+ * @param mode SPI mode
+ * @param flags Logical OR of spi_cfg_flag values.
+ * @see spi_cfg_flag
+ */
+void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) {
+ spi_reconfigure(dev, flags | mode);
+}
- return spi->DR;
+/**
+ * @brief Nonblocking SPI transmit.
+ * @param dev SPI port to use for transmission
+ * @param buf Buffer to transmit. The sizeof buf's elements are
+ * inferred from the dev's data frame format (i.e., are
+ * correctly treated as 8-bit or 16-bit quantities).
+ * @param len Maximum number of elements to transmit.
+ * @return Number of elements transmitted.
+ */
+uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) {
+ spi_reg_map *regs = dev->regs;
+ uint32 txed = 0;
+ if (spi_dff(dev) == SPI_DFF_16_BIT) {
+ const uint8 *buf8 = (const uint8*)buf;
+ while ((regs->SR & SPI_SR_TXE) && (txed < len)) {
+ regs->DR = buf8[txed++];
+ }
+ } else {
+ const uint16 *buf16 = (const uint16*)buf;
+ while ((regs->SR & SPI_SR_TXE) && (txed < len)) {
+ regs->DR = buf16[txed++];
+ }
+ }
+ return txed;
}
-uint8 spi_tx(uint32 spi_num, uint8 *buf, uint32 len) {
- SPI *spi;
- uint32 i = 0;
- uint8 rc;
+/**
+ * @brief Call a function on each SPI port
+ * @param fn Function to call.
+ */
+void spi_foreach(void (*fn)(spi_dev*)) {
+ fn(SPI1);
+ fn(SPI2);
+#ifdef STM32_HIGH_DENSITY
+ fn(SPI3);
+#endif
+}
- ASSERT(spi_num == 1 || spi_num == 2);
- spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE;
+/**
+ * @brief Enable a SPI peripheral
+ * @param dev Device to enable
+ */
+void spi_peripheral_enable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 1);
+}
- if (!len) {
- return 0;
- }
+/**
+ * @brief Disable a SPI peripheral
+ * @param dev Device to disable
+ */
+void spi_peripheral_disable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 0);
+}
- while (i < len) {
- while (!(spi->SR & SR_TXE))
- ;
+/**
+ * @brief Enable DMA requests whenever the transmit buffer is empty
+ * @param dev SPI device on which to enable TX DMA requests
+ */
+void spi_tx_dma_enable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 1);
+}
- spi->DR = buf[i];
+/**
+ * @brief Disable DMA requests whenever the transmit buffer is empty
+ * @param dev SPI device on which to disable TX DMA requests
+ */
+void spi_tx_dma_disable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 0);
+}
- while (!(spi->SR & SR_RXNE))
- ;
+/**
+ * @brief Enable DMA requests whenever the receive buffer is empty
+ * @param dev SPI device on which to enable RX DMA requests
+ */
+void spi_rx_dma_enable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 1);
+}
- rc = spi->DR;
- i++;
- }
- return rc;
+/**
+ * @brief Disable DMA requests whenever the receive buffer is empty
+ * @param dev SPI device on which to disable RX DMA requests
+ */
+void spi_rx_dma_disable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 0);
}
-static void spi_gpio_cfg(const spi_dev *dev) {
- gpio_set_mode(dev->gpio_d, dev->sck_pin, GPIO_AF_OUTPUT_PP);
- gpio_set_mode(dev->gpio_d, dev->miso_pin, GPIO_AF_OUTPUT_PP);
- gpio_set_mode(dev->gpio_d, dev->mosi_pin, GPIO_AF_OUTPUT_PP);
+/*
+ * SPI auxiliary routines
+ */
+
+static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) {
+ spi_irq_disable(dev, SPI_INTERRUPTS_ALL);
+ spi_peripheral_disable(dev);
+ dev->regs->CR1 = cr1_config;
+ spi_peripheral_enable(dev);
}
+
+/*
+ * IRQ handlers (TODO)
+ */
diff --git a/libmaple/spi.h b/libmaple/spi.h
index 5ebf52d..e2820dd 100644
--- a/libmaple/spi.h
+++ b/libmaple/spi.h
@@ -24,99 +24,440 @@
/**
* @file spi.h
- * @brief libmaple serial peripheral interface (SPI) prototypes and
- * declarations
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Serial Peripheral Interface (SPI) and Integrated
+ * Interchip Sound (I2S) peripheral support.
+ *
+ * I2S support is currently limited to register maps and bit definitions.
*/
#ifndef _SPI_H_
#define _SPI_H_
#include "libmaple_types.h"
+#include "rcc.h"
+#include "nvic.h"
+#include "gpio.h"
#include "util.h"
#ifdef __cplusplus
extern "C" {
#endif
-/* peripheral addresses */
-#define SPI1_BASE 0x40013000
-#define SPI2_BASE 0x40003800
-#define SPI3_BASE 0x40003C00
-
-/* baud rate prescaler bits */
-#define CR1_BR 0x00000038
-#define CR1_BR_PRESCALE_2 0x00000000
-#define CR1_BR_PRESCALE_4 0x00000008
-#define CR1_BR_PRESCALE_8 0x00000010
-#define CR1_BR_PRESCALE_16 0x00000018
-#define CR1_BR_PRESCALE_32 0x00000020
-#define CR1_BR_PRESCALE_64 0x00000028
-#define CR1_BR_PRESCALE_128 0x00000030
-#define CR1_BR_PRESCALE_256 0x00000038
-
-#define CR1_LSBFIRST BIT(7) // data frame format
-#define CR1_MSTR BIT(2) // master selection
-#define CR1_SSM BIT(9) // software slave management
-#define CR1_SSI BIT(8) // internal slave select
-#define CR1_SPE BIT(6) // peripheral enable
-
-/* Status register bits */
-#define SR_RXNE BIT(0) // rx buffer not empty
-#define SR_TXE BIT(1) // transmit buffer empty
-#define SR_BSY BIT(7) // busy flag
-
-typedef struct SPI {
- __io uint16 CR1;
- uint16 pad0;
- __io uint8 CR2;
- uint8 pad1[3];
- __io uint8 SR;
- uint8 pad2[3];
- __io uint16 DR;
- uint16 pad3;
- __io uint16 CRCPR;
- uint16 pad4;
- __io uint16 RXCRCR;
- uint16 pad5;
- __io uint16 TXCRCR;
- uint16 pad6;
-} SPI;
-
-enum {
- SPI_MSBFIRST = 0,
- SPI_LSBFIRST = BIT(7),
-};
-
-enum {
- SPI_PRESCALE_2 = (0x0 << 3),
- SPI_PRESCALE_4 = (0x1 << 3),
- SPI_PRESCALE_8 = (0x2 << 3),
- SPI_PRESCALE_16 = (0x3 << 3),
- SPI_PRESCALE_32 = (0x4 << 3),
- SPI_PRESCALE_64 = (0x5 << 3),
- SPI_PRESCALE_128 = (0x6 << 3),
- SPI_PRESCALE_256 = (0x7 << 3)
-};
-
-void spi_init(uint32 spi_num,
- uint32 prescale,
- uint32 endian,
- uint32 mode);
-uint8 spi_tx_byte(uint32 spi_num, uint8 data);
-uint8 spi_tx(uint32 spi_num, uint8 *buf, uint32 len);
-
-static inline uint8 spi_rx(uint32 spi_num) {
- SPI *spi;
-
- ASSERT(spi_num == 1 || spi_num == 2);
- spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE;
-
- return spi->DR;
+/*
+ * Register maps
+ */
+
+/** SPI register map type. */
+typedef struct spi_reg_map {
+ __io uint32 CR1; /**< Control register 1 */
+ __io uint32 CR2; /**< Control register 2 */
+ __io uint32 SR; /**< Status register */
+ __io uint32 DR; /**< Data register */
+ __io uint32 CRCPR; /**< CRC polynomial register */
+ __io uint32 RXCRCR; /**< RX CRC register */
+ __io uint32 TXCRCR; /**< TX CRC register */
+ __io uint32 I2SCFGR; /**< I2S configuration register */
+ __io uint32 I2SPR; /**< I2S prescaler register */
+} spi_reg_map;
+
+/** SPI1 register map base pointer */
+#define SPI1_BASE ((struct spi_reg_map*)0x40013000)
+/** SPI2 register map base pointer */
+#define SPI2_BASE ((struct spi_reg_map*)0x40003800)
+/** SPI3 register map base pointer */
+#define SPI3_BASE ((struct spi_reg_map*)0x40003C00)
+
+/*
+ * Register bit definitions
+ */
+
+/* Control register 1 */
+
+#define SPI_CR1_BIDIMODE_BIT 15
+#define SPI_CR1_BIDIOE_BIT 14
+#define SPI_CR1_CRCEN_BIT 13
+#define SPI_CR1_CRCNEXT_BIT 12
+#define SPI_CR1_DFF_BIT 11
+#define SPI_CR1_RXONLY_BIT 10
+#define SPI_CR1_SSM_BIT 9
+#define SPI_CR1_SSI_BIT 8
+#define SPI_CR1_LSBFIRST_BIT 7
+#define SPI_CR1_SPE_BIT 6
+#define SPI_CR1_MSTR_BIT 2
+#define SPI_CR1_CPOL_BIT 1
+#define SPI_CR1_CPHA_BIT 0
+
+#define SPI_CR1_BIDIMODE BIT(SPI_CR1_BIDIMODE_BIT)
+#define SPI_CR1_BIDIMODE_2_LINE (0x0 << SPI_CR1_BIDIMODE_BIT)
+#define SPI_CR1_BIDIMODE_1_LINE (0x1 << SPI_CR1_BIDIMODE_BIT)
+#define SPI_CR1_BIDIOE BIT(SPI_CR1_BIDIOE_BIT)
+#define SPI_CR1_CRCEN BIT(SPI_CR1_CRCEN_BIT)
+#define SPI_CR1_CRCNEXT BIT(SPI_CR1_CRCNEXT_BIT)
+#define SPI_CR1_DFF BIT(SPI_CR1_DFF_BIT)
+#define SPI_CR1_DFF_8_BIT (0x0 << SPI_CR1_DFF_BIT)
+#define SPI_CR1_DFF_16_BIT (0x1 << SPI_CR1_DFF_BIT)
+#define SPI_CR1_RXONLY BIT(SPI_CR1_RXONLY_BIT)
+#define SPI_CR1_SSM BIT(SPI_CR1_SSM_BIT)
+#define SPI_CR1_SSI BIT(SPI_CR1_SSI_BIT)
+#define SPI_CR1_LSBFIRST BIT(SPI_CR1_LSBFIRST_BIT)
+#define SPI_CR1_SPE BIT(SPI_CR1_SPE_BIT)
+#define SPI_CR1_BR (0x7 << 3)
+#define SPI_CR1_BR_PCLK_DIV_2 (0x0 << 3)
+#define SPI_CR1_BR_PCLK_DIV_4 (0x1 << 3)
+#define SPI_CR1_BR_PCLK_DIV_8 (0x2 << 3)
+#define SPI_CR1_BR_PCLK_DIV_16 (0x3 << 3)
+#define SPI_CR1_BR_PCLK_DIV_32 (0x4 << 3)
+#define SPI_CR1_BR_PCLK_DIV_64 (0x5 << 3)
+#define SPI_CR1_BR_PCLK_DIV_128 (0x6 << 3)
+#define SPI_CR1_BR_PCLK_DIV_256 (0x7 << 3)
+#define SPI_CR1_MSTR BIT(SPI_CR1_MSTR_BIT)
+#define SPI_CR1_CPOL BIT(SPI_CR1_CPOL_BIT)
+#define SPI_CR1_CPOL_LOW (0x0 << SPI_CR1_CPOL_BIT)
+#define SPI_CR1_CPOL_HIGH (0x1 << SPI_CR1_CPOL_BIT)
+#define SPI_CR1_CPHA BIT(SPI_CR1_CPHA_BIT)
+
+/* Control register 2 */
+
+/* RM0008-ism: SPI CR2 has "TXDMAEN" and "RXDMAEN" bits, while the
+ * USARTs have CR3 "DMAR" and "DMAT" bits. */
+
+#define SPI_CR2_TXEIE_BIT 7
+#define SPI_CR2_RXNEIE_BIT 6
+#define SPI_CR2_ERRIE_BIT 5
+#define SPI_CR2_SSOE_BIT 2
+#define SPI_CR2_TXDMAEN_BIT 1
+#define SPI_CR2_RXDMAEN_BIT 0
+
+#define SPI_CR2_TXEIE BIT(SPI_CR2_TXEIE_BIT)
+#define SPI_CR2_RXNEIE BIT(SPI_CR2_RXNEIE_BIT)
+#define SPI_CR2_ERRIE BIT(SPI_CR2_ERRIE_BIT)
+#define SPI_CR2_SSOE BIT(SPI_CR2_SSOE_BIT)
+#define SPI_CR2_TXDMAEN BIT(SPI_CR2_TXDMAEN_BIT)
+#define SPI_CR2_RXDMAEN BIT(SPI_CR2_RXDMAEN_BIT)
+
+/* Status register */
+
+#define SPI_SR_BSY_BIT 7
+#define SPI_SR_OVR_BIT 6
+#define SPI_SR_MODF_BIT 5
+#define SPI_SR_CRCERR_BIT 4
+#define SPI_SR_UDR_BIT 3
+#define SPI_SR_CHSIDE_BIT 2
+#define SPI_SR_TXE_BIT 1
+#define SPI_SR_RXNE_BIT 0
+
+#define SPI_SR_BSY BIT(SPI_SR_BSY_BIT)
+#define SPI_SR_OVR BIT(SPI_SR_OVR_BIT)
+#define SPI_SR_MODF BIT(SPI_SR_MODF_BIT)
+#define SPI_SR_CRCERR BIT(SPI_SR_CRCERR_BIT)
+#define SPI_SR_UDR BIT(SPI_SR_UDR_BIT)
+#define SPI_SR_CHSIDE BIT(SPI_SR_CHSIDE_BIT)
+#define SPI_SR_CHSIDE_LEFT (0x0 << SPI_SR_CHSIDE_BIT)
+#define SPI_SR_CHSIDE_RIGHT (0x1 << SPI_SR_CHSIDE_BIT)
+#define SPI_SR_TXE BIT(SPI_SR_TXE_BIT)
+#define SPI_SR_RXNE BIT(SPI_SR_RXNE_BIT)
+
+/* I2S configuration register */
+
+/* RM0008-ism: CR1 has "CPOL", I2SCFGR has "CKPOL". */
+
+#define SPI_I2SCFGR_I2SMOD_BIT 11
+#define SPI_I2SCFGR_I2SE_BIT 10
+#define SPI_I2SCFGR_PCMSYNC_BIT 7
+#define SPI_I2SCFGR_CKPOL_BIT 3
+#define SPI_I2SCFGR_CHLEN_BIT 0
+
+#define SPI_I2SCFGR_I2SMOD BIT(SPI_I2SCFGR_I2SMOD_BIT)
+#define SPI_I2SCFGR_I2SMOD_SPI (0x0 << SPI_I2SCFGR_I2SMOD_BIT)
+#define SPI_I2SCFGR_I2SMOD_I2S (0x1 << SPI_I2SCFGR_I2SMOD_BIT)
+#define SPI_I2SCFGR_I2SE BIT(SPI_I2SCFGR_I2SE_BIT)
+#define SPI_I2SCFGR_I2SCFG (0x3 << 8)
+#define SPI_I2SCFGR_I2SCFG_SLAVE_TX (0x0 << 8)
+#define SPI_I2SCFGR_I2SCFG_SLAVE_RX (0x1 << 8)
+#define SPI_I2SCFGR_I2SCFG_MASTER_TX (0x2 << 8)
+#define SPI_I2SCFGR_I2SCFG_MASTER_RX (0x3 << 8)
+#define SPI_I2SCFGR_PCMSYNC BIT(SPI_I2SCFGR_PCMSYNC_BIT)
+#define SPI_I2SCFGR_PCMSYNC_SHORT (0x0 << SPI_I2SCFGR_PCMSYNC_BIT)
+#define SPI_I2SCFGR_PCMSYNC_LONG (0x1 << SPI_I2SCFGR_PCMSYNC_BIT)
+#define SPI_I2SCFGR_I2SSTD (0x3 << 4)
+#define SPI_I2SCFGR_I2SSTD_PHILLIPS (0x0 << 4)
+#define SPI_I2SCFGR_I2SSTD_MSB (0x1 << 4)
+#define SPI_I2SCFGR_I2SSTD_LSB (0x2 << 4)
+#define SPI_I2SCFGR_I2SSTD_PCM (0x3 << 4)
+#define SPI_I2SCFGR_CKPOL BIT(SPI_I2SCFGR_CKPOL_BIT)
+#define SPI_I2SCFGR_CKPOL_LOW (0x0 << SPI_I2SCFGR_CKPOL_BIT)
+#define SPI_I2SCFGR_CKPOL_HIGH (0x1 << SPI_I2SCFGR_CKPOL_BIT)
+#define SPI_I2SCFGR_DATLEN (0x3 << 1)
+#define SPI_I2SCFGR_DATLEN_16_BIT (0x0 << 1)
+#define SPI_I2SCFGR_DATLEN_24_BIT (0x1 << 1)
+#define SPI_I2SCFGR_DATLEN_32_BIT (0x2 << 1)
+#define SPI_I2SCFGR_CHLEN BIT(SPI_I2SCFGR_CHLEN_BIT)
+#define SPI_I2SCFGR_CHLEN_16_BIT (0x0 << SPI_I2SCFGR_CHLEN_BIT)
+#define SPI_I2SCFGR_CHLEN_32_BIT (0x1 << SPI_I2SCFGR_CHLEN_BIT)
+
+/*
+ * Devices
+ */
+
+/** SPI device type */
+typedef struct spi_dev {
+ spi_reg_map *regs;
+ rcc_clk_id clk_id;
+ nvic_irq_num irq_num;
+} spi_dev;
+
+/** SPI device 1 */
+extern spi_dev *SPI1;
+/** SPI device 2 */
+extern spi_dev *SPI2;
+#ifdef STM32_HIGH_DENSITY
+/** SPI device 3 */
+extern spi_dev *SPI3;
+#endif
+
+/*
+ * SPI Convenience functions
+ */
+
+void spi_init(spi_dev *dev);
+
+void spi_master_gpio_cfg(gpio_dev *nss_dev,
+ gpio_dev *comm_dev,
+ uint8 nss_bit,
+ uint8 sck_bit,
+ uint8 miso_bit,
+ uint8 mosi_bit);
+
+void spi_slave_gpio_cfg(gpio_dev *nss_dev,
+ gpio_dev *comm_dev,
+ uint8 nss_bit,
+ uint8 sck_bit,
+ uint8 miso_bit,
+ uint8 mosi_bit);
+
+/**
+ * @brief SPI mode configuration.
+ *
+ * Determines a combination of clock polarity (CPOL), which determines
+ * idle state of the clock line, and clock phase (CPHA), which
+ * determines which clock edge triggers data capture.
+ */
+typedef enum {
+ SPI_MODE_0, /**< Clock line idles low (0), data capture on first
+ clock transition. */
+ SPI_MODE_1, /**< Clock line idles low (0), data capture on second
+ clock transition */
+ SPI_MODE_2, /**< Clock line idles high (1), data capture on first
+ clock transition. */
+ SPI_MODE_3 /**< Clock line idles high (1), data capture on
+ second clock transition. */
+} spi_mode;
+
+/**
+ * @brief SPI baud rate configuration, as a divisor of f_PCLK, the
+ * PCLK clock frequency.
+ */
+typedef enum {
+ SPI_BAUD_PCLK_DIV_2 = SPI_CR1_BR_PCLK_DIV_2, /**< f_PCLK/2 */
+ SPI_BAUD_PCLK_DIV_4 = SPI_CR1_BR_PCLK_DIV_4, /**< f_PCLK/4 */
+ SPI_BAUD_PCLK_DIV_8 = SPI_CR1_BR_PCLK_DIV_8, /**< f_PCLK/8 */
+ SPI_BAUD_PCLK_DIV_16 = SPI_CR1_BR_PCLK_DIV_16, /**< f_PCLK/16 */
+ SPI_BAUD_PCLK_DIV_32 = SPI_CR1_BR_PCLK_DIV_32, /**< f_PCLK/32 */
+ SPI_BAUD_PCLK_DIV_64 = SPI_CR1_BR_PCLK_DIV_64, /**< f_PCLK/64 */
+ SPI_BAUD_PCLK_DIV_128 = SPI_CR1_BR_PCLK_DIV_128, /**< f_PCLK/128 */
+ SPI_BAUD_PCLK_DIV_256 = SPI_CR1_BR_PCLK_DIV_256, /**< f_PCLK/256 */
+} spi_baud_rate;
+
+/**
+ * @brief SPI initialization flags.
+ * @see spi_master_enable()
+ * @see spi_slave_enable()
+ */
+typedef enum {
+ SPI_BIDIMODE = SPI_CR1_BIDIMODE, /**< Bidirectional mode enable */
+ SPI_BIDIOE = SPI_CR1_BIDIOE, /**< Output enable in bidirectional
+ mode */
+ SPI_CRCEN = SPI_CR1_CRCEN, /**< Cyclic redundancy check (CRC)
+ enable */
+ SPI_DFF_8_BIT = SPI_CR1_DFF_8_BIT, /**< 8-bit data frame format (this is
+ the default) */
+ SPI_DFF_16_BIT = SPI_CR1_DFF_16_BIT, /**< 16-bit data frame format */
+ SPI_RX_ONLY = SPI_CR1_RXONLY, /**< Receive only */
+ SPI_SW_SLAVE = SPI_CR1_SSM, /**< Software slave management */
+ SPI_SOFT_SS = SPI_CR1_SSI, /**< Software (internal) slave
+ select. This flag only has an
+ effect when used in combination
+ with SPI_SW_SLAVE. */
+ SPI_FRAME_LSB = SPI_CR1_LSBFIRST, /**< LSB-first (little-endian) frame
+ format */
+ SPI_FRAME_MSB = 0, /**< MSB-first (big-endian) frame
+ format (this is the default) */
+} spi_cfg_flag;
+
+void spi_master_enable(spi_dev *dev,
+ spi_baud_rate baud,
+ spi_mode mode,
+ uint32 flags);
+
+void spi_slave_enable(spi_dev *dev,
+ spi_mode mode,
+ uint32 flags);
+
+uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len);
+
+void spi_foreach(void (*fn)(spi_dev (*dev)));
+
+void spi_peripheral_enable(spi_dev *dev);
+void spi_peripheral_disable(spi_dev *dev);
+
+void spi_tx_dma_enable(spi_dev *dev);
+void spi_tx_dma_disable(spi_dev *dev);
+
+void spi_rx_dma_enable(spi_dev *dev);
+void spi_rx_dma_disable(spi_dev *dev);
+
+/**
+ * @brief Determine if a SPI peripheral is enabled.
+ * @parm dev SPI device
+ * @return True, if and only if dev's peripheral is enabled.
+ */
+static inline uint8 spi_is_enabled(spi_dev *dev) {
+ return dev->regs->CR1 & SPI_CR1_SPE_BIT;
+}
+
+/**
+ * @brief Disable all SPI peripherals
+ */
+static inline void spi_peripheral_disable_all(void) {
+ spi_foreach(spi_peripheral_disable);
+}
+
+/** Available SPI interrupts */
+typedef enum {
+ SPI_TXE_INTERRUPT = SPI_CR2_TXEIE, /**< TX buffer empty interrupt */
+ SPI_RXNE_INTERRUPT = SPI_CR2_RXNEIE, /**< RX buffer not empty interrupt */
+ SPI_ERR_INTERRUPT = SPI_CR2_ERRIE /**<
+ * Error interrupt (CRC, overrun,
+ * and mode fault errors for SPI;
+ * underrun, overrun errors for I2S)
+ */
+} spi_interrupt;
+
+/**
+ * @brief Mask for all spi_interrupt values
+ * @see spi_interrupt
+ */
+#define SPI_INTERRUPTS_ALL (SPI_TXE_INTERRUPT | \
+ SPI_RXNE_INTERRUPT | \
+ SPI_ERR_INTERRUPT)
+
+/**
+ * @brief Enable SPI interrupt requests
+ * @param dev SPI device
+ * @param interrupt_flags Bitwise OR of spi_interrupt values to enable
+ * @see spi_interrupt
+ */
+static inline void spi_irq_enable(spi_dev *dev, uint32 interrupt_flags) {
+ dev->regs->CR2 |= interrupt_flags;
+ nvic_irq_enable(dev->irq_num);
+}
+
+/**
+ * @brief Disable SPI interrupt requests
+ * @param dev SPI device
+ * @param interrupt_flags Bitwise OR of spi_interrupt values to disable
+ * @see spi_interrupt
+ */
+static inline void spi_irq_disable(spi_dev *dev, uint32 interrupt_flags) {
+ dev->regs->CR2 &= ~interrupt_flags;
+}
+
+/**
+ * @brief Get the data frame format flags with which a SPI port is
+ * configured.
+ * @param dev SPI device whose data frame format to get.
+ * @return SPI_DFF_8_BIT, if dev has an 8-bit data frame format.
+ * Otherwise, SPI_DFF_16_BIT.
+ */
+static inline spi_cfg_flag spi_dff(spi_dev *dev) {
+ return ((dev->regs->CR1 & SPI_CR1_DFF) == SPI_CR1_DFF_8_BIT ?
+ SPI_DFF_8_BIT :
+ SPI_DFF_16_BIT);
+}
+
+/**
+ * @brief Determine whether the device's peripheral receive (RX)
+ * register is empty.
+ * @param dev SPI device
+ * @return true, iff dev's RX register is empty.
+ */
+static inline uint8 spi_is_rx_nonempty(spi_dev *dev) {
+ return dev->regs->SR & SPI_SR_RXNE;
+}
+
+/**
+ * @brief Retrieve the contents of the device's peripheral receive
+ * (RX) register.
+ *
+ * You may only call this function when the RX register is nonempty.
+ * Calling this function clears the contents of the RX register.
+ *
+ * @param dev SPI device
+ * @return Contents of dev's peripheral RX register
+ * @see spi_is_rx_reg_nonempty()
+ */
+static inline uint16 spi_rx_reg(spi_dev *dev) {
+ return (uint16)dev->regs->DR;
+}
+
+/**
+ * @brief Determine whether the device's peripheral transmit (TX)
+ * register is empty.
+ * @param dev SPI device
+ * @return true, iff dev's TX register is empty.
+ */
+static inline uint8 spi_is_tx_empty(spi_dev *dev) {
+ return dev->regs->SR & SPI_SR_TXE;
+}
+
+/**
+ * @brief Load a value into the device's peripheral transmit (TX) register.
+ *
+ * You may only call this function when the TX register is empty.
+ * Calling this function loads val into the peripheral's TX register.
+ * If the device is properly configured, this will initiate a
+ * transmission, the completion of which will cause the TX register to
+ * be empty again.
+ *
+ * @param dev SPI device
+ * @param val Value to load into the TX register. If the SPI data
+ * frame format is 8 bit, the value must be right-aligned.
+ * @see spi_is_tx_reg_empty()
+ * @see spi_init()
+ * @see spi_master_enable()
+ * @see spi_slave_enable()
+ */
+static inline void spi_tx_reg(spi_dev *dev, uint16 val) {
+ dev->regs->DR = val;
+}
+
+/**
+ * @brief Determine whether the device's peripheral busy (SPI_SR_BSY)
+ * flag is set.
+ * @param dev SPI device
+ * @return true, iff dev's BSY flag is set.
+ */
+static inline uint8 spi_is_busy(spi_dev *dev) {
+ return dev->regs->SR & SPI_SR_BSY;
}
+/*
+ * I2S convenience functions (TODO)
+ */
+
#ifdef __cplusplus
}
#endif
#endif
-