spi.c ( File view )

  • By gregkam 2014-10-06
  • View(s):4272
  • Download(s):1
  • Point(s): 1
 * spi.c --- STM32F4 SPI driver.
 * Copyright (C) 2012, Galois, Inc.
 * All Rights Reserved.
 * This software is released under the "BSD3" license.  Read the file
 * "LICENSE" for more information.

#include <stdint.h>

#include <stm32f4xx.h>

#include <FreeRTOS.h>
#include <queue.h>
#include <task.h>
#include <semphr.h>

#include "hwf4/interrupt.h"
#include "hwf4/rcc.h"
#include "hwf4/gpio.h"
#include "hwf4/spi.h"

 * Utility macros to make busy waits obvious.  These could be moved to
 * a general utility header.

/** Busy wait until a condition is true. */
#define BUSY_UNTIL(cond)                        \
  do {
    while (!(cond)) {

}                          \
} while (0)

/** Busy wait until a condition is false. */
#define BUSY_WHILE(cond) BUSY_UNTIL(!(cond))

/** Busy wait until a bit in a register is set. */
#define BUSY_UNTIL_SET(reg, bit) BUSY_UNTIL((reg) & (bit))

/** Busy wait until a bit in a register is clear. */
#define BUSY_UNTIL_CLEAR(reg, bit) BUSY_WHILE((reg) & (bit))

/** Debug LED for testing the SPI IRQ. */
// #define DEBUG_LED (pin_d15)

/** Error LED for testing. */
#define ERROR_LED (pin_d14)

/** SPI error bits in the status register. */
                        SPI_SR_CRCERR | SPI_SR_UDR)

 * Information about the current transfer in progress.
 * The "spi_transfer" function fills in these fields, and they are
 * updated by the SPI ISR as characters are transmitted and received.
struct spi_transfer {

  volatile const uint8_t *tx_buf; /* transmit buffer */
  volatile uint8_t *rx_buf;       /* receive buffer */
  volatile size_t tx_len;         /* tx length */
  volatile size_t rx_len;         /* rx length */


struct spi_bus {

  // Peripheral configuration.
  SPI_TypeDef *dev;             /* peripheral registers */
  enum RCCDevice rcc_dev;       /* RCC clock */
  enum IRQn irq;                /* IRQ number */

  // GPIO pin configuration.
  enum pin_af af;               /* GPIO alternate function */
  struct pin *mosi_pin;         /* MOSI GPIO pin */
  struct pin *miso_pin;         /* MISO GPIO pin */
  struct pin *sck_pin;          /* SCK GPIO pin */

  // Current transfer in progress.
  struct spi_transfer transfer;

  // Synchronization.
  xSemaphoreHandle lock;        /* bus mutex */
  xSemaphoreHandle complete;    /* completion signal from ISR */


/** Clear some bits in the SPI CR1 register. */
static inline void spi_cr1_clear(struct spi_bus *bus, uint32_t val)

  bus->dev->CR1 &= ~val;


/** Set some bits in the SPI CR1 register. */
static inline void spi_cr1_set(struct spi_bus *bus, uint32_t val)

  bus->dev->CR1 |= val;


/** Clear some bits in the SPI CR2 register. */
static inline void spi_cr2_clear(struct spi_bus *bus, uint32_t val)

  bus->dev->CR2 &= ~val;


/** Set some bits in the SPI CR2 register. */
static inline void spi_cr2_set(struct spi_bus *bus, uint32_t val)

  bus->dev->CR2 |= val;


/** Configure the baud rate setting for a SPI bus. */
static inline void spi_set_baud(struct spi_bus *bus, enum spi_baud baud)

  spi_cr1_clear(bus, SPI_CR1_BR);
  spi_cr1_set(bus, (uint32_t)baud << 3);


/** Configure the clock polarity for a SPI bus. */
static inline void spi_set_clock_polarity(struct spi_bus *bus,
                                          enum spi_clock_polarity cpol)

  spi_cr1_clear(bus, SPI_CR1_CPOL);
  spi_cr1_set(bus, (uint32_t)cpol << 1);


/** Configure the clock phase for a SPI bus. */
static inline void spi_set_clock_phase(struct spi_bus *bus,
                                       enum spi_clock_phase cpha)

