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. */
#define SPI_ERROR_BITS (SPI_SR_OVR | SPI_SR_MODF | \
                        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)
    pin_set(dev->chip_select);
  else
    pin_reset(dev->chip_select);

}

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

  if (dev->chip_select_active)
    pin_reset(dev->chip_select);
  else
    pin_set(dev->chip_select);

}

/**
 * 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
  pin_set(DEBUG_LED);
#endif

  if (sr & SPI_SR_RXNE) {

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

    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;

      ++bus->transfer.tx_buf;
      --bus->transfer.tx_len;
    
}
  
}

  if (sr & SPI_ERROR_BITS) {

#ifdef ERROR_LED
    pin_set(ERROR_LED);
#endif
  
}

#ifdef DEBUG_LED
  pin_reset(DEBUG_LED);
#endif

  if (should_yield == pdTRUE)
    taskYIELD();

}

/*
 * Public Interface
 */

bool spi_init(struct spi_bus *bus)
{

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

#ifdef ERROR_LED
  pin_enable(ERROR_LED);
  pin_reset(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);
#endif

  rcc_enable(bus->rcc_dev);

  /* Configure GPIO pins for SPI operation. */
  pin_enable(bus->miso_pin);
  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_enable(bus->mosi_pin);
  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_enable(bus->sck_pin);
  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;

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

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

Want complete source code? Download it here

Point(s): 1

Download
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

LOGIN

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!
OK

Warm tip!

CodeForge to FavoriteFavorite by Ctrl+D