  spi_cr1_clear(bus, SPI_CR1_CPHA);
  spi_cr1_set(bus, (uint32_t)cpha);


/** Configure the bit order for a SPI bus. */
static inline void spi_set_bit_order(struct spi_bus *bus,
                                     enum spi_bit_order val)

  spi_cr1_clear(bus, SPI_CR1_LSBFIRST);
  spi_cr1_set(bus, (uint32_t)val << 7);


 * Enable and configure a SPI bus for a device.
static void spi_enable(struct spi_bus *bus, struct spi_device *dev)

  /* Ensure the peripheral is disabled, then configure it. */
  spi_cr1_clear(bus, SPI_CR1_SPE);

  bus->dev->CR1 = 0;
  bus->dev->CR2 = 0;

  /* The SSM and SSI bits are necessary to prevent the SPI peripheral
   * from disabling SPI if the NSS pin is not high.  Since we are
   * assuming all devices use GPIOs for slave selection, this should
   * be the right thing.  If that changes, we will need to make this
   * configurable. */
  spi_cr1_set(bus, SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI);
  spi_set_baud(bus, dev->baud);
  spi_set_clock_polarity(bus, dev->clock_polarity);
  spi_set_clock_phase(bus, dev->clock_phase);
  spi_set_bit_order(bus, dev->bit_order);
  spi_cr1_set(bus, SPI_CR1_SPE);


 * Disable the SPI peripheral for the given bus.
static void spi_disable(struct spi_bus *bus)

  spi_cr1_clear(bus, SPI_CR1_SPE);


/** Select a device, asserting its chip select pin. */
static inline void spi_device_select(struct spi_device *dev)

  if (dev->chip_select_active)


/** Deselect a device, de-asserting its chip select pin. */
static inline void spi_device_deselect(struct spi_device *dev)

  if (dev->chip_select_active)


 * Handle an IRQ for the given SPI bus.
 * This is actually quite tricky---be very careful making changes to
 * this ISR.
 * It's important to note here that both RXNE and TXE can be set in
 * the status register in the same interrupt.  This can happen when
 * another task disables interrupts during a transfer.
 * To make sure that we never drop any received bytes, we always
 * alternate between TXing and RXing by toggling the TX/RX interrupts
 * as they occur.
 * This makes us slightly less than optimal, as we could load the
 * second byte into the TX register before we've RX'ed the first byte,
 * but the additional complexity doesn't warrant complicating this ISR
 * any further until we need the extra performance.
static void spi_irq(struct spi_bus *bus)

  portBASE_TYPE should_yield = pdFALSE;
  uint32_t sr = bus->dev->SR;

#ifdef DEBUG_LED

  if (sr & SPI_SR_RXNE) {

    *bus->transfer.rx_buf = bus->dev->DR;

    if (bus->transfer.rx_len == 0) {

      spi_cr2_clear(bus, SPI_CR2_RXNEIE);
      xSemaphoreGiveFromISR(bus->complete, &should_yield);
} else {

      spi_cr2_set(bus, SPI_CR2_TXEIE);
} else if (sr & SPI_SR_TXE) {

    spi_cr2_clear(bus, SPI_CR2_TXEIE);

    if (bus->transfer.tx_len != 0) {

      spi_cr2_set(bus, SPI_CR2_RXNEIE);
      bus->dev->DR = *bus->transfer.tx_buf;


  if (sr & SPI_ERROR_BITS) {

#ifdef ERROR_LED

#ifdef DEBUG_LED

  if (should_yield == pdTRUE)


 * Public Interface

bool spi_init(struct spi_bus *bus)

#ifdef DEBUG_LED
  /* Grab a debug LED for testing. */
  pin_set_mode(DEBUG_LED, PIN_MODE_OUTPUT);
  pin_set_otype(DEBUG_LED, PIN_TYPE_PUSHPULL);
  pin_set_ospeed(DEBUG_LED, PIN_SPEED_2MHZ);

#ifdef ERROR_LED
  pin_set_mode(ERROR_LED, PIN_MODE_OUTPUT);
  pin_set_otype(ERROR_LED, PIN_TYPE_PUSHPULL);
  pin_set_ospeed(ERROR_LED, PIN_SPEED_2MHZ);


  /* Configure GPIO pins for SPI operation. */
  pin_set_af(bus->miso_pin, bus->af);
  pin_set_mode(bus->miso_pin, PIN_MODE_AF);
  pin_set_pupd(bus->miso_pin, PIN_PUPD_NONE);

  pin_set_af(bus->mosi_pin, bus->af);
  pin_set_mode(bus->mosi_pin, PIN_MODE_AF);
  pin_set_otype(bus->mosi_pin, PIN_TYPE_PUSHPULL);

  pin_set_af(bus->sck_pin, bus->af);
  pin_set_mode(bus->sck_pin, PIN_MODE_AF);
  pin_set_otype(bus->sck_pin, PIN_TYPE_PUSHPULL);

  bus->lock = xSemaphoreCreateMutex();
  if (bus->lock == NULL)
    goto fail;

  if (bus->complete == NULL)
    goto fail;

  /* Take the semaphore initially so we will block next time. */
(Please download the complete source code to view)
Expand> <Close

Want complete source code? Download it here

Point(s): 1

0 lines left, continue to read
Sponsored links

File list

Tips: You can preview the content of files by clicking file names^_^
Name Size Date
01.97 kB
.gitignore19.00 B2013-04-23|01:09
01.97 kB
.gitignore8.00 B2013-04-23|01:09
build.mk1.82 kB2013-04-23|01:09
01.97 kB
default_handlers.c6.01 kB2013-04-23|01:09
default_hooks.c159.00 B2013-04-23|01:09
syscalls.c3.28 kB2013-04-23|01:09
Makefile327.00 B2013-04-23|01:09
README.md217.00 B2013-04-23|01:09
config.mk.example151.00 B2013-04-23|01:09
01.97 kB
FreeRTOSConfig.h8.59 kB2013-04-23|01:09
arm_common_tables.h1.09 kB2013-04-23|01:09
arm_math.h234.69 kB2013-04-23|01:09
core_cm0.h31.20 kB2013-04-23|01:09
core_cm3.h68.09 kB2013-04-23|01:09
core_cm4.h77.42 kB2013-04-23|01:09
core_cm4_simd.h23.42 kB2013-04-23|01:09
core_cmFunc.h15.32 kB2013-04-23|01:09
core_cmInstr.h15.73 kB2013-04-23|01:09
stm32f4xx.h507.96 kB2013-04-23|01:09
system_stm32f4xx.h2.17 kB2013-04-23|01:09
01.97 kB
01.97 kB
cortex-m4.mk646.00 B2013-04-23|01:09
01.97 kB
stm32f4.mk131.00 B2013-04-23|01:09
command.mk768.00 B2013-04-23|01:09
toolchain.mk150.00 B2013-04-23|01:09
01.97 kB
build.mk227.00 B2013-04-23|01:09
startup_stm32f4xx.s22.85 kB2013-04-23|01:09
system_stm32f4xx.c22.24 kB2013-04-23|01:09
01.97 kB
.gitignore8.00 B2013-04-23|01:09
build.mk1.04 kB2013-04-23|01:09
01.97 kB
01.97 kB
eeprom.h1.97 kB2013-04-23|01:09
gpio.h14.62 kB2013-04-23|01:09
i2c.h1.63 kB2013-04-23|01:09
interrupt.h1.61 kB2013-04-23|01:09
led.h904.00 B2013-04-23|01:09
rcc.h1.65 kB2013-04-23|01:09
spi.h3.46 kB2013-04-23|01:09
01.97 kB
irq.h6.68 kB2013-04-23|01:09
timer.h2.26 kB2013-04-23|01:09
usart.h3.38 kB2013-04-23|01:09
usb_cdc.h1.32 kB2013-04-23|01:09
01.97 kB
eeprom.c2.97 kB2013-04-23|01:09
fault.c1.38 kB2013-04-23|01:09
gpio.c5.12 kB2013-04-23|01:09
i2c.c9.10 kB2013-04-23|01:09
interrupt.c744.00 B2013-04-23|01:09
led.c2.11 kB2013-04-23|01:09
rcc.c11.74 kB2013-04-23|01:09
spi.c12.33 kB2013-04-23|01:09
timer.c10.08 kB2013-04-23|01:09
usart.c10.83 kB2013-04-23|01:09
usb_cdc.c1.21 kB2013-04-23|01:09
01.97 kB
gdb-init61.00 B2013-04-23|01:09
gdb-tdesc-cortexm-fpa.xml5.74 kB2013-04-23|01:09
openocd-stm32f4.cfg124.00 B2013-04-23|01:09
stm32_flash.ld5.33 kB2013-04-23|01:09
01.97 kB
01.97 kB
main.c2.92 kB2013-04-23|01:09
01.97 kB
main.c1.51 kB2013-04-23|01:09
01.97 kB
.gitignore32.00 B2013-04-23|01:09
build.mk1.12 kB2013-04-23|01:09
main.c740.00 B2013-04-23|01:09
01.97 kB
main.c11.32 kB2013-04-23|01:09
01.97 kB
main.c2.51 kB2013-04-23|01:09
01.97 kB
main.c2.14 kB2013-04-23|01:09
01.97 kB
main.c1.03 kB2013-04-23|01:09
01.97 kB
main.c1.49 kB2013-04-23|01:09
01.97 kB
main.c1.90 kB2013-04-23|01:09
01.97 kB
.gitignore8.00 B2013-04-23|01:09
build.mk1.17 kB2013-04-23|01:09
01.97 kB
01.97 kB
usb_bsp.h2.29 kB2013-04-23|01:09
usb_conf.h10.48 kB2013-04-23|01:09
usb_core.h12.29 kB2013-04-23|01:09
usb_dcd.h4.48 kB2013-04-23|01:09
usb_dcd_int.h3.03 kB2013-04-23|01:09
usb_defines.h6.52 kB2013-04-23|01:09
usb_hcd.h2.76 kB2013-04-23|01:09
usb_hcd_int.h3.65 kB2013-04-23|01:09
usb_otg.h1.97 kB2013-04-23|01:09
usb_regs.h21.22 kB2013-04-23|01:09
usbd_cdc_core.h3.87 kB2013-04-23|01:09
usbd_cdc_if_template.h1.85 kB2013-04-23|01:09
usbd_cdc_vcp.h2.89 kB2013-04-23|01:09
usbd_conf.h3.29 kB2013-04-23|01:09
usbd_core.h2.50 kB2013-04-23|01:09
usbd_def.h4.82 kB2013-04-23|01:09
usbd_desc.h3.35 kB2013-04-23|01:09
usbd_ioreq.h2.89 kB2013-04-23|01:09
usbd_req.h2.50 kB2013-04-23|01:09
usbd_usr.h3.00 kB2013-04-23|01:09
01.97 kB
usb_bsp.c3.27 kB2013-04-23|01:09
usb_core.c55.46 kB2013-04-23|01:09
usb_dcd.c9.21 kB2013-04-23|01:09
usb_dcd_int.c21.32 kB2013-04-23|01:09
usb_hcd.c5.62 kB2013-04-23|01:09
usb_hcd_int.c21.58 kB2013-04-23|01:09
usb_otg.c9.48 kB2013-04-23|01:09
usbd_cdc_core.c24.57 kB2013-04-23|01:09
usbd_cdc_if_template.c6.07 kB2013-04-23|01:09
usbd_cdc_vcp.c6.71 kB2013-04-23|01:09
usbd_core.c11.39 kB2013-04-23|01:09
usbd_desc.c8.23 kB2013-04-23|01:09
usbd_ioreq.c5.38 kB2013-04-23|01:09
usbd_req.c19.55 kB2013-04-23|01:09
usbd_usr.c3.48 kB2013-04-23|01:09
Sponsored links

spi.c (278.02 kB)

Need 1 point
Your Point(s)

Your Point isn't enough.

Get point immediately by PayPal

More(Debit card / Credit card / PayPal Credit / Online Banking)

Submit your source codes. Get more point


Don't have an account? Register now
Need any help?
Mail to: support@codeforge.com


CodeForge Chinese Version
CodeForge English Version

Where are you going?

^_^"Oops ...

Sorry!This guy is mysterious, its blog hasn't been opened, try another, please!

Warm tip!

CodeForge to FavoriteFavorite by Ctrl+D